tags:

views:

412

answers:

3

I'm working with a Ruby project for school, and have sadly not been able to find an answer to this question in my literature.

I have an array of camping lots, each containing a guest. I initialize the lots like this:

lots = Array.new

for i in (1..36)
  lots[i] = Lot.new(i)
end

Further down I create a Guest object, initialize it, and now I want to add the Guest to my Lot. The method in the class Lot looks like this:

def AddGuest(guest)
  @guest = guest
end

The problem comes when I want to call the method, as the Lot is in an Array.

lots[lotnumber].AddGuest(guest)

This call gives me the error:

undefined method `+@' for #<Guest:0x2c1ff14> (NoMethodError)

I have used require, so the classes know about each other. I've had quite a hard time understanding Ruby, could my error be that I try to access the AddGuest method in the Array class? I'm used to doing things like this in C++.

Below is the full source (the relevant parts at least).

Entire Lot class:

class Lot

  def initialize(number)
    @gauge = rand(2000) + 2000
    @number = number
    @guest = false
  end

  def Occupied()
    return @guest
  end

  def AddGuest(guest)
    @guest = guest
  end

  def RemoveGuest()
    @guest = false
  end

end

Parts of main.rb

#includes
require 'guest'
require 'lot'

#initiate comparison variables
userInput = "0"
numberOfGuests = 0
foundLot = false
guests = Array.new
lots = Array.new

#initialize lot list
for i in (1..36)
  lots[i] = Lot.new(i)
end

Player input omitted

#make sure lot is not taken
while foundLot == false do
  lotnumber = rand(35)+1
  if lots[lotnumber].Occupied() == false then
    foundLot = "true"
  end
end
foundLot = false

guest = Guest.new(firstName, lastName, adress, phone, arrival, lotnumber)
guests.insert(numberOfGuests, guest)
numberOfGuests++

lots[lotnumber].AddGuest(guest) #this is where error hits

end

end

end
A: 

Generally Ruby method names are not capitalized. The convention are simply: ClassName, CONSTANT, method_name.

Since you have an Array of Lot objects, the following should be true:

lots.class # => Array
lots[1].class # => Lot

The method called should be defined for Lot.

tadman
Sorry if i was unclear, the method called is defined for Lot. When i was talking about the Array class i meant the built-in one, i have not written my own.
Tobbe
I just posted a different response that addresses the code sample you pasted. I hope that helps.
tadman
+5  A: 

The error appears to be related to your use of the ++ operator, which is, quite naturally, supported in C++, but is not supported in Ruby.

The equivalent is:

numberOfGuests += 1
tadman
That solved it! Thanks everybody for the great quick answers. Now if i could just stop adding semicolons to the end of my lines... :)
Tobbe
A: 

A couple little tips...

[1]

A slightly more idiomatic way to write this...

for i in (1..36)
  lots[i] = Lot.new(i)
end

would be...

(1..36).each { |i| lots[i] << Lot.new(i) }

[2]

To remove a Guest from a Lot, you might want to set it to nil rather than false. This would be my suggestion...

class Lot

  def initialize(number)
    @gauge = rand(2000) + 2000
    @number = number
    # Don't need to set @guest -- it's nil by default.
  end

  # In Ruby, methods that return a boolean often have a "?".
  # Makes it "read better" when you call the method. (See
  # usage sample.)
  def occupied?
    ! @guest.nil?
  end

  # There's a more commonplace way to do this. See below...
  def add_guest(guest)
    @guest = guest
  end

  def remove_guest()
    @guest = nil
  end

end

Example of usage:

>> lot = Lot.new(2)
=> #<Lot:0x1300920 @number=2, @gauge=3444>
>> lot.occupied
=> false
>> lot.add_guest('A guest')
=> "A guest"
>> lot.occupied?
=> true
>> lot.remove_guest
=> nil
>> lot.occupied?
=> false

Take two...

It's conventional to use attr_accessor methods in your class definition. They automatically add getter and setter methods to your class. You could do that instead of add_guest and remove_guest if you wanted to follow the common Ruby pattern...

class Lot

  attr_accessor :number, :gauge, :guest

  def initialize(number)
    @gauge = rand(2000) + 2000
    @number = number
  end

  def occupied?
    ! @guest.nil?
  end

end

Usage...

irb(main):017:0> lot = Lot.new(3)
=> #<Lot:0xb7f7fca8 @gauge=3186, @number=3>

Set the Guest of a Lot (like add_guest)...

irb(main):019:0> lot.guest = 'A guest'
=> "A guest"
irb(main):020:0> lot.occupied?
=> true

Get the Guest for a Lot...

irb(main):025:0> lot.guest
=> "A guest"

Remove the Guest...

irb(main):021:0> lot.guest = nil
=> nil
irb(main):023:0> lot.occupied?
=> false
Ethan
Actually, even better is to use Array#collect to do the dirty work for you. (1..36).collect { |i| Lot.new(i) } though this will be 0-indexed, not 1-indexed as in the example.
tadman
Or `Array.new(36) { |i| Lot.new(i) }`
rampion
Don't you want "=", not "<<", in "lots[i] << Lot.new(i)"?
Andrew Grimm
"# Don't need to set @guest -- it's nil by default." - if you use warnings, then using an uninitialized @guest will generate a warning. Your mileage may vary.
Andrew Grimm