It’s all under control..

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, in order to understand private and protected policies, let’s have a quick recall on the Ruby message concept.

Before to start

I’m thrilled to share with you our latest project: Fun Facts about Ruby — Volume 1

Please feel free to spread the word and share this post! 🙏

Thank you for your time!

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) which 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:

a receiver: "a-string"

a message name: :split

a payload: '-', 3

and a sender which is the main object

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’ve a better overview 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

end irb> 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 send the private_message 's message to the receiver self . So as we’re in a Receiver context and the message receiver is implicit then the 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

end class Mailbox < Receiver

def mb_public_message

::Mailbox.new.protected_message

end

end irb> 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 as enumerated above.

Receiver.new.protected_message raises a NoMethodError because the message is sent from outside of the Receiver object.

Kernel#send: the anarchist

The Kernel#send method 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

end irb> 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

Voilà !

May I have your attention please 🎤🎤

Feel free to subscribe here: www.rubycademy.com