Search code examples
rubyclassrspecnomethoderror

NoMethodError Ruby on Class Initialize


I am doing an "Intro to Classes" exercise through an online course. The objective is to create a class Calculator that initializes with two numbers. The numbers can then be added, subtracted, multiplied, and divided. My code seems functional on local environment:

class Calculator
  def initialize(x,y)
    @x, @y = x, y
  end
  def self.description
    "Performs basic mathematical operations"
  end
  def add
    @x + @y
  end
  def subtract
    @x - @y
  end
  def multiply
    @x * @y
  end
  def divide
    @x.to_f/@y.to_f
  end
end

But the site has Rspec specs:

describe "Calculator" do
  describe "description" do
    it "returns a description string" do
      Calculator.description.should == "Performs basic mathematical operations"
    end
  end
  describe "instance methods" do
    before { @calc = Calculator.new(7, 2) }
    describe "initialize" do
      it "takes two numbers" do
        expect( @calc.x ).to eq(7)
        expect( @calc.y ).to eq(2)
      end
    end
    describe "add" do
      it "adds the two numbers" do
        expect( @calc.add ).to eq(9)
      end
    end
    describe "subtract" do
      it "subtracts the second from the first" do
        expect( @calc.subtract ).to eq(5)
      end
    end
    describe "multiply" do
      it "should return a standard number of axles for any car" do
        expect( @calc.multiply ).to eq(14)
      end
    end
    describe "divide" do
      it "divides the numbers, returning a 'Float' if appropriate" do
        expect( @calc.divide ).to eq(3.5)
      end
    end
  end
end

and the site's spec throws a NoMethodError:

NoMethodError
undefined method `x' for #<Calculator:0x007feb61460b00 @x=7, @y=2>
    exercise_spec.rb:14:in `block (4 levels) in <top (required)>'

Solution

  • Just add this line

    attr_reader :x, :y
    

    Here is the corrected code :

    class Calculator
      attr_reader :x, :y
    
      def initialize(x,y)
        @x, @y = x, y
      end
      def self.description
        "Performs basic mathematical operations"
      end
      def add
        # once you defined reader method as above you can simple use x to get the
        # value of @x. Same is true for only y instead of @y.
        x + y 
      end
      def subtract
        x - y
      end
      def multiply
        x * y
      end
      def divide
        x.to_f/y.to_f
      end
    end
    

    Look the below spec code :-

    describe "initialize" do
          it "takes two numbers" do
            expect( @calc.x ).to eq(7)
            expect( @calc.y ).to eq(2)
          end
          #...
    

    You are calling @calc.x and @calc.y. But you didn't define any method named as #x and #y as instance methods inside the class Calculator. Which is why you got very definitive exception as NoMethod error.

    When you will write attr_reader :x, :y it will create those methods for you internally. Read this answer to understand reader and writer method in Ruby.