Appendix: The Shop on Rails

This text was originally prepared for the lecture I delivered in January 2009 for Kingston University final year students to introduce them to Ruby on Rails. This tutorial demonstrates how a simple on-line shop can be quickly built using Ruby on Rails. The same presentation is now re-used for the first year students, as their last lecture before completing the Practical Programming course.

The final year students who haven't yet experienced any Ruby on Rails programming may find some sections rather difficult to follow. Apparently, it is not easy to build up an on-line shop in a language you are using for the first time in your life. But please treat it as a taster - if you think it's interesting, just navigate to the beginning of this course and teach yourself Ruby on Rails using this 10-chapter long tutorial.

The first year students have just finished their regular FakeBook Tutorial and should soon submit their final piece of coursework. The goal of this additional chapter of your tutorial is to explore a completely different area of application. We will start a new web application from scratch and finish it - all in a single chapter. What we create is almost a complete online shop application, with electronic cart etc. The only missing part is check-out. One of many interesting points here is the use of a scaffold - one of the most powerful generate tools, which we have accidently avoided so far...

The material presented here is extra-curricular for the first year students and will not make a part of any question in your final test.

  1. Intsallation and creating a starter application

    The recommended installation package of Ruby on Rails called BitNami RubyStack. This is the full package that greatly simplifies both installation and then development in Ruby on Rails. It is available for Windows, Linux and Mac. It comes complete with all required components, including Ruby, Gems, Rails, several web servers (Apache, Mongrel, Webricks, Lighttp) and two database systems (MySQL and sqlite). BitNami RubyStack is distributed for free under the Apache 2.0 license. Download the NATIVE version 2.1-0, which currently supports Ruby 1.8.7 and Rails 2.3.9 (we are not yet working with Rails 3!).

    Once installed, run a program entitled Use BitNami RubyStack (on KU computers: Open Ruby Console Window)

    To create To create an empty Rails application, run the command in the Ruby Console Window:

    rails shop_on_rails

    To start the webserver use:

    cd shop_on_rails ruby script/server

    To continue, open another Ruby Console Window and type:

    cd shop_on_rails

    The one you used previously is now engaged by the web server and unavailable.

    Your new application should be available at: http://localhost:3000. You should by now see the Welcome page.

    Your application files can be found at c:/rails/rails_apps/shop_on_rails (assuming that you installed Rails at c:/rails). All pathnames in this tutorial will be relative to that.

  2. Using Scaffold to create the Catalog structure

    Firstly, we will use Scaffold to create the general structure of the on line Catalog:

    ruby script/generate scaffold Item title:string description:text price:decimal image_url:string

    Secondly, rake the application - which means that we will automatically created the database structure imposed by the Scaffold:

    rake db:migrate

    Check the new functionality of your application at http://localhost:3000/items.

  3. Adding Data

    With this simple functionality, we can now put the data into our application. Firstly, download these images and put them into public/images folder in your application:

    sample image sample image sample image sample image sample image sample image

    You can also download full size images that are available by clicking on the thumbnails above.

    You should enter data about at least three items, including at least one longer description, to be able to fully test the look and feel of the application. Entering the image_url, use just the filename (no path). Available filenames are: bus.jpg, jam.jpg, pate.jpg, soap.jpg, train.jpg, bulldozer.jpg.

    I used the following stuff as a description of a Double Decker Bus item (you can copy & paste it):

    A classical London double-decker bus in a miniature, faithfully made in finest details by one of our most talented toy masters. Metal body and plastic chassis ensures durability. Product will satisfy both adults and children guaranteeing long hour of happy playtime. Available in red, green and blue, currently only four wheels and automatic transmission is available. No toxic part, product fully approved.

  4. Changing the Look and Feel of your Application

    To change the look and feel of the application, we will:

    1. Change app/views/layouts/application.html.erb file which defines the general layout of all the HTML stuff in the application
    2. Provide a suitable stylesheet that should go to public/stylesheets as default.css.

    Both files do not exist now. You will have to create them in their proper directories. The application.html.erb is shown below, you can easily edit the file using Notepad, or, even better, a dedicated editor called SciTE (available at KU computers, or from this download site).

    It is important not to leave a file items.html.erb!

    app/views/layouts/application.html.erb
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
           "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    
    <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
    <head>
      <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
      <title>Items: <%= controller.action_name %></title>
      <%= stylesheet_link_tag 'default' %>
    </head>
    <body>
    	
        <div id="page">
          <div id="header">Shop on Rails</div>
          <div id="nav">
            <div style="float:left;text-align:left">
    		
              <%= link_to_unless_current "Home",  :controller => "items", 
                                                     :action => "index" %> | 
              <%= link_to_unless_current "About Us",:controller => "site", 
                                                     :action => "about" %> |
              <%= link_to_unless_current "Contact Us",:controller => "site", 
                                                     :action => "contact" %>
            </div>
            <div style="text-align:right">
            
    		  <%= link_to_unless_current "Cart",  :controller => "cart", 
                                                     :action => "index" %> | 
              <% unless session[:login] %>
                <%= link_to_unless_current "Admin", :controller => "user", 
                                                       :action => "admin_login" %>
              <% else %>
                <%= link_to_unless_current "Logout", :controller => "user", 
                                                        :action => "logout" %>
              <% end %>
            </div>
          </div>
          <div id="content">
          	<% if flash[:notice] -%>
            	<p style="color: green"><%= flash[:notice] %></p>
    		<% end -%>
            <%= yield %>
          </div>
        </div>
    
    </body>
    </html>
    
    

    You can also download this file. See the end of this document for the list of all downloadables.

    This is not a CSS tutorial, so the stylesheet is only for download:

    File to download:
    public/stylesheets/default.css
    To download, right click on the link, choose Save Target As... and navigate to the appropriate directory.

    Check the new look and feel of your application at http://localhost:3000/items. It should look now more or less like this:

    Screen shot

  5. Adding About Us and Contact Us

    To provide skeleton form of even more pages, we will use a controller generator to create About Us and Contact Us pages:

    ruby script/generate controller Site about contact

    That's all! Test your new pages starting at http://localhost:3000/items.

    If they don't work, no worry! Just stop the web server (by pressing Ctrl+C) and re-run it!

    We'll leave Cart and Admin for later use.

  6. Setting up the Catalog Layout

    The main page is not very good. We will introduce following changes:

    1. Put the image on the left-hand side of each item.
    2. Provide a prominent Title page on the right.
    3. Display description below.
    4. Provide links to Edit and Destroy underneath.
    5. Make both the Title and the Images active: clicking on them will follow what was so far a Show link.
    6. Get rid of the Show link.

    Here is the first version of the layout for the Catalog view:

    app/views/items/index.html.erb
    <h1>Catalog</h1>
    
    <table class="catalog">
    <% for item in @items %>
      <tr>
        <td>
            <div class="image">
                <%= link_to (image_tag item.image_url), item %>
            </div>
        </td>
        <td>
            <div class="title">
                <%= link_to item.title, item %>
            </div>
            <div class="description">
                <%=h item.description %>
            </div>
            <div class="links">
                <%= link_to 'Edit', edit_item_path(item) %> |
                <%= link_to 'Destroy', item, :confirm => 'Are you sure?', :method => :delete %>
            </div>
            <div class="price">
                <%= number_to_currency(item.price, :unit => "&pound;") %>
            </div>
        </td>
      </tr>
    <% end %>
    </table>
    
    <p><%= link_to 'New item', new_item_path %></p>
    
    

    See it at: http://localhost:3000/items. It should look now more or less like this:

    Screen shot

  7. Creating limited User functionality

    This tutorial does not cover user registration, login, logout and similar functions. Please check my Ruby on Rails tutorial for detailed discussion of this stuff. Now we will only provide a very basic functionality to mimic admin and customer login.

    In the Ruby Console Script type:

    ruby script/generate controller User

    Let's add stuff to the newly created controller:

    app/controllers/user_controller.rb
    class UserController < ApplicationController
    
      def login
      end
    
      def admin_login
        session[:login] = 1
        session[:cart] = nil
        flash[:notice] = "Admin user successfully logged in, cart reset."
        redirect_to :controller => :items
      end
    
      def logout
        session[:login] = nil
        session[:cart] = nil
        flash[:notice] = "User logged out, cart reset."
        redirect_to :controller => :items
      end
    end
    
    

    This code adds following things:

    • The session[:login] session variable that keeps 1 in the Admin mode and nil otherwise. Normally, this variable should contain an identifier of a logged-in user (if any).
    • Additionally, the session[:cart] session variable (which we will be using when developing the cart) is reset at admin login and logout.
    • A flash message is set.
    • The application is redirected back to the Catalog index so no views are necessary at this stage.

    Try out at http://localhost:3000/items. When you click Admin menu option it should change to Logout. Restart the web server if it does not. Analyse app/views/layouts/application.html.erb to discover how it works.

  8. Providing the Add to Cart button

    The Catalog page lacks so far of the Add to Cart button and it is actually, with its editing links, rather an Admin than a Customer site.

    We will now use session[:login] value, introduced in the previous section, to provide slightly different view for the admin and for the regular user:

    app/views/items/index.html.erb
    <h1>Catalog</h1>
    
    <table class="catalog">
    <% for item in @items %>
      <tr>
        <td>
            <div class="image">
                <%= link_to (image_tag item.image_url), item %>
            </div>
        </td>
        <td>
            <div class="title">
                <%= link_to item.title, item %>
            </div>
            <div class="description">
                <%=h item.description %>
            </div>
            <div class="links">
                <% if session[:login] == 1 %>
                    <%= link_to 'Edit', edit_item_path(item) %> |
                    <%= link_to 'Destroy', item, :confirm => 'Are you sure?', :method => :delete %>
                <% else %>
                    <%= button_to "Add to Cart", :controller => :Cart, :action => :add, :id => item %>
                <% end %>
            </div>
            <div class="price">
                <%= number_to_currency(item.price, :unit => "&pound;") %>
            </div>
        </td>
      </tr>
    <% end %>
    </table>
    
    <% if session[:login] == 1 %>
        <p><%= link_to 'New item', new_item_path %></p>
    <% end %>
    
    

    New code is emphasized with ruby coloured letters.

    Run the application at http://localhost:3000/items. Click on Admin and Logout to see the difference.

  9. Writing the Cart in Ruby

    The Add to Cart button added in the previous section links to the controller Cart, action add. The Cart menu option links to the same controller, action index. We will now generate this controller, rather surprisingly providing just one action:

    ruby script/generate controller Cart index

    Clicking on the Cart option should now display a generic Cart#index page, and clicking the Add to Cart button will generate Unknown action error. Check it at http://localhost:3000/items (restart the server if it doesn't work).

    The Cart in Ruby on Rails is based on the session[:cart] session variable. Here is the code for both actions:

    class CartController < ApplicationController
    
      def add
        id = params[:id]
        
        cart = session[:cart] ||= {}
        cart[id] = (cart[id] || 0) + 1
            
        redirect_to :action => :index
      end
    
      def index
        @cart = session[:cart] || {}
      end
      
     end
    
    

    The id provides the it of the item to be added. The next two lines are actually how the cart can look like in ruby. Quite smart, isn't it? The final line redirects to the cart index, this is why we won't need a separate view for the add action.

    The index action simply provides the cart to be available as a @cart for the view.

    To get a full version we only need now the view. It uses the @cart variable to access the cart, and is loosely based on the Catalog index code:

    app/views/cart/index.html.erb
    <h1>Your Cart</h1>
    
    <% if @cart.empty? %>
        <p>Your Cart is empty.</p>
    <% end %>
    
    <% total = 0 %>
    	
    <table class="cart">
        <tr>
            <td class="legend"></td>
            <td class="legend">Item</td>
            <td class="legend">Price</td>
            <td class="legend">Qty</td>
            <td class="legend">Total</td>
        </tr>
    	
    <% @cart.each do | id, quantity | %>
        <% item = Item.find_by_id(id) %>
        <tr>
            <td>
                <div class="image">
                    <%= link_to (image_tag item.image_url), item %>
                </div>
            </td>
            <td class="title"><%= link_to item.title, item %></td>
            <td class="price"><%= number_to_currency(item.price) %></td>
            <td class="quantity"><%= quantity %><br /></td>
            <td class="price">
            <%= number_to_currency(quantity * item.price, :unit => "£") %>
        </td>
        </tr>
        <% total += quantity * item.price %>
    <% end %>
    
        <tr>
            <td colspan="4">
                <div class="total">Total:</div>
            </td>
            <td>
                <div class="price"><%= number_to_currency(total, :unit => "£") %></div>
            </td>
        </tr>
    </table>
    
    <p>
        <%= link_to 'Proceed to Checkout', :action => :checkout %> |
        <%= link_to 'Continue Shopping', :controller => :items %>
    </p>
    
    
  10. What Next?

    The application is now finished. Test it at http://localhost:3000/items.

    What is left undone is the Checkout. If you click on Proceed to checkout link in your cart you will see that the action checkout is not implemented. So, why not to try out to do it yourself?

    The full user registration, login and logout functionality is available in the FakeBook application, which is the sample app for my online Ruby on Rails Tutorial that was created for my first year students.

    Also, I can recommend these two books:

    Railsspace Railsspace

    I like RailsSpace because it uses a very nice social networking site to teach Rails. This site is very similar to FakeBook that I use in my tutorial mentioned above. Agile Web Development book starts with showing how to set up an on-line shop and then provides the reader with a really deep insight into how Rails work.

  11. Downloadables

    Here is the list of all files that can be downloaded.

    The application layout:
    app/views/layouts/application.html.erb
    The CSS stylesheet:
    public/stylesheets/default.css
    The Items Catalog View:
    app/views/items/index.html.erb
    The User Controller:
    app/controllers/user_controller.rb
    The Cart Controller:
    app/controllers/cart_controller.rb
    The Cart Index View:
    app/views/cart/index.html.erb

    To download, right click on the link, choose Save Target As... and then navigate to the appropriate directory to save it. Be particularly careful with *.html.erb as Internet Explorer will propose to save it under inappropriate extension.

    To rebuild the application from scratch, type in the Ruby Console Window:

    rails shop_on_rails2 cd shop_on_rails2 ruby script/generate scaffold Item title:string description:text price:decimal image_url:string ruby script/generate controller Site about contact ruby script/generate controller User ruby script/generate controller Cart index rake db:migrate ruby script/server

    Then, download all the files listed above, and save them in the proper locations. For this to work, you will also have to remove the file app/views/layouts/items.html.erb generated with the scaffold. Go then to Section 3 and add some sample data.

    Altenatively, you can also download entire application.

Valid XHTML 1.0 Strict Valid CSS!