Ruby's Private is for Methods Only

The other day I saw a pull request, on a Rails project, that declared a private inner class. Having my fair share of experience with Java, I am very familiar with the construct. Essentially, you declare one class inside of another class and declare it as private. This is often done to clean up your internal data structures and hide the implementation details from others.

I couldn't recall ever seeing this in Ruby before, so it gave me pause. As we know, in Ruby, "private" isn't really all that private. It just means that a method cannot be called with an explicit receiver.

class Tommy
  def pinball
    "wizard"
  end

  private
  def blind
    "how do you think he does it?"
  end
end

>tommy = Tommy.new
=> #<Tommy:0x007f8f91074bf8>
> tommy.pinball
=> "wizard"
> tommy.blind
NoMethodError: private method `blind' called for #<Tommy:0x007f8f91074bf8>
> tommy.send(:blind)
=> "how do you think he does it?"

"What does this do?" I thought to myself, looking at the private class declaration. I didn't know, so I decided to test it out.

class Tommy
  def pinball
    "wizard"
  end

  private
  class Senses
    def blind
      "how do you think he does it?"
    end
  end
end

> senses = Tommy::Senses.new
=> #<Tommy::Senses:0x007f8f9211a958>
> senses.blind
=> "how do you think he does it?"

Hmm...that wasn't exactly what I expected. The concept of a private inner class exists in many languages, so I thought that maybe Ruby wouldn't let me inherit from Tommy::Senses and override the implementation. It is private, after all.

class Tommy
  def pinball
    "wizard"
  end

  private
  class Senses
    def blind
      "how do you think he does it?"
    end
  end
end

class BallyTableKing < Tommy::Senses
  def blind
    "sure plays a mean pinball"
  end
end

> king = BallyTableKing.new
=> #<BallyTableKing:0x007f8f920d2ba8>
> king.blind
=> "sure plays a mean pinball"

Again, not at all what I expected. I decided to go to the Ruby docs to read up on private.

With no arguments, sets the default visibility for subsequently defined methods to private. With arguments, sets the named methods to have private visibility.

There are no mention of classes at all, which explains a lot. The private keyword in Ruby only exists to declare methods as private, not classes.

by Karle Durante