Reputation: 3025
I wrote some code that used a "dynamic hash" to return values for keys, where the values were calculated. I tested it under irb (RUBY_VERSION 2.3.3) and everything seemed good. Below is a trivial example demonstrating the idea.
PROPS = Hash.new { |hash,key| key.to_s + "!" }
"Foo: %{foo} Bar: %{bar}" % PROPS # => "Foo: foo! Bar: bar!"
PROPS[:xyzzy] # => "xyzzy!"
But then deploying my code into the environment where it used (a plugin for the modeling tool Sketchup) which apparently has Ruby 2.2.4 the string formatting example above yields a KeyError: key{foo} not found.
PROPS = Hash.new { |hash,key| key.to_s + "!" }
"Foo: %{foo} Bar: %{bar}" % PROPS # KeyError: key{foo} not found
PROPS[:xyzzy] # => "xyzzy!"
But accessing the hash with any key works fine... Reading at http://ruby-doc.org/core-2.2.4/Kernel.html#method-i-sprintf doesn't provided much in the way of specifying why hash defaults would not behave as expected.
Obviously I can do different things, like invent my own replacement functions and variable syntax. As an aside, apparently the "hashes" passed to "%" or sprintf must actually BE Hash objects, violating Ruby's supposed duck-typing flexibility.
Upvotes: 3
Views: 1046
Reputation: 4716
I can confirm that it does not work in Ruby 2.1.5 .
I will give some hints on how you could find out which ruby code to define to get the stuff going without reading C or Ruby code from MRI.
I know, this is not a full answer, but the text is too long to give in a comment.
Following is an irb session
>> detector = Object.new
=> #<Object:0x00000002257900>
>> def detector.method_missing m
>> puts m.to_s
>> end
=> :method_missing
>> "Foo: %{fnoo} Bar: %{bar}" % detector
to_ary
to_hash
ArgumentError: one hash required
from (irb):37:in `%'
from (irb):37
from /home/felix/.rvm/rubies/ruby-2.1.5/bin/irb:11:in `<main>'
This tells us that during interpolation methods were called that are not implemented by our dummy "detector" Object (to_hash
to be precisely; through other tests I know that to_ary is also called if given object is a Hash, so we can ignore that one).
It does however not tell us whether already something like detector.class
or detector is_a Hash?
etcpp. were called.
Now I await the downvotes ;)
Btw, if you want to dive in via C - and I came to believe that this is probably needed in this case - you can start digging here: https://github.com/ruby/ruby/blob/6d728bdae9de565ad9d0b2fee2d4c2a33c6f4eac/sprintf.c#L579 (more or less "sprintf" on ruby 2.1).
Upvotes: 2