Agile Web Development with Rails, Edition 4
    
    
  
  
    Agile Web Development with Rails, Edition 4
    
      10.2 Iteration E2: Handling Errors
      9.4 Playtime
    
    
      10.1 Iteration E1: Creating a Smarter Cart
    
    Change the cart to track the quantity of each product.
    Add a quantity column to the line_item table in the database.
    rails generate migration add_quantity_to_line_items quantity:integer
          invoke  active_record
          create    db/migrate/20120212172715_add_quantity_to_line_items.rb
    Modify the migration to add a default value for the new column
    edit db/migrate/20120212172715_add_quantity_to_line_items.rb
    class AddQuantityToLineItems < ActiveRecord::Migration
      def change
        add_column :line_items, :quantity, :integer, default: 1
     
      end
    end
    Apply the migration
    rake db:migrate
    mv 20120212172715_add_quantity_to_line_items.rb 20110711000004_add_quantity_to_line_items.rb
    ==  AddQuantityToLineItems: migrating =========================================
    -- add_column(:line_items, :quantity, :integer, {:default=>1})
       -> 0.0056s
    ==  AddQuantityToLineItems: migrated (0.0057s) ================================
     
    Create a method to add a product to the cart by either incrementing the quantity of an existing line item, or creating a new line item.
    edit app/models/cart.rb
     
      def add_product(product_id)
        current_item = line_items.find_by_product_id(product_id)
        if current_item
          current_item.quantity += 1
        else
          current_item = line_items.build(product_id: product_id)
        end
        current_item
      end
    Replace the call to LineItem.new with a call to the new method.
    edit app/controllers/line_items_controller.rb
      def create
        @cart = current_cart
        product = Product.find(params[:product_id])
        @line_item = @cart.add_product(product.id)
     
        respond_to do |format|
          if @line_item.save
            format.html { redirect_to @line_item.cart,
              notice: 'Line item was successfully created.' }
            format.json { render json: @line_item,
              status: :created, location: @line_item }
          else
            format.html { render action: "new" }
            format.json { render json: @line_item.errors,
              status: :unprocessable_entity }
          end
        end
      end
    Update the view to show both columns.
    edit app/views/carts/show.html.erb
    <% if notice %>
    <p id="notice"><%= notice %></p>
    <% end %>
     
    <h2>Your Pragmatic Cart</h2>
    <ul>    
      <% @cart.line_items.each do |item| %>
        <li><%= item.quantity %> × <%= item.product.title %></li>
      <% end %>
    </ul>
    Look at the cart, and see that's not exactly what we intended
    get /carts/1
    
  
    
    Pragmatic Bookshelf
  
 
  
    
    
      
Your Pragmatic Cart
    
    - 1 × Programming Ruby 1.9
 
    - 1 × Programming Ruby 1.9
 
     
   
     
    
    Generate a migration to combine/separate items in carts.
    rails generate migration combine_items_in_cart
          invoke  active_record
          create    db/migrate/20120212172723_combine_items_in_cart.rb
    Fill in the self.up method
    edit db/migrate/20120212172723_combine_items_in_cart.rb
      def up
        # replace multiple items for a single product in a cart with a single item
        Cart.all.each do |cart|
          # count the number of each product in the cart
          sums = cart.line_items.group(:product_id).sum(:quantity)
     
          sums.each do |product_id, quantity|
            if quantity > 1
              # remove individual items
              cart.line_items.where(product_id: product_id).delete_all
     
              # replace with a single item
              cart.line_items.create(product_id: product_id, quantity: quantity)
            end
          end
        end
      end
    Combine entries
    rake db:migrate
    mv 20120212172723_combine_items_in_cart.rb 20110711000005_combine_items_in_cart.rb
    ==  CombineItemsInCart: migrating =============================================
    ==  CombineItemsInCart: migrated (0.1009s) ====================================
     
    Verify that the entries have been combined.
    get /carts/1
    
  
    
    Pragmatic Bookshelf
  
 
  
     
    
    Fill in the self.down method
    edit db/migrate/20110711000005_combine_items_in_cart.rb
      def down
        # split items with quantity>1 into multiple items
        LineItem.where("quantity>1").each do |line_item|
          # add individual items
          line_item.quantity.times do 
            LineItem.create cart_id: line_item.cart_id,
              product_id: line_item.product_id, quantity: 1
          end
     
          # remove original item
          line_item.destroy
        end
      end
    Separate out individual items.
    rake db:rollback
    ==  CombineItemsInCart: reverting =============================================
    ==  CombineItemsInCart: reverted (0.0280s) ====================================
     
    Every item should (once again) only have a quantity of one.
    get /carts/1
    
  
    
    Pragmatic Bookshelf
  
 
  
    
    
      
Your Pragmatic Cart
    
    - 1 × Programming Ruby 1.9
 
    - 1 × Programming Ruby 1.9
 
     
   
     
    
    Recombine the item data.
    rake db:migrate
    ==  CombineItemsInCart: migrating =============================================
    ==  CombineItemsInCart: migrated (0.1015s) ====================================
     
    Add a few products to the order.
    post /line_items?product_id=2
    
    
You are being 
redirected.    
 
    
    get http://localhost:3000/carts/1
    
  
    
    Pragmatic Bookshelf
  
 
  
    
    
      Line item was successfully created.
Your Pragmatic Cart
    
    - 2 × Programming Ruby 1.9
 
    - 1 × CoffeeScript
 
     
   
     
    
    post /line_items?product_id=3
    
    
You are being 
redirected.    
 
    
    get http://localhost:3000/carts/1
    
  
    
    Pragmatic Bookshelf
  
 
  
    
    
      Line item was successfully created.
Your Pragmatic Cart
    
    - 3 × Programming Ruby 1.9
 
    - 1 × CoffeeScript
 
     
   
     
    
    Try something malicious.
    get /carts/wibble
    
  ActiveRecord::RecordNotFound
    in CartsController#show
Couldn't find Cart with id=wibble
Rails.root: /home/rubys/git/awdwr/edition4/work-200-40/depot
    Application Trace |
    
Framework Trace |
    
Full Trace 
    
      app/controllers/carts_controller.rb:16:in `show'
     
    
      /home/rubys/git/rails/activerecord/lib/active_record/relation/finder_methods.rb:340:in `find_one'
