Simple subclass question

If I want a class and its children to have different values for the same
class variables, how would I go about making that happen?

- donald

Hi Donald, has been a long time...

504/4 > cat subclass-vars.rb && ruby subclass-vars.rb
#!/usr/bin/ruby
# vim: sts=2 sw=2 nu expandtab tw=0:

···

On 5/1/07, Ball, Donald A Jr (Library) <donald.ball@nashville.gov> wrote:

If I want a class and its children to have different values for the same
class variables, how would I go about making that happen?

- donald

#
P = Class.new { @a = 42 }
class << P
  attr_accessor :a
end

S = Class.new P

puts P.a
puts S.a
S.a = 43
puts P.a
puts S.a
------>
42
nil
42
43

Hopefully I understood what you wanted.

Cheers
Robert
--
You see things; and you say Why?
But I dream things that never were; and I say Why not?
-- George Bernard Shaw

Ball, Donald A Jr (Library) wrote:

If I want a class and its children to have different values for the same
class variables, how would I go about making that happen?

- donald

Instead of:

class A
  def A.var=(v)
    @@var = v
  end
  def A.var
    @@var
  end
end

class B < A
end

where A and B will share the class variable, use a class instance
variable instead:

class A
  def A.var=(v)
    @var = v
  end
  def A.var
    @var
  end
end

class B < A
end

where A and B will have different class instance variables.

You can use the attr_accessor notation to create class instance variable
getters and setters like this:

class A
  class << self
    attr_accessor :var
  end
end

best,
Dan

···

--
Posted via http://www.ruby-forum.com/\.

Thanks to you and Robert for the quick answers. I think I get it now; I
was a little fuzzy on the notion of class instance variables before. I
now use a construct like so:

class A
  class << self
    attr_accessor :var
  end
end

class B < A
  class << self
    def var
      @var || superclass.var
    end
  end
end

to allow subclasses's instance variables to default to their
superclass's until and unless overridden. I'll admit the class << self
syntax continues to mystify somewhat; can anyone proffer an explanation
that would help me grok it fully?

- donald

Actually, I spoke too quickly. If I use this construct:

class A
  class << self
    attr_accessor :var
  end
end

class B < A
  class << self
    def var
      @var || superclass.var
    end
  end
end

How can I initialize var in A? I've tried:

class A
  class << self
    @var = 'foo'
    attr_accessor :var
  end
end

to no avail. Also tried doing it in an initialize method. I'm still
missing something key, I think.

- donald

Thanks to you and Robert for the quick answers. I think I get it now; I
was a little fuzzy on the notion of class instance variables before. I
now use a construct like so:

class A
  class << self
    attr_accessor :var
  end
end

class B < A
  class << self
    def var
      @var || superclass.var
    end
  end
end

to allow subclasses's instance variables to default to their
superclass's until and unless overridden.

That was the point I missed, but of course you did that nicely :).
I'll admit the class << self

syntax continues to mystify somewhat; can anyone proffer an explanation
that would help me grok it fully?

I will take a chance by saying that

class << A
  def x...
  end
end

is the same as

class A
   def self.x
   end
end

although there might be some subtle differences

Do e.g, this
class << A
puts self
end
you can see kind of a Proxy object, but it behaves pretty much transparently.

Cheers
Robert

···

On 5/1/07, Ball, Donald A Jr (Library) <donald.ball@nashville.gov> wrote:

--
You see things; and you say Why?
But I dream things that never were; and I say Why not?
-- George Bernard Shaw

Hi --

···

On 5/1/07, Ball, Donald A Jr (Library) <donald.ball@nashville.gov> wrote:

Thanks to you and Robert for the quick answers. I think I get it now; I
was a little fuzzy on the notion of class instance variables before. I
now use a construct like so:

class A
  class << self
    attr_accessor :var
  end
end

class B < A
  class << self
    def var
      @var || superclass.var
    end
  end
