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.
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 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 -
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.
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.
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.
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.
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 private
the_same_continent?(other_region)- it uses protected
can_be_crowdy?- it uses private
Let’s go through the code step by step:
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 is
other_region.continent- The receiver is
self.consider_as_densely_populated?- The receiver is
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 -
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 -
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:
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!
More posts in this category
8 Ruby Frameworks That Are Not Rails
Although the most popular, Rails are not the only Ruby frameworks out there. Well, one can say “what’s the point of having others?”, while others would quarrel with the “the more...
Feb 21· 5 min read
Chain of Responsibility Pattern in Ruby on Rails
In Ruby, chain of responsibility can help you with more effective optimizing of the legacy code. How? Read on to get a real-life project example, in which we used this method to...
Jan 24· 6 min read