In Ruby, which callers may invoke a public, a private and a protected method, and can a subclass call its parent's private methods?
answer
- the rule is about the receiver
- private: no receiver, or literal self
- self. allowed since Ruby 2.7
- protected: caller's self is_a? owner
- subclasses inherit private methods
basics
~20 sPublic methods are callable by anyone. Private methods can be called only without an explicit receiver or with literal self. Protected methods accept an explicit receiver when the caller's self is a kind of the defining class. Subclasses can call inherited private methods.
solid answer
~40 sRuby's visibility rules are about the **receiver** of a call, not about which class the code sits in. `public`, the default, allows any caller. `private` allows a call only with no explicit receiver, or, since Ruby 2.7, with the literal `self.` as receiver; `other.secret` raises `NoMethodError` even when `other` is the same class, and so does `copy = self; copy.secret`. `protected` allows an explicit receiver as long as the calling code's `self` is a kind of the class or module that defines the method, which lets one `Salary` read another's protected `amount`. Because a subclass calls inherited methods on `self` with no receiver, it can use its parent's private methods freely: Ruby's `private` does not hide anything from subclasses.
code
ruby · 19 linesclass Salary
def initialize(amount) = @amount = amount
def doubled = amount * 2 # implicit receiver: fine
def doubled_self = self.amount * 2 # literal self: fine since Ruby 2.7
def compare(other) = other.amount # explicit receiver: NoMethodError
private
def amount = @amount
end
class Bonus < Salary
def tripled = amount * 3 # inherited private method: fine
end
Bonus.new(10).tripled # => 30
Salary.new(1).compare(Salary.new(2))
# NoMethodError: private method 'amount' called for an instance of Salarygo deeper
Recall the three levels: public for anyone, private only without a receiver or with self., protected for other objects of the same class family.
Explain that the checks are about the receiver, that private is per object, that the literal self exemption arrived in 2.7, and that subclasses can call inherited private methods.
Show how you use visibility to shape interfaces: small public surfaces, protected only for peer operations like comparison, and awareness that send can still reach private methods.
Weigh visibility as a communication tool against its limits, deciding where a team relies on it versus separate objects or modules to keep internals out of reach.
## The rule is about the receiver Every Ruby method call has a **receiver**: the object before the dot, or `self` when there is no dot. Ruby's three visibility levels decide which receivers are acceptable: | Visibility | Implicit receiver (`amount`) | Literal `self.amount` | Other object (`other.amount`) | |---|---|---|---| | `public` | allowed | allowed | allowed | | `private` | allowed | allowed (Ruby 2.7+) | `NoMethodError` | | `protected` | allowed | allowed | allowed only if the caller's `self` is a kind of the defining class | A violation raises `NoMethodError` with a message such as `private method 'amount' called for an instance of Salary` or `protected method 'amount' called for an instance of Salary`. ## Private: per object, not per class The Ruby syntax documentation puts it precisely: a private method may only be called without a receiver, or with a literal `self` as the receiver. Two consequences surprise newcomers: - **Another instance of the same class cannot call it.** Inside `Salary`, `other_salary.amount` raises when `amount` is private, even though the code is in the same class. - **Only the literal keyword counts.** `copy = self; copy.amount` raises too, because the receiver is a variable, not the word `self`. Before Ruby 2.7, `self.amount` raised as well; only private writers such as `self.amount = 5` were allowed with `self`. Ruby 2.7 allowed any private method to be called on a literal `self`. ## Subclasses see everything In Ruby, **subclasses already have access to all methods defined in the parent class, even private ones**, as the `Module#protected` documentation states. A subclass method that calls `amount` with no receiver finds the inherited private method through normal lookup and runs it. So: 1. `private` is not "hidden from subclasses". 2. `protected` is not "visible to subclasses" either; it is about **peers**, other objects whose class inherits from the defining class. 3. The only thing `private` blocks is calling the method on an explicit receiver other than `self`. ## Protected: for peers `protected` exists for methods that objects of one family need to call on each other, most often comparison operators. Ruby checks one condition when a protected method is called with an explicit receiver: the object running the calling code must be a kind of the class or module where the method is defined. If `Salary` defines protected `amount`, then any `Salary`, or instance of a `Salary` subclass, may call `other.amount` on another `Salary`, while outside code gets `NoMethodError`. ## Private writers and self Writers are the one place where `self.` was always required for a private method. A bare `amount = 5` inside a method creates a local variable, so the only way to call a private `amount=` is `self.amount = 5`, and Ruby has allowed that form for private writers for a long time. Ruby 2.7 extended the same exemption to every private method, so `self.amount` and `self.amount += 1` work too. The exemption stays narrow on purpose: - It applies to the keyword `self` written in the call, nothing else. - It does not let code in another object call the method, whatever that object's class. - It does not change protected methods, which already accepted `self` as a receiver. ## Where the defaults come from - Methods defined in a class body are **public** unless a modifier says otherwise. - `initialize`, `initialize_copy` and `respond_to_missing?` are made **private** automatically. - Methods defined at the top level of a script become **private** methods of `Object`, which is why they can be called anywhere without a receiver but not as `42.helper`. ## Visibility is not security Visibility guards against accidental use. Reflection such as `send` can still reach a private method, so it expresses intent and keeps interfaces small rather than enforcing a security boundary.
- In Ruby, why does copy = self; copy.amount raise when self.amount works for a private amount?The private rule checks the syntax of the call, not the identity of the object. Only the keyword `self` written as the receiver is exempt. A local variable holding the same object is an ordinary explicit receiver, so the call is rejected with `NoMethodError`.
- In Ruby, what visibility do methods defined at the top level of a script get?They become private instance methods of `Object`. That is why a top-level helper can be called with no receiver from anywhere, including inside other classes, while `some_object.helper` raises `NoMethodError` for a private method.
saying these in an interview costs you the question
- Private methods are invisible to subclasses
- Another instance of the same class can call a private method
- self.method never works for private methods on Ruby 4.0
- Protected means accessible to subclasses, as in Java
- Any variable pointing at self may call a private method