I’ve been using private and protected methods in Ruby for years, I always knew that these scope definitions weren’t exactly as you may expect if you come from other languages.
In other languages (C++, Java, etc.):
- A private method can only be accessed by instances of the class where it’s declared;
- While protected methods can be accessed by instances of the class or any of its descendants.
In Ruby, as defined in the Ruby Programming Book:
- A private method can’t be called with an explicit receiver.
- And protected methods can be invoked only by objects of the defining class and its subclasses.
Let’s create a playground to show a few examples:
class Dog
protected def protected_bark
puts "Woof woof woof!"
end
private def private_bark
puts "Woof!"
end
end
class Samoyedo < Dog; end
pluto = Dog.new
goofy = Dog.new
laika = Samoyedo.new
Protected methods
Protected methods can be accessed from other objects of the same class where the method was defined:
pluto.instance_eval do
goofy.protected_bark
# => Prints "Woof woof woof!"
end
And also any object of a descendant class:
laika.instance_eval do
goofy.protected_bark
# => Prints "Woof woof woof!"
end
But you can’t call it from anywhere else:
goofy.protected_bark
# => protected method `protected_bark' called for #<Dog:...> (NoMethodError)
Private methods
It’s very striking to see that the definition of private only states that the method can’t be called with an explicit receiver. It doesn’t say anything about the context where the method is accessible.
Let’s see how to call a private method without an explicit receiver:
pluto.instance_eval do
private_bark
# => Prints "Woof!"
end
Now, with an explicit receiver:
pluto.instance_eval do
self.private_bark
# => private method `private_bark' called for #<Dog:...> (NoMethodError)
end
Interestingly, forcing the absence of receiver implies that the method can’t be called anywhere else but the same scope.
Unlike other languages, we can access a private method of an ancestor class. As we can call a private method just as long as we don’t make explicit the receiver, just call the method without a receiver:
laika.instance_eval do
private_bark
# => Prints "Woof!"
end
Difference with other OO languages
It’s often said that private in Ruby is like protected in other languages, and it’s similar because:
- It’s accessible from methods in the same class.
- It’s accessible from methods in any descendant class.
But differs because it’s not accessible to other objects of the same class:
pluto.instance_eval do
goofy.private_bark
# => private method `private_bark' called for #<Dog:...> (NoMethodError)
end