I'm learning metaprogramming in Ruby and am just trying out defining missing methods via method_missing and define_method. I'm getting some unexpected behaviour and am wondering if anyone can explain this. Here is my class:
class X
def method_missing(m, *args, &block)
puts "method #{m} not found. Defining it."
self.class.send :define_method, m do
puts "hi from method #{m}"
end
puts "defined method #{m}"
end
end
Now, this code:
x = X.new
x.some_method
puts
x.some_method
puts
puts x
Produces the output:
method some_method not found. Defining it.
defined method some_method
hi from method some_method
method to_ary not found. Defining it.
defined method to_ary
#<X:0x007fcbc38e5030>
What I don't get is the last part: why is Ruby calling to_ary in a call to puts? Why would Ruby try to convert my object into an array just to print it?
I've Googled around and found these related links:
- http://tenderlovemaking.com/2011/06/28/til-its-ok-to-return-nil-from-to_ary/
- http://yehudakatz.com/2010/01/02/the-craziest-fing-bug-ive-ever-seen/
These also talk about method_missing and to_ary gotchas, but not specifically about why puts would call to_ary.
I should also mention that the behaviour does not change when I define a to_s, e.g.
def to_s
"I'm an instance of X"
end
The output of "puts x" is then:
method to_ary not found. Defining it.
defined method to_ary
I'm an instance of X
puts
is a synonym for$stdout.puts
. $stdout is anIO
class, so look at the documentation for IO.puts:This mean that
puts
method is intended to write several lines of output. Thus it tries to callto_ary
method on an object and ifto_ary
is defined, then prints each element of the returnedArray
on a new line, elseputs
callsto_s
method.to_ary
internal usage is really not well documented in the Ruby documentation (Matz points this out in his The Ruby Programming Language book).Methods
print
andp
on the other hand don't callto_ary
, onlyto_s
.Sidenote: Interesting, that
to_ary
must return realArray
object, not an object definingeach
method or something else: