Felix
Felix

Reputation: 89576

Difference between instance_eval and class << self?

I can't seem to grasp the exact difference between these two "constructs". To my mind, 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's my rationale:

My current guess is that class << self inserts a ghost class between Example and Class and sets self to that, but the output of #<Class:Example> is not confirming that at all.

So what is wrong with my rationale?

Upvotes: 4

Views: 559

Answers (2)

tadman
tadman

Reputation: 211590

In my opinion, class << self has been one of the most obnoxious bits of syntax in Ruby. People new to the language have little idea what it means, apart from cargo-cult conventions, and even those intimately familiar with the language have only a hazy understanding of what differentiates it from instance_method, as the two do seem to be remarkably similar.

Here's an example of two different ways of defining 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 check that these work by calling the methods:

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

The difference is when you're dynamically creating methods using define_method since the binding does appear to be incorrect on 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 results in the instance_def method being defined, but not being bound 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 with class << self. The method instance_def appears to be created and discarded as it doesn't show up in Example.methods even inside that block.

Upvotes: 3

J&#246;rg W Mittag
J&#246;rg W Mittag

Reputation: 369458

class << obj sets self to whatever obj is in the given block, which in my case is the instance of Class that is Example (this is where I'm probably wrong);

No, class << obj opens up the singleton class of obj. As you correctly pointed out, inside of a class declaration, self refers to the class itself, so, in this case, the "inner" self (i.e. the one being passed to puts) refers to the singleton class of Example.

Upvotes: 6

Related Questions