views:

153

answers:

1

The class

class A

  private
  def foo
    puts :foo
  end

  public
  def bar
    puts :bar
  end

  private
  def zim
    puts :zim
  end

  protected
  def dib
    puts :dib
  end
end

instance of A

a = A.new

test

a.foo rescue puts :fail
a.bar rescue puts :fail
a.zim rescue puts :fail
a.dib rescue puts :fail
a.gaz rescue puts :fail

test output

fail
bar
fail
fail
fail

.send test

[:foo, :bar, :zim, :dib, :gaz].each { |m| a.send(m) rescue puts :fail }

.send output

foo
bar
zim
dib
fail

The question

The section labeled "Test Output" is the expected result. So why can I access private/protected method by simply Object#send?

Perhaps more important:

What is the difference between public/private/protected in Ruby? When to use each? Can someone provide real world examples for private and protected usage?

+4  A: 

Technically: Because send doesn't do anything to check method visibility. (It would be more work to do so.)

Philosophically: Ruby is a very permissive language. You can already just open up a class and make any method you want public. The language designers implemented send in a way that allows it to override the restrictions normally imposed by private. Ruby 1.9 was originally going to have two variants, a private-respecting send and an unsafe variant called send!, but this was apparently dropped for backwards compatibility.

As for what private, protected and public mean:

  • public methods can be called by any sender
  • protected methods cannot be called outside of an instance of the method's class or an instance of a subclass
  • private methods cannot be called with an explicit receiver (with a couple of exceptions, such as setter methods, which always have to have an explicit receiver, and so can be called within the class that way)
Chuck
This makes sense, thank you. Could you add some real-world examples for *appropriate* use of `protected` and `private`? :)
macek
Good answer. It may be worth pointing out that Ruby 1.9 introduces `Kernel#public_send` which will not call protected/private methods. It's available for Ruby 1.8 with the `backports` gem.
Marc-André Lafortune
@smotchkkiss, that sounds like a separate question altogether.
glenn jackman