2011-08-25 12 views
5

¿Es posible hacer esto funcionar, sin tener que incluir el módulo al final de la clase y simplemente incluirlo en la parte superior?¿Módulo para envolver un método de clase?

module VerboseJob 
    def self.included(job_class) 
    class << job_class 
     alias_method :original_perform, :perform 
     def perform(*args) 
     JobLogger.verbose { original_perform(*args) } 
     end 
    end 
    end 
end 

class HelloJob 
    include VerboseJob 

    def self.perform(arg1, arg2) 
    puts "Job invoked with #{arg1} and #{arg2}" 
    end 
end 

Lo que quiero que suceda, es para HelloJob.perform a realidad invocan VerboseJob.perform (que a su vez llama al método original dentro de un bloque). Debido a que el módulo aquí se incluye en la parte superior de la clase, esto no funciona, ya que perform aún no está definido. Mover el include hasta el final funciona, pero ¿hay alguna manera de que sea un poco más indulgente? Me gusta mantener todos los módulos incluidos en la parte superior de las definiciones de mi clase.

Estoy buscando algún método llamado en Module o Class cuando se ha cargado por completo, en lugar de como lo interpreta el tiempo de ejecución.

Respuesta

2

Aquí es una manera bastante rotonda/hacker de hacerlo que me ocurrió mediante el aplazamiento de la definición del método de envoltura hasta que el método original había sido definida:

module A 
    def self.included(base) 
    base.class_eval do 
     def self.singleton_method_added(name) 
     @@ran||=false 
     if name==:perform && [email protected]@ran 
      @@ran=true 
      class<<self 
      alias_method :original_perform, :perform 
      def perform(*args) 
       puts "Hello" 
       original_perform(*args) 
      end 
      end 
     end 
     end 
    end 
    end 
end 

class B 
    include A 

    def self.perform 
    puts "Foobar" 
    end 
end 

B.perform 

Editar:

d11wtq simplifica esto a la mucho más limpio:

module VerboseJob 
    module ClassMethods 
    def wrap_perform! 
     class << self 
     def perform_with_verbose(*args) 
      JobLogger.verbose { perform_without_verbose(*args) } 
     end 

     alias_method_chain :perform, :verbose \ 
      unless instance_method(:perform) == instance_method(:perform_with_verbose) 
     end 
    end 

    def singleton_method_added(name) 
     wrap_perform! if name == :perform 
    end 
    end 

    def self.included(job_class) 
    job_class.extend ClassMethods 
    job_class.wrap_perform! if job_class.respond_to?(:perform) 
    end 
end 
+0

¿Por qué no usar 'method_added' para envolver' perform' cuando ... er .. added? :) –

+2

No estoy seguro de entender tu significado, ¿no estoy haciendo eso? –

+2

Tomé su respuesta (combinado con este http://pivotallabs.com/users/rolson/blog/articles/1162-redefine-a-method-from-a-module-like-a-gentleman) y se me ocurrió la más sucinto https://gist.github.com/1170661 (funciona sin importar dónde se haga el 'include'). Siéntase libre de editar su respuesta. Lo aceptaré de todos modos :) – d11wtq

-1

Suponiendo que desea todas sus clases definidas antes desea perform plazo, es posible que desee utilizar Kernel#at_exit:

Convierte bloque a un objeto Proc (y por lo tanto se une en el punto de llamada) y la registra para la ejecución cuando el programa termina. Si se registran múltiples manipuladores, se ejecutan en el orden inverso del registro.

def do_at_exit(str1) 
    at_exit { print str1 } 
    end 
    at_exit { puts "cruel world" } 
    do_at_exit("goodbye ") 
    exit 

produce:

adiós mundo cruel

También puede que desee ver la forma en que los marcos de pruebas unitarias, tales como Test :: Unidad o Minitest, manejar el retraso de la ejecución de tareas.

+0

No estoy seguro de entender cómo se relaciona esto con mi pregunta, que trata de "envolver" un método de clase/instancia desde dentro de un Módulo incluido? – d11wtq

+0

@ d11wtq: Estoy tratando de resolver el problema que tiene de la manera más adecuada. Consulte [Describa el objetivo, no el paso] (http://catb.org/~esr/faqs/smart-questions.html#goal) –

Cuestiones relacionadas