How accessors work in Ruby?

Category: Ruby :: Published at: 20.05.2022

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. :)


- Click if you liked this article

Views: 71