dynamically adding class variables

S

Siemen Baader

Hi,

I am writing an object relational mapper for an educational project
where I need to add new class variables to classes in an inheritance
hierarchy. The variables hold class-specific information such as which
tables are mapped and validation logic so they cannot be shared among
the hierarchy like inherited class variables from the superclass would.
Because of that I could not declare them in the parent and just
initialize them in the subclasses. Instead, I decided to add and set
them at runtime after subclassing.

However, i think ruby behaves a bit odd here. In class methods inherited
from the superclass I can see the class variables through the method
class_variables, but to use them in the child class I have to re-open
the children and re-define the class methods which use the variables!


?> class ORM?> Article.vars
=> ["@@table"]NameError: uninitialized class variable @@table in ORM
from (irb):22:in `table?'
from (irb):31
from :0?> Article.table?
=> :articles

Could anyone enlighten me on why class_variables finds the class
variable in the subclass and why the class methods act like they were
invoked on the parent class? Apparently class_variables finds the
variables in a different way than ruby does when it evaluates the method
code...

Also, has anyone a good idea how I can give each child class variables
which are shared among all instances of the classes but isolated from
parents and other children?

thanks,
siemen baader
 
P

Phrogz

FYI:

C:\>qri class_variable_set
----------------------------------------------
Module#class_variable_set
obj.class_variable_set(symbol, obj) => obj
------------------------------------------------------------------------
Sets the class variable names by _symbol_ to _object_.

class Fred
@@foo = 99
def foo
@@foo
end
end

def Fred.foo
class_variable_set:)@@foo, 101) #=> 101
end
Fred.foo
Fred.new.foo #=> 101

However, given your problems, I would advise using instance variables
on the class itself along with accessor methods for them.

Instead of:

class Foo
@@bar = 12
def jam
p @@bar
end
end

Use:

class Foo
@bar = 12
class << self
attr_accessor :bar
end
def jam
p self.class.bar
end
end
 
A

ara.t.howard

Could anyone enlighten me on why class_variables finds the class variable in
the subclass and why the class methods act like they were invoked on the
parent class? Apparently class_variables finds the variables in a different
way than ruby does when it evaluates the method code...

@@vars are shared among all child classes. @var belong to only one class even
though methods which access them might be inherited - in otherwords you must
initialize them in each child. ruby provides not built-in way to do this.
Also, has anyone a good idea how I can give each child class variables which
are shared among all instances of the classes but isolated from parents and
other children?

traits and attributes both solve this problem completely. traits.rb is a bit
heavy-weight and offers many other features. attributes.rb is only 42 lines
of code but solves the issue you are dealing with. i reccomend using
attributes.rb

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

to use with classes just do

class Base
class << self
attribute:)a){ 42 }
end
end

class Derived < Base
end

the will setup both classes so that the default value of 'a' is 42 and it is
not shared, but it can be overriden easily too

Derived.a = 42.0

traits info

# gem install trails
http://codeforpeople.com/lib/ruby/traits/traits-0.9.2/README

regards.

-a
 
G

Gary Wright

I am writing an object relational mapper for an educational project
where I need to add new class variables to classes in an
inheritance hierarchy. The variables hold class-specific
information such as which tables are mapped and validation logic
so they cannot be shared among the hierarchy like inherited class
variables from the superclass would. Because of that I could not
declare them in the parent and just initialize them in the
subclasses. Instead, I decided to add and set them at runtime after
subclassing.

However, i think ruby behaves a bit odd here. In class methods
inherited from the superclass I can see the class variables through
the method class_variables, but to use them in the child class I
have to re-open the children and re-define the class methods which
use the variables!

Don't use Ruby 'class variables' use Ruby 'class instance variables'.

Programmers coming to Ruby and expecting per class state often gravitate
towards the mis-named 'class variables' when what they *really* want are
'class instance variables'.

I fault the misleading name and explanations in Pickaxe and The Ruby Way
for this never ending source of confusion. For example on p. 33 of
the Pickaxe:

"Sometimes classes themselves need to have their own states. This is
where class variables come in."

And on page 3 and 4 of The Ruby Way is a several paragraph
description of
per class state that consistently uses the term 'class variable' when,
with respect to Ruby, 'class instance variable' is more applicable.

Gary Wright
 
S

Siemen Baader

Thanks very much for the answers Phrogz, Ara and Gary!

I actually had it set up with class instance vars first, but they were
so longish to access (self.class.method) that I thought there had to be
a cleaner, more rubyish solution I was missing...

Also thanks for the google link, it was great help to me even though the
query is pretty obvious, at least after you see it .. :)

As for the strange beavior af class variables:

- It seems ok to me that they are singleton in the whole inheritance
tree; that's a design choice.

- But: Is it intended behaviour that they can be seen by class_variables
but not used in class methods like in my example and here:

--->
http://www.oreillynet.com/ruby/blog/2007/01/nubygems_dont_use_class_variab_1.html

- siemen
 
H

hemant

Thanks very much for the answers Phrogz, Ara and Gary!

I actually had it set up with class instance vars first, but they were
so longish to access (self.class.method) that I thought there had to be
a cleaner, more rubyish solution I was missing...

Well I have written this small bit of code, which makes use of class
instance variable still more simpler:

class Object

def self.metaclass; class << self; self; end; end

def self.iattr_accessor *args

metaclass.instance_eval do
attr_accessor *args
end

args.each do |attr|

class_eval do
define_method(attr) do
self.class.send(attr)
end
end

end
end
end

This is how you use it:

class Foobar
iattr_accessor :foo
end

Foobar.foo = "blah"
p Foobar.foo

lol = Foobar.new
p lol.foo
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,994
Messages
2,570,223
Members
46,810
Latest member
Kassie0918

Latest Threads

Top