December 21, 2012

Private methods in Ruby

Consider the following code:

(This blew my mind the other day.)

class Hello
  def public_hello
    self.private_hello
  end

  private
  def private_hello
    puts "Hello!"
  end
end

You would expect that this would work fine: private_hello is a private method, but it's being called from within the class.

Nope.

>> hello = Hello.new
=> #<Hello:0x10d0cc200>
>> hello.public_hello
NoMethodError: private method `private_hello' called for #<Hello:0x10d0cc200>
  from (irb):3:in `public_hello'
  from (irb):13

I spent an embarassingly long time trying to figure out what was wrong ("Do I just not understand how private methods work?!"), and confused one of my coworkers as well in the process of doing so.

However, it turned out to be old news. One post puts the issue pretty succinctly:

private methods can never be called with an explicit receiver, even if the receiver is self

So, the problem with self.private_hello is that the private method is being called on an explicit receiver, even though the receiver is technically the same object—you'd need to call private_hello by itself instead.

Having learned access control modifiers in Java first, I thought this was really bizarre. I guess I need to learn Ruby a little better! (: