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.
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.
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.
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:
2
3
class Location < ActiveRecord::Base
belongs_to :owner
end
And in the view:
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:
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:
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.