如何获取通过attr_reader或attr_accessor定义的属性

时间:2012-04-04 07:43:56

标签: ruby metaprogramming

假设我有一个班级A

class A
  attr_accessor :x, :y

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

如何在不知道其命名方式的情况下获取xy属性。

E.g。

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

6 个答案:

答案 0 :(得分:31)

使用内省,卢克!

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]

答案 1 :(得分:13)

虽然塞尔吉奥的答案有所帮助,但它会返回所有实例变量,如果我理解OP的问题,那就不是问了。

如果您只想返回具有以下内容的“属性”一个改变者,你必须做一些稍微复杂的事情,例如:

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(或使用手动创建的mutator)声明的属性,并保持内部实例变量隐藏。如果你想要用attr_reader声明的那些,你可以做类似的事情。

答案 2 :(得分:7)

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,但是如果你只关心为一个类做这个(而不是一切继承自的基类),那么这应该有效。

答案 3 :(得分:3)

查看此其他Stack Overflow Question。它们会覆盖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 :(得分:1)

当你使用attr_accessor定义类中的属性时,Ruby使用refexion,为声明的每个属性定义几个方法,一个用于获取值,另一个用于设置,一个属性相同名称的实例变量< / p>

您可以使用

查看此方法
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 :(得分:1)

如果您的属性定义了attr_writer s / attr_accessor,则可以通过匹配=$正则表达式轻松检索它们:

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

OR

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