Ruby Method Permissions: The Differences Between Public, Protected, and Private


It’s easy to code in Ruby for years, and never pay attention to method permissions. I know, because I did it. I came from a C/C++ background, so I understood the concepts of public vs private. And I vaguely understood that Ruby’s protected fell somewhere in between. I just never really cared enough to look it up. Eventually I got curious. I’ll run down the different types of method visibility here.

Examples

It’s hard to think of a good, simple example to use for this. I’ll go with hobbits. They’re friendly to other species, but they have private lives, as well. Much of their privacy seems to be open to other hobbits, since they appear to be able to wander into each others’ houses in the shire at will. Of course, they also have a few choice secrets kept just to themselves. They have all the qualities of a well-rounded Ruby method. Let’s define their class here:

class Hobbit
  def initialize(name, rooms, has_ring)
    @name, @rooms, @has_ring = name, rooms, has_ring
  end

  def name
    @name
  end

  def name_of(hobbit)
    hobbit.name
  end

  def rooms_of(hobbit)
    hobbit.rooms
  end

  def hobbit_has_ring?(hobbit)
    hobbit.has_ring?
  end

  protected

  def rooms
    @rooms
  end

  private

  def has_ring?
    @has_ring
  end
end

Each hobbit has three attributes. Their name is public, the number of rooms in their house is protected, and whether or not they have The Ring is private. There are methods to read these attributes, and to try to read them from other hobbits. Now let’s create a couple hobbits to work with:

irb(main):001:0> require 'hobbit.rb'
=> true
irb(main):002:0> frodo = Hobbit.new('Frodo', 3, true)
=> #<Hobbit:0x10038b5c0 @rooms=3, @name="Frodo", @has_ring=true>
irb(main):003:0> samwise = Hobbit.new('Samwise', 2, false)
=> #<Hobbit:0x1003769e0 @rooms=2, @name="samwise", @has_ring=false>

Public Methods

This is the default method type in Ruby (and most languages), and it means that it can be called from anywhere, by any hunk of code that knows what the object is. We can easily find out the names of our hobbits:

irb(main):004:0> frodo.name
=> "Frodo"
irb(main):005:0> samwise.name
=> "Samwise"

Protected Methods

Now, what about the rooms in their houses? This info is accessible only through protected methods:

irb(main):006:0> frodo.rooms
NoMethodError: protected method `rooms' called for #<Hobbit:0x10034a4a8 @rooms=3, @name="Frodo", @has_ring=true>
	from (irb):6
	from :0
irb(main):007:0> frodo.rooms_of(frodo)
=> 3
irb(main):008:0> frodo.rooms_of(samwise)
=> 2

As you can see, we can’t call the hobbits’ room methods directly, but one hobbit can call the protected rooms method for itself, or any other hobbit. In purely programming terms, public methods are globally accessible, where protected methods can only be called by other methods in the class.

Private Methods

What about ring status? This info is locked in private methods. How can we access this?

irb(main):013:0> frodo.has_ring?
NoMethodError: private method `has_ring?' called for #<Hobbit:0x10034a4a8 @rooms=3, @name="Frodo", @has_ring=true>
	from (irb):13
	from :0
irb(main):014:0> frodo.hobbit_has_ring?(frodo)
NoMethodError: private method `has_ring?' called for #<Hobbit:0x10034a4a8 @rooms=3, @name="Frodo", @has_ring=true>
	from ./hobbit.rb:19:in `hobbit_has_ring?'
	from (irb):14
	from :0
irb(main):015:0> frodo.hobbit_has_ring?(samwise)
NoMethodError: private method `has_ring?' called for #<Hobbit:0x100374c80 @rooms=2, @name="Samwise", @has_ring=false>
	from ./hobbit.rb:19:in `hobbit_has_ring?'
	from (irb):15
	from :0

We can’t access the has_ring? method directly. We can’t access it indirectly from another method, specifying the hobbit we’re interested in. Not even if that hobbit is ourselves! Let’s reopen our class and add the i_have_the_ring? method:

class Hobbit
  def i_have_the_ring?
     has_ring?
  end
end

Now frodo has a way to check if he has the ring:

irb(main):021:0> frodo.i_have_the_ring?
=> true

In Ruby, protected methods are still open to other objects of the same type. So as long as the “receiver” (hobbit in hobbit.rooms) is the same class as the calling object, method access is granted.

But private methods are not allowed to specify a receiver at all. I can’t say frodo.has_ring? or even self.has_ring? inside the frodo object at all. I’m only allowed to say has_ring? and the object will assume it’s calling its own method.

Summary

While public methods are fairly straightforward, the difference between protected and private can cause problems if you’re not aware of it. Ruby’s private implementation is even stricter than C’s, not allowing receivers. But protected balances that out with the ability to “walk into sibling objects’ houses”.

I tend to use protected methods when I don’t intend for them to be called directly, and I don’t write tests for them. I write tests for the public methods that call them. And I generally avoid private methods, because I can’t envision needing to block sibling-to-sibling access (although I probably should if I’m not testing them, right?) but I don’t want to spend hours debugging a method call that breaks the no-receiver rule.

Advertisements

Tags: , , , , , ,

4 Responses to “Ruby Method Permissions: The Differences Between Public, Protected, and Private”

  1. Vladimir Says:

    I don’t understand the last sentence.

    Why you need to test reseivers?

    What if we needn’t to use some methods outside the object? I think that private methods needed when we want to secure the object’s implementation and for semantic code, where we show that some methods shouldn’t be used outside the object, isn’t it?

    If I have written something wrong please fix me=)

  2. Jaime Bellmyer Says:

    Hi Vladimir-

    My point about testing was that if a method is never meant to be used directly, it doesn’t need to be tested directly. As long as the methods that use it pass their tests, you should be fine.

    As for what should or shouldn’t be a public method, some developers/languages think methods should be public by default, and others think they should be private unless there’s a good reason to open them up. C/C++ prefers privacy first, but more open languages like Perl and Ruby lean toward openness.

    In Ruby 1.8, there’s a way to call protected and private methods directly – you can use the object’s “send” method. Starting in Ruby 1.9, this no longer works. For that reason especially, I tend to think very carefully before making a method private.

    As for deciding whether to make a method protected or private, I don’t have a good answer. I started using the “protected” keyword before I learned the difference, and it’s become habit. I think the small added openness (other objects of the same class being able to call them directly) could be useful, although I haven’t needed it yet.

  3. Vladimir Says:

    Jaime, I have one another question: What tools you using for testing your code?

    Oh, and thank you for your good answer above.

    P.S. I have translated this post to my blog, you don’t mind?

  4. Jaime Bellmyer Says:

    Hi Vladimir-

    Feel free to translate my posts on your blog, as long as you link back to my original article. I appreciate your help!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


%d bloggers like this: