How to get attributes that were defined through attr_reader or attr_accessor
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]
Get attr_accessor/instance variables in ruby
Well, they don't yet exist. Instance variables spring into existence upon first assignment. If you want them in a brand new instance, then touch them in the constructor.
class Walrus
attr_accessor :flippers, :tusks
def initialize
self.flippers = self.tusks = nil
end
end
w = Walrus.new
w.instance_variables # => [:@tusks, :@flippers]
Why use Ruby's attr_accessor, attr_reader and attr_writer?
You may use the different accessors to communicate your intent to someone reading your code, and make it easier to write classes which will work correctly no matter how their public API is called.
class Person
attr_accessor :age
...
end
Here, I can see that I may both read and write the age.
class Person
attr_reader :age
...
end
Here, I can see that I may only read the age. Imagine that it is set by the constructor of this class and after that remains constant. If there were a mutator (writer) for age and the class were written assuming that age, once set, does not change, then a bug could result from code calling that mutator.
But what is happening behind the scenes?
If you write:
attr_writer :age
That gets translated into:
def age=(value)
@age = value
end
If you write:
attr_reader :age
That gets translated into:
def age
@age
end
If you write:
attr_accessor :age
That gets translated into:
def age=(value)
@age = value
end
def age
@age
end
Knowing that, here's another way to think about it: If you did not have the attr_... helpers, and had to write the accessors yourself, would you write any more accessors than your class needed? For example, if age only needed to be read, would you also write a method allowing it to be written?
What is attr_accessor in Ruby?
Let's say you have a class Person
.
class Person
end
person = Person.new
person.name # => no method error
Obviously we never defined method name
. Let's do that.
class Person
def name
@name # simply returning an instance variable @name
end
end
person = Person.new
person.name # => nil
person.name = "Dennis" # => no method error
Aha, we can read the name, but that doesn't mean we can assign the name. Those are two different methods. The former is called reader and latter is called writer. We didn't create the writer yet so let's do that.
class Person
def name
@name
end
def name=(str)
@name = str
end
end
person = Person.new
person.name = 'Dennis'
person.name # => "Dennis"
Awesome. Now we can write and read instance variable @name
using reader and writer methods. Except, this is done so frequently, why waste time writing these methods every time? We can do it easier.
class Person
attr_reader :name
attr_writer :name
end
Even this can get repetitive. When you want both reader and writer just use accessor!
class Person
attr_accessor :name
end
person = Person.new
person.name = "Dennis"
person.name # => "Dennis"
Works the same way! And guess what: the instance variable @name
in our person object will be set just like when we did it manually, so you can use it in other methods.
class Person
attr_accessor :name
def greeting
"Hello #{@name}"
end
end
person = Person.new
person.name = "Dennis"
person.greeting # => "Hello Dennis"
That's it. In order to understand how attr_reader
, attr_writer
, and attr_accessor
methods actually generate methods for you, read other answers, books, ruby docs.
What does it mean that `attr_accessor` / `attr_reader` create an instance variable?
That's a bug/misleading wording in documentation. The attr_reader
/attr_accessor
themselves don't create any variables. How can they? They work outside of class instance lifecycle. And even read access don't make the instance variables come to life. Only write access creates them.
class Foo
attr_accessor :bar
end
foo = Foo.new
foo.instance_variables # => []
foo.bar # try read ivar
foo.instance_variables # => [], nope, not yet
foo.bar = 2 # write ivar
foo.instance_variables # => [:@bar], there it is
how to give attr_accessor to all the instance variables in class ruby
A bit hackish and limited to your specific example, but just answering the question (in its second incarnation):
class A
def initialize
@foo = 1
@bar = 2
#add as many instance variables you need
end
attr_accessor *A.new.instance_variables.map { |s| s[1..-1] }
end
obj = A.new
obj.foo #=> 1
obj.bar #=> 2
obj.bar = 3 #=> 3
obj.bar #=> 3
See Object#instance_variables
for more info.
Fastest/One-liner way to list attr_accessors in Ruby?
There is no way (one-liner or otherwise) to list all methods defined by attr_accessor and only methods defined by attr_accessor without defining your own attr_accessor.
Here's a solution that overrides attr_accessor in MyBaseClass to remember which methods have been created using attr_accessor:
class MyBaseClass
def self.attr_accessor(*vars)
@attributes ||= []
@attributes.concat vars
super(*vars)
end
def self.attributes
@attributes
end
def attributes
self.class.attributes
end
end
class SubClass < MyBaseClass
attr_accessor :id, :title, :body
end
SubClass.new.attributes.inspect #=> [:id, :title, :body]
Attr_accessor on class variables
attr_accessor
defines accessor methods for an instance. If you want class level auto-generated accessors you could use it on the metaclass
class Parent
@things = []
class << self
attr_accessor :things
end
end
Parent.things #=> []
Parent.things << :car
Parent.things #=> [:car]
but note that this creates a class level instance variable not a class variable. This is likely what you want anyway, as class variables behave differently than you might expect when dealing w/ inheritance. See "Class and Instance Variables In Ruby".
Difference between @instance_variable and attr_accessor
An instance variable is not visible outside the object it is in; but when you create an attr_accessor
, it creates an instance variable and also makes it visible (and editable) outside the object.
Example with instance variable (not attr_accessor
)
class MyClass
def initialize
@greeting = "hello"
end
end
m = MyClass.new
m.greeting #results in the following error:
#NoMethodError: undefined method `greeting' for #<MyClass:0x007f9e5109c058 @greeting="hello">
Example using attr_accessor
:
class MyClass
attr_accessor :greeting
def initialize
@greeting = "hello"
end
end
m2 = MyClass.new
m2.greeting = "bonjour" # <-- set the @greeting variable from outside the object
m2.greeting #=> "bonjour" <-- didn't blow up as attr_accessor makes the variable accessible from outside the object
Hope that makes it clear.
Related Topics
Ruby Methods and Optional Parameters
How to Get the Line of Code That Triggers a Query
Extracting the Last N Characters from a Ruby String
Passing Headers and Query Params in Httparty
How to Unfreeze an Object in Ruby
Rails Migration Changing Column to Use Postgres Arrays
How to Switch to an Older Version of Rails
How to Install Ruby-Debug in Ruby 1.9.3/Rails 3.2.1
Logging All Method Calls in a Rails App
How to Spawn an Eventmachine "Inside" a Rails App
Eager Loading: the Right Way to Do Things
How to Convert PDF Files to Images Using Rmagick and Ruby
How to Check If a Variable Is a Number or a String
Get Class Location from Class Object
Retrieving a Braintree Customer's Subscriptions
How to Add MAC-Specific Gems to Bundle on MAC But Not on Linux