Home > Software engineering >  Ruby, why did my code for this project not work until i changed this?
Ruby, why did my code for this project not work until i changed this?

Time:12-02

I am new to coding and I am currently learning Ruby. So i was working on a oyster card project (oyster card is what we use for public transport in London, for those of you unfamiliar with it) and I ran into a problem. So my code used to be:

class Oystercard
  attr_reader :balance, :in_journey, :entry_station, :journey, :journey_history

  MAXIMUM_BALANCE = 90
  MINIMUM_CHARGE = 3

  def initialize
    @balance = 0
    @in_journey = false
  end

@journey = Hash.new

  def top_up(amount)
    raise "Cannot top-up more than #{MAXIMUM_BALANCE}" if (@balance   amount) > MAXIMUM_BALANCE

    @balance  = amount
  end

  def in_journey?
    !!entry_station
  end

  def touch_in(station)
    raise 'You need to top-up first' if @balance < 1

    @entry_station = station
    @in_journey = true
    @journey[:entry] = @entry_station
    @journey_history.push(journey)
  end

  def touch_out
    deduct(MINIMUM_CHARGE)
    @in_journey = false
    @entry_station = nil
    @journey[:exit] = @entry_station
  end

  private

  def deduct(fare = MINIMUM_CHARGE)
    @balance -= fare
  end
end

Now let me explain the problem. When i say IRB and required this file. This is what i did in IRB:

oyster = Oystercard.new
oyster.top_up(50)
oyster.touch_in("Paddington")

The error i would receive is

NoMethodError (undefined method `[]=' for nil:NilClass)

The error refers to the last line of the touch_in method. What I am trying to do is make an entry key and make the value, the station that the user inputs. Now i managed to fix this code by adding @journey = Hash.new into the initialize method instead of the line before. So my new code was

def initialize
    @balance = 0
    @in_journey = false
    @journey = Hash.new
  end

now as soon as i made this change, and made journey into an attribute, my code started to work. However, I am failing to understand why. If you need any more information then let me know and thank you in advance :)

CodePudding user response:

In Ruby, classes are objects. When you write:

class Oystercard
  @journey = Hash.new
end

You're setting an instance variable on the class. Inside the class block, self is the Oystercard class object.

When you write:

class Oystercard
  def initialize
    @journey = Hash.new
  end
end

You set the instance variable on each new object because Oystercard.new will allocate a new object and call initialize on it. Inside the method, self is the new instance.

CodePudding user response:

  1. The square brackets [] in ruby are used to get or set item in Hash or Array.
  2. In your code you wrote @journey[:entry] = @entry_station that means you area assigning a value to the item with the key :entry of a hash that is the variable @journey. So if it works, this would be true @journey == {:entry => @entry_station}.
  3. Before doing this @journey[:entry] = @entry_station, @journey has to be a hash, in other words, you have to define as a hash, otherwise it would fail. This is what you did when you wrote @journey = Hash.new, alternatively, you can write @journey = {}.
  4. You must declare @journey as a hash inside your initialize method. Thus @journey is defined as a hash before you add an item to it.
  • Related