public, protected and private
private
A public method is the interface: anyone may call it. A private method is behind it: only code inside the same object may call it without a receiver. The rule is about the receiver, not about the file, which is why a private method can still be called with send:
ruby -e 'class A; private def x = 1; end; p A.new.send(:x)'
1
Calling it the normal way raises, and the message names the method and the visibility:
ruby -e 'class A; private def x = 1; end; A.new.x'
-e:1:in '<main>': private method 'x' called for an instance of A (NoMethodError)
private def x is the short form; the older form writes def x; end and then private :x. Both work, and the short form keeps the visibility next to the method.
protected
A protected method may be called with an explicit receiver, as long as the receiver is the same kind of object. That is what an object needs to compare itself with another:
class Score
def initialize(points)
@points = points
end
def higher_than?(other)
points > other.points
end
protected
def points
@points
end
end
ruby -e 'class A; def initialize(n) = @n = n; protected def n = @n; def bigger?(o) = n > o.n; end; p A.new(2).bigger?(A.new(1))'
true
bigger? calls o.n, and that is allowed because the caller is another A. From outside the class, the same call is refused.
module_function
A module that is used as a namespace rather than mixed in can expose a method as both a module method and a private instance method. module_function does that for one name:
ruby -e 'module M; def self.x = 1; def y = 2; module_function :y; end; p M.x; p M.y'
1
2
The method y is callable as M.y and, inside a class that includes the module, as a private method. That is the usual way to write a helper module with a public entry point and internal steps.
What to watch for
Private is a message to the next reader, not a security boundary.
send and instance_variable_get
reach everything, and a test that uses them is testing the
implementation. Test the public method whose behaviour depends on the
private one.
A private method on a class (def self.x under
private) is not private in the same way: class
methods live on the singleton class, and
private_class_method is the one that hides them
from callers.
Next: Keyword arguments and forwarding
Article author: Arthur Isaev