Подтвердить что ты не робот

Как получить атрибуты, которые были определены через attr_reader или attr_accessor

Предположим, что у меня есть класс A

class A
  attr_accessor :x, :y

  def initialize(x,y)
    @x, @y = x, y
  end
end

Как я могу получить атрибуты x и y, не зная, как именно они были названы.

например.

a = A.new(5,10)
a.attributes # => [5, 10]
4b9b3361

Ответ 1

Используйте интроспекцию, Люк!

class A
  attr_accessor :x, :y

  def initialize(*args)
    @x, @y = args
  end

  def attrs
    instance_variables.map{|ivar| instance_variable_get ivar}
  end
end

a = A.new(5,10)
a.x # => 5
a.y # => 10
a.attrs # => [5, 10]

Ответ 2

В то время как ответ Серхио помогает, он вернет все переменные экземпляра, которые, если я правильно понимаю вопрос ОП, не являются тем, что задано.

Если вы хотите вернуть только "атрибуты", например, мутатор, вам нужно сделать что-то более сложное, например:

attrs = Hash.new
instance_variables.each do |var|
  str = var.to_s.gsub /^@/, ''
  if respond_to? "#{str}="
    attrs[str.to_sym] = instance_variable_get var
  end
end
attrs

Это возвращает только атрибуты, объявленные с помощью attr_accessor (или с помощью созданного вручную мутатора), и сохраняйте скрытые внутренние переменные экземпляра. Вы можете сделать что-то подобное, если хотите, чтобы объявления были объявлены с помощью attr_reader.

Ответ 3

Смотрите этот вопрос о переполнении стека. Они переопределяют attr_accessor.

  def self.attr_accessor(*vars)
    @attributes ||= []
    @attributes.concat vars
    super(*vars)
  end

  def self.attributes
    @attributes
  end

  def attributes
    self.class.attributes
  end

Ответ 4

когда вы используете attr_accessor для определения атрибутов в классе, Ruby с использованием refexion, определите пару методов для каждого объявленного атрибута, чтобы получить значение и другое для установки, переменную экземпляра с тем же именем атрибута

вы можете увидеть эти методы, используя

p A.instance_methods

[:x, :x=, :y, :y=, :nil?, :===, :=~, :!~, :eql?, :hash, :<=>, :class, :singleton_class, :clone, :dup, :initialize_dup, :initialize_clone, :taint, :tainted?, :untaint, :untrust, :untrusted?,..

Итак, эти атрибуты доступны, вне класса, с

p "#{a.x},#{a.y}"

или внутри класса через соответствующую переменную экземпляра

class A
  ...
  def attributes
    [@x,@y]
  end
  ...
end
p a.attributes   #=> [5,10]

Ответ 5

class A
  ATTRIBUTES = [:x, :y]
  attr_accessor *ATTRIBUTES

  def initialize(x,y)
    @x, @y = x, y
  end

  def attributes
    ATTRIBUTES.map{|attribute| self.send(attribute) }
  end
end

Это не может быть DRY-est, но если вы только занимаетесь этим для одного класса (в отличие от базового класса, который все наследует), тогда это должно работать.

Ответ 6

Если у вас есть attr_writer s/attr_accessor, определенные в ваших атрибутах, их можно легко найти, сопоставив =$ regexp:

A.instance_methods.each_with_object([]) { |key, acc| acc << key.to_s.gsub(/=$/, '') if key.match(/\w=$/) }

ИЛИ

A.instance_methods.each_with_object([]) { |key, acc| acc << key if key = key.to_s.match(/^(.*\w)=$/)&.[](1) }