Pregunta


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.

Original post follows:

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!

¿Fue útil?

Solución 3

Long story short, sending foo to Test.class works because:

Test.class == Class and Class.class == Class

Class.class points to itself.

Otros consejos

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.)

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top