En el proyecto que estoy trabajando ahora mismo necesitamos diferenciar en vista ciertos elementos en función del tipo al que pertenecen. Sin embargo, el tipo no está asociado directamente a ellos. Para entendernos:
class Kind < ActiveRecord::Base has_many :items end class Item < ActiveRecord::Base belongs_to :kind end class ItemData < ActiveRecord::Base belongs_to :item end
Un tipo consta de nombre y label. Para saber si un Item es de un tipo concreto podemos solucionarlo facilmente con un poco de metaprogramación básica:
if ActiveRecord::Base.connection.tables.include?('kinds')
Kind.all.map(&:label).each do |label|
define_method "#{label}?" do
self.kind.label == label
end
end
end
Ahora tenemos métodos como por ejemplo item.a?, item.b?, etc… Sin embargo, por manos del diablo, en vista no trabajamos directamente con un objeto de tipo Item sino con objetos de tipo ItemData. Si todo estuviera cerradito podríamos hacer cosas como:
class ItemData < ActiveRecord::Base belongs_to :item delegate :a?, :to => :item end
Pero no es nuestro caso, no está cerrado. ¿Cómo lo hacemos? Le echamos un ojo a cómo está hecho el método delegate y tras poner aquí y quitar allá, nos queda algo como esto:
class ItemData < ActiveRecord::Base
belongs_to :item
if ActiveRecord::Base.connection.tables.include?('kinds')
Kind.all.map(&:label).each do |label|
method = label + '?'
module_eval(<<-EOS, "(__DELEGATION__)", 1)
def #{method}
notice.__send__(#{method.inspect})
end
EOS
end
end
end
Y ya está. Ahora podemos hacer cosas como @item_data.a? y si el día de mañana se añaden tipos, tendremos su método delegate correspondiente.
Actualización: incluyo un condicional para saber si está disponible o no la tabla Kind, ya que de otro modo, rake ni siquiera nos deja correr migraciones para que exista.
Etiquetas: delegate, metaprogramación