fzzfzzfzz
fzzfzzfzz

Reputation: 1248

Ruby private attr_accessor and unexpected nil

Back when I used Ruby regularly, I had a bad habit of leaving everything public and ignoring privacy. Unfortunately, that ignorance is coming back to haunt me. Here's a simpler version of my problem:

class Something
    private
    attr_accessor :sneaky
    public

    def initialize
        @sneaky = 0
    end

    def test
        while sneaky < 10
            puts "#{sneaky}"
            sneaky = (sneaky + 1)
        end
    end
end

test = Something.new
test.test

This prints the correct value of sneaky (0), then errors out at sneaky = (sneaky + 1), saying that sneaky is nil:

0
test.rb:13:in `test': undefined method `+' for nil:NilClass (NoMethodError)
    from test.rb:19:in `<main>'

What's up with that? @sneaky's been set to 0 in the constructor. And if it really were nil, shouldn't that puts print a blank line and not a 0?

EDIT: Yep, replacing sneaky = (sneaky + 1) with self.sneaky = sneaky + 1 solves the problem, even though self.sneaky= looks like a privacy violation, since it has an explicit receiver. Apparently an exception is made for setters. But the weird interaction with privacy means you can't say self.sneaky += 1 (you end up getting test.rb:14:in 'test': private method 'sneaky' called for #<Something:0x000001019004c8 @sneaky=0> (NoMethodError)). Fortunately I'm not the only one who thinks that's weird.

Upvotes: 4

Views: 2975

Answers (1)

J&#246;rg W Mittag
J&#246;rg W Mittag

Reputation: 369458

A form of foo = bar assigns to a local variable called foo. If you want to call an attr_writer, you need to use an explicit receiver: self.sneaky += 1.

This has nothing to do with private, it's just basic Ruby syntax for local variable assignments.

Upvotes: 6

Related Questions