Mixins and class variables

B

Brubix

I can't figure out how to set class variables from class methods
inherited from a module ?

The following doesn't work as I expected (I spare you my others
pathetic attempts):

module MyModule
def self.extended(base)
base.setClassVar(base.name.downcase)
end
def setClassVar(value)
puts "self is #{self.name}"
puts "@@classVar=#{value}"
@@classVar = value
end
def getClassVar
@@classVar
end
end

class Class1
extend MyModule
end
class Class2
extend MyModule
end

puts "classVar for Class1 is " + Class1.getClassVar
puts "classVar for Class2 is " + Class2.getClassVar

It gives:
self is Class1
@@classVar=class1
self is Class2
@@classVar=class2
classVar for Class1 is class2 # Shouldn't be class1 ?!
classVar for Class2 is class2

Many thanks in advance.

Brubix
 
M

MonkeeSage

I can't figure out how to set class variables from class methods
inherited from a module ?

The following doesn't work as I expected (I spare you my others
pathetic attempts):

module MyModule
def self.extended(base)
base.setClassVar(base.name.downcase)
end
def setClassVar(value)
puts "self is #{self.name}"
puts "@@classVar=#{value}"
@@classVar = value
end
def getClassVar
@@classVar
end
end

class Class1
extend MyModule
end
class Class2
extend MyModule
end

puts "classVar for Class1 is " + Class1.getClassVar
puts "classVar for Class2 is " + Class2.getClassVar

It gives:
self is Class1
@@classVar=class1
self is Class2
@@classVar=class2
classVar for Class1 is class2 # Shouldn't be class1 ?!
classVar for Class2 is class2

Many thanks in advance.

Brubix

Use Module#class_variable_get/set for this kind of thing.

module MyModule
def self.extended(base)
base.setClassVar(base.name.downcase)
end
def setClassVar(value)
puts "self is #{self.name}"
puts "@@classVar=#{value}"
class_variable_set:)@@classVar, value)
end
def getClassVar
class_variable_get:)@@classVar)
end
end

HTH,
Jordan
 
B

Brubix

HTH,

Indeed !

No trace of them in my copy of the Pickaxe, so I feel my ignorance
partially excused.

Thanks.
 
T

Tim Connor

Indeed !

No trace of them in my copy of the Pickaxe, so I feel my ignorance
partially excused.

Thanks.

Yeah, the pickaxe is only really a jumping off point. It definitely
is slim on some of the more advanced topics, such as the full power of
mixins and dynamic code.
 
G

Gary Wright

I can't figure out how to set class variables from class methods
inherited from a module ?

The following doesn't work as I expected (I spare you my others
pathetic attempts):

module MyModule
def self.extended(base)
base.setClassVar(base.name.downcase)
end
def setClassVar(value)
puts "self is #{self.name}"
puts "@@classVar=#{value}"
@@classVar = value
end
def getClassVar
@@classVar
end
end

class Class1
extend MyModule
end
class Class2
extend MyModule
end

puts "classVar for Class1 is " + Class1.getClassVar
puts "classVar for Class2 is " + Class2.getClassVar

It gives:
self is Class1
@@classVar=class1
self is Class2
@@classVar=class2
classVar for Class1 is class2 # Shouldn't be class1 ?!
classVar for Class2 is class2

The New Haven Ruby Brigade spent an hour or so one night trying
to figure out the scoping rules for class variables.

The key to understanding them is to realize that instance variables
are dynamical scoped relative to self while class variables
are *lexically* scoped (i.e. the scoping is defined by the
syntactic structure of the code).

In your example, '@@classvar' is lexically located within MyModule.
That means it will *always* be associated with MyModule regardless
of the value of self at the time setClassVar or getClassVar are
executed.

Here is another situation that illustrates this difference:

class A
@@cvar = 'class A'
def cvar
@@cvar
end
end

puts A.new.cvar # => class A
puts A.class_eval { @@cvar } # => NameError, @@cvar undefined

The class_eval version fails because @@cvar in the block is
not lexically within the class A/end block. It is *dynamically*
in the scope of class A, but not lexically within the scope of
class A. In this case, @@cvar is resolved to the top-level
scope which is associated with Object.

MonkeeSage's suggestion to use Module#class_variable_get/set
forces the variables to be resolved relative to the receiver
and thus bypasses the lexical scoping rules for class variables.

This is another situation where the syntactic similarity of the
instance variable sigil ("@") and the class variable sigil ("@@")
causes people to think they behave in the same manner. They don't.


Gary Wright
 
M

Michael Schuerig

I can't figure out how to set class variables from class methods
inherited from a module ?

Consider if class variables are really what you want.

$ irb=> nil

@x in this case is not a class variable, it is an instance variable of
the singleton class of class C. You'll find some information on that in
the Pickaxe.

The notable difference compared to class variables proper is that class
variables are shared in the inheritance hierarchy, whereas singleton
class instance variables are not.

$ irbNameError: uninitialized class variable @@x in C
from (irb):6:in `get'
from (irb):9=> "foo"


Michael
 
M

MonkeeSage

The New Haven Ruby Brigade spent an hour or so one night trying
to figure out the scoping rules for class variables.

The key to understanding them is to realize that instance variables
are dynamical scoped relative to self while class variables
are *lexically* scoped (i.e. the scoping is defined by the
syntactic structure of the code).

In your example, '@@classvar' is lexically located within MyModule.
That means it will *always* be associated with MyModule regardless
of the value of self at the time setClassVar or getClassVar are
executed.

Here is another situation that illustrates this difference:

class A
@@cvar = 'class A'
def cvar
@@cvar
end
end

puts A.new.cvar # => class A
puts A.class_eval { @@cvar } # => NameError, @@cvar undefined

The class_eval version fails because @@cvar in the block is
not lexically within the class A/end block. It is *dynamically*
in the scope of class A, but not lexically within the scope of
class A. In this case, @@cvar is resolved to the top-level
scope which is associated with Object.

MonkeeSage's suggestion to use Module#class_variable_get/set
forces the variables to be resolved relative to the receiver
and thus bypasses the lexical scoping rules for class variables.

This is another situation where the syntactic similarity of the
instance variable sigil ("@") and the class variable sigil ("@@")
causes people to think they behave in the same manner. They don't.

Gary Wright

From the standpoint of intuitiveness, one would also expect that
whatever scoping rules class variables follow, the same would be true
of class methods; but that isn't right either. Of course, class
methods *have to* be dynamically scoped or you couldn't actually call
them (doh!)...but it's still strange to have class methods and class
variables follow different scoping rules.

Regards,
Jordan
 
B

Brubix

Consider if class variables are really what you want.

You are totally right !
It seems that what I wanted was just "instance variables of the
singleton class".

Nevertheless I found confusing the fact that they have the same sigil
of "true" instance variables (single at) and, even worse (or
better ;-), they don't get mixed with instance variables with the same
name.

Is there a place where this subject is fully explained (hopefully in
simple terms) ?

Many thanks to all !

Brubix
 

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

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,483
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top