Method_missing有效,但const_missing不起作用

时间:2017-01-30 11:28:21

标签: ruby metaprogramming method-missing

我有一个简单的ruby函数:

module MyModule
    def foo(param)
      puts param
    end
end

我希望能够使用barBaz等未定义的令牌进行调用,如下所示:

foo bar
foo Baz

请注意,我需要能够传递任何以字母开头的标记名称(大写或小写)。

我可以通过添加method_missing来处理第一种情况(小写起始名bar):

module MyModule
  def method_missing(meth, *args, &blk)
    meth.to_s
  end
end

但是第二行(Baz)给了我一个未初始化的常量错误,所以我尝试添加一个类似的const_missing:

module MyModule
  def const_missing(const_name)
    const_name.to_s
  end
end

但我仍然使用Baz得到未初始化的常量错误 - 如何捕获以大写字母开头的缺少名称并返回字符串?

更新:以下是重现场景的完整代码:

module MyModule
  def foo(param)
    puts param
  end

  def method_missing(meth, *args, &blk)
    meth.to_s
  end

  def const_missing(const_name)
    const_name.to_s
  end
end

include MyModule
foo "bar" #=> bar
foo bar #=> bar
foo Baz #=> uninitialized constant Baz (NameError)

1 个答案:

答案 0 :(得分:1)

解决方案

如果您只想撰写Baz,则需要定义Object.const_missing并拨打MyModule.const_missing

def Object.const_missing(const_name)
  MyModule.const_missing(const_name)
end

说明

您收到错误,因为当前作用域中没有常量Baz,这是主要的ruby对象Object

这是更好,更具启发性的测试代码:

include MyModule
p foo(bar)
p bar
p MyModule.Baz
p Baz

输出是这样的:

bar # 1
nil # from puts
"bar" # 2
"Baz" # 3
test.rb:19:in `<main>': uninitialized constant Baz (NameError) # 4
  1. foo bar调用方法foo,永远不会调用缺少方法。
  2. bar在当前对象中搜索方法,然后调用Object上的method_missing,其中包含MyModule
  3. MyModule.Baz在模块MyModule中搜索常量,然后调用const_missing
  4. Baz在当前模块中搜索constant_missing,并调用const_missing Object.const_missing,即Object的类实例方法。
  5. 当ruby查找方法时,它以对象方法开始,只有当它无法定义它们时才会查看它的模块,然后查看父类,然后是父类模块等。不能覆盖它的对象/类方法模块。

    Object(ruby的主要对象,包括内核)定义了它的const_missing方法,但不是method_missing

    search = "method_"
    Object.public_methods.select { |s| s.to_s.include?(search) }
    => [:method_defined?, :public_method_defined?, 
    :private_method_defined?, :protected_method_defined?]
    

    注意没有方法丢失。搜索const_会找到const_missing

    search = "const_"
    => "const_"
    irb(main):011:0> Object.public_methods.select { |s| s.to_s.include?(search) }
    => [:const_get, :const_defined?, :const_set, :const_missing]