Rails Best Practices 4: Scrivere i metodi nel model corretto

Nel post di oggi voglio mostrare alcune ottimizzazioni che si possono fare per i model. In particolare la definizione dei metodi nel model corretto e l’uso del costrutto delegate per ottenere un codice più pulito.

1. Definire i metodi nel model corretto

Nel nostro esempio supponiamo di voler rappresentare il mondo animale definendo un model Kind che rappresenta tipi di animale ed un model Animal che rappresenta gli animali.
Per ogni tipo (quadrupede, bipede, volatile) esistono diversi animali da cui la relazione has_many :animals definita nel codice seguente.

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

Successivamente, nell’AnimalsController definiamo un metodo herbivores che, data una tipologia di animali trovi tutti gli erbivori contenuti in quella tipologia.

1
2
3
4
5
6

class AnimalsController < ApplicationController
def herbivores
@kind = Kind.find(params[ :id])
@animals = @kind.find_herbivores
end
end

Il difetto in questo codice è quello di aver definito il metodo find_herbivores nel model Kind quando invece fa riferimento ad una proprietà dell’oggetto Animal.

Vediamo dunque come riscriverlo in modo più corretto, utilizzando una named_scope nel model Animal.

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 questo secondo esempio supponiamo di avere una classe Location associata ad un proprietario e di voler mostrare nella view della Location anche i dati del proprietario.
Una prima semplice implementazione è la seguente

1
2
3

class Location < ActiveRecord::Base
belongs_to :owner
end

E nella view:

1
2
3
4

<%= @location.owner.name %>
<%= @location.owner.city %>
<%= @location.owner.street %>
<%= @location.owner.email %>

Un’implementazione di questo tipo è molto comune nelle applicazioni rails, ma la si può rendere più elegante utilizzando il cotrutto delegate nel seguente modo:

1
2
3
4
5

class Location < ActiveRecord::Base
belongs_to :owner
delegate :name, :city :street, :email :to => :owner,
:prefix => true
end

A questo punto possiamo riscrivere la view nel modo seguente:

1
2
3
4

<%= @location.owner_name %>
<%= @location.owner_city %>
<%= @location.owner_street %>
<%= @location.owner_email %>

Il risultato non cambia, ma sicuramente il codice risulta più elegante.