Private & Protected in Ruby
As a Ruby method is — behind the scene — a message handler
associated with a block of instructions that returns an object, the private
and protected
policies are strongly correlated with the Ruby message
concept.
So, to understand private
and protected
policies, let’s have a quick recall on the Ruby message
concept.
Message, Receiver, and Message Handler
A message is composed of a name (commonly a symbol
) and an optional payload (the arguments list).
A message requires a receiver
(an object) that responds to this message via a message handler (a method).
The message sender is always the calling object context. This can be the main object if the message is called from outside of a class context.
We can explicitly send
a message to a receiver by using the Kernel#send
method
receiver name payload
| | |
__________ ______ ______
"a-string".send(:split, '-', 3)
In the above example, the message is composed of:
So "a-string"
responds to the message named :split
via the message handler String#split
.
However, you should be more familiar with the dot syntax
receiver name payload
| | |
__________ _____ ______
"a-string".split('-', 3)
So here, the message split
is implicitly sent to the receiver "a-string"
with the payload ('-', 3)
.
Now that we have a better understanding of what’s a message in Ruby, let’s detail the notion of private
and protected
methods.
Private methods
In Ruby, a private method (or private message handler) can only respond to a message with an implicit receiver (self
). It also cannot respond to a message called from outside of the private message handler context (the object)
class Receiver
def public_message
private_message
end def self_public_message
self.private_message
end private
def private_message
puts "This is a private message"
end
endirb> Receiver.new.public_message
This is a private message
=> nil
irb> Receiver.new.self_public_message
NoMethodError: private method `private_message' called for #<Receiver:0x007b>
irb> Receiver.new.private_message
NoMethodError: private method `private_message' called for #<Receiver:0x007b>
In Receiver#public_message
, the Receiver
instance implicitly sends theprivate_message
's message to the receiver self
. So as we’re in a Receiver
context and the message receiver is implicit then Receiver#private_message
can respond to the message.
Receiver#self_public_message
explicitly calls the private method for the receiver self
. As a private message handler cannot respond to a message with a receiver, then a NoMethodError
is raised.
An explicit call to Receiver.new.private_message
will raise a NoMethodError
because the message is sent from outside of the private_message
context (which should be an instance of Receiver
).
Protected methods
In Ruby, a protected method (or protected message handler) can only respond to a message with an implicit/explicit receiver (object) of the same family. It also cannot respond to a message sent from outside of the protected message handler context.
class Receiver
def public_message
protected_message
end def self_public_message
self.protected_message
end protected
def protected_message
puts "This is a protected message"
end
endclass Mailbox < Receiver
def mb_public_message
::Mailbox.new.protected_message
end
endirb> Receiver.new.public_message
This is a protected message
=> nil
irb> Receiver.new.self_public_message
This is a protected message
=> nil
irb> Mailbox.new.mb_public_message
This is a protected message
=> nil
irb> Receiver.new.protected_message
NoMethodError: protected method `protected_message' called for #<Receiver:0x007fbed691bdf0>
In Receiver#public_message
, protected
and private
methods share the same policy.
Receiver#self_public_message
explicitly call the protected method for the receiver self
. The protected message handler Receiver#protected_message
can respond to the message because it contains:
- a receiver of the same family
- and the message is sent by the
Receiver
object.
Mailbox.new.mb_public_method
also works fine for the same reasons enumerated above.
Receiver.new.protected_message
raises a NoMethodError
because the message is sent from outside of the Receiver
object.
Kernel#send: the anarchist
Kernel#send
has a specificity that can be useful in some cases (testing, etc..).
Indeed, when a message is sent by using this method, the private
and protected
policies are bypassed
class Receiver
def public_message
protected_message
end def self_public_message
self.protected_message
end protected
def protected_message
puts "This is a protected message"
end private
def private_message
puts "this is a private message"
end
endirb> Receiver.new.send(:private_message)
this is a private message
=> nil
irb> Receiver.new.send(:protected_message)
This is a protected message
=> nil
Since Ruby 2.7
Ruby 2.7+ allows private methods to be called with self
as receiver
puts "hello world!" # => hello world!
self.puts "hello world!" # => hello world!
Object.new.puts "hello world!" # NoMethodError
Ruby Mastery
We’re currently finalizing our first online course: Ruby Mastery.
Join the list for an exclusive release alert! 🔔
Also, you can follow us on x.com as we’re very active on this platform. Indeed, we post elaborate code examples every day.
💚