Before Ruby 2.7
Before Ruby 2.7, calling a private write/assignment method with literal self
as the receiver was allowed, but calling any other private method with self
would throw a NoMethodError
error.
class Counter
def reset
self.count = 0 # allowed
puts self.count # raises error
end
private
attr_accessor :count
end
Counter.new.reset
Traceback (most recent call last):
3: from /Users/puneetsutar/.rbenv/versions/2.5.3/bin/irb:11:in `<main>'
2: from (irb):10
1: from (irb):4:in `reset'
NoMethodError (private method `count' called for #<Counter:0x00007fe56509df10 @count=0>)
As shown above, calling writer method self.count = 0
does not raise an error.
But, calling reader method self.count
raises an error.
This is inconsistent behavior.
A more practical example would be something as below.
class Counter
def initialize
self.count = 0
end
def increment
self.count += 1
end
def decrement
self.count -= 1
end
private
attr_accessor :count
end
counter = Counter.new
counter.increment
Traceback (most recent call last):
3: from /Users/puneetsutar/.rbenv/versions/2.5.3/bin/irb:11:in `<main>'
2: from (irb):18
1: from (irb):7:in `increment'
NoMethodError (private method `count' called for #<Counter:0x00007fe3f4103670 @count=0>)
Initializing the Counter
object works fine, but when we try to increment the counter, we get an error.
To understand why we get the error, let’s expand the code within Counter#increment
;
self.count += 1
is equivalent to self.count = self.count + 1
. self.count
is not legal.
After Ruby 2.7
Ruby 2.7 aims at standardizing the interaction between self
and private
methods.
The above inconsistency has been
fixed
in Ruby 2.7.
If we execute the above example in Ruby 2.7, then we won’t get any error.
This feature was requested and discussed on RubyLang at Feature #16123 and Feature #11297