DRY, herencia, attachment_fu y asoc iaciones polimórficas

He seguido la receta #19 del Advanced Rails Recipes, donde en un ejemplo
de
aplicación de Albums y Covers, se modifica el modelo Album para que
tenga un
método save_with_cover y así pueda salvarse subiendo la foto del cover
con
attachment_fu. OK, todo estupendo…

Pero en mi aplicación real, tengo varias cosas que pueden tener una
foto.
Para eso tiro de la receta #23 del Rails Recipes, de asociaciones
polimórficas, para tener por ejemplo Productos y Disenadores, que ambos
son
fotografiables…

##########################
class Foto < ActiveRecord::Base
belongs_to :fotografiable, :polymorphic => true
end

class Producto < ActiveRecord::Base
has_many :fotos, :as => :fotografiable
end

class Disenador < ActiveRecord::Base
has_one :foto, :as => :fotografiable
end
##########################
etc… ¿me seguís?

La cosa es que el método save_con_foto lo necesito en cada modelo
fotografiable, con lo que estoy duplicando código… el mismo método
save_con_foto me vale para Producto, Disenador, y todo lo fotografiable:

##########################
def save_con_foto
foto = Foto.new
begin
self.transaction do
if uploaded_foto_data && uploaded_foto_data.size > 0
foto.uploaded_data = uploaded_foto_data
foto.thumbnails.clear
foto.save!
self.foto = foto
end
save!
end
rescue
if foto.errors.on(:size)
errors.add_to_base(“La imagen subida es demasiado grande.” )
end
if foto.errors.on(:content_type)
errors.add_to_base(“El tipo de imagen no es válido.” )
end
false
end
end
##########################

Mi pregunta es… ¿cómo puedo hacer para que este metodo save_con_foto
esté
en todos mis modelos fotografiables? No estoy muy puesto en herencia. Mi
primer intento ha sido tener un modelo Fotografiable:

##########################
class Fotografiable < ActiveRecord::Base
def save_con_foto

end
end
##########################

y heredar los fotografiables de él:

##########################
class Producto < Fotografiable

end

class Disenador < Fotografiable

end
##########################

pero no es buena idea, pq Fotografiable no tiene su tabla
correspondiente…

¿La solución pasa por usar single-table inheritance? No me vale, creo
que
quedaría muy guarro todo en una sola tabla, hay muchos modelos
fotografiables.

Jaime I.
escribió:> ¿La solución pasa por usar single-table inheritance? No me vale, creo

que quedaría muy guarro todo en una sola tabla, hay muchos modelos
fotografiables.

A priori yo usaría un mixin (metes la funcionalidad compartida en un
módulo y luego lo incluyes en tus clases).

Más info:


Raul M. - Freelance Web D.
http://raul.murciano.net

Buenas,

Mi pregunta es… ¿cómo puedo hacer para que este metodo save_con_foto
esté en todos mis modelos fotografiables? No estoy muy puesto en
herencia. Mi primer intento ha sido tener un modelo Fotografiable:

pero no es buena idea, pq Fotografiable no tiene su tabla
correspondiente…

tienes varias opciones. Una, como te decía Raúl, es usar mix-ins. Otra
es hacerte un plug-in de acts_as_fotografiable, que al fin y al cabo
acabas haciendo un mix-in pero con el método acts_as… en lugar de usar
el include.

En cuanto a usar herencia, no estoy del todo seguro de que agrupando a
nivel lógico realmente tengas la clase fotografiable y que producto,
disenador y foto sean subclases suyas. Digamos que a nivel semántico no
me parece que encaje demasiado bien. No veo que sean subclases, sino que
más bien son subconjuntos de funcionalidades que esas clases tienen en
común. Eso en java es lo que se llamaría un interface y aquí en Ruby lo
suyo es tirar de un módulo y un mix-in. PERO…

Pero… si realmente fueran subclases, podrías sin problema hacer lo que
dices de crearte un modelo Fotografiable y hacer que los otros extiendan
de él. ActiveRecord tiene en cuenta esto y, ya que Ruby no soporta
clases abstractas, te da una funcionalidad tal que si en cualquier
modelo ActiveRecord le dices self.abstract_class = true (o si le
defines un método abstract_class? que devuelva true), esa clase se va a
tratar como si fuera abstracta, es decir que no se va a intentar buscar
una tabla asociada a ella y te va a permitir ponerle callbacks en la
clase padre y que se ejecuten también cuando lo uses desde las hijas.
Además, si una clase de AR extiende de una clase padre que es abstract,
AR lo tiene en cuenta y usa el nombre correcto para la tabla hija.

De paso, si tienes instalado el plugin RedHillOnRailsCode (es necesario
si usas el de ForeignKeyMigrations, que es un plugin muy recomendable)
hace “magia” propia y automáticamente cualquier modelo que tenga por
nombre AbstractXXXXX te lo pone automáticamente como abstract.

En tu caso, funcionaría perfectamente con una “clase abstracta”, pero a
nivel semántico creo que en este caso aplica mejor el concepto de
interface/mix-in

saludos,

javier ramírez

Gracias Raúl y Javier, me habéis iluminado…

Voy a tirar por el tema de mixins, y me apunto esos dos plugins que
dices
para el tema de clases abstractas.

Voy a tirar por el tema de mixins, y me apunto esos dos plugins que
dices para el tema de clases abstractas.
creo que me expliqué mal con lo del plug-in :slight_smile:

el plug-in es para hacer cosas con la base de datos. En concreto yo uso
el plugin de foreign keys de redhillonrails en todos los proyectos. De
esa forma, cuando en una migration aparece un campo XXX_id te crea
automáticamente una foreign_key en la base de datos, que nunca viene
mal. Como efecto colateral, ese plug-in añade unas cuantas cosas sobre
migrations y activerecord, y una de las que añade es el hacer que tus
clases sean abstractas automáticamente si el nombre empieza por abstract.

saludos,

j