32

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

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 值?

例如,我希望能够做到这一点:

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

7 回答 7

35

这个答案不会污染类空间,例如..如果我这样做了,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
于 2012-08-07T17:02:51.313 回答
25

这可以使用__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
于 2011-02-11T00:18:50.700 回答
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

看看这是否适合你

于 2011-02-11T00:20:12.567 回答
6

您可能想要使用 OpenStruct:

require "ostruct"

class Mine < OpenStruct
end

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

我不知道你为什么要dude.my_1返回 1 - 这不是把你已经拥有的东西还给你吗?

于 2011-06-07T00:09:17.803 回答
4

这里的两种方法存在一个问题……如果在一个实例中设置了一个实例变量,那么它的访问器将对所有实例都可用,因为您是在定义方法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.

于 2011-06-06T21:37:57.043 回答
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
于 2013-04-09T01:37:04.497 回答
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
于 2018-07-07T16:51:28.863 回答