Adding Price Functionality to the Cart

Free Ruby on Rails Tutorial

Delve into this comprehensive tutorial on Ruby on Rails, covering topics such as calculating the subtotal, using delegates, fixing the order summary, and displaying the number of items in a cart.

This exercise is excerpted from Noble Desktop’s past web development training materials. Noble Desktop now teaches JavaScript and the MERN Stack in our Full Stack Development Certificate. To learn current skills in web development, check out our coding bootcamps in NYC and live online.

Topics covered in this Ruby on Rails tutorial:

Calculating the subtotal, Delegates, Fixing the order summary, Displaying the number of items in the cart

Exercise Overview

In this exercise, we will work on getting product prices to display accurately. Each item in the cart should have a subtotal that reflects the quantity. We also need to make the order summary functional.

  1. If you completed the previous exercises, you can skip the following sidebar. We recommend you finish the previous exercises (8A-9C) before starting this one. If you haven’t finished them, do the following sidebar.

    If You Did Not Do the Previous Exercises (8A-9C)

    1. Close any files you may have open.
    2. Open the Finder and navigate to Class Files > yourname-Rails Class
    3. Open Terminal.
    4. Type cd and a single space (do NOT press Return yet).
    5. Drag the yourname-Rails Class folder from the Finder to the Terminal window and press ENTER.
    6. Run rm -rf nutty to delete your copy of the nutty site.
    7. Run git clone https://bitbucket.org/noble-desktop/nutty.git to copy the That Nutty Guy git repository.
    8. Type cd nutty to enter the new directory.
    9. Type git checkout 9C to bring the site up to the end of the previous exercise.
    10. Run bundle to install any necessary gems.
    11. Run yarn install --check-files to install JavaScript dependencies.

Displaying an Accurate Product Price with Subtotals

  1. For this exercise, we’ll continue working with the nutty folder located in Desktop > Class Files > yourname-Rails Class > nutty.

    If you haven’t already done so, we suggest opening the nutty folder in your code editor if it allows you to (like Sublime Text does).

  2. You should still have a window with two tabs open in Terminal from the last exercise, the first of which is running the server. If you don’t, complete the following sidebar.

    Restarting the Rails Server

    1. In Terminal, cd into the nutty folder:
    • Type cd and a space.
    • Drag the nutty folder from Desktop > Class Files > yourname-Rails Class onto the Terminal window (so it will type out the path for you).
    • In Terminal, hit Return to change directory.
    1. In Terminal, type the following:

      rails s
      
    2. Open a new tab (Cmd–T) leaving our server running in the old tab.
    3. In the new tab, cd into the nutty folder:
    • Type cd and a space.
    • Drag the nutty folder from Desktop > Class Files > yourname-Rails Class onto the Terminal window (so it will type out the path for you).
    • In Terminal, hit Return to change directory.
  3. In your code editor, open nutty > app > views > cart > index.html.erb

  4. Look around line 41. This is the code that sets the Total Price column:

    <td class="total-price"><%= number_to_currency line_item.product.price %></td>
    
  5. Edit that line as shown in bold:

    <td class="total-price"><%= number_to_currency line_item.subtotal %></td>
    
  6. Save the file.

  7. In your code editor, open nutty > app > models > line_item.rb

  8. Add the following code shown in bold:

    class LineItem < ApplicationRecord
       belongs_to :cart
       belongs_to :product
    
       def subtotal
          product.price * quantity
       end
    end
    
  9. Save the file.

  10. Go to the browser where localhost:3000/cart should be open and reload the page. If you closed the browser or didn’t do the last exercise:

    • Go to localhost:3000 and click on any product.
    • Set the Quantity to 9 and click Add to Cart.

    The Total Price of the products in the cart now accurately reflects their quantities.

Full-Stack Web Development Certificate: Live & Hands-on, In NYC or Online, 0% Financing, 1-on-1 Mentoring, Free Retake, Job Prep. Named a Top Bootcamp by Forbes, Fortune, & Time Out. Noble Desktop. Learn More.

