【问题标题】:How do I declare a method dynamically with method_missing?如何使用 method_missing 动态声明方法?
【发布时间】:2011-11-10 05:12:50
【问题描述】:

我有一个 ruby​​ 程序,我想接受用户的组合方法,并用该名称创建一个新方法。我试过这个:

def method_missing(meth,*args,&block)
  name = meth.to_s
  class << self
    define_method(name) do
      puts "hello " + name
    end
  end
end

我收到以下错误:

`define_method': interning empty string (ArgumentError) in 'method_missing'

有什么想法吗?谢谢。

编辑:

我让它以不同的方式工作,但我仍然很好奇如何以这种方式做到这一点。这是我的代码:

def method_missing(meth,*args,&block)
  Adder.class_eval do
    define_method(meth) do
      puts "hello " + meth
    end
  end
  send("#{meth}")
end

【问题讨论】:

    标签: ruby closures metaprogramming


    【解决方案1】:

    变量 name 在类定义 (class &lt;&lt; self) 范围内不可用。它不会抛出 NameError,因为您已经覆盖了 method_missing

    要做你想做的事,你需要保持name的范围。为了做到这一点,你必须只使用基于块的方法(例如class_eval)而不是直接打开类,所以像这样:

    def method_missing(meth,*args,&block)
      name = meth.to_s
      eigenclass = class << self; self; end
      eigenclass.class_eval do
        define_method(name) do
          puts "hello " + name
        end
      end
    end
    

    但实际上,meth 中的符号就足够了——你根本不需要名字。 (尽管您仍然需要上述技术。)不过,除此之外,您还想立即执行该方法。最简单的方法就是重新发送消息:

    def method_missing(meth,*args,&block)
      eigenclass = class << self; self; end
      eigenclass.class_eval do
        define_method(meth) do
          puts "hello #{meth}"
        end
      end
      send(meth, *args, &block)
    end
    

    【讨论】:

    • @Aditya Sanghi:感谢您的更正。但我实际上是想切换到插值,这只是全方位更好(更快,更少垃圾,读取更清洁并且适用于符号!)。
    • 而不是eigenclass = class &lt;&lt; self; self; end,你可以简单地做 - self.class.class_eval
    【解决方案2】:

    问题在于class &lt;&lt; self 不充当闭包,这意味着变量name 在方法定义中不可用。

    另一方面,当您使用class_eval 时,您传递的是一个块 (Proc),它是一个闭包,这意味着当前绑定中的所有局部变量都将在块体内可用.

    【讨论】:

      【解决方案3】:

      在调用method_missing 时动态定义方法的另一种方法是调用define_singleton_method

      class Person
        def method_missing(method_name, *args, &block)
          define_singleton_method(method_name) do
            puts "I'm a new method called #{__method__}"
          end
          # Add this is you want to automatically call the new method
          public_send(method_name)
        end
      
        # Add this for proper introspection.
        def respond_to_missing?(method_name, include_private = false)
          true
        end
      end
      

      这会给你

      Person.new.test_method # => "I'm a new method called test_method"
      

      注意:该解决方案与提出的其他解决方案略有不同,因为它仅定义当前实例上的方法而不是类。同一个类的新实例不会有新定义的方法;但这通常是人们在 method_missing 中定义新方法时的目标。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-02-16
        • 2016-09-16
        • 2020-07-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-06-17
        相关资源
        最近更新 更多