views:

68

answers:

2

I am trying to populate a nested field, product_name, it an Item.

In my Item model:

 class Item < ActiveRecord::Base
   attr_writer :product_name
   belongs_to :order
   belongs_to :product
   def product_name

     #Product.find_by_id(self.product_id) #=> returns the product object
     #self.product #=> returns the product object

     #Product.find_by_id(self.product_id).name #=> complains that 'name' is being called on nil
     #self.product.name #=> complains that 'name' is being called on nil
     #"foooobar" #=> returns "foooobar"

   end     
 end

Each of those commented lines behaves as described. What I don't get is, how can something return an object successfully, but then complain that the object is nil when you access an attribute of it?

Here is the whole error:

 NoMethodError in Orders#edit

Showing app/views/orders/_form.haml where line #18 raised:

undefined method `name' for nil:NilClass

Extracted source (around line #18):

17:       = item.hidden_field :product_id
18:       = item.text_field :product_name, :class => 'auto_complete_input'
19:       = item.text_field :quantity, :size => 2

Thanks

A: 

It sounds like you're asking a really general question. If you're asking a totally different question than I'm hearing, ignore this. :)

Yes, Ruby is successfully returning an object, and the object is nil. In Ruby, a method can return any value, and nil is just another value that a method can return. Those statements are probably doing a query that isn't returning any results, so the method returns nil instead of the object you are expecting.

The longer answer is that Product.find_by_id(x) calls a generated find_by_ attribute method for the id field, which is the same as calling Product.find(:first, :conditions => ["id = ?", x]). If you look at the documentation for .find, you will notice that it says

Find first - This will return [blah blah black...] If no record can be matched, nil is returned.

Jesse Millikan
A: 

Hi Doctororange,

You may want to take a look at the andand plugin at http://github.com/raganwald/andand.

Basically, what it does is handle the errors that may arise from trying to call a method on another that has the possibility of being nil.

Based solely on your example:

Product.find_by_id(self.product_id).name #=> complains that 'name' is being called on nil

An implementation of andand would look like:

Product.find(self.product_id).andand.name 

And even if the first statement, Product.find(self.product_id), returns nil it would no longer fire the undefined method on nil error.

PS:

Again, based solely on your code, I can see that you're reproducing the functionality generated by using the :belongs_to association. Essentially, what it means is that you can use:

self.product

insead of:

Product.find_by_id(self.product_id)
nicosuria
Thanks for your reply. As in my code example, self.product does return the product object, but self.product.name complains of nil.
doctororange