Helper or model or controller?
Let's say I have a form_for with a select menu to assign a user to a belongs_to association:
...
form.select :user_id, @users, :prompt => "Select a User"
...
I currently have @users in my controller like this:
@users = User.all.map { |u| [u.full_name, u.id] }
I feel like this logic could be carried over to a helper or even a model. But I am confused as to where one can handle this and how.
source to share
The general answer depends on how often you intend to use it:
- helper: Used frequently, but only in views or controllers.
- model: used often wherever a model can be used (other models) Controller
- : used rarely and only for certain actions.
However, in your case, the answer will not be one of the above and stop trying to reinvent the wheel. About 95% of the things people try to do with Rails are tasks that others have already done. There's a very good chance it either exists in Rails Core or exists in gem form or a plugin.
What you are trying to do is already done and built into the Rails core. This is the ActionView :: Helpers :: FormOpitionsHelper method called collection_select
collection_select does exactly what you want to do, it is also much more robust than the single-target method.
It has the form
collection_select(object, method, collection, value_method,
text_method, select_options = {}, html_options)
value_method and text_method are sent to each item in the collection to get the select value and display text for each select parameter. It is not required to have either column names.
Use it like this:
<% form_for @whatever do |form| %>
<%= form.collection_select :user_id, User.all, :id,
:full_name, :prompt => "Select a User" %>
<% end %>
source to share
You should put this in the model as it is logic oriented and by the way you should never do
@users = User.all.map { |u| [u.full_name, u.id] }
but
@users = User.all(:select => "full_name, id")
and if full_name is a method, something like this:
@users = User.all(:select => "last_name, first_name, id").map{|u| [User.full_name(u.first_name, u.last_name), u.id]}
source to share
I had a similar problem and ended up using the module to stay as dry as possible (most of my models had a name and ID)
The module looked like this:
#lib/all_for_select.rb
module AllForSelect
def all_for_select(permission = :read)
#used declarative authorization for checking permissions
#replace first line with self.find(:all, if not using it
with_permissions_to(permission).find( :all,
:select =>"#{table_name}.id, #{table_name}.name",
:order => "#{table_name}.name ASC"
)
end
end
On your model, you simply extend the module:
class Client < ActiveRecord::Base
extend AllForSelect
...
end
On the controller, you can call Client.all_for_select. I usually do this on the before_filter file
class SalesController < ApplicationController
before_filter :fill_selects, :only => [:new, :edit, :update, :create]
...
private
def fill_selects
@clients = Client.all_for_select
end
source to share