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
"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