如何为动态实例变量设置attr_accessor?

时间:2011-02-11 00:01:19

标签: ruby instance-variables attr attr-accessor

我在我的类中动态创建了一个实例变量:

class Mine
  attr_accessor :some_var

  def intialize
    @some_var = true
  end

  def my_number num
    self.instance_variable_set "@my_#{num}", num
  end
end

如何将@my_#{num}现在设为attr值?

e.g。我希望能够做到这一点:

dude = Mine.new
dude.my_number 1
dude.my_1
=> 1

7 个答案:

答案 0 :(得分:27)

这个答案没有污染类空间,例如..如果我mine.my_number 4那么Mine的其他实例将不会得到my_4方法...这是因为我们发生了使用对象的单例类而不是类。

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

答案 1 :(得分:24)

这可以使用__send__来完成。在这里:

class Mine
  attr_accessor :some_var

  def intialize
    @some_var = true
  end

  def my_number num
    self.class.__send__(:attr_accessor, "my_#{num}")
    self.__send__("my_#{num}=", num)
  end
end

dude = Mine.new
dude.my_number 1
puts dude.my_1

=> 1

答案 2 :(得分:10)

易。您可以在my_number方法中动态定义属性读取器:

  def my_number num
     self.instance_variable_set "@my_#{num}", num
     self.class.class_eval do
        define_method("my_#{num}") { num }
     end
  end

看看它是否适合你

答案 3 :(得分:5)

这里有两个方法存在一个问题...如果在一个实例中设置了一个实例变量,它的访问器将可用于所有实例,因为你在self.class而不是在self上定义方法。

dude = Mine.new
dude.my_number 1
puts dude.my_1
dudette = Mine.new
dudette.my_1 = 2    # works, but probably shouldn't
dudette.my_number 2
dude.my_2 = 3       # works, but probably shouldn't

您可能想要做的只是修改具有实例变量的实例:

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

这样,实例变量只能访问为其创建的对象的访问器。我也没有打扰instance_variable_set,因为如果你正在设置一个访问器,那么我认为重用它会更好。但这是一种风格调用。最重要的是调用class << self而不是self.class

答案 4 :(得分:3)

您可能想要使用OpenStruct:

require "ostruct"

class Mine < OpenStruct
end

dude = Mine.new
dude.my_number = 1
dude.my_number # => 1

我不知道为什么你希望dude.my_1返回1 - 这不是能让你回到已有的东西吗?

答案 5 :(得分:0)

较旧的帖子,但我觉得它很有用,谢谢你。这是代码Dorkus Prime的答案,但也从哈希的名称\值中获取实例变量

@cookies = browser.cookies.to_a

@cookies.each do |cookie|
self.class.__send__(:attr_accessor, "#{cookie[:name]}")
self.__send__("#{cookie[:name]}=",cookie[:value])
end

答案 6 :(得分:0)

define_singleton_method又是另一个解决方案:

class Mine
  def my_number num
    define_singleton_method("num_#{num}") { num }
  end
end

所有这些解决方案的一个副作用是,如果用不同的数字多次调用它,最终将在对象上产生一堆方法:

dude = Mine.new
dude.my_number 1
dude.my_number 5
dude.my_1
=> 1
dude.my_5
=> 5

我们可以通过删除旧方法来解决此问题:

class Mine
  def my_number num
    old_num = @num
    if @num
      # need to use `old_num` local variable
      # instance var scope is different inside `class_eval`
      singleton_class.class_eval { remove_method("num_#{old_num}") }
    end

    @num = num

    define_singleton_method("num_#{num}") { @num }
  end
end