29

I'm reading the Metaprogramming section of Programming Ruby 1.9 and I'm having trouble understanding what's going on internally between class_eval/class_exec vs. instance_eval/instance_exec.

So first of all, my understanding is that def adds a method to the method table of self (the class object):

class A
  puts self  # => A
  def foo; 42; end  # added to the method table of self, so becomes an instance method
end
A.new.foo  # => 42

And if we use class_eval, we get the same behavior:

A.class_eval do
  puts self  # => A
  def bar; 42; end  # same as above
end
A.new.bar  # => 42

But somehow in the instance_eval case, things are different:

A.instance_eval do
  puts self  # => A
  def baz; 42; end  # added to the method table of an anonymous
                    # singleton class of self, so becomes a class method
end
puts A.baz  # => 42

s = 'string'
s.instance_eval do ... end  # same behavior, so now def creates an instance method

So I understand the functional difference between class_eval and instance_eval.

But the contexts inside the class_eval and instance_eval blocks look exactly the same to me -- in particular, self points to the same object, and the local_variables are the same. So what's going on inside the blocks (internally) that's making def act different?

Is there some piece of documentation I could read? The RDoc for instance_eval and class_eval doesn't help. Looking at the source, instance_eval seems to set up a singleton class object whereas class_eval doesn't -- but is this difference visible outside the C code, on the Ruby level?

Jo Liss
  • 30,333
  • 19
  • 121
  • 170

2 Answers2

34

I think your confusion comes from the fact that def does not depend on the current self, you might think about it as being a "current class" that has it's own rules.

Following your examples:

class A
  # defs here go to A
  puts self  # => A
  class << self
     #defs here go to A's eigenclass
  end
end

A.class_eval do
  #defs here go to A
end

A.instance_eval do
  #defs here go to A's eigenclass     
end

s = "Hello World"

class << s
  #defs here go to s's eigenclass
end

Here's the portion of the chapter that talks about the issue and it's pretty clear about the behaviour

class_eval and instance_eval both set self for the duration of the block. However, they differ in the way they set up the environment for method definition. class_eval sets things up as if you were in the body of a class definition, so method definitions will define instance methods In contrast, calling instance_eval on a class acts as if you were working inside the singleton class of self. Therefore, any methods you define will become class methods.

The only thing I think is worth adding is that you can call instance_eval in any object, not just classes, and the behaviour doesn't change but has different consequences.

Some relevant reading:

Ruby: instance_eval and class_eval method definitions

Chapter 4 of this most excelent series

Joel
  • 4,503
  • 1
  • 27
  • 41
krusty.ar
  • 4,015
  • 1
  • 25
  • 28
  • Hm, I see -- that makes sense. Is there any way to inspect that "current class" in Ruby code? – Jo Liss Dec 10 '10 at 13:25
  • 1
    I think in `A.instance_eval`, you meant to write `#defs here go to A's eigenclass`? Or am I mistaken? – Jo Liss Dec 10 '10 at 13:33
  • It's not exactly simple, I will post some relevant links in the answer – krusty.ar Dec 10 '10 at 13:36
  • @krusty.ar: Google for singleton class, meta class, or eigenclass. (I have seen it referred to as all 3) It is an instance specific class that sits between the instance and the instances class in the inheritance heirarchy – Matt Briggs Dec 10 '10 at 13:52
  • @Matt Briggs: yes, I know, did I miss something in my answer? – krusty.ar Dec 10 '10 at 14:03
  • Awesome -- I think I understand Ruby a little better now! :) Thanks krusty, and Joerg too! – Jo Liss Dec 11 '10 at 18:06
3

Just to add to @krusty.ar's answer: def and define_method add methods to the current method definition context (I believe that's what it's called, I'm not sure), not to the current self.

It's just that inside of a module, class or singleton class body, those two happen to be the same.

But, for example, in a script body (aka top-level), self is the main object, but the current method definition context is Object.

Jörg W Mittag
  • 363,080
  • 75
  • 446
  • 653