I've been noticing that some of our newer Rails developers are having trouble picking which layer (Model, View or Controller) a method belongs in. Here are some heuristics I've picked up along the way - in no particular order.
1 - Don't put it into a library unless it really has nothing to do with the rest of your site (or you want to package it up to hand to other people).
I've recently noticed a few methods being added to a library I wrote that like this: my_method(@my_object) This is a prime candidate for going into the MyObject model itself. Especially when the body of said method just accesses some related objects eg:
# instead of: module MyLibrary def my_method(my_obj) my_obj.sub_classes.map {|sc| [sc.name, sc.id] } end end # put it on the model class MyModel < ActiveRecord::Base has_many :sub_classes def my_method return [] if sub_classes.blank? sub_classes.map {|sc| [sc.name, sc.id] } end end
2 - A model should know how to deal with itself
I'd like to say that everything that can go into a model should... but that's not quite true. Anything data-related, however, is a prime candidate. A model should know how to deal with it's own data. It should know how to calcuate and derive from its own data. It should know how to pull stuff out of related objects, and to parse other objects into a form that allows it to stuff data into itself.
I've seen too many of these sorts of calculations going into views or helpers, or getting stuck in the middle of controller-code. These will only come back to bite you later, when the client suddenly realises they want the report to also be generated into pdf or graphical form as well...
If it can go on the model, put it there, because you'll always have access to the methods on your model.
3 - Don't put html into your model
By all means put display-names and other, similar methods that produce displayable information... but don't put html into it. After all, right now you may be concentrating on web-only delivery of your data... but who's to say the client won't be asking for xml next week... and pdf the week after that... followed by CSV?
Make your model-based display methods text-only eg:
class MyModel < ActiveRecord::Base def good_display_name name || "Unspecified" end def bad_display_name "<div class=\"left_aligned_red_box\">#{good_display_name}</div>" end end
4 - Put format-specific display functions in the helpers
If you're displaying a selection of a certain class of objects (eg Dates) in a specific format in multiple places across the site, and want consistency of appearance (a Good Thing), feel free to add a display helper to the ApplicationHelper file, or even update the generic display function for that class in your environment.rb
# eg add these to application helper def display_percentage(num) return "" unless num "#{number_with_precision(num,2)}%" end def display_date(date) return "" unless date date.strftime("%a %d/%m/%Y") end def display_datetime(datetime) return "" unless datetime "#{display_date(datetime)} #{datetime.strftime("%H:%M")}" end
Note that you can over-ride the default datetime display in rails using the following (in environment.rb or similar) then use "to_s" or "to_s(:datetime)" in your views.
ActiveSupport::CoreExtensions::Time::Conversions::DATE_FORMATS.merge!( :default => "%a %d/%m/%Y", :datetime => "%a %d/%m/%Y %H:%M" )