user2634156
user2634156

Reputation: 1751

ruby- method_missing return no method error

I am trying to use method_missing to convert dollar to different currencies.

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'

I guess 3.euro isn't working because :euro.to_s.gsub!(/s$/,'') returns nil. I am not sure why it returns no method error.

Thanks for any help.

Upvotes: 3

Views: 555

Answers (2)

Arup Rakshit
Arup Rakshit

Reputation: 118261

When method_missing will be called, then euro will be assigned to the method_id as a symbol. But your @@currency hash holds all keys as a strings, you need to convert them as symbols.

method_id.to_s.gsub!(/s$/,'') no way will change the actual object method_id. Because method_id.to_s will give you a new string object, and #gsub! will work on this only. No way you are changing the method_id, it remains as it is. Better to use non-bang one, because it will give you the actual object if no change made, or changed object, if change is made, then you need to assign this return value to a new variable.

With your @@currency, @@currency.has_key?(method_id) evaluated as false and super class method_missing has been called. Why? As method_id didn't convert into strings, which you expected may happened.

But, if you want to respond for both like euro or euros, then

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

Upvotes: 4

Platinum Azure
Platinum Azure

Reputation: 46183

gsub! modifies a string in place and returns nil. That won't work very well with the string you're using, which is a temporary created by to_s and never stored in a variable. Also, you are not even storing the result anywhere anyway. Why should gsub! on a string be expected to modify symbols, which are immutable anyway?

Try using gsub instead, which returns the modified string and leaves the caller alone. You'll also need to store the return value.

real_method_name = method_id.to_s.gsub(/s$/, "")

Also: The reason 3.euro didn't work is because your hash uses strings as keys but method_id is passed to the method as a symbol. If you didn't have to do string manipulations (to remove s suffixes, in this case), I would have suggested to just use symbols as your hash keys. As it is, though, you need to do string operations anyway, so my answer uses strings.

Upvotes: 1

Related Questions