Ruby Conditional-Assignment and Private Methods

2019-02-08 22:47发布

问题:

From the code below, it appears the ||= operator is being evaluated from outside of the class.

class Foo
  attr_reader :bar

  def baz
    self.bar ||= 'baz'
  end

  private

  attr_writer :bar
end

puts Foo.new.baz
# => in `baz': private method `bar=' called for #<Foo:0x007fd9720829a8> (NoMethodError)

Quoting from the accepted answer on Official expansion of ||= conditional assignment operator:

In other words, the expansion c = c || 3 is (excluding bugs like in pre-1.9) correct.

Rewriting the baz method as self.bar = self.bar || 'baz' does not raise the error.

I am looking for a definitive answer on how and why Ruby is behaving in this way, since it seems counter-intuitive.

This behaviour is present on Ruby versions 1.9.3, 2.0.0 and 2.1.2, which leads me to believe this is not a bug.

回答1:

That looks like a bug.

UPDATE: The bug was fixed in trunk, and is slated for back porting to 2.1 and 2.0.

Note that the problem is more general than that, it is broken for all abbreviated assignments, not just conditional abbreviated assignments:

private def foo=(*) end
public  def foo; 0  end

self.foo =  42

self.foo += 42
# private method `foo=' called for main:Object (NoMethodError)

private :foo

self.foo += 42
# private method `foo' called for main:Object (NoMethodError)