Ruby: dynamically generate attribute_accessor

11,603

Solution 1

You need to call the (private) class method attr_accessor on the Event class:

    self.class.send(:attr_accessor, name)

I recommend you add the @ on this line:

    instance_variable_set("@#{name}", value)

And don't use them in the hash.

    data = {:datetime => '2011-11-23', :duration => '90', :class => {:price => '£7', :level => 'all'}}

Solution 2

You could do a bit of meta-magic to solve this, using method_missing:

class Event
  def method_missing(method_name, *args, &block)
    if instance_variable_names.include? "@#{method_name}"
      instance_variable_get "@#{method_name}"
    else
      super
    end
  end
end

What this will do is allow access to object instance variables via object.variable syntax, if the object has those variables defined, without resorting to modifying the entire class via attr_accessor.

Solution 3

attr_accessor is a class method and as such needs to be invoked on the class. It is also a private method, so you need to invoke it in a context in which the class object is self.

As an example:

class C
  def foo
    self.class.instance_eval do
      attr_accessor :baz
    end
  end
end

After creating an instance of C and calling foo on that instance, that instance -- and all future instances -- will contain methods baz and baz=.

Share:
11,603
Yannick Schall
Author by

Yannick Schall

http://www.zerply.com/profile/zapatoche/public

Updated on June 06, 2022

Comments

  • Yannick Schall
    Yannick Schall about 1 year

    I'm trying to generate the attr_reader from a hash (with nested hash) so that it mirror the instance_variable creation automatically.

    here is what i have so far:

    data = {:@datetime => '2011-11-23', :@duration => '90', :@class => {:@price => '£7', :@level => 'all'}}
    class Event
     #attr_reader :datetime, :duration, :class, :price, :level
      def init(data, recursion)
       data.each do |name, value|
        if value.is_a? Hash
          init(value, recursion+1)
        else
          instance_variable_set(name, value)
          #bit missing: attr_accessor name.to_sym 
        end
      end
    end
    

    But i can't find out a way to do that :(

  • Yannick Schall
    Yannick Schall over 11 years
    You ARE my new best friend!!!! that just work and is so simple... Ruby is magic :) and YOU are a star :) (I'm happy because I looked for a while)
  • Yannick Schall
    Yannick Schall over 11 years
    Just a quick question more, is there any way to prepend the name of the containing hash to the accessor name, so that i could call my_class.class.price, or something like that?
  • rdvdijk over 11 years
    First of all, 'class' is a reserved keyword, don't use it. Your second question in your comment here goes beyond the scope of this particular question. You might want to start a new question, since it deals with recursion and such, not with attribute accessors. (PS, don't forget to accept the best answer)
  • Mike Campbell
    Mike Campbell over 8 years
    It's worth noting that because you're acting on the class, any future instances of the class will also have your attribute accessors that have previously been defined dynamically.
  • Rpant
    Rpant over 5 years
    @MikeCampbell Is it possible to do this on an object instance. Specifically i am trying to conditionally add attributes in an included parent class.