3

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.

zishe
  • 10,665
  • 12
  • 64
  • 103
user2634156
  • 1,631
  • 2
  • 11
  • 6

2 Answers2

4

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
Arup Rakshit
  • 116,827
  • 30
  • 260
  • 317
  • And that will modify immutable symbols? – Platinum Azure Jun 01 '14 at 05:40
  • @PlatinumAzure No `:sym.to_s` gives you completely new string object 'sym'. Thus now if you use bang method like `#gsub!` on `'sym'`, there shouldn't be any effect on `:sym`. `a = :sym a.to_s.sub!('s' ,'d') # => "dym" a # => :sym`. – Arup Rakshit Jun 01 '14 at 05:51
  • @PlatinumAzure Thanks for your appreciation and time to re read my answer. Thank you very much! – Arup Rakshit Jun 01 '14 at 13:32
  • 1
    You could further improve the answer by removing the first code snippet, which is still technically wrong. :-) – Platinum Azure Jun 01 '14 at 18:48
  • @PlatinumAzure I am always hungry to improve myself. Thanks for your further comments... You have the liability to correct me always if I am wrong.. – Arup Rakshit Jun 01 '14 at 18:52
1

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.

Arup Rakshit
  • 116,827
  • 30
  • 260
  • 317
Platinum Azure
  • 45,269
  • 12
  • 110
  • 134