rails example create callbacks before_update before association after_create after active ruby-on-rails ruby callback

ruby on rails - example - Ruby method_added callback not trigger incluyendo Módulos



rails after action (3)

Quería escribir un poco de lib de "Deprecate" y usé la devolución de llamada "method_added" mucho. Pero ahora noté que esta devolución de llamada no se desencadena, cuando se incluye un módulo.

¿Hay devoluciones de llamada o soluciones alternativas para informar a la clase "Foobar" cuando se incluye algo?

Pequeña demostración para demostrar:

# Including Moduls won''t trigger method_added callback module InvisibleMethod def invisible "You won''t get a callback from me" end end class Foobar def self.method_added(m) puts "InstanceMethod: ''#{m}'' added to ''#{self}''" end def visible "You will get a callback from me" end include InvisibleMethod end [:invisible, :visible, :wont_exist].each do |meth| puts "#{meth}: #{Foobar.public_method_defined? meth}" end

Ese es el resultado:

InstanceMethod: ''visible'' added to ''Foobar'' invisible: true visible: true wont_exist: false

Información Adicional:

Realmente necesito usar un gancho como method_added.

ActiveModel está agregando métodos_instancia_pública a la clase durante el tiempo de ejecución mediante módulos anónimos.


Creo que deprecation no es una gran idea para requerir una biblioteca. se implementa así en datamapper . Acerca de hook method_added ; está funcionando como se esperaba porque los métodos ya se han agregado al module no a la class . Solo usted puede obtener su resultado esperado Parche de monos included gancho.

# got from https://github.com/datamapper/dm-core/blob/master/lib/dm-core/support/deprecate.rb module Deprecate def deprecate(old_method, new_method) class_eval <<-RUBY, __FILE__, __LINE__ + 1 def #{old_method}(*args, &block) warn "/#{self.class}##{old_method} is deprecated, use /#{self.class}##{new_method} instead (/#{caller.first})" send(#{new_method.inspect}, *args, &block) end RUBY end end # module Deprecate class MyClass extend Deprecate def old_method p "I am old" end deprecate :old_method, :new_method def new_method p "I am new" end end m = MyClass.new m.old_method # MyClass#old_method is deprecated, use MyClass#new_method instead (pinger.rb:27:in `<main>'') # "I am new"


Tal como lo sugiere uno de los comentarios, puede usar algún otro enlace para obtener el comportamiento que desea. Por ejemplo, intente agregar esto al comienzo de su código:

class Module def included(klass) if klass.respond_to?(:method_added) self.instance_methods.each do |method| klass.method_added(method) end end end end

Cada vez que se incluye un módulo en una clase, todos los métodos de instancia de ese módulo se notificarán a la clase, siempre que defina el método method_added . Al ejecutar su código con el cambio anterior obtengo este resultado:

InstanceMethod: ''visible'' added to ''Foobar'' InstanceMethod: ''invisible'' added to ''Foobar'' invisible: true visible: true wont_exist: false

Lo cual creo que es el comportamiento que quieres.


El problema es que incluir módulos no agrega métodos a las clases, solo cambia la cadena de llamadas del método. Esta cadena define qué clases / módulo se buscará para un método, que no está definido para la clase en cuestión. Lo que sucede cuando se incluye un módulo es una adición de una entrada en esa cadena.

Esto es exactamente lo mismo que cuando agrega un método en una superclase; esto no llama a method_added ya que no está definido en la superclase. Sería muy extraño si una subclase pudiera cambiar el comportamiento de una superclase.

Podrías solucionarlo mediante el método de llamada manual agregado para un módulo incluido, al redefinir include para tu clase:

class Foobar def self.include(included_module) included_module.instance_methods.each{|m| self.method_added(m)} super end end

Y es mucho más seguro que redefinir el método included en el Module : el cambio se reduce solo a las clases que usted mismo definió.