From: Joel VanderWerf Date: 2007-10-09T02:41:47+09:00 Subject: Re: Accessing class variables in method made using define_method ara.t.howard wrote: > cfp:~ > cat a.rb > class A; @@aa = 42;end > > A.class_eval{ define_method(:foo){ A.send :class_variable_get, '@@aa' } } > p A.new.foo #=> 42 > > A.send(:define_method, :foo){ A.send :class_variable_get, '@@aa' } > p A.new.foo #=> 42 > > @@aa = 'this is what is meant by closure' > > A.send(:define_method, :foo){ @@aa } > p A.new.foo #=> ?? > > > cfp:~ > ruby a.rb > 42 > 42 > "this is what is meant by closure" > > > blocks form closures, which is what makes them useful, when you are > defining methods via blocks you need to consider the enclosing scope as > it will be the *primary* namespace lookup. understanding closures is > step one to metaprogramming bliss. Careful. Putting @@aa at the top level makes it into a kind of global, so there's no need for a closure to access it: class A; end @@aa = 'this is sorta global' A.class_eval{ define_method(:foo){ A.send :class_variable_get, '@@aa' } } p A.new.foo # ==> "this is sorta global" p Object.send(:class_variable_get, "@@aa") # ==> "this is sorta global" It's global in the sense that @@aa is defined at the root of the class tree (Object), and class var lookup wanders along this tree towards the root, until it finds a class with @@aa defined. An example that works _only_ because class vars are affected by closures: class A; @@bb = 3 end class B @@bb = "belongs to B, yet visible in closure" A.send(:define_method, :bar){ @@bb } end p A.new.bar # ==> "belongs to B, yet visible in closure" begin p Object.send(:class_variable_get, "@@bb") rescue => ex puts ex # ==> uninitialized class variable @@bb in Object end p A.send(:class_variable_get, "@@bb") # ==> 3 (Note that the @bb=3 is hidden when inside the scope of class B...end.) But note that the behavior of instance vars in closures is different: the lookup rules for @foo are dynamic, based on which object is the self when the code is executed. class A; end @@aa = 'this is what is meant by closure' @a = "bar" A.send(:define_method, :foo){ @@aa } A.send(:define_method, :bar){ @a } p A.new.foo # ==> "this is what is meant by closure" p A.new.bar # ==> nil It is a good idea to avoid class vars, for the following reason: class A @@one_by_this_name = 42 end class B < A @@one_by_this_name = 43 @@two_by_this_name = "zap" end class A @@two_by_this_name = "pow" end class A p @@one_by_this_name # == > 43 <-- surprise! p @@two_by_this_name # == > "pow" end class B p @@one_by_this_name # == > 43 p @@two_by_this_name # == > "zap" end The order of assignment determines whether a class and subclass share the class variable! -- vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407