2012-01-23 8 views
6

¿Hay alguna forma de agregar dinámicamente after_add y after_remove devoluciones de llamada a una relación existente has_many o has_and_belongs_to_many?¿Cree dinámicamente after_add y after_remove callbacks para has_many o habtm?

Por ejemplo, supongamos que tengo modelos User, Thing, y un modelo UserThingRelationship unirse, y el modelo User es algo como esto:

class User < ActiveRecord::Base 
    has_many :user_thing_relationships 
    has_many :things, :through => :user_thing_relationships 
end 

Me gustaría ser capaz de, en un módulo que extiende User, agrega :after_add y :after_remove devoluciones de llamada a la relación User.has_many(:things, ...). Es decir, tener algo como

module DoesAwesomeStuff 
    def does_awesome_stuff relationship, callback 
    # or however this can be achieved... 
    after_add(relationship) callback 
    after_remove(relationship) callback 
    end 
end 

Para que

class User < ActiveRecord::Base 
    has_many :user_thing_relationships 
    has_many :things, :through => :user_thing_relationships 

    does_awesome_stuff :things, :my_callback 
    def my_callback; puts "awesome"; end 
end 

es efectivamente el mismo que

class User < ActiveRecord::Base 
    has_many :user_thing_relationships 
    has_many :things, :through => :user_thing_relationships, :after_add => :my_callback, :after_remove => :my_callback 

    def my_callback; puts "awesome"; end 
end 

Esto se puede hacer de manera muy efectiva para añadir after_save, etc, devoluciones de llamada al modelo que es siendo extendido, ya que ActiveRecord::Base#after_save es solo un método de clase.

Respuesta

5

pude llegar a la siguiente mediante el uso de ActiveRecord::Reflection:

module AfterAdd 
    def after_add rel, callback 
    a = reflect_on_association(rel) 
    send(a.macro, rel, a.options.merge(:after_add => callback)) 
    end 
end 

class User < ActiveRecord::Base 
    extend AfterAdd 

    has_many :user_thing_relationships 
    has_many :things, :through => :user_thing_relationships 

    after_add :things, :my_callback 

    def my_callback 
    puts "Hello" 
    end 
end 

no quiero responder a mi propia pregunta, por lo que no voy a entregarme a responder a crédito si otra persona puede llegar con una mejor solución en los próximos días.

+0

Lo siento por enterrar esto, pero que yo sepa, su devolución de llamada se escapará, ¿no? Es decir. si se ejecutaran dos hilos after_add al mismo tiempo, ¿no sabría qué devolución de llamada es válida? – nambrot

10

Lo más fácil sería

User.after_add_for_things << lambda do |user, thing| 
    Rails.logger.info "#{thing} added to #{user}" 
end 
+0

tengo curiosidad acerca de cómo se pudo encontrar acerca de esta característica dinámica – davidtingsu

+0

Al leer la fuente Rails! – glebm

+0

Esta función se encuentra en 'define_callback' en' lib/active_record/associations/builder/collection_association.rb'. (https://github.com/rails/rails/blob/master/activerecord/lib/active_record/associations/builder/collection_association.rb) –

Cuestiones relacionadas