Как установить attr_accessor для переменной динамического экземпляра? - PullRequest
31 голосов
/ 11 февраля 2011

Я динамически создал переменную экземпляра в своем классе:

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

Ответы [ 7 ]

27 голосов
/ 07 августа 2012

этот ответ не загрязняет пространство классов, например .. если я сделаю mine.my_number 4, то другие экземпляры Mine не получат метод my_4 .. это происходит потому, что мы используем класс singleton объект вместо класса.

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
24 голосов
/ 11 февраля 2011

Это можно сделать с помощью __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
10 голосов
/ 11 февраля 2011

Легко. Вы можете динамически определить средство чтения атрибутов в методе 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

посмотрите, подходит ли вам это

5 голосов
/ 07 июня 2011

Здесь есть одна проблема с этими двумя методами ... если переменная экземпляра установлена ​​в одном экземпляре, ее метод доступа будет доступен для всех экземпляров, потому что вы определяете методы для self.class вместо для себя.

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.

3 голосов
/ 07 июня 2011

Вы можете использовать OpenStruct:

require "ostruct"

class Mine < OpenStruct
end

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

Я не знаю, почему вы хотите, чтобы dude.my_1 вернул 1 - разве это не возвращает вам то, что у вас уже есть?

0 голосов
/ 07 июля 2018

Еще одно решение добавить в кучу, 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
0 голосов
/ 09 апреля 2013

более старая тема, но я нашел ее полезной, спасибо.Вот код ответа Доркуса Прайма, а также взятие переменных экземпляра из имени \ значения в хэше

@cookies = browser.cookies.to_a

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