Forum: Ruby Object Troubles - undefined method `+'

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Paul D. Kraus (Guest)
on 2006-06-06 00:01
(Received via mailing list)
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
Johan Veenstra (Guest)
on 2006-06-06 00:13
(Received via mailing list)
>
> 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
Paul D. Kraus (Guest)
on 2006-06-06 00:14
(Received via mailing list)
never mind re-read some more and figured it out.
Daniel H. (Guest)
on 2006-06-06 00:18
(Received via mailing list)
On Jun 5, 2006, at 10:00 PM, Paul D. Kraus wrote:
>    def increment_ndainvoice
>        ndainvoice = ndainvoice + 1
>    end

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
Jacob F. (Guest)
on 2006-06-06 00:21
(Received via mailing list)
On 6/5/06, Paul D. Kraus <removed_email_address@domain.invalid> wrote:
> ./parse.rb:14:in `incndainvoice': undefined method `+' for nil:NilClass
> (NoMethodError)
>         from ./parse.rb:31
>         from ./parse.rb:20

<snip>

>         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 F.
This topic is locked and can not be replied to.