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:
- The square brackets
[]
in ruby are used to get or set item in Hash or Array. - 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}
. - 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 = {}
. - You must declare
@journey
as a hash inside yourinitialize
method. Thus@journey
is defined as a hash before you add an item to it.