/home/rubys/git/rails/activerecord/lib/active_record/relation/finder_methods.rb:311:in `find_with_ids'
/home/rubys/git/rails/activerecord/lib/active_record/relation/finder_methods.rb:107:in `find'
/home/rubys/git/rails/activerecord/lib/active_record/querying.rb:6:in `find'
/home/rubys/git/rails/actionpack/lib/action_controller/metal/implicit_render.rb:4:in `send_action'
/home/rubys/git/rails/actionpack/lib/abstract_controller/base.rb:167:in `process_action'
/home/rubys/git/rails/actionpack/lib/action_controller/metal/rendering.rb:10:in `process_action'
/home/rubys/git/rails/actionpack/lib/abstract_controller/callbacks.rb:18:in `block in process_action'
/home/rubys/git/rails/activesupport/lib/active_support/callbacks.rb:374:in `_run__3534897644878667656__process_action__callbacks'
/home/rubys/git/rails/activesupport/lib/active_support/callbacks.rb:366:in `__run_callbacks'
/home/rubys/git/rails/activesupport/lib/active_support/callbacks.rb:79:in `run_callbacks'
/home/rubys/git/rails/actionpack/lib/abstract_controller/callbacks.rb:17:in `process_action'
/home/rubys/git/rails/actionpack/lib/action_controller/metal/rescue.rb:29:in `process_action'
/home/rubys/git/rails/actionpack/lib/action_controller/metal/instrumentation.rb:30:in `block in process_action'
/home/rubys/git/rails/activesupport/lib/active_support/notifications.rb:123:in `block in instrument'
/home/rubys/git/rails/activesupport/lib/active_support/notifications/instrumenter.rb:20:in `instrument'
/home/rubys/git/rails/activesupport/lib/active_support/notifications.rb:123:in `instrument'
/home/rubys/git/rails/actionpack/lib/action_controller/metal/instrumentation.rb:29:in `process_action'
/home/rubys/git/rails/actionpack/lib/action_controller/metal/params_wrapper.rb:204:in `process_action'
/home/rubys/git/rails/activerecord/lib/active_record/railties/controller_runtime.rb:18:in `process_action'
/home/rubys/git/rails/actionpack/lib/abstract_controller/base.rb:121:in `process'
/home/rubys/git/rails/actionpack/lib/abstract_controller/rendering.rb:44:in `process'
/home/rubys/git/rails/actionpack/lib/action_controller/metal.rb:197:in `dispatch'
/home/rubys/git/rails/actionpack/lib/action_controller/metal/rack_delegation.rb:14:in `dispatch'
/home/rubys/git/rails/actionpack/lib/action_controller/metal.rb:240:in `block in action'
/home/rubys/git/rails/actionpack/lib/action_dispatch/routing/route_set.rb:67:in `call'
/home/rubys/git/rails/actionpack/lib/action_dispatch/routing/route_set.rb:67:in `dispatch'
/home/rubys/git/rails/actionpack/lib/action_dispatch/routing/route_set.rb:30:in `call'
/home/rubys/git/journey/lib/journey/router.rb:68:in `block in call'
/home/rubys/git/journey/lib/journey/router.rb:56:in `each'
/home/rubys/git/journey/lib/journey/router.rb:56:in `call'
/home/rubys/git/rails/actionpack/lib/action_dispatch/routing/route_set.rb:586:in `call'
/home/rubys/git/rails/actionpack/lib/action_dispatch/middleware/best_standards_support.rb:17:in `call'
rack (1.4.1) lib/rack/etag.rb:23:in `call'
rack (1.4.1) lib/rack/conditionalget.rb:25:in `call'
/home/rubys/git/rails/actionpack/lib/action_dispatch/middleware/head.rb:14:in `call'
/home/rubys/git/rails/actionpack/lib/action_dispatch/middleware/params_parser.rb:21:in `call'
/home/rubys/git/rails/actionpack/lib/action_dispatch/middleware/flash.rb:224:in `call'
rack (1.4.1) lib/rack/session/abstract/id.rb:205:in `context'
rack (1.4.1) lib/rack/session/abstract/id.rb:200:in `call'
/home/rubys/git/rails/actionpack/lib/action_dispatch/middleware/cookies.rb:347:in `call'
/home/rubys/git/rails/activerecord/lib/active_record/query_cache.rb:35:in `call'
/home/rubys/git/rails/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb:416:in `call'
/home/rubys/git/rails/actionpack/lib/action_dispatch/middleware/callbacks.rb:28:in `block in call'
/home/rubys/git/rails/activesupport/lib/active_support/callbacks.rb:366:in `_run__4102902303491471926__call__callbacks'
/home/rubys/git/rails/activesupport/lib/active_support/callbacks.rb:366:in `__run_callbacks'
/home/rubys/git/rails/activesupport/lib/active_support/callbacks.rb:79:in `run_callbacks'
/home/rubys/git/rails/actionpack/lib/action_dispatch/middleware/callbacks.rb:27:in `call'
/home/rubys/git/rails/actionpack/lib/action_dispatch/middleware/reloader.rb:64:in `call'
/home/rubys/git/rails/actionpack/lib/action_dispatch/middleware/remote_ip.rb:33:in `call'
/home/rubys/git/rails/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb:16:in `call'
/home/rubys/git/rails/actionpack/lib/action_dispatch/middleware/show_exceptions.rb:31:in `call'
/home/rubys/git/rails/railties/lib/rails/rack/logger.rb:26:in `call_app'
/home/rubys/git/rails/railties/lib/rails/rack/logger.rb:16:in `call'
/home/rubys/git/rails/actionpack/lib/action_dispatch/middleware/request_id.rb:22:in `call'
rack (1.4.1) lib/rack/methodoverride.rb:21:in `call'
rack (1.4.1) lib/rack/runtime.rb:17:in `call'
/home/rubys/git/rails/activesupport/lib/active_support/cache/strategy/local_cache.rb:72:in `call'
rack (1.4.1) lib/rack/lock.rb:15:in `call'
/home/rubys/git/rails/actionpack/lib/action_dispatch/middleware/static.rb:53:in `call'
/home/rubys/git/rails/railties/lib/rails/engine.rb:479:in `call'
/home/rubys/git/rails/railties/lib/rails/application.rb:209:in `call'
rack (1.4.1) lib/rack/content_length.rb:14:in `call'
rack (1.4.1) lib/rack/handler/webrick.rb:59:in `service'
/home/rubys/.rvm/rubies/ruby-head-n34575/lib/ruby/2.0.0/webrick/httpserver.rb:138:in `service'
/home/rubys/.rvm/rubies/ruby-head-n34575/lib/ruby/2.0.0/webrick/httpserver.rb:94:in `run'
/home/rubys/.rvm/rubies/ruby-head-n34575/lib/ruby/2.0.0/webrick/server.rb:191:in `block in start_thread'
     
    
      /home/rubys/git/rails/activerecord/lib/active_record/relation/finder_methods.rb:340:in `find_one'
/home/rubys/git/rails/activerecord/lib/active_record/relation/finder_methods.rb:311:in `find_with_ids'
/home/rubys/git/rails/activerecord/lib/active_record/relation/finder_methods.rb:107:in `find'
/home/rubys/git/rails/activerecord/lib/active_record/querying.rb:6:in `find'
app/controllers/carts_controller.rb:16:in `show'
/home/rubys/git/rails/actionpack/lib/action_controller/metal/implicit_render.rb:4:in `send_action'
/home/rubys/git/rails/actionpack/lib/abstract_controller/base.rb:167:in `process_action'
/home/rubys/git/rails/actionpack/lib/action_controller/metal/rendering.rb:10:in `process_action'
/home/rubys/git/rails/actionpack/lib/abstract_controller/callbacks.rb:18:in `block in process_action'
/home/rubys/git/rails/activesupport/lib/active_support/callbacks.rb:374:in `_run__3534897644878667656__process_action__callbacks'
/home/rubys/git/rails/activesupport/lib/active_support/callbacks.rb:366:in `__run_callbacks'
/home/rubys/git/rails/activesupport/lib/active_support/callbacks.rb:79:in `run_callbacks'
/home/rubys/git/rails/actionpack/lib/abstract_controller/callbacks.rb:17:in `process_action'
/home/rubys/git/rails/actionpack/lib/action_controller/metal/rescue.rb:29:in `process_action'
/home/rubys/git/rails/actionpack/lib/action_controller/metal/instrumentation.rb:30:in `block in process_action'
/home/rubys/git/rails/activesupport/lib/active_support/notifications.rb:123:in `block in instrument'
/home/rubys/git/rails/activesupport/lib/active_support/notifications/instrumenter.rb:20:in `instrument'
/home/rubys/git/rails/activesupport/lib/active_support/notifications.rb:123:in `instrument'
/home/rubys/git/rails/actionpack/lib/action_controller/metal/instrumentation.rb:29:in `process_action'
/home/rubys/git/rails/actionpack/lib/action_controller/metal/params_wrapper.rb:204:in `process_action'
/home/rubys/git/rails/activerecord/lib/active_record/railties/controller_runtime.rb:18:in `process_action'
/home/rubys/git/rails/actionpack/lib/abstract_controller/base.rb:121:in `process'
/home/rubys/git/rails/actionpack/lib/abstract_controller/rendering.rb:44:in `process'
/home/rubys/git/rails/actionpack/lib/action_controller/metal.rb:197:in `dispatch'
/home/rubys/git/rails/actionpack/lib/action_controller/metal/rack_delegation.rb:14:in `dispatch'
/home/rubys/git/rails/actionpack/lib/action_controller/metal.rb:240:in `block in action'
/home/rubys/git/rails/actionpack/lib/action_dispatch/routing/route_set.rb:67:in `call'
/home/rubys/git/rails/actionpack/lib/action_dispatch/routing/route_set.rb:67:in `dispatch'
/home/rubys/git/rails/actionpack/lib/action_dispatch/routing/route_set.rb:30:in `call'
/home/rubys/git/journey/lib/journey/router.rb:68:in `block in call'
/home/rubys/git/journey/lib/journey/router.rb:56:in `each'
/home/rubys/git/journey/lib/journey/router.rb:56:in `call'
/home/rubys/git/rails/actionpack/lib/action_dispatch/routing/route_set.rb:586:in `call'
/home/rubys/git/rails/actionpack/lib/action_dispatch/middleware/best_standards_support.rb:17:in `call'
rack (1.4.1) lib/rack/etag.rb:23:in `call'
rack (1.4.1) lib/rack/conditionalget.rb:25:in `call'
/home/rubys/git/rails/actionpack/lib/action_dispatch/middleware/head.rb:14:in `call'
/home/rubys/git/rails/actionpack/lib/action_dispatch/middleware/params_parser.rb:21:in `call'
/home/rubys/git/rails/actionpack/lib/action_dispatch/middleware/flash.rb:224:in `call'
rack (1.4.1) lib/rack/session/abstract/id.rb:205:in `context'
rack (1.4.1) lib/rack/session/abstract/id.rb:200:in `call'
/home/rubys/git/rails/actionpack/lib/action_dispatch/middleware/cookies.rb:347:in `call'
/home/rubys/git/rails/activerecord/lib/active_record/query_cache.rb:35:in `call'
/home/rubys/git/rails/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb:416:in `call'
/home/rubys/git/rails/actionpack/lib/action_dispatch/middleware/callbacks.rb:28:in `block in call'
/home/rubys/git/rails/activesupport/lib/active_support/callbacks.rb:366:in `_run__4102902303491471926__call__callbacks'
/home/rubys/git/rails/activesupport/lib/active_support/callbacks.rb:366:in `__run_callbacks'
/home/rubys/git/rails/activesupport/lib/active_support/callbacks.rb:79:in `run_callbacks'
/home/rubys/git/rails/actionpack/lib/action_dispatch/middleware/callbacks.rb:27:in `call'
/home/rubys/git/rails/actionpack/lib/action_dispatch/middleware/reloader.rb:64:in `call'
/home/rubys/git/rails/actionpack/lib/action_dispatch/middleware/remote_ip.rb:33:in `call'
/home/rubys/git/rails/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb:16:in `call'
/home/rubys/git/rails/actionpack/lib/action_dispatch/middleware/show_exceptions.rb:31:in `call'
/home/rubys/git/rails/railties/lib/rails/rack/logger.rb:26:in `call_app'
/home/rubys/git/rails/railties/lib/rails/rack/logger.rb:16:in `call'
/home/rubys/git/rails/actionpack/lib/action_dispatch/middleware/request_id.rb:22:in `call'
rack (1.4.1) lib/rack/methodoverride.rb:21:in `call'
rack (1.4.1) lib/rack/runtime.rb:17:in `call'
/home/rubys/git/rails/activesupport/lib/active_support/cache/strategy/local_cache.rb:72:in `call'
rack (1.4.1) lib/rack/lock.rb:15:in `call'
/home/rubys/git/rails/actionpack/lib/action_dispatch/middleware/static.rb:53:in `call'
/home/rubys/git/rails/railties/lib/rails/engine.rb:479:in `call'
/home/rubys/git/rails/railties/lib/rails/application.rb:209:in `call'
rack (1.4.1) lib/rack/content_length.rb:14:in `call'
rack (1.4.1) lib/rack/handler/webrick.rb:59:in `service'
/home/rubys/.rvm/rubies/ruby-head-n34575/lib/ruby/2.0.0/webrick/httpserver.rb:138:in `service'
/home/rubys/.rvm/rubies/ruby-head-n34575/lib/ruby/2.0.0/webrick/httpserver.rb:94:in `run'
/home/rubys/.rvm/rubies/ruby-head-n34575/lib/ruby/2.0.0/webrick/server.rb:191:in `block in start_thread'
     
 
Request
Parameters: 
{"id"=>"wibble"}
Show session dump
_csrf_token: "eFmGYNDeZfs/ckMgyBEX6MNLz+xlDT6AOXcLxsGLHG0="
cart_id: 1
session_id: "ff5277864df575f95d31ac12640f70b0"
 
Show env dump
GATEWAY_INTERFACE: "CGI/1.1"
HTTP_ACCEPT: "text/html"
REMOTE_ADDR: "127.0.0.1"
REMOTE_HOST: "127.0.0.1"
SERVER_NAME: "localhost"
SERVER_PROTOCOL: "HTTP/1.1"
 
Response
Headers: 
None
     
    
    
      10.2 Iteration E2: Handling Errors
      9.4 Playtime