DEV Community

hungle00
hungle00

Posted on

Ruby - DIY accessor methods

1. Ruby accessor methods

Ruby does not allow instance variables to be accessed outside of methods for that object.

class Person  
  def initialize(name)
    @name = name
    @age = 10
  end
end
p = Person.new('steve')
p.name  # =>  undefined method `name=' for #<Person ..>
Enter fullscreen mode Exit fullscreen mode

This design ensures OOP encapsulation - an object’s data is protected from the outside world.

In some languages like Java, to access private instance variables, you must define getter/setter methods for each attribute. But in Ruby, you can generate getter/setter methods by using one of the methods in the Module#attr_* family.

class Person
  attr_reader :name, :age      # getter 
  attr_writer :name, :age      # setter
  # attr_accessor :name, :age  # getter + setter
  # other code ....
end
p = Person.new('steve')
p.name  # =>  steve
p.age = 20
p.age # => 20
Enter fullscreen mode Exit fullscreen mode

It just takes one or two lines for all of the attributes. These methods are called Class Macros which are class methods only used when in a class definition.

2. DIY accessor methods

There are advanced ways to access instance variables, like instance_variable_get, instance_eval.
For example:

p = Person.new('steve')
p.instance_variable_get(:@name)
p.instance_eval { @name }
Enter fullscreen mode Exit fullscreen mode

Using some metaprogramming techniques, we can build attribute accessor methods by myself.

module GetterSetter
  def attr_getter(*attributes)
    attributes.each do |attribute|
      define_method attribute do
        instance_variable_get("@#{attribute}")
      end
    end
  end

  def attr_setter(*attributes)
    attributes.each do |attribute|
      define_method "#{attribute}=" do |value|
        instance_variable_set("@#{attribute}", value)
      end
    end
  end

  def attr_getter_and_setter(*attributes)
    attr_getter(*attributes)
    attr_setter(*attributes)
  end
end
Enter fullscreen mode Exit fullscreen mode

For simplicity, I define getter/setter in separated module and include in Person class.

class Person
  extend GetterSetter

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

  attr_getter :name, :age
  attr_setter :name, :age
  # or 
  # attr_getter_and_setter :name, :age
end

p = Person.new('Luke')
p.name = 'anakin'
p.name # => anakin
Enter fullscreen mode Exit fullscreen mode

Summary

Class macros are one of Ruby's magic methods that help developers get rid of a lot of tedious, boilerplate methods. In rails, class macros are used a lot, for associations ( has_many, belongs_to ), validation, ...

You can learn more about Ruby class macros or other metaprogramming techniques in the Metaprogramming Ruby book ( https://pragprog.com/titles/ppmetr2/metaprogramming-ruby-2/)

Top comments (0)