Class variables, module inclusion and instance_eval

Hi all,
I’ve been writing a library that one uses by including it in one’s own
classes. The purpose of the library is not important - the problem is to
do with including modules in classes, in particular to do with class
variables.

Exhibit A:

irb(main):001:0> module Mod
irb(main):002:1> @@foo = "Mod"
irb(main):003:1> end
=> "Mod"
irb(main):004:0> class KlassA
irb(main):005:1> include Mod
irb(main):006:1> end
=> KlassA
irb(main):007:0> class KlassB
irb(main):008:1> include Mod
irb(main):009:1> @@foo = "B"
irb(main):010:1> end
=> "B"
irb(main):011:0> class KlassA
irb(main):012:1> puts @@foo
irb(main):013:1> end
B
=> nil

If I create a class variable from within a module, that variable is
shared between all classes that include it. So far, so good - this is
useful.

Exhibit B:

irb(main):001:0> module Mod
irb(main):002:1> end
=> nil
irb(main):003:0> class KlassA
irb(main):004:1> include Mod
irb(main):005:1> @@foo = "A"
irb(main):006:1> end
=> "A"
irb(main):007:0> class KlassB
irb(main):008:1> include Mod
irb(main):009:1> @@foo = "B"
irb(main):010:1> end
=> "B"
irb(main):011:0> class KlassA
irb(main):012:1> puts @@foo
irb(main):013:1> end
A
=> nil

If I create a class variable from within a class, then no matter how
many modules or superclasses the classes have in common, it doesn’t get
shared. No surprises thus far - POLS, and all.

Exhibit B.2:

irb(main):014:0> module Mod
irb(main):015:1> @@foo = "Mod"
irb(main):016:1> end
=> "Mod"
irb(main):017:0> class KlassA
irb(main):018:1> puts @@foo
irb(main):019:1> end
A
=> nil
irb(main):020:0> class KlassB
irb(main):021:1> puts @@foo
irb(main):022:1> end
B
=> nil

Even if I subsequently define the variable in the module, it still
doesn’t get shared. This is probably also POLS - having variables
clobber each other later is somewhat surprising.

Now, here comes the fun bit. When you include a module in a class, the
module’s append_features method gets called, and the class passed as an
argument. Watch this:

irb(main):001:0> module Mod
irb(main):002:1> def self.append_features(aModule)
irb(main):003:2> super
irb(main):004:2> aModule.instance_eval do
irb(main):005:3* @@foo = aModule.name
irb(main):006:3> end
irb(main):007:2> end
irb(main):008:1> end
=> nil
irb(main):009:0> class KlassA
irb(main):010:1> include Mod
irb(main):011:1> end
=> KlassA
irb(main):012:0> class KlassB
irb(main):013:1> include Mod
irb(main):014:1> end
=> KlassB
irb(main):015:0> class KlassA
irb(main):016:1> puts @@foo
irb(main):017:1> end
KlassB
=> nil

Somehow, even though the class variable is defined within the context of
the class (via instance_eval) it still gets shared, contrary to the
example of Exhibit B. This is frustrating me, because I want my library
to be able to define a class variable that belongs to only one class,
and I can’t find a way to do it. Currently, I’m doing this:

irb(main):001:0> module Mod
irb(main):002:1> @@foo = {}
irb(main):003:1> def self.append_features(aModule)
irb(main):004:2> super
irb(main):005:2> aModule.module_eval do
irb(main):006:3* @@foo[id] = aModule.name
irb(main):007:3> end
irb(main):008:2> end
irb(main):009:1> end
=> nil
irb(main):010:0> class KlassA
irb(main):011:1> include Mod
irb(main):012:1> end
=> KlassA
irb(main):013:0> class KlassB
irb(main):014:1> include Mod
irb(main):015:1> end
=> KlassB
irb(main):016:0> class KlassA
irb(main):017:1> puts @@foo[id]
irb(main):018:1> end
KlassA
=> nil

It works, but it’s messy and hackish and breaks encapsulation - if you
know the class’s id, you can get at its “class variable”. Can anyone see
a better way to do this, and/or explain to me why Ruby behaves this way
in the first place?

Tim Bates

···


tim@bates.id.au

I can’t answer your clearly-phrased questions on class variables, but there
might be another solution: class instance variables. They are private to the
class, not even inherited by subclasses. They’re not particularly convenient
to use though, because within an instance method, ‘@foo’ refers to the
instance variable of the instance of A, not of the class of A.

But they do work:

module Foo
def self.append_features(aModule)
super
aModule.instance_eval { @foo = Time.now }
end
def foo
self.class.instance_eval { @foo }
end
end

class A
include Foo
end

p A.new.foo

sleep 2

class B
include Foo
end
p B.new.foo

