Method access control is the concept of using access modifiers to change the visibility of methods in a class. In Ruby by default, method are public unless specified otherwise.
Ruby has the public, private, and protected access modifiers.
To make methods work inside the class but not available to the rest of the program, one can use the private access modifier.
protected is an in-between access modifier:
-
From inside the class
protectedmethods are accessible likepublicmethods -
From outside the class
protectedmethods act likeprivatemethods
Access Modifiers and Getters/Setters
Getters and setters (in Ruby, the built-in attr_* methods) being methods, their visibility can be changed just like other methods.
It easily becomes confusing because of Ruby’s syntactical sugar. Using for example attr_accessor :name accessor methods provides us with name and name= getter/setter instance methods, and @name instance variables. The setter instance method, particularly, is easily confused with a local variable initialization or reassignement.
Let’s see two examples using private and protected.
class Account
def initialize(name, balance=100)
@name = name
@balance = balance
end
attr_reader :name
def display_balance(pin_number)
if pin_number == pin
puts "Balance: $#{balance}."
else
pin_error
end
end
def withdraw(pin_number, amount)
if pin_number == pin
self.balance -= amount # We need `self` here
puts "Withdrew #{amount}. New balance: $#{balance}."
else
pin_error
end
end
private
attr_accessor :balance
def pin
@pin = 1234
end
def pin_error
"Access denied: incorrect PIN."
end
end
checking_account = Account.new('Dave', 1_000_000)
checking_account.display_balance(1234)
checking_account.withdraw(1234, 3_000)
On the example above, we use self with balance=, otherwise Ruby would interprete balance as a new local variable (and return an error because balance = balance - amount is intepreted as balance = nil - amount).
Here, balance and balance= accessor methods are accessible inside the instance but cannot be called outside of it, i.e., checking_account.balance is not permitted.
class Student
def initialize(n, g)
@name = n
@grade = g
end
def better_grade_than?(other_student)
grade > other_student.grade # => Note this line
end
protected
attr_reader :grade
end
joe = Student.new('Joe', 18)
bob = Student.new('Bob', 16)
puts 'Well done!' if joe.better_grade_than?(bob)
Here, grade is accessible inside the instance but not outside. Would we use private instead, accessor method grade (getter for instance joe) would have been accessible, but the other accessor method grade (getter for instance bob) would not, because we try to access it from outside.
Ruby would have raise an easy to misinterpret error message:
irb:8:in `better_grade_than?': private method `grade' called for #<Student:0x0000557a533462c8 @name="Bob", @grade=16> (NoMethodError)
Note @name="Bob", this is the important part to know whether private method ``grade' concerns the method on the left or on the right.