Misleading Intuitions: Defining Private Class Methods in Ruby
Beware your gut! It’s only ever right some of the time.
Beware your gut! It’s only ever right some of the time.
After working with a technology for a certain amount of time, we begin to develop a mental model of how we expect things to behave. We begin to feel how we should do things, and many a time these feelings are correct.
In this article, I will highlight an instance of when our intuitions might betray us by examining how private class methods are defined in Ruby. Additionally, I will demonstrate the ways by which Ruby allows us to define private class methods.
Before looking at how we define private class methods, let us revisit how we create public and private instance methods as well as public class methods. Seeing how these are done is necessary for understanding how we might be led to intuit, and consequently write, incorrect code for defining private class methods.
Defining Public and Private Instance Methods
Take this trite code snippet below for example. It shows how we define public and private instance methods and the actual behaviors when we try to use them.
Defining Public Class Methods
Approach #1: I typically use this approach when I’m defining one or two class methods.
Approach #2: I use this other approach when it no longer makes sense to use approach #1 — that is, when there are enough class methods to make prepending each one with self
a chore.
Defining Private Class Methods
Let’s say we want to refactor self.name
to use a private method that returns the name of the car.
Based on what we know about defining private instance methods and public class methods, our intuitions might lead us to think of two approaches to define private class methods. We are going to look at both of these approaches.
Note: Changing self.name
in the way I’ve done below is not good refactoring in practice— in fact, it’s the exact opposite. But, bear with me for the sake of making a point.
Approach #1: This approach combines what we know about defining private instance methods (put the methods below private
) and defining public class methods (prepending the method name with self) from approach #1 in the previous section.
Plot Twist! Although seeming as if it should work, this is not how we define private class methods in Ruby.
Here is a situation where we will not get any warnings or signals that we’ve done something we did not intend to do. If we didn’t check if Toyota.i_am
was actually private, we would remain ignorant, possibly until it caused some failure in our software and/or cost us some money.
Approach #2: This approach brings together what we know about defining private instance methods (put the methods below private
) and defining public class methods from approach #2 in the previous section.
There are no surprises here. This works as expected. However, there is a third approach!
Approach #3: The approach shown below is the quick and easy way of tagging a class method as private. It’s done simply by prepending the method definition with a private_class_method
method call.
Defining more than just one or two class methods in this way will quickly get annoying and ugly. You’ll probably see a variation of this approach that’s similar to the code segment below.
The problem with using this second version of approach #3 is that we have to be at the bottom of our class to see which methods are private class methods.
I tend to favor approach #2 for defining both public and private class methods in my day-to-day because of the very obvious separation of class methods from instance methods.
Summary
In this article, we saw an example of how our intuitions might lead us astray using the definition of private class methods in Ruby as our subject. Moreover, we saw the correct ways of defining private class methods and should now be able to choose the approach that best suits our needs.
Remember, our intuitions are only right some of the time.
Special thanks to two of my friends Alston and Brandon for proofreading and giving me their feedback on this article.