Public Indecency, Protect Your Bits and Don’t Touch My Privates

One thing that I remember as confusing when I was starting out in ruby was “protected.” I understood the difference between private and public, but I remember protected seeming mysterious for quite a while. In the same vein as my class and instance variables article, I’ve decided to throw some examples together in hopes that those new to ruby will find it helpful. If I miss anything or am flat out wrong on something, let me know in the comments.

Public

If you don’t explicitly state private or protected, your methods are public. This is pretty easy to understand and the code samples below show that public methods can be called for an instance of a class, in an instance of an unrelated class and in an instance of an inherited class. In other words, you can call a public methods wherever you want.

class A
  def foo
    puts 'foo in A'
  end
end

a = A.new
a.foo

class B
  def foo
    puts 'foo in B'
    a = A.new
    a.foo
  end
end

b = B.new
b.foo

class C < A
  def bar
    puts 'foo in C'
    foo
  end
end

c = C.new
c.bar

# Outputs:
# foo in A
# foo in B
# foo in A
# foo in C
# foo in A

Private

Private methods are named that for a reason. It means that they are private to the class they belong to and cannot be used outside of that class, without using send.

class A
  private
    def foo
      puts 'foo in A'
    end
end

a = A.new
a.foo

# NoMethodError: private method ‘foo’ called for ...

Likewise, as one would expect, subclassing and attempting to use a private method outside of the class definition will result in much of the same.

class A
  private
    def foo
      puts 'foo in A'
    end
end

class B < A
end

b = B.new
b.foo

# NoMethodError: private method ‘foo’ called for ...
Valid Private Uses

You can however use private methods inside of a class definition and inside of a subclass definition.

class A
  def bar
    puts 'bar in A'
    foo
  end

  private
    def foo
      puts 'foo in A'
    end
end

class B < A
  def baz
    puts 'baz in B'
    foo
  end
end

a = A.new
a.bar

b = B.new
b.baz

# Outputs:
# bar in A
# foo in A
# baz in B
# foo in A

Protected

Protected is similar to private but has one quite subtle difference. Like private, protected cannot be used outside of the class definition.

class A
  protected
    def foo
      puts 'foo in A'
    end
end

a = A.new
a.foo

# NoMethodError: protected method ‘foo’ called for ...

class B < A
end

b = B.new
b.foo

# NoMethodError: protected method ‘foo’ called for ...

Also, like private, you can use protected inside of a class definition as long as it is wrapped with a public method.

class A
  def bar
    puts 'bar in A'
    foo
  end

  protected
    def foo
      puts 'foo in A'
    end
end

a = A.new
a.bar

class B < A
  def baz
    puts 'baz in B'
    foo
  end
end

b = B.new
b.baz

# Outputs:
# bar in A
# foo in A
# baz in B
# foo in A
The Subtle Difference

This is where the weird part of protected comes in. You can instantiate a class and call a protected instance method on that class inside of a subclass and things will work just fine.

class A
  protected
    def foo
      puts 'foo in A'
    end
end

class B < A
  def baz
    puts 'baz in B'
    a = A.new
    a.foo
  end
end

b = B.new
b.baz

# Outputs:
# baz in B
# foo in A

The same is not true of private, however, and you will end up with a NoMethodError.

class A
  private
    def foo
      puts 'foo in A'
    end
end

class B < A
  def baz
    puts 'baz in B'
    a = A.new
    a.foo
  end
end

b = B.new
b.baz

# Outputs:
# baz in B
# NoMethodError: private method ‘foo’ called for ...

Leave a Reply