Joel VanderWerf
10/8/2007 5:42:00 PM
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