Simple subclass question


#1

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

#2

On 5/1/07, Ball, Donald A Jr (Library) removed_email_address@domain.invalid
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

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:

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


#3

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

#4

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

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

#5

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


#6

On 5/1/07, Ball, Donald A Jr (Library) removed_email_address@domain.invalid
wrote:

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

#7

Hi –

On 5/1/07, Ball, Donald A Jr (Library) removed_email_address@domain.invalid
wrote:

class B < A
that would help me grok it fully?
Possibly; have a look at: http://www.rubypal.com/singletons.html

David


#8

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


#9

On 5/1/07, Ball, Donald A Jr (Library) removed_email_address@domain.invalid
wrote:

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


#10

On 5/2/07, Brian C. removed_email_address@domain.invalid 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

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

hth,
-Harold


#11

On 5/1/07, Logan C. removed_email_address@domain.invalid wrote:

to no avail. Also tried doing it in an initialize method. I’m still
(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.

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.


Rick DeNatale

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


#12

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

#13

On 5/2/07, Rick DeNatale removed_email_address@domain.invalid wrote:

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’


Cheers
Robert


#14

On 5/2/07, Ball, Donald A Jr (Library) removed_email_address@domain.invalid
wrote:

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

......
> 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


#15
......

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

#16

On 5/2/07, Ball, Donald A Jr (Library) removed_email_address@domain.invalid
wrote:

......

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


#17

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


#18

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 W.


#19

On 5/3/07, Gary W. removed_email_address@domain.invalid wrote:

 def inherited(other)

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

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

Gary W.

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


#20

On 5/2/07, Robert D. removed_email_address@domain.invalid wrote:

is exactly the same as

@var = ‘foo’


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!


Rick DeNatale

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