# Variable class idea and request for critique

**URL:** <https://www.ruby-forum.com/t/variable-class-idea-and-request-for-critique/60629>\
**Category:** Ruby\
**Created:** [May 16, 2006, 11:10am UTC](https://www.ruby-forum.com/t/variable-class-idea-and-request-for-critique/60629 "2006-05-16T11:10:43Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![Edmund\_Jorgensen](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/e/bc8723/32.png) [@Edmund\_Jorgensen](https://www.ruby-forum.com/u/Edmund_Jorgensen)\
**Post date:** [May 16, 2006, 11:10am UTC](https://www.ruby-forum.com/t/variable-class-idea-and-request-for-critique/60629/1 "2006-05-16T11:10:43Z")

</div>

Hi all,

I’m fairly new to Ruby but really enjoying it. I’ve been working on a  
medium large project that is non rails but uses ActiveRecord and  
assorted other pieces of the rails framework. I’d been finding myself  
having more and more difficulty writing unit tests as I went because  
many of my classes were calling class methods of other classes  
internally–such as the models from rails–and these were hard to factor  
out. For example:

class ImportantModel \< ActiveRecord::Base  
end

class Processor  
def self.do\_hard\_processing(something)  
#…  
end  
end

class SomeClass

```
def a_method
         #...
         something = ImportantModel.find_by_a_lot_of_stuff(one, two)
         processed = Processor.do_hard_processing(something)
         if _the_time_is_right?(processed)
            #...etc
    end

```

end

If those had been objects I could have swapped mocks in for them when I  
wanted to test SomeClass, but since they’re classes–constants by  
definition–I couldn’t! I know about fixtures to help with this  
problem, but sometimes (as in the case of the Processor class) they  
aren’t really possible, and sometimes you really don’t want to involve  
the working of a model…you just want to test SomeClass’s logic with  
some hard coded mock objects.

One solution is pretty simple:

class SomeClass

```
attr_accessor :important_model
    attr_accessor :processor

def initialize
   @important_model = ImportantModel
       @processor = Processor
    end

def a_method
         something =
	important_model.find_by_a_lot_of_stuff(one, two)
         processed = processor.do_hard_processing(something)
         if _the_time_is_right?(processed)
            #...etc
    end

```

end

This is an improvement, because now in a testing environment, for  
instance, you could set

sc = SomeClass.new  
sc.important\_model = HardCodedModel  
#or even  
sc.imporant\_model = mockObjectOfSomeSort  
#since there’s nothing says it has to be a class

But there were two things I didn’t like: one, the repetetive  
nature–you’re writing some variation of important\_model 3 times, for  
each class you want to make variable Second of all, I wanted to make it  
easy to decide on the class/object returned conditionally or  
dynamically, with a block, which can save you a lot of time in testing  
code (and could be useful in production, for that matter).

The solution I came up with was to add a class method called  
variable\_class to Object, which would provide an indirect way to refer  
to classes like ImportantModel or Processor (or even allow them to be  
not classes at all but arbitrary objects), and which had support for a  
block.

require ‘rubygems’  
require ‘active\_support/inflector’

class Object

def self.variable\_class(klass, accessor\_name = nil, &block)  
klass\_str = klass.name  
klass\_sym = klass\_str.to\_sym  
klass\_translated\_str =  
“#{Inflector.underscore(klass\_str.tr\_s(’::’,’\_’))}\_class”  
instance\_var\_sym = “@#{klass\_translated\_str}”.to\_sym

```
get_method_sym = accessor_name ? accessor_name.to_sym :
                                 klass_translated_str.to_sym
set_method_sym = "#{get_method_sym.to_s}=".to_sym

if block_given?

  define_method(get_method_sym) do
    block.call(self)
  end

else

  define_method(get_method_sym) do
    instance_variable_get(instance_var_sym) ||

```

instance\_variable\_set(instance\_var\_sym,

klass)  
end

```
  define_method(set_method_sym) do |custom_class|
    instance_variable_set(instance_var_sym, custom_class)
  end

end

```

end

Basically, given two classes (here I’m just going to make them classes  
and not note the object possibility anymore)

class One  
def self.do  
puts “one”  
end  
end

class Two  
def self.do  
puts “two”  
end  
end

All the following will work:

class SimplestCase  
variable\_class One

def test  
one\_class.do  
end  
end

sc = SimplestCase.new  
sc.test #“one”  
sc.one\_class = Two  
sc.test #“two”

The default is to make the class variable settable/gettable with the  
underscored name of the class plus \_class. Any :: operators get turned  
into underscores as well, so Mod::KlassName would become  
mod\_klass\_name\_class by default.

If you need a shorter/different name, you can pass it as a symbol in the  
second argument slot.

class AlmostAsSimple  
variable\_class One, :my\_own\_name

def test  
my\_own\_name.do  
end  
end

sc = AlmostAsSimple.new  
sc.test #“one”  
sc.my\_own\_name = Two  
sc.test #“two”

If you want to use a block, you can do it like so:

class BlockExample  
@@use\_two = false

```
    def self.go_two
       @@use_two = true
    end

variable_class One do
       @@use_two ? Two : One
    end

    def test
      one_class.do
    end

```

end

be = BlockExample.new  
be.test #“one”  
BlockExample.go\_two  
be.test #“two”

And if you want to use instance rather than class methods / variables in  
the block, self is passed along for you.

class BlockExample2

```
def initialize
	@use_two = false
end

    def go_two
       @use_two = true
    end

def use_two?
          @use_two
end

variable_class One do |be2obj|
       be2obj.use_two? ? Two : One
    end

    def test
      one_class.do
    end

```

end

be2 = BlockExample2.new  
be2.test #“one”  
be2.go\_two  
be2.test #“two”

Using this approach, I’ve been able to actually test the UNITS in my  
unit tests, by being able to swap in mocks for classes as well as  
objects and isolating the behavior of the class I’m testing. But I  
wanted to run it by the people on this fine list to see what suggestions  
people had for it before I put it in too many spots. Is there a much  
simpler way to accomplish the same? Or something that could be added  
now to make it more flexible? Or a way to tighten up the code? I want  
it to be as Ruby-like as possible.

Thanks for any critiques or suggestions.

Edmund
