# Alternatives to class variables

**URL:** <https://www.ruby-forum.com/t/alternatives-to-class-variables/146415>\
**Category:** Ruby\
**Created:** [August 20, 2008, 8:30pm UTC](https://www.ruby-forum.com/t/alternatives-to-class-variables/146415 "2008-08-20T20:30:34Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![samhendley](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/ce73a5/32.png) [@samhendley](https://www.ruby-forum.com/u/samhendley)\
**Post date:** [August 20, 2008, 8:30pm UTC](https://www.ruby-forum.com/t/alternatives-to-class-variables/146415/1 "2008-08-20T20:30:34Z")

</div>

Hello, this is my first post to this list and I have only been using  
ruby for a couple of weeks so forgive me if its overly simple, I spent a  
few hours searching and wasn’t able to figure this out myself.

What I am trying to do is use some meta-programming techniques to create  
a simple DSL that allows me to define functions dynamically and collect  
other information that I can iterate over. I have an implementation that  
is using “Class Variables” that is working but I have to specify all of  
the functions that use the @@ variables in all of the subclasses which  
violates DRY and my sensiblities. What I am looking for is a way to have  
subclass specific information used by a common class.

Below is the implementation I have (boiled down to its simplest possible  
form with a testcase to show the behavior I need). I know there must be  
a better way to do this but my ruby-fu is not up to the task yet, im  
really interested to see what the idiomatic ruby way to do this is.

Sam H.

------------ Example.rb --------------  
require ‘test/unit’

module MethodCollector  
def self.included( klass)  
klass.extend ClassMethods  
end  
module ClassMethods  
def addpoint(array\_name, name)  
is\_defined = send(:class\_variable\_defined?, array\_name)  
if !is\_defined  
arr = Array.new  
send(:class\_variable\_set, array\_name, arr)  
elsif  
arr = send(:class\_variable\_get,array\_name)  
end  
arr \<\< name  
end

```
    def add_method(name)
        method_name = (name.to_s).to_sym
        self.send :define_method, method_name do
            "MethodName is: #{method_name}"
        end
        addpoint(:@@extra_methods, name)
    end

end

```

end

class MethodAdderBase  
include MethodCollector

```
# ideally I would define this method (and any others with other

```

common functionality that  
# use the “class specific” variables

```
#def extra_methods
# @@extra_methods
#end

```

end

class Foo \< MethodAdderBase

```
add_method :foo_1
add_method :foo_2
add_method :foo_3

def extra_methods
    @@extra_methods
end

```

end

class Bar \< MethodAdderBase

```
add_method :bar_1
add_method :bar_2
add_method :bar_3
add_method :bar_4

def extra_methods
    @@extra_methods
end

```

end

class Check \< Test::Unit::TestCase  
def test\_methods\_created  
foo = Foo.new  
bar = Bar.new

```
assert_equal(foo.foo_1, "MethodName is: foo_1")
assert_equal(bar.bar_1, "MethodName is: bar_1")

```

end  
def test\_methods\_collected  
foo = Foo.new  
bar = Bar.new

```
assert_equal(foo.extra_methods.size, 3)
assert_equal(bar.extra_methods.size, 4)

```

end  
end

---

<div class="post-metadata">

**Author:** ![samhendley](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/ce73a5/32.png) [@samhendley](https://www.ruby-forum.com/u/samhendley)\
**Post date:** [August 21, 2008, 1:00pm UTC](https://www.ruby-forum.com/t/alternatives-to-class-variables/146415/2 "2008-08-21T13:00:29Z")

</div>

2008/8/20 Sam H. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid):

> Hello, this is my first post to this list and I have only been using  
> ruby for a couple of weeks so forgive me if its overly simple, I spent a  
> few hours searching and wasn’t able to figure this out myself.

Welcome Sam. Before answering, let me ask you two more questions 🙂

1. Why are you asking the instances about the extra methods? Instead of

Foo.new.extra\_methods

I would expect

Foo.extra\_methods

Or do you want to define extra methods for individual objects, too?

1. How deep will your class hierarchy be? What about subclasses of  
Foo, for example, after

class SubFoo \< Foo  
add\_method :foo\_4  
end

what should be the result of

SubFoo.new.extra\_methods.size

BTW: the normal indentation of Ruby code is with two spaces per level.

Regards,  
Pit

---

<div class="post-metadata">

**Author:** ![samhendley](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/ce73a5/32.png) [@samhendley](https://www.ruby-forum.com/u/samhendley)\
**Post date:** [August 21, 2008, 4:30pm UTC](https://www.ruby-forum.com/t/alternatives-to-class-variables/146415/3 "2008-08-21T16:30:41Z")

</div>

Pit C. wrote:

> 2008/8/20 Sam H. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid):
> 
> > Hello, this is my first post to this list and I have only been using  
> > ruby for a couple of weeks so forgive me if its overly simple, I spent a  
> > few hours searching and wasn’t able to figure this out myself.
> 
> Welcome Sam. Before answering, let me ask you two more questions 🙂
> 
> 1. Why are you asking the instances about the extra methods? Instead of
> 
> Foo.new.extra\_methods
> 
> I would expect
> 
> Foo.extra\_methods
> 
> Or do you want to define extra methods for individual objects, too?
> 
> 1. How deep will your class hierarchy be? What about subclasses of  
> Foo, for example, after
> 
> class SubFoo \< Foo  
> add\_method :foo\_4  
> end
> 
> what should be the result of
> 
> SubFoo.new.extra\_methods.size
> 
> BTW: the normal indentation of Ruby code is with two spaces per level.
> 
> Regards,  
> Pit

Thanks for the response.

1. I am asking the individual instances for their “extra\_methods”  
because I am creating collections of these objects that higher level  
objects that use the meta-data I collect during the method declaration  
to do some cool things.

I hadn’t thought of doing something like  
instance.class.new.extra\_methods but I’m not sure if it would work  
(without being just as ugly as it is now) because of the class variable  
inheritance rules.

1. the class hierarchy won’t be any deeper than one level (Base +  
SubClass).

I’m open to anything at this point, it occured to me that I could just  
store all of the extra information directly in another object (better  
the base class?) using a hash on the subclass type. The objects  
themselves don’t need to use meta-data I’m collecting.

\< snip 5 minutes \>

Well your questions got me started down the right road and I have a  
solution that solves my immediate problem (though I am still interested  
in the “right way” that keeps the infomration with the classes  
themselves).

I used a class level hash on the base class that looks up the extra  
methods based on self.class on a call. I applied this to my “real world”  
problem as well and it worked perfectly, should have thought of this  
earlier, but I got fixated on keeping all the information in the classes  
themselves.

For completeness sake and if someone finds this on google here is my  
solution:

module MethodCollector  
def self.included( klass)  
klass.extend ClassMethods  
end  
module ClassMethods  
def addpoint(name)  
MethodAdderBase.extra\_methods[self] ||= []  
MethodAdderBase.extra\_methods[self] \<\< name  
end

```
def add_method(name)
  method_name = (name.to_s).to_sym
  self.send :define_method, method_name do
    "MethodName is: #{method_name}"
  end
  addpoint(name)
end

```

end  
end

class MethodAdderBase  
include MethodCollector  
@@extra\_methods = {}

def self.extra\_methods  
@@extra\_methods  
end

def extra\_methods  
@@extra\_methods[self.class]  
end  
end

---

<div class="post-metadata">

**Author:** ![samhendley](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/ce73a5/32.png) [@samhendley](https://www.ruby-forum.com/u/samhendley)\
**Post date:** [August 22, 2008, 7:17am UTC](https://www.ruby-forum.com/t/alternatives-to-class-variables/146415/4 "2008-08-22T07:17:18Z")

</div>

2008/8/21 Sam H. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid):

> Well your questions got me started down the right road and I have a  
> solution that solves my immediate problem

Great!

> (though I am still interested  
> in the “right way” that keeps the infomration with the classes  
> themselves).

If you don’t need to support deep class hierarchies you don’t need  
class variables, so I’d use the simpler class instance variables  
instead. I’m not sure why you decided to split the functionality into  
a module and a base class, but if you want this structure, here’s how  
I would code it (but I don’t claim to know the “right way”…)

module MethodCollector  
def self.included( klass)  
klass.extend ClassMethods  
end  
module ClassMethods  
def addpoint(name)  
extra\_methods \<\< name  
end

```
  def add_method(name)
    define_method name.to_s do
      "MethodName is: #{name}"
    end
    addpoint(name)
  end
end

```

end

class MethodAdderBase  
include MethodCollector

```
def self.extra_methods
  @extra_methods ||= []
end

def extra_methods
  self.class.extra_methods
end

```

end

Regards,  
Pit
