How to Set an Attr_Accessor for a Dynamic Instance Variable

How do I set an attr_accessor for a dynamic instance variable?

this answer doesn't pollutes the class space, example.. if i do mine.my_number 4 then the other instances of Mine will not get the my_4 method.. this happens because we use the singleton class of the object instead of the class.

class Mine
def my_number num
singleton_class.class_eval { attr_accessor "my_#{num}" }
send("my_#{num}=", num)
end
end

a = Mine.new
b = Mine.new
a.my_number 10 #=> 10
a.my_10 #=> 10
b.my_10 #=> NoMethodError

Ruby: dynamically generate attribute_accessor

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'}}

Dynamically Create Class Attributes with attr_accessor

One way (there are others) is to use instance_variable_set and instance_variable_get as so:

class Test
def create_method( name, &block )
self.class.send( :define_method, name, &block )
end

def create_attr( name )
create_method( "#{name}=".to_sym ) { |val|
instance_variable_set( "@" + name, val)
}

create_method( name.to_sym ) {
instance_variable_get( "@" + name )
}
end
end

t = Test.new
t.create_attr( "bob" )
t.bob = "hello"
puts t.bob

Set dynamic values when generating setter methods using attr_accessor in ruby

attr_accessor has no magic embedded. For each of params passed to it, it basically executes something like (the code is simplified and lacks necessary checks etc):

def attr_accessor(*vars)
vars.each do |var|
define_method var { instance_variable_get("@#{var}") }
define_method "#{var}=" { |val| instance_variable_set("@#{var}", val) }
end
end

That said, the attr_accessor :var1, :var2 DSL simply brings new 4 plain old good ruby methods. For what you are asking, one might take care about defining these methods (or some of them, or none,) themselves. For instance, for cumbersome setting with checks one might do:

attr_reader :variable # reader is reader, no magic

def variable=(val) do
raise ArgumentError, "You must be kidding" if val.nil?
@variable = val
end

The above is called as usual:

instance.variable = 42
#⇒ 42
instance.variable = nil
#⇒ ArgumentError: You must be kidding

Ruby Metaprogramming: dynamic instance variable names

The method you are looking for is instance_variable_set. So:

hash.each { |name, value| instance_variable_set(name, value) }

Or, more briefly,

hash.each &method(:instance_variable_set)

If your instance variable names are missing the "@" (as they are in the OP's example), you'll need to add them, so it would be more like:

hash.each { |name, value| instance_variable_set("@#{name}", value) }

How to set dynamically variable values?

attr_accessor :variable1, :variable2, :variable3

def set_variables(*attributes)
attributes.each {|attribute| self.send("#{attribute}=", true)}
end

create instance variable dynamically in ruby unknown number variables needed

Think it from this approach:

  • You have objects (lets say GenericObject)
    • Objects have many attributes (GenericObject#attributes => [GenericObject::Attribute])
      • Attributes have a name, a value, and a type (GenericObject::Attribute#value, #name and #type)

Which translates into code like this:

class GenericObject
attr_accessor :attributes

def add_attribute(name, value, type)
(@attributes ||= []) << Attribute.new(name, value, type)
end

class Attribute
attr_accessor :name, :value, :type
def initialize(name, value, type)
@name, @value, @type = name, value, type
end
end
end

# so...
cat = GenericObject.new
cat.add_attribute :leg_number, 4, :integer
cat.add_attribute :fur_color, 'Orange', :color
cat.add_attribute :name, 'Garfield', :string

cat.attributes.each { |attr| puts "My cat's #{attr.name} is #{attr.value} (#{attr.type})" }
# My cat's leg_number is 4 (integer)
# My cat's fur_color is Orange (color)
# My cat's name is Garfield (string)

You can make a fancy initializer for GenericObject or whatever you see fit.
Or you can just to a little fix

class GenericObjectArray
def initialize(attrs = {})
attrs.each { |attr,val| instance_variable_set "@#{attr}", val }
end
end

GenericObjectArray.new(:data_fields=> ["may_sales", "june_sales", "july_sales"])

Dynamic Variables to access class methods in Ruby

You could either try to call the getter (preferably, since it honors encapsulation):

pdc = PoorlyDesignedClass.new
1.upto(number_of_things.times do |i|
pdc.public_send(:"thing#{i}").bar = value[i]
end

or get the instance variable (less preferred, since it breaks encapsulation):

pdc = PoorlyDesignedClass.new
1.upto(number_of_things) do |i|
pdc.instance_variable_get(:"@thing#{i}").bar = value[i]
end

So, you were on the right track, there were just two problems with your code: instance variable names start with an @ sign, and . is not a legal character in an identifier.



Related Topics



Leave a reply



Submit