2011-08-25 99 views
5

是否有可能完成这项工作,而不必在课程结束时包含该模块并将其包含在顶部?模块包装类方法?

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 

我希望发生什么,是HelloJob.perform实际调用VerboseJob.perform(然后调用块内的原始方法)。因为这里的模块包含在类的顶部,所以这不起作用,因为perform尚未定义。将include移到最后确实有效,但有没有更宽容的方法?我喜欢将所有包含的模块保留在我的类定义的顶部。

我正在寻找某种方法,它在完全加载时调用ModuleClass,而不是像运行时所解释的那样。

回答

2

这里是做的相当迂回/ hackish的方式,我想出了通过推迟包装方法的定义,直到原来的方法已经被定义为:

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 

编辑:

d11wtq简化了这一对更清洁:

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

为什么不只是使用'method_added'来包装执行'当它是..呃..添加? :) –

+2

我不确定我是否明白你的意思,我不是那样做的吗? –

+2

我把你的答案(结合这个http://pivotallabs.com/users/rolson/blog/articles/1162-redefine-a-method-from-a-module-like-a-gentleman)并提出了更简洁的https://gist.github.com/1170661(它可以在'include'完成的地方工作)。随意编辑你的答案。我会接受它:) – d11wtq

-1

假设你想之前,所有的类定义要perform运行,则可能要使用Kernel#at_exit

转换块以一个Proc对象(因此在该点处呼叫的 结合它),并把它寄存为执行时,程序退出。如果 多个处理程序已注册,则它们将以相反的顺序执行 注册。

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

生产:

再见残酷的世界

你可能也想看看如何单元测试框架,如Test ::单位或MINITEST,处理延迟任务的运行。

+0

我不确定我是否理解这与我的问题有关,这是关于从包含的模块中“包装”类/实例方法的问题? – d11wtq

+0

@ d11wtq:我试图以最合适的方式解决问题。请参阅[描述目标,而不是步骤](http://catb.org/~esr/faqs/smart-questions.html#goal) –