ActiveRecord: delegación de asociaciones
Visita este artículo en http://www.estadobeta.com/2007/06/25/activerecord-delegacion-de-asociaciones/
Por Ismael en Desarrollo, Documentación, Ruby & Rails, artículos, tipsGracias a la magia de ActiveRecord, en Rails es fácil definir las asociaciones entre clases:
-
class Blog < ActiveRecord::Base
-
has_many :posts, :order => ‘published_on DESC’
-
end
Esto nos permite bellezas como
-
mi_blog = Blog.find(:first)
-
articulos = mi_blog.posts
Pero supongamos que queremos en Blog un método que nos retorne sólo los 10 artículos más recientes Una solución es definir otra asociación para la misma clase:
-
class Blog < ActiveRecord::Base
-
has_many :posts, :order => ‘published_on DESC’
-
has_many :recent_posts, :class_name => ‘Post’, :order => ‘published_on DESC’, :limit => 10
-
end
De esta forma podemos disponer de estos 10 artículos fácilmente:
-
recientes = mi_blog.recent_posts
Pero hay un acercamiento más poderoso que me ha resultado muy útil en el último tiempo: la Delegación de Asociaciones (”Association Proxies”).
-
class Blog < ActiveRecord::Base
-
has_many( :posts, :order=>’published_on’ ) do
-
def recent(limit = 10)
-
find :all, :limit => limit
-
end
-
end
-
end
Así es. Las asociaciones de Activerecord aceptan un bloque de Ruby como argumento opcional. Si en ese bloque definimos métodos, éstos son agregados como métodos de instancia a la colección de objetos asociados. El ejemplo se usa así:
-
todos = mi_blog.posts
-
10_recientes = mi_blog.posts.recent #retorna 10 posts por defecto
-
5_recientes = mi_blog.posts.recent(5)
Los métodos definidos en el bloque aceptan los mismos parámetros que ActiveRecord::Base#find, pero las condiciones serán aplicadas sólo a los objetos definidos en la asociación ( en este caso, Post). Por ejemplo, si quisieramos distinguir entre artículos publicados y no publicados:
-
class Blog < ActiveRecord::Base
-
has_many( :posts, :order=>’published_on’ ) do
-
def published
-
find :all, :conditions => "posts.is_published = 1"
-
end
-
def unpublished
-
find :all, :conditions => "posts.is_published = 0"
-
end
-
end
-
end
Todo muy bonito, pero hay un problema. Cuando usamos las asociaciones de modo normal, ActiveRecord tiene la delicadeza de cachear los resultados; es decir, la primera vez que invoquemos a mi_blog.posts, ActiveRecord generará el SQL necesario, lo ejecutará en la base de datos y guardará los resultados en un atributo. La segunda vez que hagamos el mismo llamado, ActiveRecord nos entregará los resultados almacenados en lugar de ir de nuevo a la base de datos. Nada de esto funciona cuando definimos métodos extra en las asociaciones.
La solución es implementar nosotros mismos un simple cache usando atributos de instancia en Ruby.
-
class Blog < ActiveRecord::Base
-
has_many( :posts, :order=>’published_on’ ) do
-
def published
-
@publicados ||= find( :all, :conditions => "posts.is_published = 1" )
-
end
-
def unpublished
-
@no_publicados ||= find( :all, :conditions => "posts.is_published = 0" )
-
end
-
end
-
end
- Servicios:
- Comentarios RSS
- Menear!
- Del.icio.us

6/26/2007 at 10:18 pm
Muy buen artículo. Este ruby (o este Rails?) es una caja de sorpresas. Eso del bloque me dejo plop!. Como cuando estas llamando un metodo puedas asignarle a ese misma “cuestion” mas métodos?? Crazy!. Todavía no puedo abrir mi mente xD.