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 ..>
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
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 }
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
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
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/)