Ruby Private Class Methods24 Jan 2016
In the Ruby programming language, defined methods come in two variants: instance methods and class methods. Instance methods are available after an object has been initialized, creating an instance. Class methods, on the other hand, are available without creating an instance of the class they are defined upon.
Ruby methods can vary in visibility. Public methods are available in any context, while private methods’ availability is restricted within the instance of a class and its descendants. A third visibility scope, protected, behaves similarly to private methods, but protected methods can be called by other instances of the same class.
For a quick refresher, public and private instance methods look like this:
class Dog def do_trick bark end private def bark puts 'woof woof' end end
When the public method is called:
> dog = Dog.new > dog.do_trick # => woof woof
And the private method:
> dog = Dog.new > dog.bark # => NoMethodError: private method `bark' called for <Dog>
Private class methods might not be as common as private instance methods, but they still have their place. For instance, a class method may require internal helper methods to complete its function. Whatever the reason, defining private class methods has value but is not always intuitive.
Dog class needs to maintain a list of
tricks that will be used within the other public class methods. This list should not be accessible to any callers outside the
The wrong way
A first pass at writing the private
tricks method could look like:
class Dog private def self.tricks [:bark, :roll_over, :fetch] end end
However, when testing the visibility of the
> Dog.tricks # => [:bark, :roll_over, :fetch]
Uh oh, no error was thrown indicating a that a private method was called, this method is completely public.
The reason that the above code did not produce a private method has to do with Ruby’s object hierarchy, interactions amongst internal classes, instances of those classes, and eigenclasses. A detailed write up about how Ruby’s objects work with one another can be found in a previous post.
When defining methods in a class, the default context and the context introduced by the
self. method declaration are distinct.
The private method scope can not be used in the above way as it does not handle the context change between methods defined on the
Dog class and defined within
So what are the alternatives?
class << self
One alternative way to define class methods on an object is to use the
class << self syntax. This syntax opens an eigenclass for the supplied argument. In this example,
self within the
Dog class will open the
class Dog class << self private def tricks [:bark, :roll_over, :fetch] end end end
One thing to note is that when defining methods like this, the declaration has changed from
def self.tricks to simply
Now, the private scope is preserved and expected behaviour is achieved:
> Dog.tricks # => NoMethodError: private method `tricks' called for Dog:Class
Modules in ruby are collections of methods and constants. These collections can be used as encapsulation tools or, in this case, alternatives to defining public and private class methods.
When a class
extends a module, all the methods within that module become class methods on the subject class*. This pattern can be used to define the
tricks method on
*Note: This only pertains to methods defined in a “typical sense” any
def self. or
def Dog. method definitions in a module do not automatically become class methods in the same way when
class Dog module ClassMethods private def tricks [:bark, :roll_over, :fetch] end end extend ClassMethods end > Dog.tricks # => NoMethodError: private method `tricks' called for Dog:Class
A benefit of this approach is readability. The module named
ClassMethods, which is contained and thus encapsulated by the
Dog class, is the clear home for all desired class methods.
Visibility modifiers like
private behave accordingly and a simple
extend at the bottom of the module definition brings it all together.
A third approach is to use the built in method
Module#private_class_method. This method’s purpose is to change the visibility of existing class methods.
Unlike the other two solutions, this one does not require a different style of method definition from the incorrect solution:
class Dog def self.tricks [:bark, :roll_over, :fetch] end private_class_method :tricks end > Dog.tricks # => NoMethodError: private method `tricks' called for Dog:Class
This method can also be in-lined during the class’ method definition:
class Dog private_class_method def self.tricks [:bark, :roll_over, :fetch] end end > Dog.tricks # => NoMethodError: private method `tricks' called for Dog:Class
If a single class method must be private and saving lines is very important, this style might be applicable; but, it certainly sacrifices a level of readability.
A major strength of
private_class_method is its explicitness. Unlike the other approaches, this one does not require a special module definition or method definition context switching.
The aforementioned solutions will get the job done but might not answer lingering questions about why things work the way they do. A few great articles on the ruby eigenclass and Matz’s thoughts on Ruby method design should help paint a more concise picture of why Ruby’s private class method definition is complex.