Method visibility in Ruby

Posted by Jamis on February 23, 2007 @ 09:05 AM

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.

Posted in Essays and Rants

Comments

Have something to add? Click here to leave a comment.

23 Feb 2007

1. Dave Hoover said...

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.

2. Kevin Clark said...

You’re missing the interpolation of receiver on safe_send Jamis.

3. Kevin Clark said...

Gah, I’m wrong. Eval takes care of it. Too damn early to look at code. Sorry.

4. Jamis said...

You’re right, Dave, thanks for pointing that out. I’ve updated the article to mention that condition.

5. Dave Hoover said...

I’ve posted my example of “the exercise” here: http://redsquirrel.com/cgi-bin/dave/dynamic/ruby.protected.access.html :-)

6. Jamis said...

Dave, thanks! Good example.

7. Jay Phillips said...

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. :)

8. Jay Phillips said...

Whoops, the newline formatting got stripped.

Use your imagination to put newlines after each method. :)

9. Jamis said...

Thanks, Jay. I generally prefer the semi-colons, since the empty parens just look noisy to me. Different people, different preferences. :)

10. Nick Sieger said...

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.

11. Rob Sanheim said...

For Ruby 2 you’ll be able to use obj.funcall(:private_meth) in place of send.

14 Mar 2007

12. rich said...

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