7

I understand the basic difference between instance_eval and class_eval. What I've discovered though when playing around is something strange involving attr_accessor. Here's an example:

A = Class.new
A.class_eval{ attr_accessor :x }

a = A.new
a.x = "x"
a.x
=> "x"  # ... expected

A.instance_eval{ attr_accessor :y }

A.y = "y"
=> NoMethodError: undefined method `y=' for A:Class

a.y = "y"
=> "y"      # WHATTT?

How is it that:

  1. the instance_eval didn't at the accessor onto our A class (object)
  2. it then in fact added it onto instances of A?

4 Answers 4

10

At first, your understanding (or intuition) is correct, methods defined inside #instance_eval and #class_eval are not the same

A = Class.new

A.instance_eval { def defined_in_instance_eval; :instance_eval; end }
A.class_eval { def defined_in_class_eval; :class_eval; end }

A.new.defined_in_class_eval # => :class_eval
A.defined_in_instance_eval # => :instance_eval

a side note: while self is the same in both instance_eval and class_eval, the default definee is different, see http://yugui.jp/articles/846

What really does the trick is Module#attr_accessor itself, look at its definition: http://rxr.whitequark.org/mri/source/vm_method.c#620

it does not use def, it does not read context, self or a default definee. It just "manually" inserts methods into a module. That's why the result is counterintuitive.

Sign up to request clarification or add additional context in comments.

Comments

2

For the difference between class_eval and instance_eval, see Dynamically creating class method

class A; end
A.class_eval do
    attr_accessor :x
    def barx; end
    define_method :foox do; end
end

print 'A.instance_methods  : '; p A.instance_methods(false).sort
print 'A.singleton_methods : '; p A.singleton_methods

class B; end
B.instance_eval do
    attr_accessor :y
    def bary; end
    define_method :fooy do; end
end

print 'B.instance_methods  : '; p B.instance_methods(false).sort
print 'B.singleton_methods : '; p B.singleton_methods

class C; end
singleton_class = class << C; self end
singleton_class.instance_eval do
    attr_accessor :z
    def barz; puts 'where is barz ?' end
    define_method :fooz do; end
end

print 'C.instance_methods  : '; p C.instance_methods(false).sort
print 'C.singleton_methods : '; p C.singleton_methods

print 'singleton_class.barz : '; singleton_class.barz
print 'singleton_class.methods  : '; p singleton_class.methods(false)

Output (ruby 1.8.6):

A.instance_methods  : ["barx", "foox", "x", "x="]
A.singleton_methods : []
B.instance_methods  : ["fooy", "y", "y="]
B.singleton_methods : ["bary"]
C.instance_methods  : []
C.singleton_methods : ["z", "z=", "fooz"]
singleton_class.barz : where is barz ?
singleton_class.methods  : ["barz"]

As you can see with B, despite the fact that instance_eval usually creates singleton methods, obviously attr_accessor and define_method force the definition of instance methods.

1 Comment

man that's messed up. Great examples. @Daniel_Vartanov's answer actually explains why attr_accessor doesn't apply to self, but this is a great way of illustrating exactly what happens with each type of method def'n. Thanks a bunch.
1
A.singleton_class.class_eval { attr_accessor :y }
A.y = 'y'
A.y

Comments

0

The method attr_accessor is a class method such that, when called in the body of a class, then accessor methods are defined on the instances of that class.

When you do A.class_eval{...}, you are calling it within the body of a class A, so its instances such as a are assigned accessors.

When you do A.instance_eval{...}, you calling it within a non-body of a class A, so its instances are not assigned accessors.

If you do Class.class_eval{attr_accessor :z}, then you are calling it withing the body of a class Class, so its instances such as A will be assigned accessors: A.z = ....

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.