4. attr_reader and attr_writer

Let's take a break from our dogs. In this lesson, let's build a Cat class.

In C9, create a new file in your ruby_fundamentals directory called cat.rb. Open the file and create a new Cat class:

class Cat

end

As we learned in previous lessons, for each class, we need an initialize method:

class Cat
  def initialize
  end
end

For each cat, let's store the name and the age of the cat. That means that when we create a new Cat object, we should be able to pass in the name and age as a parameter to the new method (Cat.new(name, age)).

Let's add the appropriate parameters to the initialize method and store the values as instance variables:

class Cat
  def initialize(name, age)
    @name = name
    @age = age
  end
end

Now we can make a new instance of the Cat class:

class Cat
  def initialize(name, age)
    @name = name
    @age = age
  end
end

cat = Cat.new("Cathy", 4)

What if we want to change the name or the age? Right now, there's no way to change the attributes for the cat. Let's create setter methods that will allow us to do this.

Setter methods are methods that allow you to set the values of instance variables for the object. On the other hand, getter methods are methods that return the value of an instance variable. As we'll see in this lesson, Ruby doesn't require that you create these methods for the most part, but in languages like Java, creating these setter and getter methods can be seen quite often.

class Cat
  def initialize(name, age)
    @name = name
    @age = age
  end

  def name=(name)
    @name = name
  end

  def age=(age)
    @age = age
  end
end

cat = Cat.new("Cathy", 4)

puts "Before: " + cat.inspect

cat.name = "Catherine"
cat.age = 5

puts "After: " + cat.inspect

The output will look like this:

image.png

As you can see, the name and age has changed.

Defining setter methods

If you are confused about the weird syntax of having = in a method name - don't worry it's not too complicated.

When you execute cat.name = "Catherine", you're actually executing a method that is named name=. That is why we created a method called name=:

def name=(name)
  @name = name
end

In other words, name = is just another method. We can rename the method to something random and it would still work:

class Cat
  def initialize(name, age)
    @name = name
    @age = age
  end

  def coding_is_fun=(name)
    @name = name
  end

  def age=(age)
    @age = age
  end
end

cat = Cat.new("Cathy", 4)

# This will work now
cat.coding_is_fun = "Catherine"

# This will give an error since the method name = isn't defined anymore
cat.name = "Cath"

Next, what if we want to get the name and the age of a cat? What we need to do next is define a getter method. A getter method is a method that returns the value of a particular instance variable.

Defining a getter method is easy:

class Cat
  def initialize(name, age)
    @name = name
    @age = age
  end

  def name=(name)
    @name = name
  end

  def age=(age)
    @age = age
  end

  def name
    @name
  end

  def age
    @age
  end
end

cat = Cat.new("Cathy", 4)

puts "Before: " + cat.inspect

cat.name = "Catherine"
cat.age = 5

puts "After: " + cat.inspect

puts "Name: #{cat.name}"
puts "Age: #{cat.age}"

The output should look like this:

image.png

The methods we defined above return the instance variable when called.

A better way of defining getter and setter methods

We learned how we can create setter and getter methods for our classes. However, if we need to create these methods for every single attribute for each object, things can get repetitive and boring.

Fortunately in Ruby, we can define these setter and getter methods in a much more concise way. Introducing attr_reader and attr_writer.

attr_writer replaces the setter methods:

class Cat
  attr_writer :name, :age

  def initialize(name, age)
    @name = name
    @age = age
  end

  # No need to define setter methods anymore!

  # def name=(name)
    # @name = name
  # end

  # def age=(age)
    # @age = age
  # end

  def name
    @name
  end

  def age
    @age
  end
end

attr_reader replaces the getter methods:

class Cat
  attr_writer :name, :age
  attr_reader :name, :age

  def initialize(name, age)
    @name = name
    @age = age
  end

  # No need to define setter methods anymore!

  # def name=(name)
    # @name = name
  # end

  # def age=(age)
    # @age = age
  # end

  # No need to define getter methods anymore either!

  # def name
    # @name
  # end

  # def age
    # @age
  # end
end

Often times, we will use both attr_writer and attr_reader. To make things easier for us, Ruby also provides attr_accessor, which is a combination of attr_writer and attr_reader:

class Cat
  attr_accessor :name, :age

  # attr_reader :name, :age
  # attr_writer :name, :age

  def initialize(name, age)
    @name = name
    @age = age
  end

  # No need to define setter methods anymore!

  # def name=(name)
    # @name = name
  # end

  # def age=(age)
    # @age = age
  # end

  # No need to define getter methods anymore either!

  # def name
    # @name
  # end

  # def age
    # @age
  # end
end

The final code is this:

class Cat
  attr_accessor :name, :age

  def initialize(name, age)
    @name = name
    @age = age
  end
end

Wow! Look how much less code we need to write! (Ruby is awesome.)

Overview

Assignment