Accessors can be hard to understand for new developers.
In this article i will try to explain how they work and when to use them.
From definition, you create accessors, when you want to create getter and setter methods
without defining them inside a class.
In Ruby we have 3 types of accessors:
- attr_reader
- attr_writer
- attr_accessor
All of them can be used only inside a class.
ATTR_READER
Attr_reader is the most basic accessor, which creates for us a getter method for given attribute.
Let's say we have class like this:
# frozen_string_literal: true
class Car
def initialize(brand, model)
@brand = brand
@model = model
end
def brand
@brand
end
def model
@model
end
end
car = Car.new("Honda", "Civic")
puts car.brand
puts car.model
# => "Honda"
# => "Civic"
As you can see before, inside a class we have 2 public methods - brand and model.
If you call them, you will receive back the values of the attributes you passed before.
The problem is, you need to write 7 lines of code to access them. It is not optimal way to do it.
With the use of attr_reader your code will be a lot simplier.
Check this out:
# frozen_string_literal: true
class Car
attr_reader :brand, :model
def initialize(brand, model)
@brand = brand
@model = model
end
end
car = Car.new("Honda", "Civic")
puts car.brand
puts car.model
# => "Honda"
# => "Civic"
The attr_reader do exactly same thing as the public methods we wrote in previous example.
With the use of just one line of code we can create attribute methods which will help us to get the data from the class.
ATTR_WRITER
Attr_writer is quite opposite to the Attr_reader. Instead of getter method it will help us to use setter method
without defining it inside a class.
Let's look on the example below:
# frozen_string_literal: true
class Car
def initialize(brand, model)
@brand = brand
@model = model
end
def brand=(brand)
@brand = brand
end
def model=(model)
@model = model
end
attr_reader :brand, :model
end
car = Car.new("Honda", "Civic")
car.brand = "Opel"
car.model = "Astra"
puts car.brand
puts car.model
# => "Opel"
# => "Astra"
As you can see above, we have attr_readers, which means we can read attributes from the class.
But also we have defined setter methods directly inside our class. Thanks to this,
we can reassign attributes, after creating the object. This makes our class mutable.
As same as before, this way of defining setter methods is very long. It is a lot easier to define attr_writer accessor.
# frozen_string_literal: true
class Car
def initialize(brand, model)
@brand = brand
@model = model
end
attr_reader :brand, :model
attr_writer :brand, :model
end
car = Car.new("Honda", "Civic")
car.brand = "Opel"
car.model = "Astra"
puts car.brand
puts car.model
# => "Opel"
# => "Astra"
That's all - with the use of attr_writer we can reassign our class attributes in easy way.
ATTR_ACCESSOR
If you understand how attr_reader and attr_writer works, attr_accessor should not be a problem for you.
In simple words, if you need attr_reader and attr_writer for the same attributes inside class, you can just replace those with attr_accessor.
Just compare the code below with our example above:
# frozen_string_literal: true
class Car
def initialize(brand, model)
@brand = brand
@model = model
end
attr_accessor :brand, :model
end
car = Car.new("Honda", "Civic")
car.brand = "Opel"
car.model = "Astra"
puts car.brand
puts car.model
# => "Opel"
# => "Astra"
It will work same way. :)