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.