Как получить атрибуты, которые были определены через 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]
Ответы
Ответ 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) }