2014-04-03 80 views
4

我正在尝试编写一个通用模块来将method_missing模式应用于一些Rails模型的动态方法创建。这些模型有类方法和实例方法。虽然我可以写一个模块相当直截了当的任一类情况:覆盖类和实例方法的method_missing?

module ClassVersion 
    extend ActiveSupport::Concern 

    module ClassMethods 
     def method_missing(meth, *args, &block) 
     if meth.to_s =~ /^(.+)_async$/ 
      Async::handle_async self, $1, *args, &block 
     else 
      super meth, *args, &block 
     end 
     end 

     # Logic for this method MUST match that of the detection in method_missing 
     def respond_to_missing?(method_name, include_private = false) 
     Async::async?(method_name) || super 
     end 
    end 
    end 

或实例情况:

module InstanceVersion 
    extend ActiveSupport::Concern 

    def method_missing(meth, *args, &block) 
     if meth.to_s =~ /^(.+)_async$/ 
     Async::handle_async self, $1, *args, &block 
     else 
     super meth, *args, &block 
     end 
    end 

    # Logic for this method MUST match that of the detection in method_missing 
    def respond_to_missing?(method_name, include_private = false) 
     Async::async?(method_name) || super 
    end 
    end 

...我似乎无法支持这两种情况下在同一个班。有没有更好的方法来覆盖method_missing,这样两种情况都被支持?我on Rails的3.2 ....

回答

6

什么你正在努力实现是非常简单的,并在同一时间不同寻常。 ActiveSupport::Concern给你定义嵌套ClassMethods模块的可能性,该模块扩展了included模块钩子的基类。通常这很方便,因为你不想用相同的方法来扩充类和它的实例。

你需要做的是停止使用ActiveSupport::Concernincluded钩满足您的特定需求:

module AsyncModule 
    def method_missing(meth, *args, &block) 
    if meth.to_s =~ /^(.+)_async$/ 
     Async::handle_async self, $1, *args, &block 
    else 
     super meth, *args, &block 
    end 
    end 

    # Logic for this method MUST match that of the detection in method_missing 
    def respond_to_missing?(method_name, include_private = false) 
    Async::async?(method_name) || super 
    end 

    private 

    def self.included(base) 
    base.extend self 
    end 
end 
+0

古朴典雅。谢谢! –

+0

似乎关键是:如果通过模块向类中添加'method_missing',则还必须为模块提供'respond_to_missing?',否则'method_missing'不会生效。 –

0

你试过以下

module ClassAndInstanceVersion 
    extend ActiveSupport::Concern 

    def method_missing(meth, *args, &block) 
     self.class.method_missing(meth, *args, &block) 
    end 

    def respond_to_missing?(method_name, include_private = false) 
     self.class.respond_to_missing?(method_name, include_private) 
    end 

    module ClassMethods 
     def method_missing(meth, *args, &block) 
     if meth.to_s =~ /^(.+)_async$/ 
      Async::handle_async self, $1, *args, &block 
     else 
      super meth, *args, &block 
     end 
     end 

     # Logic for this method MUST match that of the detection in method_missing 
     def respond_to_missing?(method_name, include_private = false) 
     Async::async?(method_name) || super 
     end 
    end 
    end