为什么 `gsub` 调用 `to_hash`?
Why does `gsub` call `to_hash`?
我正在写 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
。
gsub
可能在某处使用 method_missing
本身,因此似乎在全局定义它会导致方法调用出现内部问题。如果你打算使用 method_missing
确保你总是在模块或 class:
中定义它
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.
String#gsub
根据参数数量和类型做不同的事情,如果给出一个块:
gsub(pattern, replacement) → new_str
gsub(pattern, hash) → new_str
gsub(pattern) {|match| block } → new_str
gsub(pattern) → enumerator
第二个记录为:
If the second argument is a Hash
, and the matched text is one of its keys, the corresponding value is the replacement string.
但是怎么和第一个区别呢?两者都有两个参数!这有点复杂,但在你的情况下 Ruby (好吧,参考实现称为 CRuby 或确切地说是 MRI)首先检查第二个参数是否具有内部类型 T_HASH
(由于 #to_s
),它很可能 T_STRING
),然后检查是否可以调用 #to_hash
。要么因为它响应它,要么 #method_missing
可以代替。您已经定义了它,因此 Ruby 调用了它。但是它没有 return T_HASH
,这就是您发布的异常的原因。
一个可能的解决方案是定义 main.method_missing
而不是 Object#method_missing
(因为 String
继承自 Object
):
def self.method_missing(m, *arg)
m.to_s
end
不过,如果这种文件不符合 Ruby 的语法,我建议坚持使用引号或编写您自己的小型解析器。使用 *_missing
可能会导致出现混乱或无用的错误消息。甚至 none(我猜 create arg1 arg2
应该是 create arg1, arg2
)。
我正在写 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
。
gsub
可能在某处使用 method_missing
本身,因此似乎在全局定义它会导致方法调用出现内部问题。如果你打算使用 method_missing
确保你总是在模块或 class:
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.
String#gsub
根据参数数量和类型做不同的事情,如果给出一个块:
gsub(pattern, replacement) → new_str gsub(pattern, hash) → new_str gsub(pattern) {|match| block } → new_str gsub(pattern) → enumerator
第二个记录为:
If the second argument is a
Hash
, and the matched text is one of its keys, the corresponding value is the replacement string.
但是怎么和第一个区别呢?两者都有两个参数!这有点复杂,但在你的情况下 Ruby (好吧,参考实现称为 CRuby 或确切地说是 MRI)首先检查第二个参数是否具有内部类型 T_HASH
(由于 #to_s
),它很可能 T_STRING
),然后检查是否可以调用 #to_hash
。要么因为它响应它,要么 #method_missing
可以代替。您已经定义了它,因此 Ruby 调用了它。但是它没有 return T_HASH
,这就是您发布的异常的原因。
一个可能的解决方案是定义 main.method_missing
而不是 Object#method_missing
(因为 String
继承自 Object
):
def self.method_missing(m, *arg)
m.to_s
end
不过,如果这种文件不符合 Ruby 的语法,我建议坚持使用引号或编写您自己的小型解析器。使用 *_missing
可能会导致出现混乱或无用的错误消息。甚至 none(我猜 create arg1 arg2
应该是 create arg1, arg2
)。