Accessors: attr_reader, attr_writer, attr_accessor
Prerequisites: Classes and objects
Instance variables are private to their object: code outside the
object can't read rex.@name. To expose them, you write methods — and
Ruby has shortcuts that write those methods for you.
Writing them by hand
class Product
def initialize(name)
@name = name
end
def name # a "getter"
@name
end
def name=(value) # a "setter" — note the = in the name
@name = value
end
end
The shortcuts
| Shortcut | Creates |
|---|---|
attr_reader |
a getter only (read) |
attr_writer |
a setter only (write) |
attr_accessor |
both a getter and a setter |
Example
class Product
attr_reader :name
attr_accessor :price
def initialize(name, price)
@name = name
@price = price
end
end
book = Product.new("Ruby Book", 30)
puts book.name
book.price = 25
puts book.price
book.name = "Other"
Expected output
Ruby Book
25
product.rb:15:in '<main>': undefined method 'name=' for an instance of Product (NoMethodError)
book.name = "Other"
^^^^^^^
name only has a reader, so trying to assign it fails. That's the
point: attr_reader lets you decide which data outsiders may change.
Prefer attr_reader by default. Objects whose data can't be
changed from outside are easier to reason about. Add a writer only when
changing that value from outside genuinely makes sense — and consider a
method that validates it instead:
class Product
attr_reader :price
def initialize(price)
self.price = price
end
def price=(value)
raise ArgumentError, "price can't be negative" if value.negative?
@price = value
end
end
p Product.new(10).price
10
Inside the class, self.price = value calls the setter method. Writing
just price = value would create a local variable instead — self.
is required for setters.
Common mistakes
- Writing
price = 5inside a method and expecting the setter to run. Useself.price = 5. - Using
attr_accessorfor everything, which lets any code change any value and makes bugs harder to track down.