2009-09-07 24 views
0

如何让这段代码有效?关于define_method和method_missing的问题

class Meta 

    @array = [:a,:b] 

    def self.method_missing(name, *args, &block) 
      if @array.include? name 
        self.class.send(:define_method, name) do 
          do_call(name) 
        end 
      else 
        puts "[#{name.inspect}] is not part of array!" 
      end 
    end 

    def do_call arg 
      puts "doing call for ['#{arg}'] " 
    end 
end 

的想法是有Meta.a(被定义之后)具有

def a 
do_call(a) 
end 
在其主体

。但是,在运行此之后,我的输出是:

[:do_call] is not part of array!

UPDATE:现在还挺工作是这样的:

class Meta 

    @array = [:a,:b] 

    def self.do_call arg 
      puts "doing call for ['#{arg}'] " 
    end 

    def self.method_missing(name, *args, &block) 
      puts "#{name} is missing!" 
      if @array.include? name 
        self.class.send(:define_method, name) do 
          do_call name 
      end 
      else 
        puts "[#{name.inspect}] is not part of array!" 
      end 
     end 
    end 

但尽管如此,这里是一个IRB会议的摘录:

[~/code] $ irb -r meta

irb(main):001:0> Meta.a

a is missing!

=> #

irb(main):002:0> Meta.a

doing call for ['a']

=> nil

irb(main):003:0> c = Meta.new

=> #

irb(main):004:0> c.a

NoMethodError: undefined method `a' for #

from (irb):4 

irb(main):005:0> Meta.methods

=> ["inspect", "send", "pretty_inspect", "class_eval", "clone", "yaml_tag_read_class", > >"public_methods", "protected_instance_methods", "send", "private_method_defined?", "equal?", "freeze", "do_call", "yaml_as", "methods", "instance_eval", "to_yaml", "display", "dup", "object_id", "include?", "private_instance_methods", "instance_variables", "extend", "protected_method_defined?", "const_defined?", "to_yaml_style", "instance_of?", "eql?", "name", "public_class_method", "hash", "id", "new", "singleton_methods", "yaml_tag_subclasses?", "pretty_print_cycle", "taint", "pretty_print_inspect", "frozen?", "instance_variable_get", "autoload", "constants", "kind_of?", "to_yaml_properties", "to_a", "ancestors", "private_class_method", "const_missing", "type", "yaml_tag_class_name", "instance_method", "<", "protected_methods", "<=>", "instance_methods", "==", "method_missing", "method_defined?", "superclass", ">", "pretty_print", "===", "instance_variable_set", "const_get", "is_a?", "taguri", ">=", "respond_to?", "to_s", "<=", "module_eval", "class_variables", "allocate", "class", "taguri=", "pretty_print_instance_variables", "tainted?", "public_instance_methods", "=~", "private_methods", "public_method_defined?", "autoload?", "id", "nil?", "untaint", "included_modules", "const_set", "a", "method"]

是什么赋予了? 'a'是一个类方法,它不会传递给新的Meta对象(c)。为什么?

回答

4

您将do_call定义为实例方法,而您可能打算将其定义为类方法。这就是为什么它也会为do_call调用method_missing,并且会得到您收到的错误。

另请注意,当你做self.class.send时,self.class将是Class,所以该方法将在所有类上可用,而不仅仅是meta。你可能需要相当:响应您的更新

class <<self 
    self 
end.send 

编辑:

'a' is a class method and it is not passed on to the new Meta object (c). Why?

因为a是一个类的方法[1]。一个类的实例只能获得类的实例方法。

您正在定义一个在Class上的实例方法,然后尝试在Meta实例上调用它,但这不起作用。在Class中的Ruby实例方法以及在类中定义的单例方法只能通过执行TheClass.the_method而不是instance_of_the_class.the_method来调用。如果您想调用Meta实例的方法,请将其定义为实例方法。如果你想能够做Meta.a以及Meta.new.a你必须定义一个实例以及类方法a

[1]实际上,正如我已经说过的,你定义它的方式甚至不是Meta的类方法。它是Class的一个实例方法(这意味着你也可以将它称为例如String.a)。

+0

此方法的一个重要警告是method_missing厄运问题的无限循环 –

+0

现在仍然像这样工作...:| –

+0

完成我提到的更改后,它对我来说工作正常。 http://pastie.org/609427 – sepp2k