2013-01-20 22 views
5

Rozumiem podstawową różnicę między instance_eval i class_eval. To, co odkryłem podczas zabawy, to coś dziwnego z udziałem attr_accessor. Oto przykład:Ruby instance_eval na klasie z 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? 

Jak to jest, że:

  1. instance_eval nie na akcesor na naszej klasie A (object)
  2. to wtedy faktycznie dodaje go na instancje ?

Odpowiedz

5

Początkowo zrozumienie (lub intuicja) jest poprawna, metody zdefiniowane wewnątrz #instance_eval i #class_eval nie są takie 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 

marginesie: podczas self jest taka sama w obu instance_eval i class_eval, w domyślny definee jest inny, http://yugui.jp/articles/846

Co naprawdę wystarcza sama jest Module#attr_accessor, spojrzeć na jego definicja: http://rxr.whitequark.org/mri/source/vm_method.c#620

nie używa def, nie czytać kontekst, self lub domyślnego definee. Po prostu "ręcznie" wstawia metody do modułu. Dlatego wynik jest sprzeczny z intuicją.

0

Sposób attr_accessor jest metoda klasy taki sposób, że po wywołaniu w ciała z klasy, następnie akcesor metody określa się na przypadkach tej klasy.

Kiedy robisz A.class_eval{...}, dzwonisz go w ciała z klasyA, więc jego przypadki takich jak a są przypisane akcesorów.

Kiedy robisz A.instance_eval{...}, wołasz go w spoza ciała z klasyA, więc jej przypadki nie są przypisane akcesorów.

Jeśli nie Class.class_eval{attr_accessor :z}, to jesteś nazywając ją withing się ciało z klasyClass, więc jego przypadki takie jak A zostanie przypisany akcesorów: A.z = ....

2

Dla różnicy pomiędzy class_eval i instance_eval patrz 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"] 

Jak widać z B, mimo że zazwyczaj tworzy instance_eval metody singleton, oczywiście attr_accessor i define_method wymuszają definicję metod instancji.

+0

człowiek, który jest zawalony. Świetne przykłady. @ Odpowiedź Daniel_Vartanov właściwie tłumaczy, dlaczego 'attr_accessor' nie odnosi się do siebie, ale jest to świetny sposób zilustrowania, co dokładnie dzieje się z każdym typem metody def'n. Wielkie dzięki. – brad

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