Reputation: 28803
In my Rails app when creating a new Article I need a list of Users
and Categories
for some drop-downs so that I can choose both a category and an author for the Article.
Currently I do this in the controller:
def new
@article = Article.new
@categories = Category.order('title asc').collect { |t| [t.title, t.id] }
@users = User.order('email asc').collect { |t| [t.email, t.id] }
end
And then in the view:
<%= f.select :category_id, options_for_select(@categories), :prompt => '-- Please Select --', :required => true %>
But according to RubyDocs this is bad practice, and it's not very DRY as I then have to do this for the edit method too. To prevent this, I have two possible alternatives I can think of:
1.) Use a helper like this:
def users_for_select
User.order('email asc').collect { |t| [t.email, t.id] }
end
def categories_for_select
Category.order('title asc').collect { |t| [t.title, t.id] }
end
And then in the view:
<%= f.select :category_id, options_for_select(categories_for_select), :prompt => '-- Please Select --', :required => true %>
2.) Move it to a Model:
def self.categories_for_select
Category.order('title asc').collect { |t| [t.title, t.id] }
end
def self.users_for_select
User.order('email asc').collect { |t| [t.email, t.id] }
end
And then in the controller do this:
def new
@article = Article.new
@categories = Category.categories_for_select
@users = User.users_for_select
end
Option 1 feels cleaner as it removes the code from the controller, but I was under the impression that option 2 would be better as it uses a Model for data (as intended) and the controller is still sending the data (again as intended) but more DRY.
I feel their is a sometimes some overlap between Helpers and Models for getting data.
Upvotes: 1
Views: 567
Reputation: 881
I'll personally go with Option 1.
Sure, you could put that in your model. You'll find sooner than later that that would be a great way to bloat up the model. Then you might thinking about using concerns to hide the bloat. And the messy trend continues.
Here's why I think option 1 is better. Even though you aren't creating a separate class to handle formatting, you're still abstracting functionality into a smaller segment which is easier to scale. Plus of course, composition beats inheritance.
This awesome post by Bryany gives great options on refractoring fat models.
As @damien already pointed out in his answers you'll want to use ActiveRecord's pluck
instead of ruby's collect
. Pluck
queries the db so that it returns only the objects you need.
Upvotes: 1
Reputation: 106882
I would use a helper method for this:
# in a helper
def category_options_for_select
options_for_select(Category.order(:title).pluck(:title, :id))
end
# in the view
<%= f.select :category_id, category_options_for_select, prompt: '-- Please Select --', required: true %>
Upvotes: 1
Reputation:
I would go with (1) your helper method for now. It's simple and straightforward. As I said in the comments, you could use a decorator around your model (using draper for e.g.) to add what I'd consider quite view-specific logic if you are tempted towards option (2).
One note on your helper methods - use pluck
instead of collect
so you don't select columns or instantiate a bunch of objects you don't need.
Also, order
defaults to asc
, so you can shorten the whole thing to:
def users_for_select
User.order(:email).pluck(:email, :id)
end
Upvotes: 2