[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

Mixins and class variables

Brubix

12/16/2007 3:25:00 PM

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

7 Answers

MonkeeSage

12/16/2007 4:54:00 PM

0

On Dec 16, 9:25 am, Brubix <bruno.bazz...@tin.it> wrote:
> 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

Brubix

12/16/2007 5:34:00 PM

0

> HTH,

Indeed !

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

Thanks.

Tim Connor

12/16/2007 5:43:00 PM

0

On Dec 16, 9:33 am, Brubix <bruno.bazz...@tin.it> wrote:
> > HTH,
>
> 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.

Gary Wright

12/16/2007 8:08:00 PM

0


On Dec 16, 2007, at 10:25 AM, Brubix wrote:
> 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

Michael Schuerig

12/16/2007 8:14:00 PM

0

On Sunday 16 December 2007, Brubix wrote:
> 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
>> module M
>> def set(x)
>> @x = x
>> end
>> def get
>> @x
>> end
>> end
=> nil
>> class C
>> extend M
>> end
=> C
>> C.get
=> nil
>> C.set('bla')
=> "bla"
>> C.get
=> "bla"
>> class D < C; end
=> nil
>> D.get
=> 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.

$ irb
>> class C
>> def self.set(x)
>> @@x = x
>> end
>> def self.get
>> @@x
>> end
>> end
=> nil
>> C.get
NameError: uninitialized class variable @@x in C
from (irb):6:in `get'
from (irb):9
>> C.set('bla')
=> "bla"
>> C.get
=> "bla"
>> class D < C; end
=> nil
>> C.get
=> "bla"
>> D.set('foo')
=> "foo"
>> C.get
=> "foo"


Michael

--
Michael Schuerig
mailto:michael@schuerig.de
http://www.schuerig.d...

MonkeeSage

12/16/2007 8:56:00 PM

0

On Dec 16, 2:07 pm, Gary Wright <gwtm...@mac.com> wrote:
> On Dec 16, 2007, at 10:25 AM, Brubix wrote:
>
>
>
> > 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

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

Brubix

12/17/2007 11:07:00 AM

0

On Dec 16, 9:14 pm, Michael Schuerig <mich...@schuerig.de> wrote:
> 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