Several of my friends are picking up Ruby these days. Just like me, they’re coming mostly from the Java world. Good thing about that is that they’re asking me questions about Ruby. Always a great opportunity to dig more into the language, and to write down some tidbits that came up.
When you’re coming from Java you’re used to private being private. When you declare a method as being such it’s off limits as soon as you leave the class’ scope. No way to reach it from subclasses or, god forbid, call it from another object. Well, you could use that really awkward reflection code and really ram it in, and eventually call it, but that’s just tedious.
Different story in Ruby. Using the keyword private
is more of a marker than a mechanism to enforce access restrictions to an object’s methods. You can even call private methods from subclasses, no problem. There’s just one caveat, a private method can’t be called on an object. It must be a lonely method call. No self
for you today.
class Restricted
private
def secret_method
puts "top secret!"
# do something private
end
end
Now, you’d think the deal is sealed. What’s secret, stays secret. And you’d be right:
Restricted.new.secret_method
# NoMethodError: private method `secret_method' called for #<Restricted:0x362eb4>
Oh noes! And this won’t work either:
class Freedom < Restricted
def public_domain
self.secret_method
end
end
Not to worry, the fix is right at hand:
def public_domain
secret_method # but this will!
end
Huh? Did we just call a private method from a subclass? We sure did. Again, private restricts you in a way that you can only call the method from within the context of a class (subclass or not), but without calling it on an object. Of course there’s a different way, you can just un-private the method, but that’s just mean.
class Freedom < Restricted
public :secret_method
def public_domain
self.secret_method # free as a bird
end
end
And there’s always good old send
to rely on.
class Freedom < Restricted
def public_domain
self.send(:secret_method) # free as a bird
end
end
In a world where you can change almost everything about a class, private
is merely something to remind a developer using a class that calling this method out of context or from outside the class is probably not the way to go. But if he still wants to go ahead, it’s his responsibility, including all the risks, possible internal changes in a future version and the like.