Reputation: 391
Suppose I have a class A
class A
attr_accessor :x, :y
def initialize(x,y)
@x, @y = x, y
end
end
How can I get x
and y
attributes without knowing how exactly they were named.
E.g.
a = A.new(5,10)
a.attributes # => [5, 10]
Upvotes: 26
Views: 46185
Reputation: 2163
If you have attr_writer
s/attr_accessor
s defined on your attributes, than they can be easily retrieved by matching the =$
regexp:
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) }
Upvotes: 2
Reputation: 641
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
This may not be the DRY-est, but if you are only concerned with doing this for one class (as opposed to a base class that everything inherits from), then this should work.
Upvotes: 16
Reputation: 16752
See this other Stack Overflow Question. They override 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
Upvotes: 3
Reputation: 557
While Sergio's answer helps, it will return all the instance variables, which if I understand correctly the OP's question, is not what is asked.
If you want to return only the 'attributes' that have e.g. a mutator, you have to do something slightly more complicated such as:
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
This returns only the attributes declared with attr_accessor (or with a manually created mutator), and keep the internal instance variables hidden. You can do something similar if you want the ones declared with attr_reader.
Upvotes: 18
Reputation: 55
when you use attr_accessor to define attributes in a class, Ruby using refexion, define a couple of methods, for each attribute declared, one to get the value and other to set, an instance variable of the same name of the attribute
you can see this methods using
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?,..
So this attributes are accesible, outside the class, with
p "#{a.x},#{a.y}"
or inside the class through the corresponding instance variable
class A
...
def attributes
[@x,@y]
end
...
end
p a.attributes #=> [5,10]
Upvotes: 1
Reputation: 230471
Use introspection, Luke!
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]
Upvotes: 36