2

Possible Duplicate:
Ruby.Metaprogramming. class_eval

I have this little project, the goal is to create a 'attr_accessor_with_history' method, that will keep a record of every single value assigned to variables created by it. Here's the code :

class Class
  def attr_accessor_with_history(attr_name)
  attr_name = attr_name.to_s   # make sure it's a string
  attr_reader attr_name        # create the attribute's getter
  attr_reader attr_name+"_history" # create bar_history getter

  a = %Q{
        def initialize
        @#{attr_name}_history = [nil]
    end         

    def #{attr_name}
        @#{attr_name} 
    end 

    def #{attr_name}=(new_value)
        @#{attr_name}=new_value 
        @#{attr_name}_history.push(new_value)
    end }
      puts a

      class_eval(a)

  end
end

Now, when I test the script for one variable. It works fine. But when I try to create two or more variables (like this) ....

class Foo
attr_accessor_with_history :bar
attr_accessor_with_history :lab
end

a = Foo.new
a.bar = 45
a.bar = 5
a.bar = 'taat'
puts a.bar_history

b = Foo.new
b.lab = 4
b.lab = 145
b.lab = 'tatu'
puts b.lab_history

....Ruby gives out a "no-existing 'push' method for (class_eval) bar_history.push(new_value)". I think that 'initialize' method gets overriden on the second call of attr_accessor_with_history, so the record for the first variable gets destroyed.

I have no idea how to get around this. I already tried calling 'super' . Any clue ?

Community
  • 1
  • 1
user1608920
  • 157
  • 2
  • 9

1 Answers1

2

In your setter method just check if the the history instance variable is already initialized:

def #{attr_name}=(new_value)
  @#{attr_name}=new_value
  @#{attr_name}_history ||= [nil]
  @#{attr_name}_history.push(new_value)
end

You'll need another getter for your history variable that sets your default value if it was not set before:

def #{attr_name}_history
  @#{attr_name}_history ||= [nil]
end

Then you could remove your initialize method, that was btw vulnerable to be overwritten.

unnu
  • 744
  • 1
  • 5
  • 13
  • @AlexD I don't think so, because the initial state of the history ivar in the question is `[nil]`. If this is a good approach is up to the business domain. – unnu Oct 09 '12 at 12:02