end

to allow subclasses's instance variables to default to their
superclass's until and unless overridden. I'll admit the class << self
syntax continues to mystify somewhat; can anyone proffer an explanation
that would help me grok it fully?

Possibly; have a look at: http://www.rubypal.com/singletons.html

David

--
Upcoming Rails training by Ruby Power and Light:
  Four-day Intro to Intermediate
  May 8-11, 2007
  Edison, NJ
  http://www.rubypal.com/events/05082007

···

On Wed, May 02, 2007 at 06:33:57AM +0900, Ball, Donald A Jr (Library) wrote:

I'll admit the class << self
syntax continues to mystify somewhat; can anyone proffer an explanation
that would help me grok it fully?

class A
  class << self
    @var = 'foo'
    attr_accessor :var
  end
end

to no avail. Also tried doing it in an initialize method. I'm still
missing something key, I think.

class A
  @var = 'foo'
  class << self
     attr_accessor :var
  end
end

(warning, the following explanation is not really correct)
class << self is sort of A's class (the class of the class) (It's actually
A's singleton class). By putting @var='foo' inside class << self you set the
class of A's instance var @var to 'foo' not A's @var to foo.

- donald

···

On 5/1/07, Ball, Donald A Jr (Library) <donald.ball@nashville.gov> wrote:

I always liked this explanation:
http://whytheluckystiff.net/articles/seeingMetaclassesClearly.html

hth,
-Harold

···

On 5/2/07, Brian Candler <B.Candler@pobox.com> wrote:

On Wed, May 02, 2007 at 06:33:57AM +0900, Ball, Donald A Jr (Library) wrote:
> I'll admit the class << self
> syntax continues to mystify somewhat; can anyone proffer an explanation
> that would help me grok it fully?

http://www.rubygarden.org/ruby?SingletonTutorial

Going a little further in answering Donald's question.

Instance variables come into being when they are initialized by code
running in the context of the instance. Usually that's method code.

This works in the case of class methods, class instance variables are
just instance variables of the class.

However since, I think what we are looking for is a way to initialize
class instance variables in a way analogous to class variables, we
really don't always want to define a method. Here's one way to
accomplish class instance variable intialization in-line in a class
(re)definition.

class A
   instance_eval {@var = 'foo'}
   class << self
     attr_accessor :var
   end
end

since within the class (re)definition self IS the class, instance_eval
runs the block in the context of the class.

···

On 5/1/07, Logan Capaldo <logancapaldo@gmail.com> wrote:

On 5/1/07, Ball, Donald A Jr (Library) <donald.ball@nashville.gov> wrote:
>
> class A
> class << self
> @var = 'foo'
> attr_accessor :var
> end
> end
>
> to no avail. Also tried doing it in an initialize method. I'm still
> missing something key, I think.

class A
  @var = 'foo'
  class << self
     attr_accessor :var
  end
end

(warning, the following explanation is not really correct)
class << self is sort of A's class (the class of the class) (It's actually
A's singleton class). By putting @var='foo' inside class << self you set the
class of A's instance var @var to 'foo' not A's @var to foo.

--
Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/

<snip>

class A
   instance_eval {@var = 'foo'}

I guess I lost you here Rick, is this for didactic purpose, or am I
wrong by saying that

instance_eval { @var = 'foo' }

is *exactly* the same as

@var = 'foo'

....
<snip>

Cheers
Robert

···

On 5/2/07, Rick DeNatale <rick.denatale@gmail.com> wrote:

--
You see things; and you say Why?
But I dream things that never were; and I say Why not?
-- George Bernard Shaw

Okay, so maybe this turns out to be a not-so-simple subclass question
after all. I appreciate all the thoughtful answers and links, I'm pretty
close to wrapping my head around it all. I'm still having trouble with
class instance variable initialization, and have come up with code
sample which should illustrate the point:

