Accessing class variables in method made using define_method

D

Dave Baldwin

I want to use class variables and have them accessible from class
methods that have been defined outside of the class using
define_method or something similar. When I try this the class
variable isn't in scope. A simple test case is:

class A
@@aa = 20
end

A.class_eval {define_method:)foo){puts @aa}}

a = A.new
a.foo => nil and not 20 as expected

or using

A.send:)define_method, :foo){puts @aa}
a.foo => nil and not 20 as expected

gives the same result but

class A
def foo
puts @@aa
end
end

a.foo => 20 as expected.

How do I get the same behaviour as opening the class and defining the
method normally?

Thanks,

Dave.
 
A

ara.t.howard

I want to use class variables and have them accessible from class
methods that have been defined outside of the class using
define_method or something similar. When I try this the class
variable isn't in scope. A simple test case is:

class A
@@aa = 20
end

A.class_eval {define_method:)foo){puts @aa}}

a = A.new
a.foo => nil and not 20 as expected

or using

A.send:)define_method, :foo){puts @aa}
a.foo => nil and not 20 as expected

gives the same result but

class A
def foo
puts @@aa
end
end

a.foo => 20 as expected.

How do I get the same behaviour as opening the class and defining
the method normally?

Thanks,

Dave.


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.

kind regards.

a @ http://codeforpeople.com/
 
R

Rick DeNatale

NOTE @

Another thing, in addition to Ara's point about the lexical scope of
the closure, the original posting was mixing class variables with
instance variables:

class A
@@aa = 20 # This is a class variable

def aa=
puts @aa # This is a reference to an INSTANCE variable
puts @@aa # This is a reference to a CLASS variable
end
end

There's no relationship between @aa and @aa, any more that there would
be with aa or $aa

Note that Ruby also has class instance variables:

class A
def cv_aa
@@aa
end

def cv_aa=(val)
@@aa = val
end

def iv_aa=(val)
@aa = val
end

def iv_aa
@aa
end

def self.civ_aa
@aa
end

def self.civ_aa=(val)
@aa = val
end

end

class B < A
def set_cv_aa=(val)
@@aa = val
end
end

a = A.new
a2 = A.new
b = B.new

# Instance variable belong to the instance
a.iv_aa = "Hello"
a.iv_aa # => "Hello"
a2.iv_aa # => nil
a2.iv_aa = "Ciao!"
a2.iv_aa # => "Ciao!"
a.iv_aa # => "Hello"
a2.cv_aa = 30
a2.cv_aa # => 30

# Class variables are shared
a.cv_aa = 20
a.cv_aa # => 20
a2.cv_aa # => 20
#and with subclasses
b.cv_aa # => 20
b.cv_aa = 30
b.cv_aa # => 30
a.cv_aa # => 30

# Class instance variables are instance variables of classes
A.civ_aa = "Classy!"
A.civ_aa # => "Classy!"
B.civ_aa = "Chic!"
B.civ_aa # => "Chic!"
A.civ_aa # => "Classy!"
 
J

Joel VanderWerf

ara.t.howard said:
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!
 
D

Dave Baldwin


Yeah, a typo however the problem I want to solve still exists in that
a method added to a class using define_method doesn't have the same
scope as a method added using def...end. Both can call other methods
in the class and access instance variables but define_method is
prevented from accessing class variables. This was a surprise to me.


<snipped examples of instance, class and class instance varialbes>

Dave.
 
R

Robert Dober

On 10/8/07 said:
The order of assignment determines whether a class and subclass share
the class variable!

Yeah I really think one should stay away from @@ brrrr.
However one could use closures after all ;)
---------------- 8< ----------------
class A
a = 46
(singlton = class << self; self end).
send :define_method, :a do a end
singlton.send :define_method, :a= do |v| a=v end
end

p A.a
A.a -= 4
p A.a

B= Class::new A
p B.a
B.a -= 3
p [A.a, B.a]
----------------------->8 -------------------
But this makes the "variable" shared between class and subclass, that is not
always what one wants, if we want Smalltalk behavior we might use class instance
variables
---------------- 8< --------------
class C
@a = 42
class << self
attr_accessor :a
end
end

D = Class::new C

p C.a
p D.a
D.a = "fourtytwo"
p [C.a, D.a]
---------------------- >8 ---------------------
IIRC however there is a subtle difference between ST & Ruby here,
ST seems to copy the value of the superclasses class ivar into the
subclass class ivar or am I wrong, Rick?

HTH
Robert
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,058
Latest member
QQXCharlot

Latest Threads

Top