Inheriting class-based state

This one puzzles me - I know there's something fundamental that I'm not
understanding about Ruby's object model. Consider these classes:

class Base
  def self.my_name=(value)
    @my_name = value
  end
  def self.my_name
    @my_name
  end
end

class Derived < Base; end

Base.my_name = 'John'
puts Base.my_name
John

Derived.my_name
nil
Derived.my_name = 'Mike'
puts Derived.my_name
Mike

So the class instance member @my_name isn't inherited by the Derived class.

How do I share state between a derived class and its base class?

Thanks
-John
http://www.iunknown.com

@ means instance member variable
@@ means class member variable

Just change @my_name to @@my_name and you'll be good to go.

David Koontz

···

This one puzzles me - I know there's something fundamental that I'm not
understanding about Ruby's object model. Consider these classes:

class Base
def self.my_name=(value)
   @my_name = value
end
def self.my_name
   @my_name
end
end

class Derived < Base; end

Base.my_name = 'John'
puts Base.my_name
John

Derived.my_name
nil
Derived.my_name = 'Mike'
puts Derived.my_name
Mike

So the class instance member @my_name isn't inherited by the Derived class.

How do I share state between a derived class and its base class?

Thanks
-John
http://www.iunknown.com

John Lam wrote:

This one puzzles me - I know there's something fundamental that I'm not
understanding about Ruby's object model. Consider these classes:

class Base
def self.my_name=(value)
   @my_name = value
end
def self.my_name
   @my_name
end
end

class Derived < Base; end

Base.my_name = 'John'
puts Base.my_name
John

Derived.my_name
nil
Derived.my_name = 'Mike'
puts Derived.my_name
Mike

So the class instance member @my_name isn't inherited by the Derived class.

That's right. The #my_name and #my_name= methods are inherited, but when
you call them on Derived, they operate on the instance variables of Derived.

How do I share state between a derived class and its base class?

Well, there are "class variables", written @@my_name. But they can have
some surprising behaviors in current ruby(*), and in future ruby they
will not be shared up and down the inheritance hierarchy (they will only
be shared between a class and its instances).

An alternative:

class A
  class << self
    attr_accessor :__my_name

    def my_name; A.__my_name; end
    def my_name=(n); A.__my_name = n; end
  end
end

class B < A
end

B.my_name = "fred"

p A.my_name # ==> "fred"

Or, if you don't want __my_name polluting the namespace, you could use this:

class A
  class << self
    def my_name; A.instance_variable_get(:@my_name); end
    def my_name=(n); A.instance_variable_set(:@my_name, n); end
  end
end

class B < A
end

B.my_name = "fred"

p A.my_name

The key difference between these two examples and your original attempt
is that, here, the "A" reference binds _statically_ to the class A,
whereas, in the original code, the "@my_name" reference binds
_dynamically_ to the instance on which my_name is called (which might
turn out to be A, or B or ...).

HTH.

(*) search the archives--briefly, if you assign to a subclass's @@x, and
then assign to the superclass's @x you get two different variables.
Reverse the order of assignment, and there is only one variable:

irb(main):014:0> class A; end
=> nil
irb(main):015:0> class B<A; end
=> nil
irb(main):016:0> class A; @@x=1; end
=> 1
irb(main):017:0> class B; p @@x; end
1
=> nil
irb(main):018:0> class B; @@y = 2; end
=> 2
irb(main):019:0> class A; p @@y; end
NameError: uninitialized class variable @@y in A
        from (irb):19
irb(main):020:0> class A; @@y = 3; end
=> 3
irb(main):021:0> class B; p @@y; end
2
=> nil

···

--
      vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407

Thanks, Joel - this is exactly what I needed to fix some ugliness in
RubyCLR.

I was trying to get class variables to work earlier, and actually ran across
the problem that you mentioned in your (*). I just didn't know it at the
time :slight_smile:

Cheers,
-John
http://www.iunknown.com

···

On 5/12/06, Joel VanderWerf <vjoel@path.berkeley.edu> wrote:

An alternative:

class A
  class << self
    attr_accessor :__my_name

    def my_name; A.__my_name; end
    def my_name=(n); A.__my_name = n; end
  end
end

class B < A
end

B.my_name = "fred"

p A.my_name # ==> "fred"

Or, if you don't want __my_name polluting the namespace, you could use
this:

class A
  class << self
    def my_name; A.instance_variable_get(:@my_name); end
    def my_name=(n); A.instance_variable_set(:@my_name, n); end
  end
end

class B < A
end

B.my_name = "fred"

p A.my_name

The key difference between these two examples and your original attempt
is that, here, the "A" reference binds _statically_ to the class A,
whereas, in the original code, the "@my_name" reference binds
_dynamically_ to the instance on which my_name is called (which might
turn out to be A, or B or ...).

HTH.

one can just use traits for this:

   harp:~ > gem install traits

   harp:~ > cat a.rb
   require 'traits'
   class A; class_trait 'my_name' => 'fred'; end
   class B < A; end

   p A.my_name
   p B.my_name

   B.my_name = 'joe'

   p A.my_name
   p B.my_name

   harp:~ > ruby a.rb
   "fred"
   "joe"

voila - best of both worlds - shared __and__ overridable values if required.

-a

···

On Sat, 13 May 2006, Joel VanderWerf wrote:

Well, there are "class variables", written @@my_name. But they can have
some surprising behaviors in current ruby(*), and in future ruby they
will not be shared up and down the inheritance hierarchy (they will only
be shared between a class and its instances).

An alternative:

class A
class << self
   attr_accessor :__my_name

   def my_name; A.__my_name; end
   def my_name=(n); A.__my_name = n; end
end
end

class B < A
end

B.my_name = "fred"

p A.my_name # ==> "fred"

--
be kind whenever possible... it is always possible.
- h.h. the 14th dali lama