zerosum dirt(nap)

evolution through a series of accidents

zerosum dirt(nap)

Ruby Method Visibility

November 22, 2007 by nap · Comments

In Java (and most OO languages), declaring a method as private means that the method can only be accessed within the context of the defining class. Similarly, protected means that the method can only be accessed from that class or any of its subclasses.

In Ruby, things are a bit different. Both private and protected methods can be called by any instance of the defining class and its subclasses. Inheritance plays absolutely no role in determining the visibility of a method. The difference instead is that private methods can never be called with an explicit receiver, even if the receiver is self. This means that it’s not possible to access another object’s private methods, even if the object is of the same type as the caller. A private method must be called from within the calling object.

Consider the following example:

class Person
  def public_method
    "public"
  end

  protected
  def protected_method
    "protected"
  end

  private
  def private_method
    "private"
  end
end

class SalesPerson < Person
  def check_protected_method_explicit_receiver
    "#{self.protected_method} method OK with explicit receiver"
  rescue
    "failure accessing protected method with explicit receiver"
  end

  def check_protected_method_implicit_receiver
    "#{protected_method} method OK with implicit receiver"
  rescue
    "failure accessing protected method with implicit receiver"
  end

  def check_private_method_explicit_receiver
    "#{self.private_method} method OK with explicit receiver"
  rescue
    "failure accessing private method with explicit receiver"
  end

  def check_private_method_implicit_receiver
    "#{private_method} method OK with implicit receiver"
  rescue
    "failure accessing private method with implicit receiver"
  end
end

The public method can of course be accessed from anywhere (in this case, outside the class with an explicit receiver) and both private and protected methods of Person will obviously raise a NoMethodError.

Person.new.public_method
=> "public"
Person.new.private_method
=> NoMethodError: private method `private_method' called for #<Person:0x6ec698>
Person.new.protected_method
=> NoMethodError: protected method `protected_method' called for #<Person:0x6ea5c8>

So the protected method cannot be accessed outside of the class or it’s subclass, but from within the subclass, using it with either an implicit or explicit receiver works fine:

SalesPerson.new.check_protected_method_explicit_receiver
=> "protected method OK with explicit receiver"
SalesPerson.new.check_protected_method_implicit_receiver
=> "protected method OK with implicit receiver"

The private method can also be called from the subclass, but note how it only works with an implicit receiver:

SalesPerson.new.check_private_method_explicit_receiver
=> "failure accessing private method with explicit receiver"
SalesPerson.new.check_private_method_implicit_receiver
=> "private method OK with implicit receiver"

This also means you can do stuff like:

class Person
  def ==(other)
    protected_method == other.protected_method
  end
end

x = SalesPerson.new
y = Person.new
x == y
=> true

We’re accessing the protected_method of another class instance that shares our type here, specifying an explicit receiver. If you were to try to use private_method instead, a NoMethodError would be raised. You could also just call other.send("private_method") if you really wanted to, violating our encapsulation and angering the gods of object orientation. This is only a “little sin” though, and can be permissible if the situation calls for it (like, cough cough, my latest commit to DataMapper).

In summary, method visibility and access control can be a bit confusing at first, especially if you’re coming over to Ruby from some other OO language. If you’re still confused, there’s more information available here and here. Do yourself a favor and make sure you understand, cuz it’s important stuff!

blog comments powered by Disqus