Ruby - attr_accessor, attr_writer, and attr_reader
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.rodriguesHow 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
endWriter
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
endAccessor
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
endSummary
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_readercreates just the getter.attr_writercreates just the setter.attr_accessorcreates both setter and getter.
Don’t be shy with the sugar!