关于define_method和method_missing的问题

时间:2009-09-07 17:09:27

标签: ruby metaprogramming 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]不是数组的一部分!

更新:现在有点像这样工作:

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(主):001:0> Meta.a

     

a缺少!

     

=> #

     

IRB(主):002:0> Meta.a

     

打电话给['a']

     

=>零

     

IRB(主):003:0> c = Meta.new

     

=> #

     

IRB(主):004:0> C.A

     

NoMethodError:#

的未定义方法`a'
    from (irb):4
     

IRB(主):005:0> Meta.methods

     

=> [“inspect”,“send”,“pretty_inspect”,“class_eval”,“clone”,“yaml_tag_read_class”,> >“public_methods”,“protected_instance_methods”,“发送”,“private_method_defined?”,   “相等?”,“冻结”,“do_call”,“yaml_as”,“方法”,“instance_eval”,“to_yaml”,“显示”,   “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”,“污点”,“pretty_print_inspect”,“冻结?”,   “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”,“污点?”,“public_instance_methods”,“=〜”,   “private_methods”,“public_method_defined?”,“autoload?”,“ id ”,“nil?”,“untaint”,   “included_modules”,“const_set”,“a”,“method”]

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

1 个答案:

答案 0 :(得分:4)

您将do_call定义为实例方法,而您可能希望将其定义为类方法。这就是为什么它也为do_call调用method_missing而你得到的错误。

另请注意,当您执行self.class.send时,self.class将为Class,因此该方法将适用于所有类,而不仅仅是meta。你可能更想要:

class <<self
  self
end.send

编辑以响应您的更新:

  

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

因为a是一种类方法[1]。类的实例只获取类的实例方法。

您在Class上定义了一个实例方法,然后尝试在Meta的实例上调用它,这不起作用。在ruby实例中,类的方法以及类上定义的单例方法只能通过执行TheClass.the_method而不是instance_of_the_class.the_method来调用。如果要在Meta实例上调用方法,请将其定义为实例方法。如果您希望能够Meta.a以及Meta.new.a,则必须同时定义实例和类方法a

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