Ruby instance_eval для класса с помощью attr_accessor

Я понимаю принципиальную разницу между instance_eval а также class_eval, Однако то, что я обнаружил, когда играю, - это нечто странное attr_accessor, Вот пример:

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?

Как это так:

  1. instance_eval не был у метода доступа к нашему классу A (объекту)
  2. затем он фактически добавил его на экземпляры A?

4 ответа

Решение

Во-первых, ваше понимание (или интуиция) правильно, методы определены внутри #instance_eval а также #class_eval не одинаковы

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

примечание: пока self одинаково в обоих instance_eval а также class_eval определение по умолчанию другое, см. http://yugui.jp/articles/846

Что на самом деле делает трюк Module#attr_accessor Сам посмотри на его определение: http://rxr.whitequark.org/mri/source/vm_method.c#620

он не использует def не читает контекст, self или определение по умолчанию. Он просто "вручную" вставляет методы в модуль. Вот почему результат нелогичен.

Для разницы между class_eval а также instance_evalсм. Динамически создаваемый метод класса

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)

Вывод (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"]

Как вы можете видеть с B, несмотря на то, что instance_eval обычно создает одноэлементные методы, очевидно, attr_accessor а также define_method форсировать определение методов экземпляра.

A.singleton_class.class_eval { attr_accessor :y }
A.y = 'y'
A.y

Метод attr_accessor это метод класса, такой, что при вызове в теле класса методы доступа определяются в экземплярах этого класса.

Когда вы делаете A.class_eval{...}, вы называете это в теле класса Aтак что его экземпляры, такие как a назначены аксессоры.

Когда вы делаете A.instance_eval{...}, вы называете это в не-теле класса Aтаким образом, его экземпляры не назначены методы доступа.

Если вы делаете Class.class_eval{attr_accessor :z}тогда вы называете это внутри тела класса Classтак что его экземпляры, такие как A будут назначены аксессоры: A.z = ...,

Другие вопросы по тегам