Metaprogramación: métodos y delegates

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.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos necesarios están marcados *

Puedes usar las siguientes etiquetas y atributos HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>