Introduction

If you’ve been using Ruby for a while, you’ve almost certainly seen a class defined with one of the methods in the article title. They’re used very widely in Ruby codebases and tutorials. In this blog, we’ll breakdown this simple but powerful syntactical sugar, and see what it actually does by re-implementing them in pure Ruby.

Why use them?

In case you’re brand new to them, let’s take a quick minute to see why you would actually want to use these.

If you’re defining a class in Ruby, you’ve probably got some attributes that you want to store on the class, and access. Let’s look at a super simple User class:

class User
  def initialize(name)
    @name = name
  end
end

me = User.new("michael")
me.name # => NoMethodError: undefined method `name' for ...

We just want to store a name var on the User object we created. We can do that, but we can’t access that name without getting an error. Let’s try something else:

me = User.new("michael")
me.instance_variable_get("@name") # => "michael"

OK, so that worked, but it’s absolutely ugly and tedious. Who wants to type that out every time? No one. This is where getters and setters come in. Remember that first error?

NoMethodError: undefined method `**name'**

It’s telling us exactly what we need to do.

We need to define our name method before we call it. Let’s take another stab at writing our User class, but let’s define that accessor method:

class User
  def initialize(name)
    @name = name
  end

  def name
    @name
  end
end

me = User.new("michael")
me.name # => "michael"

Boom! it works, all we had to do was define a simple getter method. Anyways, this method is super simple, and you may have a bunch of attributes on a single class. Also consider that you may want to create a setter method, so you can do something like change the name variable after creating the object:

me = User.new("michael")
me.name # => "michael"
me.name = "Mike"
me.name # => "Mike"

You will get another NoMethodError if you haven’t defined the setter, but ou can avoid writing the method by just using attr_writer or attr_accessor. Anyways, now that you know what it’s for, let’s dig a little deeper into how each one works:

Reader

How to use it

Here’s an example class with an attr_reader. We use this when we need an variable that should only be changed from private methods, but whose value still needs to be available publicly.

It’s used like this:

class Message
  attr_reader :user, :message
end

message.user
# => michael.rodrigues

How it works

The example above is essentially shorthand for the following. We manually define two getters: one for user and one for message:

class Message
  @user = nil
  @message = nil

  def user
    return @user
  end

  def message
    return @message
  end
end

Writer

How to use it

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. We want to be able to set it though:

class User
  attr_reader :name
  attr_writer :password
end

michael.password = "secretpassword"

How it works

Without the syntactical sugar in the above example, you’d again have to manually define a getter and a setter:

class User
  @name
  @password

  def name
    return @name
  end

  def password=(pass)
    @password = pass
  end
end

Accessor

How to use it

Lastly, let’s take a look at attr_accessor. It doesn’t do anything you haven’t already seen, it just creates the setter and getter methods as seen above. Let’s use it for our :name variable:

class User
  attr_accessor :name
  attr_writer :password
end

michael.name
# => Mike

michael.name = "Michael"

How it works

This example really gives you an idea of how this syntactical sugar sweetens the experience. Without it, we would have to do this:

class User
  @name
  @password

  def name
    return @name
  end

  def name=(n)
    @name = n
  end

  def password=(pass)
    @password = pass
  end
end

Summary

So, in summary:

  • These methods are syntactical sugar that create setter and getter methods
  • A setter method allows users to set the value of an instance variable on an object.
  • A getter method allows users to get the value of an instance variable from an object.
  • attr_reader creates just the getter.
  • attr_writer creates just the setter.
  • attr_accessor creates both setter and getter.

Don’t be shy with the sugar!