问题
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:
- the instance_eval didn't at the accessor onto our A class (object)
- it then in fact added it onto instances of A?
回答1:
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.
回答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.
回答3:
A.singleton_class.class_eval { attr_accessor :y }
A.y = 'y'
A.y
回答4:
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 = ...
.
来源:https://stackoverflow.com/questions/14428531/ruby-instance-eval-on-a-class-with-attr-accessor