Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ruby instance_eval on a class with attr_accessor

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?
like image 370
brad Avatar asked Jan 20 '13 19:01

brad


1 Answers

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.

like image 60
Daniel Vartanov Avatar answered Sep 19 '22 11:09

Daniel Vartanov