bootrails.com is now moving to saaslit.com  See you there!
bootrails moves to  saaslit.com
Post

Ruby - attr_accessor, attr_writer, and attr_reader

Let’s start from a simple example.

1. Without any attr_accessor

1
2
3
4
5
6
7
8
class Book  
  
  def initialize(title, author)  
    @title = title  
    @author = author  
  end  
  
end  

And then, create a new object of type Book :

1
2
# Create a book object  
book = Book.new("The book title", "Jane Doe")  

This seems okay so far. When we create our book object, the initialize function will execute, setting the instance variables @title and @author to the strings “The book title” and “Jane Doe” respectively.

But what happens if we try to print one of these attributes?

1
2
3
$> book.title  
Traceback (most recent call last):  
book.rb:9:in `<main>': undefined method `title' for #<Book:0x000055e35f3de580 @title="Ruby on Rails"> (NoMethodError)  

this returns an undefined method error. Why is this the case when we have clearly set the title property?

This is because Ruby differs from some other languages when it comes to using dot notation to access object properties. In Ruby, . is used specifically for accessing object methods. It can’t be used for accessing properties directly.

To get around this, we can define a new method (with the same name as the property) that does one thing: return the property that we want to access. Such a method is known as a getter method — its sole purpose is to get the value of the property and return it. Going back to our Book class, such methods for accessing our title and author variable would look like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Book  
  
  def initialize(title, author)  
    @title = title  
    @author = author  
  end  
  
  # Getter methods  
  def title  
    return @title  
  end  
  
  def author  
    return @author  
  end  

end  
  
book = Book.new("The book title", "Jane Doe")  
  
puts book.title  
# "The book title"  
puts book.author  
# "Jane Doe"  

Great! Now we can access the instance variables title and color with the dot (.) syntax.

We now want to change/update the title property. We might try to assign a new value to title as follows:

1
2
3
book.title = "Why I love JavaScript"  
Traceback (most recent call last):  
book.rb:13:in `<main>': undefined method `title=' for #<Book:0x0000561c2de061a0 @title="Ruby on Rails"> (NoMethodError)Did you mean? title  

Another undefined method error! Why can’t we assign a new value to this property?

Well, our title getter method will not allow us to update a variable, as its only job was to return the value. As we now want to change its value, it turns out we need to create another method to handle that (appropriately known as a setter method). In this case, the title= method, as this is the method that gets called when we try to assign the value. This method takes the new value as an argument and updates the instance variable. For example, setter methods for our Book class may look like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Book  
  
  def initialize(title, author)  
    @title = title  
    @author = author  
  end  
  
  ## Getter methods  
  def title  
    return @title  
  end  
  def author  
    return @author  
  end  
  
  ## Setter methods  
  def title=(new_title)  
    @title = new_title  
  end  
  def author=(new_author)  
    @author = new_author  
  end  
  
end  
  

We now have access to the title= and author= methods, which take an argument and assign it to the object’s instance variable which we specify inside the setter method. Now, if we try to update our book object title variable, we will no longer get an error!

1
2
3
book.title = "Why I love JavaScript"  
puts book.title  
# => "Why I love JavaScript"  

2. attr_reader, attr_writer & attr_accessor

You might be thinking that needing to create separate functions for each attribute that we want to be able to read and update is quite tedious — and it is. To make this process quicker and simpler Ruby includes some methods in its core API to help out.

attr_reader

We use this when we need a variable that should only be changed from private methods, but whose value still needs to be available publicly.

The attr_reader method takes the names of the object’s attributes as arguments and automatically creates getter methods for each. We can replace our getter methods with attr_reader and our book class definition becomes much simpler:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Book  
  
  attr_reader :title, :author # <-- Getter methods  
  
  def initialize(title, author)  
    @title = title  
    @author = author  
  end  
  
  ## Setter methods  
  def title=(new_title)  
    @title = new_title  
  end  
  def author=(new_author)  
    @author = new_author  
  end  
end  
  
book = Book.new("The book title", "Jane Doe")  
puts book.title # Read variable  
# => "The book title"  

attr_writer

With attr_writer, only the setter method is defined. The getter method is left out. Maybe we have a secret variable we don’t want other objects to be able to read

The same goes for updating our objects properties. For this however, we need to use the attr_writer method. This method works similarly to attr_reader, except that it will automatically create setter methods for our class. Replacing our setter method in our Book class with attr_writer would look like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Book  
  attr_reader :title, :author  
  attr_writer :title, :author # <-- Setter methods  
  
  def initialize(title, author)  
    @title = title  
    @author = author  
  end  
  
end  
  
book.title = "JavaScript" # Set value  
puts book.title  
=> "JavaScript"  

attr_accessor

This has cleaned up our class definition significantly and saved us a lot of time and lines of code writing out separate getter and setter methods for each instance variable.

However, when using the attr_reader and attr_writer methods, we still have to repeat all of the property names for each method twice.

attr_accessor is a shortcut method when you need both attr_reader and attr_writer

This is where the attr_accessor method comes in handy and allows us to take it one step further, creating all of the getter and setter methods in a single line as follows:

1
2
3
4
5
6
7
8
9
10
class Book  
  
  attr_accessor :title, :author # <-- Creates both getter and setter method  
  
  def initialize(title, author)  
    @title = title  
    @author = author  
  end  
  
end  

And that is why you will often see attr_accessor in Ruby classes!

3. Summary

attr_reader and attr_writer in Ruby allow us to access and modify instance variables using the . notation by creating getter and setter methods automatically. These methods allow us to access instance variables from outside the scope of the class definition. attr_accessor combines the functionality of these two methods into a single method.

Enjoy !

This post is licensed under CC BY 4.0 by the author.