loading...

Ruby Getters and Setters

k_penguin_sato profile image K-Sato Updated on ・3 min read

Table of contents

  1. What is a getter method
  2. What is a setter method
  3. What are accessors?
  4. References

What is a getter method?

A getter method is a method that gets a value of an instance variable.
Without a getter method, you can not retrieve a value of an instance variable outside the class the instance variable is instantiated from.

Here is an example.

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

obj1 = Movie.new('Forrest Gump')
p obj1.name #=> undefined method `name' for #<Movie:0x007fecd08cb288 @name="Forrest Gump"> (NoMethodError)

As you can see, the value of obj1 (name) can not be retrieved outside Movie class. if you try to retrive a value of an instance variable outside its class without a getter method, Ruby raises No Mothod Error.

Now, Let's see how to retrieve the value of obj1 outside Movie class with a getter method.
All you have to do here is to define a getter method named name. Though the name of a getter method can be anything, it is common practice to name a getter method the instance variable’s name.

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

  def name
    @name
  end
end

obj1 = Movie.new('Forrest Gump')
p obj1.name #=> "Forrest Gump"

What is a setter method?

A setter is a method that sets a value of an instance variable.
Without a setter method, you can not assign a value to an instance variable outside its class.
if you try to set a value of an instance variable outside its class, Ruby raises No Method Error just like it does when you try to retrieve a value of an instance variable outside its class without a getter method.

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

  def name #getter method
    @name
  end
end

obj1 = Movie.new('Forrest Gump')
p obj1.name #=> "Forrest Gump"
obj1.name = 'Fight Club' #=> undefined method `name=' for #<Movie:0x007f9937053368 @name="Forrest Gump"> (NoMethodError)

Defining a setter method inside a class makes it possible to set a value of an instance variable outside the class.
You can define a setter method like the code below.

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

  def name #getter method
    @name
  end

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

obj1 = Movie.new('Forrest Gump')
p obj1.name #=> "Forrest Gump"
obj1.name = 'Fight Club'
p obj1.name #=> "Fight Club"

By using name=, you can now assign a new value Fight Club to obj1.

What are accessors?

Accessors are a way to create getter and setter methods without explicitly defining them in a class.
There are three types fo accessors in Ruby.

  • attr_reader automatically generates a getter method for each given attribute.
  • attr_writer automatically generates a setter method for each given attribute.
  • attr_accessor automatically generates a getter and setter method for each given attribute.

First, let's take a look at attr_reader!
As you can see in the code below, name and year are retrieved outside Movie class even though there is no getter method for either of them. This is because attr_reader generates a getter method for each given attribute.

class Movie
  attr_reader :name, :year

  def initialize(name, year)
    @name = name
    @year = year
  end
end
obj1 = Movie.new('Forrest Gump', 1994)
p obj1.name #=> Forrest Gump
p obj1.year #=> 1994

Second, let's see how attr_writer works!
As I mentioned above, attr_witer generates a setter method for each given attribute. Therefore you can assign new values to ob1 without explicitly writing setter methods for name and year!

class Movie
  attr_reader :name, :year 
  attr_writer :name, :year

  def initialize(name, year)
    @name = name
    @year = year
  end
end
obj1 = Movie.new('Forrest Gump', 1994)
obj1.name = 'Fight Club'
obj1.year = 1999
p obj1.name #=> "Fight Club"
p obj1.year #=> 1999

Last but certainly not least, attr_accessor does what attr_reader and attr_writer do with just one line of code! It will automatically generate a getter and setter mehod for each given attribute.

class Movie
  attr_accessor :name, :year

  def initialize(name, year)
    @name = name
    @year = year
  end
end
obj1 = Movie.new('Forrest Gump', 1994)
obj1.name = 'Fight Club'
obj1.year = 1999
p obj1.name #=> "Fight Club"
p obj1.year #=> 1999

References

Ruby Getters and Setters
How getter/setter methods work in Ruby
What is attr_accessor in Ruby?
rubylearning.com

Posted on by:

k_penguin_sato profile

K-Sato

@k_penguin_sato

I am a software-engineer based somewhere on earth.

Discussion

markdown guide
 

Another neat trick is you can mark these as private so you can use them internally without worrying about them leaking out by throwing a private into the class.

This can be useful if you don't like remembering when to use @year vs year or perhaps you want to be prepared to abstract it later.

require "date"
class Movie
  attr_accessor :name, :year
  private :year, :year=

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

  def age
    Date.today.year - year
  end
end
obj1 = Movie.new("Forrest Gump", 1994)
obj1.year #=> NoMethodError: private method `year' called for #<Movie:....>
obj1.year = 2018 #=> NoMethodError: private method `year=' called for #<Movie:....>
obj1.age #=> 24
 

It seems odd to declare these as private since you can always refer to them by their @ name internally.

 

It can be odd, but it can also be useful as it allows you to more easily override it later without having to go find/replace all instances of @year in your class.

Or, if a class inherits from it and needs to change how year is generated it's easier it doesn't have to worry about parent class using @year.

If that makes sense.

It just seems inconsistent to use year and self.year = ... in the code where one's bare and the other's necessarily prefixed, instead of @year consistently. This plus the way that @year is by default "private".

One of the major goals of object-oriented design is to properly contain any changes like that, so replacing @year with something else is at least contained to the one file or scope.

Subclass concerns are valid, though in Ruby it's generally understood you need to play nice with your parent's properties and avoid really getting in there and wrecking around.

 

Thanks for sharing !!

 

These are also called "accessors" (read) and "mutators" (write) in other languages, but the principle is the same. Useful terminology for those coming to Ruby from places where those terms are used instead.

Ruby's way of declaring them as x= type methods is fairly unique and makes for some extremely concise code since there's no need for getX / setX pairs, it's just x and x=.

Another thing worth mentioning is if you have a "setter" or attr_writer you can't use that without prefixing it with some kind of object, even self.

For example:

class Example
  attr_accessor :test

  def assign!
    test = :assigned
  end
end

example = Example.new
example.assign!
example.test
# => nil

That's because in the code test = :assigned creates a variable named test, it doesn't call the test= method. To use those you must do self.test = :assigned inside the context of that method or example.test = :assigned by using some kind of variable for reference.

This leads to a lot of confusion in places like ActiveRecord where assigning to the auto-generated attributes "doesn't work".