small medium large xlarge

22 Apr 2011, 18:12
Ruby Slippers (1 post)

Hello ,

I’m very new to rails and I’m working my way through the book. I’m trying the exercises at the end of the chapter for Smarter Cart. Create a migration that copies the product price into the line item, and add_product method in the Cart model to capture the price whenever a new line item is created.

I’ve generated the migration and the code looks like this - class AddPriceToLineItem < ActiveRecord::Migration def self.up add_column :line_items, :price, :decimal LineItem.all.each do |li| li.price = li.product.price end end

def self.down remove_column :line_items, :price end end

Only problem is every time I run rake db:migrate the migration is aborted with the following error -

undefined method ‘price’ for nil:NilClass

It then goes on to point at active support 3.0.7 line 8 next to lines 4 & 5 in the above code.

I’m really not sure how to decipher this at all so any ideas or suggestions would be most appreciated. Thanks in advance for your kind patience with a nooby.

27 Apr 2011, 15:17
Suresh Iyer (6 posts)

Same issue here. Thanks for posting @RubySlippers. I spent the last 45 minutes scanning various sources, but so far no luck in resolving this one. Strange that this has been reported only during the last week (you reported on Apr 22 and me today). I _too_ am using Rails 3.0.7. Is there any link there?

And yes, the relationships are set using has_many and belongs_to, as well as the foreign key is present in the line item to point to the product. Not sure anything more is required (have followed the book very closely so far and it works precisely in the way intended/stipulated in the book till/up-to chapter E PlayTime 1.

Has something changed in 3.0.7 (or some 3.0.x) version that doesn’t allow activerecord usage in migration?

As @RubySlippers said, any help would be deeply appreciated.

At the risk of irritating [some of] the members, I am dumping the migration trace below (apologies!)

proto@protolabs:~/proto/learning/rails/awd/depot$ rake db:migrate –trace (in /home/proto/proto/learning/rails/awd/depot) ** Invoke db:migrate (first_time) ** Invoke environment (first_time) ** Execute environment ** Execute db:migrate == CopyProductPriceInLineItems: migrating ==================================== rake aborted! An error has occurred, this and all later migrations canceled:

undefined method price' for nil:NilClass /home/proto/.rvm/gems/ruby-1.9.2-p180@foo/gems/activesupport-3.0.7/lib/active_support/whiny_nil.rb:48:in method_missing’ /home/proto/proto/learning/rails/awd/depot/db/migrate/20110427140010_copy_product_price_in_line_items.rb:5:in block in up' /home/proto/proto/learning/rails/awd/depot/db/migrate/20110427140010_copy_product_price_in_line_items.rb:4:in each’ /home/proto/proto/learning/rails/awd/depot/db/migrate/20110427140010_copy_product_price_in_line_items.rb:4:in up' /home/proto/.rvm/gems/ruby-1.9.2-p180@foo/gems/activerecord-3.0.7/lib/active_record/migration.rb:312:in block in migrate’ /home/proto/.rvm/rubies/ruby-1.9.2-p180/lib/ruby/1.9.1/benchmark.rb:294:in measure' /home/proto/.rvm/gems/ruby-1.9.2-p180@foo/gems/activerecord-3.0.7/lib/active_record/migration.rb:312:in migrate’ /home/proto/.rvm/gems/ruby-1.9.2-p180@foo/gems/activerecord-3.0.7/lib/active_record/migration.rb:395:in migrate' /home/proto/.rvm/gems/ruby-1.9.2-p180@foo/gems/activerecord-3.0.7/lib/active_record/migration.rb:537:in block (2 levels) in migrate’ /home/proto/.rvm/gems/ruby-1.9.2-p180@foo/gems/activerecord-3.0.7/lib/active_record/migration.rb:611:in call' /home/proto/.rvm/gems/ruby-1.9.2-p180@foo/gems/activerecord-3.0.7/lib/active_record/migration.rb:611:in block in ddl_transaction’ /home/proto/.rvm/gems/ruby-1.9.2-p180@foo/gems/activerecord-3.0.7/lib/active_record/connection_adapters/abstract/database_statements.rb:139:in transaction' /home/proto/.rvm/gems/ruby-1.9.2-p180@foo/gems/activerecord-3.0.7/lib/active_record/transactions.rb:207:in transaction’ /home/proto/.rvm/gems/ruby-1.9.2-p180@foo/gems/activerecord-3.0.7/lib/active_record/migration.rb:611:in ddl_transaction' /home/proto/.rvm/gems/ruby-1.9.2-p180@foo/gems/activerecord-3.0.7/lib/active_record/migration.rb:536:in block in migrate’ /home/proto/.rvm/gems/ruby-1.9.2-p180@foo/gems/activerecord-3.0.7/lib/active_record/migration.rb:523:in each' /home/proto/.rvm/gems/ruby-1.9.2-p180@foo/gems/activerecord-3.0.7/lib/active_record/migration.rb:523:in migrate’ /home/proto/.rvm/gems/ruby-1.9.2-p180@foo/gems/activerecord-3.0.7/lib/active_record/migration.rb:433:in up' /home/proto/.rvm/gems/ruby-1.9.2-p180@foo/gems/activerecord-3.0.7/lib/active_record/migration.rb:415:in migrate’ /home/proto/.rvm/gems/ruby-1.9.2-p180@foo/gems/activerecord-3.0.7/lib/active_record/railties/databases.rake:142:in block (2 levels) in <top (required)>' /home/proto/.rvm/gems/ruby-1.9.2-p180@global/gems/rake-0.8.7/lib/rake.rb:636:in call’ /home/proto/.rvm/gems/ruby-1.9.2-p180@global/gems/rake-0.8.7/lib/rake.rb:636:in block in execute' /home/proto/.rvm/gems/ruby-1.9.2-p180@global/gems/rake-0.8.7/lib/rake.rb:631:in each’ /home/proto/.rvm/gems/ruby-1.9.2-p180@global/gems/rake-0.8.7/lib/rake.rb:631:in execute' /home/proto/.rvm/gems/ruby-1.9.2-p180@global/gems/rake-0.8.7/lib/rake.rb:597:in block in invoke_with_call_chain’ /home/proto/.rvm/rubies/ruby-1.9.2-p180/lib/ruby/1.9.1/monitor.rb:201:in mon_synchronize' /home/proto/.rvm/gems/ruby-1.9.2-p180@global/gems/rake-0.8.7/lib/rake.rb:590:in invoke_with_call_chain’ /home/proto/.rvm/gems/ruby-1.9.2-p180@global/gems/rake-0.8.7/lib/rake.rb:583:in invoke' /home/proto/.rvm/gems/ruby-1.9.2-p180@global/gems/rake-0.8.7/lib/rake.rb:2051:in invoke_task’ /home/proto/.rvm/gems/ruby-1.9.2-p180@global/gems/rake-0.8.7/lib/rake.rb:2029:in block (2 levels) in top_level' /home/proto/.rvm/gems/ruby-1.9.2-p180@global/gems/rake-0.8.7/lib/rake.rb:2029:in each’ /home/proto/.rvm/gems/ruby-1.9.2-p180@global/gems/rake-0.8.7/lib/rake.rb:2029:in block in top_level' /home/proto/.rvm/gems/ruby-1.9.2-p180@global/gems/rake-0.8.7/lib/rake.rb:2068:in standard_exception_handling’ /home/proto/.rvm/gems/ruby-1.9.2-p180@global/gems/rake-0.8.7/lib/rake.rb:2023:in top_level' /home/proto/.rvm/gems/ruby-1.9.2-p180@global/gems/rake-0.8.7/lib/rake.rb:2001:in block in run’ /home/proto/.rvm/gems/ruby-1.9.2-p180@global/gems/rake-0.8.7/lib/rake.rb:2068:in standard_exception_handling' /home/proto/.rvm/gems/ruby-1.9.2-p180@global/gems/rake-0.8.7/lib/rake.rb:1998:in run’ /home/proto/.rvm/gems/ruby-1.9.2-p180@global/gems/rake-0.8.7/bin/rake:31:in <top (required)>' /home/proto/.rvm/gems/ruby-1.9.2-p180@global/bin/rake:19:in load’ /home/proto/.rvm/gems/ruby-1.9.2-p180@global/bin/rake:19:in `'

27 Apr 2011, 15:27
Sam Ruby (633 posts)

If you have an undefined method ‘price’ for nil:NilClass error on the following line:

li.price = li.product.price 

… it follows that either li is nil or li.product is.

Since li was just set from LineItem.all, it seems rather unlikely that that is the problem.

Under normal circumstances, li.product should never be nil, but this is a development database and you’ve been experimenting and there is no validation code in the LineItem model to prevent this.

For the short term, you can get around this with a little defensive code:

li.price = li.product.price if li.product

In the longer term, it might make sense to add some validation to the LineItem model.

27 Apr 2011, 17:12
Suresh Iyer (6 posts)

Thanks so much for the prompt reply, @SamRuby. I saw this when I came back here now to post how I solved the issue after spending a couple of “feel dumb and humble” hours searching for some magic answer/solution and reading about activerecord, migration and relations and so on.

Yes, your analysis is spot on. The first row in the line_items table had both product_id and cart_id null. It got added thus when I clicked add to cart (submit) button before adding the full code, and that one rogue row (the first row) was causing this. In case you are still reading, I have thoroughly enjoyed the book so far. So, thanks again!

@RubySlippers: Just run the following from sqlite prompt to identify the rogue row(s) causing the problem:

$> sqlite3 _line db/development.sqlite3 # to get into sqlite3 prompt; replace _ with - sqlite> select * from line_items where product_id is null; # this should list the rogue row(s)

and here is how I solved the problem (I had only one rogue row)

sqlite3> update line_items set product_id = 1, cart_id = 1 where id = 1;

27 Apr 2011, 18:19
Suresh Iyer (6 posts)

Last bit on this:

Unfortunately, the “li.price = …” construct just doesn’t work for me (and I have no idea why).

What works is the solution provided by the reader @Marius over at – that of using li.update_attribute(…). I have opted for “.all” instead of “.find(:all)”, and so the relevant portion of my code in self.up would be:

say_with_time "Updating prices..." do
  LineItem.all.each do |li|
    li.update_attribute :price, li.product.price
You must be logged in to comment