If you’re new to ruby, then you may be tempted to implement private class methods like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class MachineBuilder
  def self.build_printer
    build(:printer)
  end

  def self.build_laser
    build(:laser)
  end

  private

  def self.build(:machine_type)
    parts = get_parts(:machine_type)
    machine = assemble(parts)
    machine
  end

  def self.get_parts(:machine_type)
    # fancy logic here
  end

  def self.assemble(parts)
    # more fancy logic here
  end
end

In this example, we have a MachineBuilder that exposes two class methods, build_printer and build_laser. These methods delegate their work to other methods. Our intent is to expose a minimal interface to anyone using MachineBuilder, so we want to hide how it actually builds printers and lasers.

We try using the private keyword to change the visibility of our “internal” methods, build, get_parts and assemble. This does not work. These three methods are still publicly visible.

Why Is This Approach Broken?

The keyword private, is a method. It sets the visibility for “subsequent methods” to private. The method applies to instance methods, not class methods.

Alternative Implementations

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class MachineBuilder
  def self.build_printer
    build(:printer)
  end

  def self.build_laser
    build(:laser)
  end

  def self.build(:machine_type)
    parts = get_parts(:machine_type)
    machine = assemble(parts)
    machine
  end

  def self.get_parts(:machine_type)
    # fancy logic here
  end

  def self.assemble(parts)
    # more fancy logic here
  end

  private_class_method :build, :get_parts, :assemble
end

This implementation works by adding class methods and then using the private_class_methodfrom Module to declare the desired methods as private.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class MachineBuilder
  def self.build_printer
    build(:printer)
  end

  def self.build_laser
    build(:laser)
  end

  class << self
    private

    def build(:machine_type)
      parts = get_parts(:machine_type)
      machine = assemble(parts)
      machine
    end

    def get_parts(:machine_type)
      # fancy logic here
    end

    def assemble(parts)
      # more fancy logic here
    end
  end
end

This alternative implementation works by adding private instance methods to MachineBuilder’s eigenclass (a.k.a. singleton or meta class).

The difference between instance and class methods will be covered in a different post.