p A.new.foo

Regards,

Brian.

···

On Wed, Aug 06, 2003 at 02:55:31PM +0900, Tim Bates wrote:

Somehow, even though the class variable is defined within the context of
the class (via instance_eval) it still gets shared, contrary to the
example of Exhibit B. This is frustrating me, because I want my library
to be able to define a class variable that belongs to only one class,
and I can’t find a way to do it.

Tim Bates wrote:

irb(main):001:0> module Mod
irb(main):002:1> def self.append_features(aModule)
irb(main):003:2> super
irb(main):004:2> aModule.instance_eval do
irb(main):005:3* @@foo = aModule.name
irb(main):006:3> end
irb(main):007:2> end
irb(main):008:1> end
=> nil
irb(main):009:0> class KlassA
irb(main):010:1> include Mod
irb(main):011:1> end
=> KlassA
irb(main):012:0> class KlassB
irb(main):013:1> include Mod
irb(main):014:1> end
=> KlassB
irb(main):015:0> class KlassA
irb(main):016:1> puts @@foo
irb(main):017:1> end
KlassB
=> nil

Somehow, even though the class variable is defined within the context of
the class (via instance_eval) it still gets shared, contrary to the
example of Exhibit B. This is frustrating me, because I want my library
to be able to define a class variable that belongs to only one class,
and I can’t find a way to do it. Currently, I’m doing this:

This has to do with Ruby’s scoping rules - here is an example I changed
append_feature'' to the more modern included’’

···

module Mod
def self.included(aModule)
# the string version creates the expected
# module aModule … end
# scope
aModule.module_eval “@@foo = #{aModule.name}”
end
end

class A
include Mod
end

module Mod
begin
@@foo
rescue NameError => mes
puts mes
end
end

class B
include Mod

end

class A
puts @@foo
end

your way

module Nod
def self.included(aModule)
# scope of @@bar is module Nod … end
aModule.module_eval do @@bar = aModule.name end
end
end

class A
include Nod
end

module Nod
puts “@@bar == #{@@bar} defined in #{self}”
end

class B
include Nod
end

class A
puts @@bar
end

results in


uninitialized class variable @@foo in Mod
A
@@bar == A defined in Nod
B

/Christoph

Christoph R. wrote:

This has to do with Ruby’s scoping rules - here is an example I changed
append_feature'' to the more modern included’’

I maybe the following example is instructive too

···

module Nod
end

class A
def Nod.included(aModule)
# scope of @@bar is class A … end
aModule.module_eval do @@bar = aModule.name end
end
end

class B
include Nod
end

class A
puts @@bar
end

However

class B
begin
@@bar
rescue => mes
puts mes
end
end

results in


B
uninitialized class variable @@bar in B

/Christoph

This has to do with Ruby’s scoping rules - here is an example I changed
append_feature'' to the more modern included’’

Hmm, I can’t find references to “included” anywhere, although I admit I
haven’t looked very hard. Where is this stuff (and by that I mean
special methods that are called in certain circumstances) documented? I
only found append_features because it happened to be mentioned in the ri
documentation of “include” - and it’s very useful.

# the string version creates the expected
#  module aModule  .. end
# scope

Okay, this works. My immediate question is, why? That’s not quite POLS.
I started out using the block form, because the stuff I was doing in it
wasn’t dynamic. But as soon as I want to start substituting variables
into it, I’m going to be using the string form - and suddenly the
semantics of my code changes. What gives?

Tim Bates

···

On Wed, Aug 06, 2003 at 06:41:57PM +0900, Christoph R. wrote:

tim@bates.id.au

Unless I’m mistaken, it’s because, when you use the string form, you’re
delaying
interpretation until the call, unlike the other way that interprets
during declaration.
When you interpret during the declaration of the class, you associate
the scope
of your variables with that class. This makes sense, more-or-less…
variables
are scoped where they are found.

Evaluated strings don’t have this attribute attached to them, it
doesn’t make sense
to give a scope to the variables inside the string, until they’re
actually executed, then
they should be associated where they land.

Maybe it’s because I’ve been playing with Ruby a lot lately, and look
at the new
interpreter’s guts a lot of late, but I find that situation to contain
a minimal amount
of surprise. I expect declared variables to be scoped where they fall.

···

On Wednesday, August 6, 2003, at 6:41 AM, Tim Bates wrote:

Okay, this works. My immediate question is, why? That’s not quite POLS.
I started out using the block form, because the stuff I was doing in it
wasn’t dynamic. But as soon as I want to start substituting variables
into it, I’m going to be using the string form - and suddenly the
semantics of my code changes. What gives?


Dave Fayram
kirindave@lensmen.net
dfayra00@umail.ucsb.edu
Developer / Idealist