Do You Really Know Public Private And Protected In Ruby
Public, protected and private access – all of the programmers are familiar with that concept. Nothing special, we work with them on a daily basis. However, as Ruby developers, do we really know the details? Read on and challenge yourself!
Public, private and protected access - all of the programmers are familiar with that concept. Nothing special, we work with them on a daily basis. However, as Ruby programmers, do we really know the details?
You can check yourself with these five statements. Try to answer: TRUE or FALSE.
Statements:
Public methods have no access control - they can be called from the outside of the class definition by the instance of that class or it's subclasses
Both protected and private methods cannot be called from the outside of the defining class
Protected methods are accessible from the subclass and private methods are not
Private methods of the defining class can be invoked by any instance of that class
Public access is the default one
We will back to the correct answers at the end of this article. For now let’s go deeper into some nuances of public, private and protected access control. To make it more practical I prepared sample code to play with. I recommend cloning this repo to make continuing with the article easier. It’s just one file. If you would like to think of the solution first, then hold off running the code.
Here is the sample code:
You can see the base class Region
here along with two child classes: Country
and City
. City
and Country
inherit from Region
. Inheritance is used to demonstrate the public, private and protected details. At the end of the file, we can find initialization part and 4 sections which we’ll discuss below. Take a look at the file to get familiar with the code.
Ok, let’s start! In the 3rd section of the initialization, the following objects are created - wroclaw
, san_francisco
and poland
. These objects will be used for demonstration purposes in the upcoming sections. I suggest treating all these segments separately. You can comment out the particular section before you go to the next one. In that way, errors won’t block the rest of the output.
I Section
Topic: Public access.
In the first section greeting
method is invoked twice: by the wroclaw
and by the poland
objects. Nothing special here. Public access is straightforward. The greeting
is accessible for its class’s object (`poland`) and for its child-class’s object (`wroclaw`).
Conclusion: Public access is evident and intuitive. There are no restrictions. Also, it is the default access modifier.
II Section
Topic: Access to the private and protected methods from the outside of the defining class.
There are two important methods in that section: protected name_info
and private population_info
. The result seems to be intuitive again. wroclaw
object has no access to neither the private methods nor the protected ones. In both cases NoMethodError
is thrown.
Conclusion: Private and protected methods cannot be called from the outside of the class. Access is restricted.
III Section
Topic: Access to private and protected methods from the inside of the defining class.
This time we have City::own_greeting
which uses inherited protected method inside (`name_info`) and Country::own_greeting
which uses inherited private method inside (`population_info`). Both private and protected methods (even if they are inherited) are accessible inside the class. So it’s not the point which distinguishes private from protected access.
Conclusion: Private and protected methods can be called from the inside of the defining class. Access is allowed.
IV Section
Topic: Actual difference between private and protected methods.
IV Section - More unexpected rules can start appearing here.
You can see 3 public methods there:
more_densely_populated?(other_region)
- it uses privatepopulation_density
inside.the_same_continent?(other_region)
- it uses protectedcontinent
inside.can_be_crowdy?
- it uses privateconsider_as_densely_populated?
inside.
Let’s go through the code step by step:
more_densely_populated?(other_region)
Hmm, that’s interesting. Private method Region::population_density
hasn’t been called, even though it is implemented inside the Region
class. The similar scenario has been worked in the III section...
This one works. Any difference here? Right, the_same_continent?
uses protected attribute - continent
. Ok, let’s continue.
NoMethodError
again. Hmm, can_be_crowdy?
also uses private consider_as_densely_populated?
method. Similar situation was working fine in the III section. So what’s going on here?
It’s all about the receiver.
Basically the receiver is the object whose method is invoked. Let’s go straight to the examples:
other_region.population_density ?
- The receiver isother_region
.other_region.continent
- The receiver isother_region
.self.consider_as_densely_populated?
- The receiver isself
.
And here is the important stuff, remember that rule: Private methods cannot be called with an explicit receiver.
Pay attention to the word 'explicit' here. Simplifying this statement - you cannot call private method by writing the invocation like this - object.some_method
. You need to use pure some_method
. In the latter case Ruby uses the implicit receiver, which is always self
. Regardless of that fact, you still cannot call a private method by self.some_method
, cause it’s still an explicit receiver, and rules are rules :)
Going back to our methods:
other_region.population_density ?
- The explicit receiver is present and the method is private - NoMethodError
other_region.continent
- The explicit receiver is present and the attribute is protected - OK
self.consider_as_densely_populated?
- The explicit receiver is present and the method is private - NoMethodError
Conclusion: This is the actual distinction between private and protected. Private methods cannot be called with an explicit receiver and protected ones can. Based on my experience, protected methods are rarely used among Ruby code lines, but they can be useful while comparing parameters, for example, when we want to restrict access to the attribute but still want to use it in the comparison method.
I hope that the access control rules are much more clear now. I encourage you to get your hands dirty with the code if you hadn’t done it already. Theory tends to get forgotten if it isn't proofed.
In the end, as I promised - answers to the statements:
TRUE
TRUE
FALSE
FALSE
TRUE
Summary
I’ve written this article because for me it was also surprising how public, private and protected access actually work in Ruby. The simplest things are the hardest to understand, or at least they can be. I really hope that this article was helpful for you. Or perhaps, you were aware of the access rules before reading it. Share your thoughts in the comments, I’m very curious of them. You can take a look at the docs too. You will find all these details there :)
Let's talk about Jamstack and headless e-commerce!
Contact us and we'll warmly introduce you to the vast world of Jamstack & headless development!