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