Rails Best Practices 4: Put method in the right model
11:35 AMDevelopment, RubyClaudio
In today’s post I will show some optimization we can do for models. I’ll focus on how to put methods inside the right model and delegation to get a better code.
1. Put method in the right model
In our example, suppose we want to represent the animal world by creating a model Kind that represents types of animal and an Animal models representing animals.
For each type (quadrupedal, bipedal, bird) there are different animals that justify the has_many: animals relation defined in the following code.
1 2 3 4 5 6 7 8 9 10 11 12 13 | class Kind < ActiveRecord::Base has_many :animals def find_herbivores self.animal.find(:all, :conditions => { :eat_herb => true }) end end class Animal < ActiveRecord::Base belongs_to :kind end |
Next, we define a method “herbivores” within the AnimalsController that, given a type of animal return all the herbivorous contained in that type.
1 2 3 4 5 6 | class AnimalsController < ApplicationController def herbivores @kind = Kind.find(params[ :id]) @animals = @kind.find_herbivores end end |
The flaw in this code is to have defined the method find_herbivores inside the Kind model when in fact refers to a property of the Animal class.
Let’s see how to rewrite it in proper way, using a named_scope in the Animal model.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | class Kind < ActiveRecord::Base has_many :animals end class Animal < ActiveRecord::Base belongs_to :kind named_scope :herbivores, :conditions => { :eat_herb => true } end class AnimalsController < ApplicationController def herbivores @kind = Kind.find(params[ :id]) @animals = @kind.animals.herbivores end end |
2. Delegate
In this second example, suppose we have a Location class associated with an owner and want to show owner’s data in the Location’s view.
A first simple implementation is the following:
1 2 3 | class Location < ActiveRecord::Base belongs_to :owner end |
And in the view:
1 2 3 4 | <%= @location.owner.name %> <%= @location.owner.city %> <%= @location.owner.street %> <%= @location.owner.email %> |
This type of implementation is very common in Rails applications, but you can make it more elegant by using the construct “delegate” as follows:
1 2 3 4 5 | class Location < ActiveRecord::Base belongs_to :owner delegate :name, :city :street, :email :to => :owner, :prefix => true end |
Now we can rewrite the view as follows:
1 2 3 4 | <%= @location.owner_name %> <%= @location.owner_city %> <%= @location.owner_street %> <%= @location.owner_email %> |
The result does not change, but the code is certainly more elegant.
Tags: Best Practices, delegate, named_scope, ruby on rails

















[...] This post was mentioned on Twitter by William Yánez, DevInterface. DevInterface said: "Rails Best Practices 4: Put method in the right modelRails Best Practices 4: Scrivere i metodi nel model corretto" – http://bit.ly/cLv2UF [...]
Great article – one small error in the sample code:
@animals = @kind.herbivores
you may want to add .animals. into that example.
NoMethodError: undefined method `herbivores’ for #
@Jayro: Thank you Jayro, I’ve fixed the example code.
I’m not a big fan of your usage of delegate. It isn’t obvious to me how replacing access to an associated object’s methods with an underscore make it more elegant than accessing the associated object and its method directly. It’s not only unnecessary, but it’s more work than you needed to do.
I am with James here: I don’t see the point in using delegate in this way. Maybe you can explain your motivation in more depth?
+1 for James’ comment. To me, the underscore looks less elegant for some reason (especially framed by the extra code you have to add to use the feature in the first place).
On delegate: leave off :prefix and you are left with:
Which some may find more elegant.
Oops, imagine the irb tags:
@location.owner_name
@location.owner_city
@location.owner_street
@location.owner_email
Tags got swallowed. Each line would look something like: @location.city
And as long as we’re delegating:
class Kind :animals
end
class AnimalsController < ApplicationController
def herbivores
@kind = Kind.find(params[ :id])
@animals = @kind.herbivores
end
end
Again with the tags. My example tried to show you can delegate :herbivores to :animals in the Kind class.
Hi guys!
Thank you all for suggestions.
About delegation: yes it probably is not really a best practices rather a good possibility that ruby offers to us.
However, I like to show an example of it because I think it is a construct that can, sometimes, be useful.
If you’re interested in learning the subject there a good discussion here: http://haacked.com/archive/2009/07/14/law-of-demeter-dot-counting.aspx
Although I like the concept of delegate, I think it should be restricted to a scope, where you can assure to include the delegating class. Otherwise you can easily spawn your DB requests.
Thanks for the post.
I’m also not a fan of the delegate method shown. It requires extra coding without any actual benefit. Secondly, someone else looking at your code has to understand that owner_name is indeed owner.name and not just some other attribute belonging to Location. Of course they can check the schema or the models, but I prefer code that’s self-explanatory, which @location.owner.name is.
i’d love to share this posting with the readers on my site. thanks for sharing!