views:

374

answers:

1
Foo = Class.new
Foo.instance_eval do
  def instance_bar
    "instance_bar"
  end
end
puts Foo.instance_bar       #=> "instance_bar"
puts Foo.new.instance_bar   #=> undefined method ‘instance_bar’

My understanding is that calling instance_eval on an object is supposed to allow you to define an instance variable or method for that object.

But in the example above, when you call it on class Foo to define the instance_bar method, instance_bar becomes a class method that can be invoked with "Foo.instance_bar". It is clear that this code has not created an instance method because Foo.new.instance_bar results in "undefined method ‘instance_bar’".

Why does instance_eval define a class method rather than an instance method in this context?

+4  A: 

x.instance_eval changes your context so self evaluates to x.

This allows you to do many things, including defining instance variables and instance methods but only for x.

 x = Object.new
 y = Object.new

 # define instance variables for x and y
 x.instance_eval { @var = 1 }
 y.instance_eval { @var = 2 }

 # define an instance method for all Objects
 class Object
   def var
     @var
   end
 end

 x.var #=> 1
 y.var #=> 2

Ruby lets you define instance methods for an object in a couple places. Normally, one defines them in a class, and those instance methods are shared among all instances of that class (like def var above).

However, we can also define an instance method for just a single object:

# here's one way to do it
def x.foo
  "foo!"
end
# here's another
x.instance_eval do
  # remember, in here self is x, so bar is attached to x.
  def bar
    "bar!"
  end
end

Even though x and y have the same class, they don't share these methods, since they were only defined for x.

x.foo #=> "foo!"
x.bar #=> "bar!"
y.foo #=> raises NoMethodError
y.bar #=> raises NoMethodError

Now in ruby, everything's an object, even classes. Class methods are just instance methods for that class object.

# we have two ways of creating a class:
class A 
end
# the former is just syntatic sugar for the latter
B = Class.new

# we have do ways of defining class methods:

# the first two are the same as for any other object
def A.baz
  "baz!"
end
A.instance_eval do
   def frog
     "frog!"
   end
end

# the others are in the class context, which is slightly different
class A
  def self.marco
    "polo!"
  end
  # since A == self in here, this is the same as the last one.
  def A.red_light
    "green light!"
  end

  # unlike instance_eval, class context is special in that methods that
  # aren't attached to a specific object are taken as instance methods for instances
  # of the class
  def example
     "I'm an instance of A, not A itself"
  end
end
# class_eval opens up the class context in the same way
A.class_eval do
  def self.telegram
    "not a land shark"
  end
end

Note again, that all these methods are A-specific, B doesn't get access to any of them:

A.baz #=> "baz!"
B.telegram #=> raises NoMethodError

The important thing to take away from here is that class methods are just instance methods of an object of class Class

rampion
When you wrote "A.instance_eval do def A.frog 'frog!' end end" is it significant that you said "A.frog"? Or could you have also said "self.frog" there? Later on when you define "A.class_eval do def self.telegram 'not a land shark' end end", you say "self.telegram" rather than "A.telegram" - again, are these interchangeable or is it significant that you use "A." for instance_eval and "self." for class_eval?
pez_dispenser
You say "The important thing to take away from here is that class methods are just instance methods of an object of class Class". So classes A and B here are actually instances even before we instantiate them (instances of class Class)? I thought we have to call the new() method before we get an instance.
pez_dispenser
Heh, I screwed up a little. The A. in A.frog wasn't necessary (I've fixed it) - so see the parallelism between x and A there.
rampion
within a class-context block (class X...end or X.class_eval {...}) def self.foo and def X.foo are equivalent, because self == X in that context.
rampion
A and B are instances of class `Class`. Here, `class A ; end` produces the same result as `A = Class.new`.
rampion
`class A ... end` defines A as an instantiation of Class.
rampion