Delegates

  1. In your code editor, open nutty > app > models > line_item.rb

    Wouldn’t it be handy if we could simply call line_item.price? Remember that line_item doesn’t have its own price field; the product always provides it. Wouldn’t it be nice if we didn’t have to add a separate price field to our model? We don’t have to, thanks to delegation. To delegate is a way of having related models pass their methods down to each other.

    relationship diagram delegation

  2. Edit the code as shown in bold:

    class LineItem < ApplicationRecord
       belongs_to :cart
       belongs_to :product
    
    delegate :price, to: :product
    
       def subtotal
          price * quantity
       end
    end
    
  3. Save the file.

  4. In your code editor, open nutty > app > views > cart > index.html.erb.

  5. Around line 40, delete the bold code (don’t miss the period):

    <td class="hidden-xs"><%= number_to_currency line_item.product. price %></td>
    
  6. Save the file once it looks like the following:

    <td class="hidden-xs"><%= number_to_currency line_item.price %></td>
    
  7. Go back to the browser and reload localhost:3000/cart

    Everything should remain the same, showing that things still work. As you can see, delegates are very handy and help you write nice, tidy code. We can delegate other methods as well.

  8. In your code editor, open nutty > app > models > line_item.rb.

  9. Add the following bold code around line 5:

    delegate :price, :title, :image, :sku, to: :product
    
  10. Save the file.

  11. In your code editor, open nutty > app > views > cart > index.html.erb.

  12. Delete the instances of product. (including the period after it) around lines 24, 29, and 31:

    <td id="thumbnail-div" class="hidden-xs">
       <%= link_to line_item.product do %>
          <%= image_tag line_item.product. image.url(:medium), alt: product.title, class: 'cart-thumbnail' %>
       <% end %>
    </td>
    <td>
       <%= link_to line_item.product do %>
          <p><%= line_item.product. title %></p>
       <% end %>
       <p class="gray-text">Item #<%= line_item.product. sku %></p>
    </td>
    
  13. Save the file.

  14. Reload localhost:3000/cart in the browser to see that it still looks good.

Making the Order Summary Functional

Next, let’s get the Order Summary section working. For now, we’ll just set Total equal to Subtotal. That way in the future, when we add Shipping and Taxes, we’ll already have a total method set up and the only code change we will need to make will be the total method in our cart model file.

  1. In your code editor, open nutty > app > models > cart.rb

  2. Add the following bold code to add subtotal and total methods:

       has_many :products, through: :line_items
    
       def subtotal
          line_items.sum(&:subtotal)
       end
    
       def total
          subtotal
       end
    end
    
  3. Let’s break down the code you just wrote:
    • For the subtotal method, we’re summing up the subtotal of all our line items. (The ampersand means apply the method called subtotal to each line item.)
    • We set up a separate method for total. This gives us something to build off of if we need to add more later.
  4. Save the file, then close it.

  5. In your code editor, open nutty > app > views > cart > index.html.erb.

  6. Edit the Subtotal and Total fields (around lines 55 and 67):

    <tr>
       <td>Subtotal</td>
       <td><%= number_to_currency @cart.subtotal %></td>
    </tr>
    

    Code Omitted To Save Space

    <tr>
       <td>Estimated Tax</td>
       <td>$0.00</td>
    </tr>
    <tr class="total-price">
       <td>Total</td>
       <td><%= number_to_currency @cart.total %></td>
    </tr>
    
  7. Save the file.

  8. Reload localhost:3000/cart in the browser to see that now the Subtotal and Total in the Order Summary section are working!

Optional Bonus: Adding a Customers Who Want to Buy This Product Field

Next, let’s update the back-end of our site to reflect some of the things we’ve been doing on the front end.

  1. In the browser go to: localhost:3000/admin and sign in.

  2. Click the Products link at the top.

  3. To the far right of any product you’ve added to your cart, click View.

    On this page, we’d like to see a list of all the customers here who have added this product to their cart.

  4. In your code editor, open nutty > app > models > cart.rb

  5. Around line 7, add a delegate for :email so we can refer directly to the customer’s username (their email) from the cart:

    has_many :products, through: :line_items
    
    delegate :email, to: :customer
    
    def subtotal
    
  6. Save the file, then close it.

  7. In your code editor, open nutty > app > admin > product.rb

  8. Around line 30, add a new row:

    row :title
    row :sku
    row 'Customers who want to buy this product' do
       product.carts.map(&:email).join(", ")
    end
    row :price
    row :description
    

    NOTE: The cool thing about this bit of code is that it’s going through four models with a single line. We’re only referring to two models explicitly. Thanks to the has_many, through relationship, we can go directly from products to carts without invoking line_items. When we call map by email, we’re invisibly invoking the customer model. Many-to-many relationships and delegates allow you to write very concise, easy-to-work-with code.

  9. Save the file.

  10. Go back to the browser and reload the product page in the admin. You should now see a new Customers who want to buy… row with your email in it (since you’re the only one who’s added anything so far)!

  11. If you want to play with this further, you can sign up for other user accounts and use them to add products to the cart. Then their emails should be displayed as wanting to buy those products on the admin page.

Optional Bonus: Displaying the Number of Items in the Cart

  1. In the browser, go to localhost:3000

  2. Click on Cart to go to the cart page.

  3. Notice that the Cart icon on the top right has a 3 next to it. This is not an accurate reflection of the items in the cart, just a part of the mockup design.

    There are a number of ways one could go about implementing this feature. We will show you one approach. Feel free to experiment and find alternate methods on your own.

  4. In your code editor, open nutty > app > controllers > application_controller.rb

  5. Look at the load_cart_or_redirect_customer method (around line 7). It combines two functionalities into one.

    We need two separate methods: one that loads the cart and one that redirects the customer. In the products_controller, we only want to try to load the cart, not redirect the customer. We do need to redirect the customer in the cart_controller and the line_items_controller.

  6. Cut (Cmd–X) the code shown in bold (around lines 8–11):

    private
       def load_cart_or_redirect_customer
          if customer_signed_in?
             current_customer.create_cart if current_customer.cart.nil?
             @cart = current_customer.cart  
          else
             redirect_to new_customer_session_path, alert: "Please sign in to access your cart." and return
          end
    
  7. Paste the cut code starting around line 7:

    private
          if customer_signed_in?
             current_customer.create_cart if current_customer.cart.nil?
             @cart = current_customer.cart  
          else
    
       def load_cart_or_redirect_customer
    
  8. Now add the code shown in bold:

    private
       def load_cart
          if customer_signed_in?
             current_customer.create_cart if current_customer.cart.nil?
             @cart = current_customer.cart  
          else
       end
    
       def load_cart_or_redirect_customer
          unless load_cart
             redirect_to new_customer_session_path, alert: "Please sign in to access your cart." and return
          end
       end
    
  9. Edit the pasted code as shown and delete the else around line 11:

    private
       def load_cart
          return false unless customer_signed_in?
          current_customer.create_cart if current_customer.cart.nil?
          @cart = current_customer.cart  
       end
    
  10. Save the file, then close it.

  11. In your code editor, open nutty > app > controllers > products_controller.rb.

  12. Add the code in bold:

    before_action :load_cart
    
  13. Save the file, then close it.

  14. In your code editor, open nutty > app > views > layouts > application.html.erb.

  15. Starting around line 45, edit the code as shown:

    <a id="cart" href="/cart">
       <span></span>Cart
       <% if @cart %>
          <span class="badge"><%= @cart.line_items.count %></span>
       <% end %>
    </a>
    
  16. Save the file, then close it.

  17. In the browser, go to localhost:3000

  18. Try adding more products to the cart to see that the Cart icon at the top updates accordingly. Awesome!

  19. Want an extra challenge? This implementation counts the number of line items in the cart. Some people would prefer that this badge show the total quantity instead. So, as implemented here, if we have two items in our cart, the badge will read “2” regardless of their quantity. Supposed we have 11 Pantone Toothbrushes and 4 Finger Tentacles in our cart. Can you get the badge to say “15”?

  20. Leave your code editor and browser open, as well as the server running in Terminal so we can continue with them in the following exercises.

Noble Desktop Publishing Team

The Noble Desktop Publishing Team includes writers, editors, instructors, and industry experts who collaborate to publish up-to-date content on today's top skills and software. From career guides to software tutorials to introductory video courses, Noble aims to produce relevant learning resources for people interested in coding, design, data, marketing, and other in-demand professions.

More articles by Noble Desktop Publishing Team

How to Learn Coding

Master coding with hands-on training. Learning how to code in JavaScript, Python, and other popular languages can pave the way to a job in tech, such as web development, data science & analytics, or software engineering.

Yelp Facebook LinkedIn YouTube Twitter Instagram