why self.method_name cannot access private method ?
Because that is how private
is defined, at least in the older version of Ruby that you are using.
It can in current versions of Ruby. The behavior you are describing only exists in older versions of Ruby.
If I run your code using my Ruby installation (Ruby 3.0.0 as implemented by YARV 3.0.0), I don't get an error.
Here is how private
has historically been defined:
A private
method can only be called without a receiver.
See, for example section 13.3.5.3 Private methods of the ISO/IEC 30170:2012 Information technology — Programming languages — Ruby specification:
A private method cannot be invoked with an explicit receiver, i.e., a private method cannot be invoked if a primary-expression or a chained-method-invocation occurs at the position which corresponds to the method receiver in the method invocation
Note that this is not the whole story, however.
How do you call a private attribute writer under this rule? The answer is: you can't! You can't use self.foo = bar
because you are not allowed to use an explicit receiver and you can't use foo = bar
because that is an assignment to the local variable foo
and not a call to the attribute writer foo=
.
So, there is actually an exception to this rule:
A private
method can only be called without a receiver, unless it is an attribute writer, then it can also be called with a receiver that is the literal pseudo-variable self
.
Note that it has to be the literal pseudo-variable self
. It cannot be any arbitrary expression that evaluates to the same object.
I.e. this is allowed:
self.foo = bar
but this is not:
this = self
this.foo = bar
# private method `foo=' called for #<…> (NoMethodError)
The exact text from the ISO Ruby Language Specification is:
A private method cannot be invoked with an explicit receiver, i.e., a private method cannot be invoked if a primary-expression or a chained-method-invocation occurs at the position which corresponds to the method receiver in the method invocation, except for a method invocation of any of the following forms where the primary-expression is a self-expression.
- single-method-assignment
- abbreviated-method-assignment
- single-indexing-assignment
- abbreviated-indexing-assignment
However, this still doesn't solve all problems: what about operators, for example self + bar
or !self
? What about methods whose names are reserved words like class
?
The proposed rules became more and more complicated. For some insight, see the following discussion on the Ruby issue tracker:
Bug #9907 Abbreviated method assignment with private attr_writer
/attr_reader
does not work. [Disclaimer: I am the one who filed the bug.]
To make this work, you would have to change the rule to something like
A private
method can only be called without a receiver, unless it is an attribute writer or an operator, then it can also be called with a receiver that is the literal pseudo-variable self
, including in an abbreviated method assignment.
As you can see, the rule gets rather complex.
A much simpler rule was proposed, and is now implemented as of Ruby 2.7:
The rule now is:
A private
method can only be called without a receiver or with an explicit receiver that is the literal pseudo-variable self
.
You might ask yourself, why not use this even simpler rule?
A private
method can only be called with the receiver self
.
The reason is that both the old rule, the in-between rules, and the new rule can be decided statically, in fact even syntactically at parse time, whereas this very simple rule cannot.