Method_missing 有效,但 const_missing 无效

Method_missing works, but const_missing does not work

我有一个简单的 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 的未初始化常量错误 - 如何捕获以大写字母开头的缺失名称和 return 字符串?

更新:这里是重现场景的完整代码:

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)

解决方案

如果你想只写 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,永远不会调用方法 missing。
  2. bar 在当前对象中搜索方法,然后在 Object 上调用 method_missing,它包含在 MyModule
  3. MyModule.Baz 在模块 MyModule 中搜索常量,然后调用 const_missing
  4. Baz 在当前模块中搜索 constant_missing 并调用 const_missingObject.const_missing,Object 的 class 实例方法。

当 ruby 查找方法时,它从对象方法开始,只有当它不能定义它们时,它才会查看它的模块,然后是父 class,然后是父 classes模块等。无法从它的模块中覆盖 objects/classes 方法。

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]