Search code examples
rubyrspecattr-accessor

Ruby 2.1.0 Reassigning an instance variable after it has a default value


I just learned Ruby within the past month, and am working on my TDD skills with RSpec.

I am trying to get the following test to pass:

    it "has a modified color" do
    @phone.color = "green"

    puts @phone.color.should == "green"
end

Here is my code:

    class Phone

      attr_reader   :name
      attr_reader   :manufacturer
      attr_accessor :color

      def initialize(name, manufacturer)
        @name           = name
        @manufacturer   = manufacturer
      end

      def color(color='black')
        @color = color
      end
    end

I am trying to do the following essentially:

    phone = Phone.new("Moto X", "Motorola")
    puts phone.color # 'black'
    phone.color = "green"
    puts phone.color # 'green'

However, it is still outputting 'black' after I try reassigning the color to green, please help.

Thanks.


Solution

  • The problem is that your def color is an attribute reader not a writer. So it will always return the default color, black, per the parameter.

    When you do the assignment:

    phone.color = "green"
    

    It is calling the implicit writer (generated by attr_accessor :color). An attribute writer would look like:

    def color=(c)
      @color = c
    end
    

    After writing the color to 'green', you attempt to access via:

    puts phone.color
    

    This will execute the attribute reader:

    def color(c='black')
      @color=c
    end
    

    Which sets the color to 'black' and returns that color.

    If you want a default black color, you can define your class like this:

    class Phone
      attr_reader   :name
      attr_reader   :manufacturer
      attr_accessor :color         # This generates your reader and writer methods
    
      def initialize(name, manufacturer)
        @name           = name
        @manufacturer   = manufacturer
      end
    
      def color
        @color || 'black'    # If @color is nil, return 'black', otherwise @color
      end
    end
    
    2.0.0-p195 :012 > phone = Phone.new("Moto X", "Motorola")
     => #<Phone:0x9ecc1ac @name="Moto X", @manufacturer="Motorola">
    2.0.0-p195 :013 > phone.color
     => "black"
    2.0.0-p195 :014 > phone.color = "green"
     => "green"
    2.0.0-p195 :015 > phone.color
     => "green"