class Breakfast
  def self.add_food(*args)
    @foods ||= []
    args.each do |arg|
      @foods << arg
    end
  end

  def self.foods
    if superclass.respond_to?(:foods)
      superclass.foods + @foods
    else
      @foods
    end
  end

  add_food :eggs
end

class RubyBreakfast < Breakfast
  add_food :grapefruit, :chunky_bacon
end

class BoringBreakfast < Breakfast
  add_food :dry_toast, :gruel
end

Everybody loves breakfast, right? This code works great, except... maybe
I don't want Breakfast to have :eggs by default, but if I remove the
add_food :eggs from Breakfast, then Breakfast.foods returns nil instead
of [] since @foods isn't initialized until add_food is called, and
RubyBreakfast.foods throws an exception trying to its delicious foods
array to nil. But I'll be damned if I can figure out how to properly
initialize @foods in Breakfast. Can someone point me in the proper
direction?

- donald

D'oh! You're right of course.

It's like those times when I ask my wife if she's seen my eyeglasses
and she tells me that I'm wearing them!

···

On 5/2/07, Robert Dober <robert.dober@gmail.com> wrote:

On 5/2/07, Rick DeNatale <rick.denatale@gmail.com> wrote:
<snip>
>
> class A
> instance_eval {@var = 'foo'}
I guess I lost you here Rick, is this for didactic purpose, or am I
wrong by saying that

instance_eval { @var = 'foo' }

is *exactly* the same as

@var = 'foo'

....
<snip>

--
Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/

Okay, so maybe this turns out to be a not-so-simple subclass question
after all. I appreciate all the thoughtful answers and links, I'm pretty
close to wrapping my head around it all. I'm still having trouble with
class instance variable initialization, and have come up with code
sample which should illustrate the point:

class Breakfast
  def self.add_food(*args)
    @foods ||=

just initialize @foods in the class

class Breakfast
    @foods = # class instance variable, I did this in my original example
                         # because of (1)
    def self.add_food( *args)
          @foods += args
    end

    ......
<snip>

Everybody loves breakfast, right? This code works great, except... maybe
I don't want Breakfast to have :eggs by default, but if I remove the
add_food :eggs from Breakfast, then Breakfast.foods returns nil instead
of since @foods isn't initialized until add_food is called, and
RubyBreakfast.foods throws an exception trying to its delicious foods
array to nil. But I'll be damned if I can figure out how to properly
initialize @foods in Breakfast. Can someone point me in the proper
direction?

Hopefully I did, if not continue asking I am a bad teacher, I know :(.

(1)

class A
    @cl_inst_var = 42
end

is the same (unless class A has been defined before) as

A = Class.new { @cl_inst_var = 42 }

Cheers
Robert

···

On 5/2/07, Ball, Donald A Jr (Library) <donald.ball@nashville.gov> wrote:

--
You see things; and you say Why?
But I dream things that never were; and I say Why not?
-- George Bernard Shaw

just initialize @foods in the class

class Breakfast
    @foods = # class instance variable, I did this in my
original example
                         # because of (1)
    def self.add_food( *args)
          @foods += args
    end

    ......

but then @foods doesn't exist in RubyBreakfast and BoringBreakfast. I
mean, sure, I could cut'n'paste @foods = in each of them, but that
doesn't smell right. In the superclass, I want a chunk of code that
initializes a class instance variable in itself and each of its class
descendents.

In point of fact, I can work around the @foods.nil? case in the
self.foods method, but I'd rather figure out either how to do this or
why it's a bad idea to do it. :slight_smile:

- donald

Ball, Donald A Jr (Library) schrieb:

In point of fact, I can work around the @foods.nil? case in the
self.foods method, but I'd rather figure out either how to do this or
why it's a bad idea to do it. :slight_smile:

Donald, I would add

   def self.my_foods
     @foods ||=
   end

and then use my_foods instead of @foods in the other two methods. You could also take a look at Ara's attributes library:

   http://codeforpeople.com/lib/ruby/attributes/attributes-3.2.0/README

Regards,
Pit

> just initialize @foods in the class
>
> class Breakfast
> @foods = # class instance variable, I did this in my
> original example
> # because of (1)
> def self.add_food( *args)
> @foods += args
> end
>
> ......

but then @foods doesn't exist in RubyBreakfast and BoringBreakfast. I
mean, sure, I could cut'n'paste @foods = in each of them, but that
doesn't smell right. In the superclass, I want a chunk of code that
initializes a class instance variable in itself and each of its class
descendents.

You mean you want to have your cake *and* eat it ;)?
Sure enough, but there is no simple solution, you could do something like this

