Mixins and class variables

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

···

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

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

···

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

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

···

On Sunday 16 December 2007, Brubix wrote:

--
Michael Schuerig
mailto:michael@schuerig.de
Michael Schürig | Sentenced to making sense

HTH,

Indeed !

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

Thanks.

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

···

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

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

···

On Dec 16, 9:14 pm, Michael Schuerig <mich...@schuerig.de> wrote:

Consider if class variables are really what you want.

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.

···

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.