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

More on Method Visibility

Jamis Buck posted a great article on this a while ago. I am hoping the fact that I did not use metaprogramming in my examples will help those who are just starting out and might not understand the syntax in Jamis’ post. That said, his article is definitely worth a read as well.

4 Comments

  1. Great post John!

    It’s these little guides to Ruby that help newcomers to better understand how things are done.

    It also serves as a reminder to more experienced programmers who might have fuzzy memories and need a quick overview.

  2. Nice write John, but you’ve made things complicated. protected methods could be called within all objects of the same class (or subclasses). So this example would work:

    class A
    def bar
    A.new.foo #ok
    #A.new.baz #not ok
    end

    protected
    def foo
    puts “foo”
    end

    private
    def baz
    puts “baz”
    end

    end


    A.new.bar


    As for private methods, only the exact same object could call them. This is very different from C++, in which objects of same class could call each other’s private methods.


    And there’s no rules that private or protected methods could only called from a public method.

  3. Sorry the code looks odd in my previous comment. Here’s new one and hopefully textile would render it right:

    class A  
      def bar
        A.new.foo #ok
        #A.new.baz #not ok
      end
      
      protected
      def foo
        puts "foo"
      end
      
      private
      def baz
        puts "baz"
      end
    end
    
    A.new.bar
    
  4. @James – Your example is the most succinct way to show the difference but the point of the article is to see the different ways method visibility works and the subtle difference between private and protected. Thanks for the codes snippet and paragraph.

Sorry, comments are closed for this article to ease the burden of pruning spam.

About

Authored by John Nunemaker (Noo-neh-maker), a programmer who has fallen deeply in love with Ruby. Learn More.

Projects

Flipper
Release your software more often with fewer problems.
Flip your features.