Long story short, sending foo to Test.class works because:
Test.class == Class and Class.class == Class
Class.class points to itself.
質問
EDIT: Let me expand a bit on what my intentions were:
Given that a ruby object gets its methods from its class' instance methods.
What I was trying to "prove" was that by adding instance methods to that object's class, they would then become method on that object itself.
So:
class Test;end
Test.class.class_eval do
def foo
puts "I am a method in #{self}"
end
end
Test.foo
=> I am a method in Test
Test.class.foo
=> I am a method in Class
I actually already figured out why this happens. That's because Class.class is... Class! Since Class has foo as part of its instance methods table, and since Class.class points to itself, it will also have foo as a callable instance method.
Just for the sake of learning more about the Ruby object model, I've been doing some experiments, and the following behavior surprised me:
Let's say we have a class Test:
class Test;end
Then, we class_eval Test.class in order to add a method to the Test object:
Test.class.class_eval do
def foo
puts 'foo called!'
end
end
(equivalent to calling Test.class.send(:define_method,:foo) ... )
Then:
irb(main):076:0> Test.foo
=> foo called!
But sending foo to class also works:
irb(main):006:0> Test.class.foo
=> foo called!
I still can't figure out why, though. The Class' singleton_class doesn't contain it (the only possible explanation that came to my mind was that somehow the method was added to the Class' singleton class):
Test.class.singleton_methods
=> [:nesting, :constants]
How does the method lookup work in this case? Why does sending :foo to Test.class also calls the method ? Am I missing something ?
As a reference, I did the same thing with an instance of Test:
irb(main):001:0> class Test;end
=> nil
irb(main):002:0> _foo = Test.new
=> #<Test:0x007fa2c39e2f38>
irb(main):003:0> _foo.class.class_eval do
irb(main):004:1* def foo
irb(main):005:2> puts 'foo called!'
irb(main):006:2> end
irb(main):007:1> end
=> :foo
irb(main):008:0> _foo.foo
foo called!
=> nil
irb(main):009:0> _foo.class.foo
NoMethodError: undefined method `foo' for Test:Class
This worked the way I expected it to.
Thanks in advance!
解決 3
Long story short, sending foo to Test.class works because:
Test.class == Class and Class.class == Class
Class.class points to itself.
他のヒント
When you call Test.class
it returns the class of Test
not the actual Test class.
irb(main):001:0> class Test;end
=> nil
irb(main):002:0> Test.class
=> Class
When you call _foo = Test.new
you are grabbing an instance of the class and then when you call _foo.class
it returns the class of the instance of foo
which is Test
In your first example, you are adding foo to the super class, and not to the Test
class.
To expand more on this, what you should be doing is calling class_eval
on the Test
class.
On top of that, what you want is a class method, not an instance method. Because the Test
class is actually an instance of Class
, you actually defined an instance method in the Class
class instead of defining a class method in Test
. What you should be doing is this:
Test.class_eval do
def self.foo
puts "foo called!"
end
end
You mixed the things up a bit.
Test.class_eval
is executed on Test
’s eigenclass (which is Test
.)Test.class.class_eval
is executed on Test
’s eigenclass’ eigenclass (which is apparently Class
.)Test.class.singleton_methods
returns methods of Class
’ eigenclass.Test.class.class_eval
actually executes code on Class
(declaring Class
’ instance method in your case.)
Test.foo
is called w/out problems because you have declared #foo
on Class
and Test
is an instance of it (as well as Test.class
.)