class Module
    def inherit_cl_inst_vars # refinement via args as you wish
        superclass.instance_variables.each do
             > ivar |
             instance_variable_set ivar,
superclass.instance_variable_get( ivar )
        end
    end
end

class A
  @a = 42
end

class B < A
  inherit_cl_inst_vars
end

class << A
  attr_accessor :a
end

puts A.a
puts B.a
B.a=1764
puts A.a
puts B.a

In point of fact, I can work around the @foods.nil? case in the
self.foods method, but I'd rather figure out either how to do this or
why it's a bad idea to do it. :slight_smile:

- donald

Robert

···

On 5/2/07, Ball, Donald A Jr (Library) <donald.ball@nashville.gov> wrote:
--
You see things; and you say Why?
But I dream things that never were; and I say Why not?
-- George Bernard Shaw

You can use #inherited to trigger your own class initialization:

class Base
   class <<self
     def inherited(other)
       other.instance_eval { initialize_class }
     end
     def initialize_class
       @foo = 42
     end
     attr_accessor :foo
   end
   initialize_class
end

class Subclass < Base
   puts "foo is #{@foo}" # 42
end

puts Subclass.foo # 42
puts Class.new(Base).foo # 42

Gary Wright

···

On May 2, 2007, at 2:57 PM, Ball, Donald A Jr (Library) wrote:

In the superclass, I want a chunk of code that
initializes a class instance variable in itself and each of its class
descendents.

Wow that scales much better than mine Gary, nice.
However maybe you want to inherit @foo dynamically, that is
I suggest this slight adaptation of Base

529/29 > cat inherited.rb && ruby inherited.rb
# vim: sts=2 sw=2 expandtab tw=0 nu:
class Base
  @foo = 42
  class <<self
    def inherited(other)
      other.instance_eval { initialize_class }
    end
    def initialize_class
      @foo = superclass.instance_variable_get("@foo")
    end
    attr_accessor :foo
  end
end

class Subclass < Base
  puts "Sub foo is #{@foo}" # 42
end
Base.foo = 1764
class Another < Base
  puts "Another foo is #{@foo}" # 42
end

puts Subclass.foo # 42
puts Class.new(Base).foo # 42
Sub foo is 42
Another foo is 1764
42
1764

What do you think, anyway the two behaviors might be what you want, I
actually need the second behavior.

Cheers
Robert

···

On 5/3/07, Gary Wright <gwtmp01@mac.com> wrote:

On May 2, 2007, at 2:57 PM, Ball, Donald A Jr (Library) wrote:
> In the superclass, I want a chunk of code that
> initializes a class instance variable in itself and each of its class
> descendents.

You can use #inherited to trigger your own class initialization:

class Base
   class <<self
     def inherited(other)
       other.instance_eval { initialize_class }
     end
     def initialize_class
       @foo = 42
     end
     attr_accessor :foo
   end
   initialize_class
end

class Subclass < Base
   puts "foo is #{@foo}" # 42
end

puts Subclass.foo # 42
puts Class.new(Base).foo # 42

Gary Wright

--
You see things; and you say Why?
But I dream things that never were; and I say Why not?
-- George Bernard Shaw