Menu
Coddy logo textTech

Custom Setters

Part of the Object Oriented Programming section of Coddy's Ruby journey. Lesson 8 of 57.

A setter is a method whose name ends in =. Writing it yourself instead of using attr_writer lets it clean up the value before storing it:

class Person
  attr_reader :name

  def name=(value)
    @name = value.strip.capitalize
  end
end

ada = Person.new
ada.name = "   aDA  "
puts "[#{ada.name}]"

Output:

[Ada]

A setter can also refuse a value by raising an error. The instance variable is only assigned when the value passes the check, so the object keeps its old state:

class Product
  attr_reader :price

  def initialize(price)
    @price = price
  end

  def price=(value)
    raise ArgumentError, "price must be positive, got #{value}" if value <= 0

    @price = value
  end
end

item = Product.new(10)
begin
  item.price = -5
rescue ArgumentError => e
  puts e.message
end
puts item.price

Output:

price must be positive, got -5
10

An assignment expression always returns the value on its right-hand side, not what the setter returns or stores. Read the attribute again to see the stored value:

class Thermometer
  attr_reader :celsius

  def celsius=(value)
    @celsius = value.round
  end
end

t = Thermometer.new
result = (t.celsius = 21.7)
puts result
puts t.celsius

Output:

21.7
22

A reader and a custom setter together give an attribute that outside code can read and change, but only to valid values. Keep the check in the setter only, so every change goes through the same rule:

class Volume
  attr_reader :level

  def level=(value)
    raise ArgumentError, "level must be 0-10" unless (0..10).cover?(value)

    @level = value
  end
end
challenge icon

Challenge

Easy

Complete the class Person. Its setter name= removes spaces around the value and capitalizes each word ( ada lovelace becomes Ada Lovelace). Its setter age= raises ArgumentError with the message invalid age: 200 when the age is outside 0 to 150, and otherwise stores it. Both values can be read.

A new Person starts with the name Unknown and age 0. The supplied code reads commands name text or age n, applies them through the setters and prints the person as Ada Lovelace (36) after each command, or the error message when a setter refuses the value.

Your code goes in person.rb. main.rb holds the supplied input/output code and cannot be edited.

Try it yourself

require_relative 'person'

# Supplied input/output code: keep it as it is
input = []
while (line = gets)
  input << line.chomp
end
person = Person.new
input.each do |line|
  command, value = line.split(" ", 2)
  begin
    command == "name" ? person.name = value : person.age = value.to_i
    puts "#{person.name} (#{person.age})"
  rescue ArgumentError => e
    puts e.message
  end
end
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming

Practice on your own: Online Ruby compiler