我正在写一部DSL。我不希望用户必须引用传递字符串的参数,因此我覆盖method_missing
以将未知方法转换为字符串。在以下示例中,create
是DSL方法,我希望用户在没有引号的情况下键入arg1
和arg2
。
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
。
答案 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_eval
和yield
的力量。我喜欢this guide。