Method visibility in Ruby
A common point of confusion to even experienced Ruby programmers is the visibility of public
, protected
, and private
methods in Ruby classes. This largely stems from the fact that the behavior of those keywords in Ruby is different from what you might have learned from Java and C.
To demonstrate these differences, let’s set up a little script:
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 |
class Foo def a; end # call 'a' with explicit 'self' as receiver def b; self.a; end # call 'a' with implicit 'self' as receiver def c; a; end end def safe_send(receiver, method, message) # can't use 'send' because it bypasses visibility rules eval "receiver.#{method}" rescue => e puts "#{message}: #{e}" else puts "#{message}: succeeded" end visibility = ARGV.shift || "public" Foo.send(visibility, :a) foo = Foo.new safe_send(foo, :a, "explicit receiver ") safe_send(foo, :b, "explicit 'self' receiver") safe_send(foo, :c, "implicit 'self' receiver") |
Basically, the script just creates a class “Foo” with three methods: a
, which we’ll invoke directly with an explicit, non-self receiver; b
, which invokes a
with self
as receiver, and c
, which invokes a
with an implicit receiver of self
. We’ll use the safe_send
method to call each of those methods and log the result.
So, first: the public
keyword. In Ruby, public
means that the method may be invoked just about any way you please; in technical terms, the receiver of the message may be either explicit (“foo.bar”), self (“self.bar”) or implicit (“bar”).
1 2 3 4 |
$ ruby demo.rb public explicit receiver : succeeded explicit 'self' receiver: succeeded implicit 'self' receiver: succeeded |
The protected
keyword puts a straitjacket around the method. Any method declared protected
may only be called if the receiver is self
, explicitly or implicitly. (Update: protected methods may actually be called any time the receiver is of the same class as ‘self’...and an explicit self as receiver is just a specific case of that. Modifying the script to demonstrate this condition is left as an exercise for the reader.)
1 2 3 4 |
$ ruby demo.rb protected explicit receiver : protected method `a' called for #<Foo:0x3fc18> explicit 'self' receiver: succeeded implicit 'self' receiver: succeeded |
Lastly, the private
keyword is the tightest setting of all. A private method cannot be called with an explicit receiver at all, even if that receiver is “self”.
1 2 3 4 |
$ ruby demo.rb private explicit receiver : private method `a' called for #<Foo:0x3fc18> explicit 'self' receiver: private method `a' called for #<Foo:0x3fc18> implicit 'self' receiver: succeeded |
Note that, unlike languages such as Java, inheritance plays absolutely no part in determining method visibility in Ruby. Subclasses can access both protected and private methods of the superclass without trouble, so long as they abide by the rules laid out above.
The difference between protected and private is very subtle, as you can see, which explains why protected is rarely used by most Rubyists. If it is used at all, it is generally as a convention, to document methods that are internal to the class, but which lie closer to the public interface than others. In Rails, for instance, you might declare your controller filter methods and model validation methods as “protected” (because the framework will call those methods) and reserve the “private” designation for those methods that are only ever called from within your own model or controller code.
Reader Comments
I think you’re a little off on the definition of protected. Protected methods can be called by any object with the same class as self.
23 Feb 2007
You’re missing the interpolation of receiver on safe_send Jamis.
23 Feb 2007
Gah, I’m wrong. Eval takes care of it. Too damn early to look at code. Sorry.
23 Feb 2007
You’re right, Dave, thanks for pointing that out. I’ve updated the article to mention that condition.
23 Feb 2007
I’ve posted my example of “the exercise” here: http://redsquirrel.com/cgi-bin/dave/dynamic/ruby.protected.access.html :-)
23 Feb 2007
Dave, thanks! Good example.
23 Feb 2007
Just a quick word to the wise: instead of defining a one-line method with “def a; self.a; end”, Ruby lets a method declaration exist on a single line if you use provide the “()” right after the method name. For example:
class Foo def a() end def b() self.a end def c() a end end
The parenthesis helps the interpreter distinctly identify the argument token. I usually prefer this way since it doesn’t use the semi-colon hack. :)
23 Feb 2007
Whoops, the newline formatting got stripped.
Use your imagination to put newlines after each method. :)
23 Feb 2007
Thanks, Jay. I generally prefer the semi-colons, since the empty parens just look noisy to me. Different people, different preferences. :)
23 Feb 2007
Thought I’d pipe in that visibility is a hot item to change in Ruby 2. In particular, private may become more like Java’s, where a method is not even visible outside the class it’s defined in (including subclasses), so no obj.send :private_method anymore (though one could still use #instance_eval to reach it if you really wanted).
See ruby-core:9996 for details.
23 Feb 2007
For Ruby 2 you’ll be able to use obj.funcall(:private_meth) in place of send.
23 Feb 2007
I’ve come from a C++/C#/Java background, and this is certainly something that initially caught me off guard. Thanks for clearing it up! :) This post in my blog extends the discussion to instance variables….
http://richtextblog.blogspot.com/2007/01/ruby-instance-variables-in-derived.html
14 Mar 2007