Ruby- method_missing 返回没有方法的错误。

3

我正在尝试使用method_missing方法将美元转换为不同的货币。

class Numeric
    @@currency={"euro"=>2, "yen"=>6}
    def method_missing(method_id,*args,&block)
        method_id.to_s.gsub!(/s$/,'') #get rid of s in order to handle "euros"
        @@currency.has_key?(method_id) ? self*@@currency[method_id] : super             
    end
end

> 3.euro   #expect to return 6
NoMethodError: undefined method 'euro' for 3:Fixnum
from (pry):24:in 'method_missing'

> 3.euros  #expect to return 6
NoMethodError: undefined method 'euros' for 3:Fixnum
from (pry):24:in 'method_missing'

我猜测3.euro无法工作的原因是:euro.to_s.gsub!(/s$/,'')返回了nil。我不确定为什么会出现“no method error”的错误。
谢谢任何帮助。
2个回答

4
当调用method_missing方法时,euro将被分配给method_id作为一个符号。但是你的@@currency哈希表将所有键都保存为字符串,所以你需要将它们转换为符号method_id.to_s.gsub!(/s$/,'')无法改变实际对象method_id。因为method_id.to_s会给你一个新的字符串对象,而#gsub!仅在此工作。你没有改变method_id,它仍然保持不变。最好使用非bang的方法,因为如果未进行更改,它将给你实际的对象,或者如果进行了更改,则需要将此返回值分配给一个新变量。
对于你的@@currency@@currency.has_key?(method_id)被评估为false,并且调用了supermethod_missing。为什么?因为method_id没有转换成字符串,这是你预计可能发生的。
但是,如果你想同时响应euroeuros,那么
class Numeric
    @@currency={"euro" => 2, "yen" => 6}
    def method_missing(method_id,*args,&block)
        #get rid of s in order to handle "euros"
        new_method_id = method_id.to_s.gsub(/s$/,'') 
        @@currency.has_key?(new_method_id) ? self*@@currency[new_method_id] : super             
    end
end

3.euros # => 6
3.euro  # => 6

那会修改不可变的符号吗? - Platinum Azure
@PlatinumAzure 如果不使用:sym.to_s,则会生成全新的字符串对象“sym”。因此,现在如果您在“sym”上使用bang方法,如#gsub!,则不应影响:syma = :sym a.to_s.sub!('s' ,'d') # => "dym" a # => :sym - Arup Rakshit
@PlatinumAzure 感谢您的赞赏和抽出时间重新阅读我的回答。非常感谢! - Arup Rakshit
1
你可以通过删除第一个代码片段来进一步改进答案,因为它仍然在技术上是错误的。 :-) - Platinum Azure
@PlatinumAzure 我一直渴望提高自己。感谢您的进一步评论... 如果我错了,您有责任随时纠正我。 - Arup Rakshit

1
gsub!会直接修改字符串并返回nil。但是,你使用的字符串是由to_s创建的临时字符串,并且从未存储在变量中,所以这种方式不太可行。另外,你也没有将结果存储在任何地方。为什么期望在一个不可变的符号上使用gsub!来修改字符串呢?
尝试使用gsub,它会返回修改后的字符串并保留原始调用者。同时,你需要将返回值存储起来。
real_method_name = method_id.to_s.gsub(/s$/, "")

此外:之所以“3.euro”无效是因为您的哈希表使用字符串作为键,但“method_id”作为符号传递给方法。如果您不必进行字符串操作(在这种情况下删除s后缀),我建议您只使用符号作为哈希键。然而,由于需要进行字符串操作,因此我的答案使用字符串。

1
这是一个问题。但它并没有解释为什么 euro 不起作用。 - John Dvorak

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接