为什么`gsub`调用`to_hash`?

时间:2015-07-26 04:25:36

标签: ruby dsl

我正在写一部DSL。我不希望用户必须引用传递字符串的参数,因此我覆盖method_missing以将未知方法转换为字符串。在以下示例中,create是DSL方法,我希望用户在没有引号的情况下键入arg1arg2

def method_missing(m, *arg)
  m.to_s
end
def create(*args)
  arg1.gsub(#do something here)
end

create arg1 arg2

然而,当我在'string'上使用gsub时会出现这种情况:

'gsub': can't convert String to Hash (String#to_hash gives String) (TypeError)

我认为method_missing覆盖是混乱的,因为看起来gsub正在调用String#to_hash,这不是String中的方法,因此它被路由到method_missing

我想知道为什么gsub调用String#to_hash,或者是否有其他方法让DSL的用户不必输入引号,而不会覆盖method_missing

2 个答案:

答案 0 :(得分:1)

String#gsub根据参数计数和类型做了不同的事情,如果给出了一个块:

gsub(pattern, replacement) → new_str
gsub(pattern, hash) → new_str
gsub(pattern) {|match| block } → new_str
gsub(pattern) → enumerator

第二个记录为:

  

如果第二个参数是Hash,并且匹配的文本是其中一个键,则相应的值是替换字符串。

但是如何区分它呢?两者都有两个论点!这有点复杂,但在你的情况下,Ruby(嗯,确切地称为CRuby或MRI的参考实现)开始于检查第二个参数是否具有内部类型T_HASH(它没有&#t; t由于T_STRING),它最有可能#to_s,然后检查是否可以调用#to_hash。要么因为它响应它,要么#method_missing可以。你定义了它,所以Ruby调用它。但是,它不会返回T_HASH,这是您发布的例外原因。

可能的解决方案是定义main.method_missing而不是Object#method_missing(因为String继承自Object):

def self.method_missing(m, *arg)
  m.to_s
end

但是,如果它不应该遵循Ruby的语法,我建议坚持使用引号或为这种文件编写自己的小解析器。使用*_missing可能会导致混淆或无用的错误消息。甚至没有(我猜create arg1 arg2应该create arg1, arg2)。

答案 1 :(得分:0)

gsub可能在某处使用method_missing本身,因此似乎全局定义它会导致方法调用的内部问题。如果您要使用method_missing,请确保始终在模块或类中定义它:

module CoolDSL
  def self.method_missing(m, *arg)
    m.to_s
  end

  def self.create(*args)
    args[0].gsub(/1/, "2")
  end

  def self.do_thing
    create arg1 arg2
  end
end

CoolDSL.do_thing

当然,这并不像DSL那样有用,所以你需要学习instance_evalyield的力量。我喜欢this guide