Dynamically adding class variables

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
>> def ORM.table table
>> class_eval("@@table = :%s" % table)
>> end
>>
?> def ORM.vars
>> class_variables
>> end
>> def ORM.table?
>> @@table
>> end
>> end
=> nil
>>
?> class Article < ORM
>> table :articles
>> end
=> :articles
>>
?> Article.vars
=> ["@@table"]
>> Article.table?
NameError: uninitialized class variable @@table in ORM
         from (irb):22:in `table?'
         from (irb):31
>>
?> class Article
>> def Article.table?
>> @@table
>> end
>> end
=> nil
>>
?> 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

···

from :0

http://www.google.com/search?q=ruby-talk+class+variables+shared

···

On Mar 27, 4:48 pm, Siemen Baader <sbaa...@ruc.dk> wrote:

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!

FYI:

C:\>qri class_variable_set

···

On Mar 27, 4:48 pm, Siemen Baader <sbaa...@ruc.dk> wrote:

>> def ORM.table table
>> class_eval("@@table = :%s" % table)
>> end

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

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

···

On Wed, 28 Mar 2007, Siemen Baader wrote:
--
be kind whenever possible... it is always possible.
- the dalai lama

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

···

On Mar 27, 2007, at 6:50 PM, Siemen Baader wrote:

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!

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

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

  # gem install trails

                   ^^^^^^

  http://codeforpeople.com/lib/ruby/traits/traits-0.9.2/README

Hey Ara, when can we expect ruby-on-trails? :stuck_out_tongue:

···

ara.t.howard@noaa.gov wrote:

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

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

···

On 3/28/07, Siemen Baader <sbaader@ruc.dk> wrote:

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

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:

--->
O'Reilly Media - Technology and Business Training

- siemen

--
gnufied
-----------
There was only one Road; that it was like a great river: its springs
were at every doorstep, and every path was its tributary.
http://people.inxsasia.com/~hemant

lol - whoops!

-a

···

On Wed, 28 Mar 2007, Joel VanderWerf wrote:

ara.t.howard@noaa.gov wrote:

  # gem install trails

                 ^^^^^^

  http://codeforpeople.com/lib/ruby/traits/traits-0.9.2/README

Hey Ara, when can we expect ruby-on-trails? :stuck_out_tongue:

--
be kind whenever possible... it is always possible.
- the dalai lama