3

How do I add a class instance variable, the data for it and a attr_reader at runtime?

class Module
  def additional_data member, data
    self.class.send(:define_method, member)  {
      p "Added method #{member} to #{name}"
    }
  end
end

For example, given this class

class Test
  additional_data :status, 55
end

So that now I can call:

p Test.status # => prints 55
Andrew Grimm
  • 78,473
  • 57
  • 200
  • 338
Zabba
  • 64,285
  • 47
  • 179
  • 207

2 Answers2

4

Module#class_eval is what you want:

def add_status(cls)
  cls.class_eval do
    attr_reader :status
  end
end

add_status(Test)
Theo
  • 131,503
  • 21
  • 160
  • 205
  • Thanks for your answer. But I want to be able to add the method status to any class, not just Test. I've updated the question with what I'm trying.. – Zabba Dec 26 '10 at 18:13
  • 1
    In Ruby everything is an object, if you can do `Test.class_eval ...` you can do `variable.class_eval ...` where `variable` is a class object. I've updated my answer with something that should better match what you want. – Theo Dec 26 '10 at 18:34
  • 1
    ...and obviously you can pass in the name of the attribute to the method if you want to. – Theo Dec 26 '10 at 18:41
  • 4
    Instead of `class_eval` you can also use `send`, which leads to the somewhat more concise: `cls.send(:attr_reader, :status)` – sepp2k Dec 26 '10 at 19:52
4

How about this?

class Object
  def self.additional_data(name, value)
    ivar_name = "@#{name}"

    instance_variable_set(ivar_name, value)

    self.class.send(:define_method, name) do
      instance_variable_get(ivar_name)
    end

    self.class.send(:define_method, "#{name}=") do |new_value|
      instance_variable_set(ivar_name, new_value)
    end
  end
end

class Foo
  additional_data :bar, 'baz'
end

puts Foo.bar # => 'baz'
Foo.bar = 'quux'
puts Foo.bar # => 'quux'

It's pretty self-explanatory, but let me know if you have any questions.

dtrasbo
  • 436
  • 3
  • 4