Why is this attribute writer not being called?

130 Views Asked by At

The code:

class Weird
  DEFAULT_FOO = 'fo0'

  attr_accessor :foo

  def initialize(val)
    @foo = val
  end

  def run
    unless foo
      foo = DEFAULT_FOO         # Fails
      # @foo = DEFAULT_FOO      # Works
    end

    bar(foo)
  end

  def bar(f)
    puts "The foo is '#{f}'"
  end
end

RSpec.describe Weird do
  subject(:weird) do
    described_class.new(foo_val)
  end
  let(:foo_val) { nil }

  describe '#run' do
    context 'without foo' do
      it 'bars the foo' do
        expect(subject).to receive(:bar).with(described_class::DEFAULT_FOO)
        subject.run
      end
    end

    context 'with foo' do
      let(:foo_val) { 'quux' }

      it 'bars the foo' do
        expect(subject).to receive(:bar).with(foo_val)
        subject.run
      end
    end
  end
end

Run this with RSpec (rspec weird.rb) and the second test fails with:

  1) Weird#run with foo bars the foo
     Failure/Error: bar(foo)

       #<Weird:0x007fc1948727f8 @foo="quux"> received :bar with unexpected arguments
         expected: ("quux")
              got: (nil)
       Diff:
       @@ -1,2 +1,2 @@
       -["quux"]
       +[nil]

     # ./test_case_2.rb:21:in `run'
     # ./test_case_2.rb:48:in `block (4 levels) in <top (required)>'

The implicitly defined foo=() attribute writer method is not being called; explicitly defining one does not work either. Instead, it seems a method-local variable foo is being created (and set) where it shouldn't be. Why is this happening?

Ruby version is ruby 2.2.2p95 (2015-04-13 revision 50295) [x86_64-darwin15].

2

There are 2 best solutions below

4
On BEST ANSWER

foo = DEFAULT_FOO fails because this is creating a local variable foo and assigning it the value DEFAULT_FOO. But when you're calling bar(foo) it will looks for the @ value first, which in this case will still be nil.

When assigning an instance variable you must either use self.foo = or @foo =.

0
On

2 ways to fix

  1. use self.foo = DEFAULT_FOO
  2. use @foo = DEFAULT_FOO