Object Troubles - undefined method `+'

I am parsing a text file that is 3 fields per line deliminted by pipes '|'
Each line contains a customer code, shipmethod, and customer name.
Each line represents an invoice that was cut.

I need to count all invoices and then count all invoices that went out next
day air saver.
From working with the pick axe it seems to me that you have a class with two
attributes invoice, and nda invoice.
You then store your objects in a hash for easy lookup.

When i run the below program I get this error...
./parse.rb:14:in `incndainvoice': undefined method `+' for nil:NilClass
(NoMethodError)
        from ./parse.rb:31
        from ./parse.rb:20

TIA,
Paul

Here is my class and program ...

#!/usr/bin/env ruby

class Customer
    attr_reader :invoices, :ndainvoices
    attr_writer :invoices, :ndainvoices
    def initialize
        @invoices = 1
        @ndainvoices = 0
    end
    def increment_invoice
        invoices = invoices + 1
    end
    def increment_ndainvoice
        ndainvoice = ndainvoice + 1
    end
end

customers = Hash.new

open('NdaInvoiceCount.txt','r').each_line do |line|
        (customer_code, ship_method, customer_name) = line.chomp.split('|')
        if customers[ customer_code ]
            customers[ customer_code ].increment_invoice
        else
            customers[ customer_code ] = Customer.new
        end
        customers[ customer_code ].increment_ndainvoice if ship_method ==
'SAVGRD'
end

here is some sample data....
0000016324|SAVGRD|Dupage Prosthetics
0000038955|SAVGRD|TRUDELL ORTH & PROS SERVICES
0000019155|UPS |Scott Orthotic Labs
0000061674|UPS |COMPREHENSIVE BRACE & LIMB CTR LLC
0000008593|U02G |Huron Valley Assoc
0000039954|SAVGRD|Island Coast Orthopedics
0000028719|UPS |Paul Richelson's Feet First
0000003455|FEDGND|CLARK ORTH
0000019297|UPS |J. Slawner LTD.
0000061508|UPS |LEVY & RAPPEL

class Customer
   attr_reader :invoices, :ndainvoices
   attr_writer :invoices, :ndainvoices
   def initialize
       @invoices = 1
       @ndainvoices = 0

Here you initialize 2 *instance* variables, @invoices and @ndainvoices

   end

   def increment_invoice
       invoices = invoices + 1

The *local* variable 'invoices' does exist yet, so 'nil + 1' raises the
error.
You probably intended:

@invoices = @invoices + 1

   end

   def increment_ndainvoice
       ndainvoice = ndainvoice + 1

same here

   end

···

end

never mind re-read some more and figured it out.

···

On 6/5/06, Paul D. Kraus <paul.kraus@gmail.com> wrote:

I am parsing a text file that is 3 fields per line deliminted by pipes '|'
Each line contains a customer code, shipmethod, and customer name.
Each line represents an invoice that was cut.

I need to count all invoices and then count all invoices that went out
next
day air saver.
From working with the pick axe it seems to me that you have a class with
two
attributes invoice, and nda invoice.
You then store your objects in a hash for easy lookup.

When i run the below program I get this error...
./parse.rb:14:in `incndainvoice': undefined method `+' for nil:NilClass
(NoMethodError)
        from ./parse.rb:31
        from ./parse.rb:20

TIA,
Paul

Here is my class and program ...

#!/usr/bin/env ruby

class Customer
    attr_reader :invoices, :ndainvoices
    attr_writer :invoices, :ndainvoices
    def initialize
        @invoices = 1
        @ndainvoices = 0
    end
    def increment_invoice
        invoices = invoices + 1
    end
    def increment_ndainvoice
        ndainvoice = ndainvoice + 1
    end
end

customers = Hash.new

open('NdaInvoiceCount.txt','r').each_line do |line|
        (customer_code, ship_method, customer_name) = line.chomp.split
('|')
        if customers[ customer_code ]
            customers[ customer_code ].increment_invoice
        else
            customers[ customer_code ] = Customer.new
        end
        customers[ customer_code ].increment_ndainvoice if ship_method ==
'SAVGRD'
end

here is some sample data....
0000016324|SAVGRD|Dupage Prosthetics
0000038955|SAVGRD|TRUDELL ORTH & PROS SERVICES
0000019155|UPS |Scott Orthotic Labs
0000061674|UPS |COMPREHENSIVE BRACE & LIMB CTR LLC
0000008593|U02G |Huron Valley Assoc
0000039954|SAVGRD|Island Coast Orthopedics
0000028719|UPS |Paul Richelson's Feet First
0000003455|FEDGND|CLARK ORTH
0000019297|UPS |J. Slawner LTD.
0000061508|UPS |LEVY & RAPPEL

You are trying to set local variables. Instance variables are prefixed with @. If you were trying to call your attr_writer method, you need to prefix it with self:

self.invoices += 1

Here is a more idiomatic way of writing what you would like:

class Customer
   attr_accessor :invoices, :ndainvoices
   def initialize
     @invoices = 0
     @ndainvoices = 0
   end
end

customers = Hash.new { |h, k| h[k] = Customer.new }
IO.foreach('NdaInvoiceCount.txt') do |line|
   (customer_code, ship_method, customer_name) = line.chomp.split('|')
   customers[customer_code].invoices += 1
   customers[ customer_code ].ndainvoices += 1 if ship_method == 'SAVGRD'
end

-- Daniel

···

On Jun 5, 2006, at 10:00 PM, Paul D. Kraus wrote:

When i run the below program I get this error...
./parse.rb:14:in `incndainvoice': undefined method `+' for nil:NilClass
(NoMethodError)
       from ./parse.rb:31
       from ./parse.rb:20

   def increment_invoice
       invoices = invoices + 1
   end
   def increment_ndainvoice
       ndainvoice = ndainvoice + 1
   end

./parse.rb:14:in `incndainvoice': undefined method `+' for nil:NilClass
(NoMethodError)
        from ./parse.rb:31
        from ./parse.rb:20

<snip>

#!/usr/bin/env ruby

class Customer
    attr_reader :invoices, :ndainvoices
    attr_writer :invoices, :ndainvoices
    def initialize
        @invoices = 1
        @ndainvoices = 0
    end
    def increment_invoice
        invoices = invoices + 1
    end
    def increment_ndainvoice
        ndainvoice = ndainvoice + 1 # this is line 14
    end
end

You're problem here is that on line 14, you are actually using a local
variable. This is a little tricky, since it's actually your assignment
that is changing the context of the right hand side. What happens is
that the parser sees "ndainvoice =" and assumes you're creating a new
local variable called "ndainvoice"; this new local variable then masks
the "ndainvoice" method. Since it's value is nil (the default value of
an unassigned-to variable), the effect of the line is:

  ndainvoice = nil + 1 # ndainvoice is a local variable here

In order to get ruby to recognize the assignment as a method call,
rather than an assignment to a new local variable, you need to prefix
"self.":

  self.ndainvoice = ndainvoice + 1

The reference to ndainvoice on the right hand side is correctly
interpreted as a method call since there is no local variable of the
same name to mask it. Alternatively, you can use the instance variable
directly by prefixing an "@" (as you do in initialize):

  @ndainvoice = @ndainvoice + 1

As a final step, you can also use += to shorten the "a = a + b" pattern:

  self.ndainvoice += 1
  # or
  @ndainvoice += 1

Incidentally, you have the same problem on line 11.

Jacob Fugal

···

On 6/5/06, Paul D. Kraus <paul.kraus@gmail.com> wrote: