Difference between instance_eval and class << self?

It seems that I do not understand the exact difference between these two "constructs". In my opinion, the following small script should output the same thing three times:

 class Example puts self class << self puts self end instance_eval do puts self end end 

However, the output is:

 Example #<Class:Example> Example 

Here is my rationale:

  • Example is an instance of Class , so self in the class class refers to this:
  • class << obj sets self to any obj in the given block, which in my case is an instance of Class , which is Example (here I am probably mistaken);
  • instance_eval starts the block in this instance, so in my case it is pretty much the same as putting the code in the block directly into the body of the class.

My current hunch is that class << self inserts a ghost class between Example and Class and assigns itself to this, but the output #<Class:Example> does not confirm this at all.

So what is wrong with my rationale?

+4
source share
2 answers

class << obj sets self to any obj in the given block, which in my case is an instance of Class , which is Example (here I am probably mistaken);

No, class << obj opens the singleton obj class. As you correctly pointed out, inside the declaration of the class self refers to the class itself, so in this case the "internal" self (that is, the one passed to puts ) refers to the singleton Example class.

+6
source

In my opinion, class << self was one of the most annoying bits of syntax in Ruby. People unfamiliar with the language have no idea what this means, except for purchase-cult agreements, and even those who are familiar with the language have only a vague understanding of what makes it different from instance_method , because these two seem surprisingly similar.

Here is an example of two different ways to define a class method:

 class Example class << self def class_def :class_def end end instance_eval do def instance_def :instance_def end end end 

You can verify that they work by calling methods:

 puts Example.class_def.inspect # => :class_def puts Example.instance_def.inspect # => :instance_def 

The difference is that you dynamically create methods using define_method , since the binding is really incorrect in the instance_eval version:

 class Example class << self define_method(:class_def) do :class_def end end instance_eval do define_method(:instance_def) do :instance_def end end end 

This leads to the definition of the instance_def method, but not tied to the class itself:

 puts Example.class_def.inspect # => :class_def puts Example.instance_def.inspect # => NoMethodError: undefined method 'instance_def' for Example:Class 

The only reliable way to create dynamic methods is class << self . The instance_def method is apparently created and discarded because it does not appear in the Example.method methods even inside this block.

+3
source

All Articles