Home arrow Ruby-on-Rails arrow Page 3 - Finishing a Shopping Cart Implementation
RUBY-ON-RAILS

Finishing a Shopping Cart Implementation


In this second part of a two-part series on implementing a shopping cart for an online bookstore, we'll create a floating cart that will show its contents to the user, and set up its capabilities. This article is excerpted from chapter five of the book Practical Rails Projects, written by Eldon Alameda (Apress; ISBN: 1590597818).

Author Info:
By: Apress Publishing
Rating: 5 stars5 stars5 stars5 stars5 stars / 11
June 04, 2010
TABLE OF CONTENTS:
  1. · Finishing a Shopping Cart Implementation
  2. · Implementing the Add Items to the Cart User Story
  3. · Ajax’ing It
  4. · Drag-and-Drop
  5. · Implementing the Remove Items from the Cart User Story
  6. · Implementing the Clear the Cart User Story

print this article
SEARCH DEVARTICLES

Finishing a Shopping Cart Implementation - Ajax’ing It
(Page 3 of 6 )

Ajax, dubbed by Jesse James Garrett of Adaptive Path, stands for Asynchronous JavaScript and XML. It’s actually not a single technique at all, but a bunch of techniques that can be used together to update a web page without doing a full page refresh. You can use Ajax to implement faster and more interactive user interfaces than could be possible with normal page refreshing. The most famous example of Ajax usage is probably Google Maps (http://maps.google.com), where the map is updated without refreshing the whole page when the user drags or zooms the map.


Tip  If you’re looking for a sound introduction to Ajax, consider Foundations of Ajax by Ryan Asleson and Nathaniel T. Schutta (ISBN 1-59059-582-3).


While Ajax is a very cool and useful addition to the arsenal of any web developer, it doesn’t come without problems. To use Ajax-driven sites, a user needs to have a fairly recent browser with JavaScript enabled (JavaScript libraries used in Rails,script.aculo.usand Prototype, officially support Internet Explorer 6.0 and up, Mozilla Firefox 1.0/Mozilla 1.7 and higher, and Apple Safari version 1.2 and up). Even having a JavaScript-capable browser is not a complete assurance of working Ajax. For example, older Opera versions don’t support theXMLHttpRequestJavaScript object, which is the heart of Ajax.

Therefore, it is advisable to make sure that your application works even without JavaScript and Ajax (unless you’re willing to abandon some of your customers). A good way to ensure backward-compatibility is to first make an application work without Ajax—what we just did—and only after that add the groovy Ajax interface.

For testing that adding items to the cart works with Ajax, we add another test to our functional test filetest/functional/cart_controller_test.rb:

def test_adding_with_xhr
  assert_difference(CartItem, :count) do
   
xhr :post, :add, :id => 5
  end
  assert_response :success
  assert_equal 1, Cart.find(@request.session[:cart_id]).cart_items.size 
end

You can see that it’s almost identical to thetest_addingtest method, with just two differences:

  1. Instead of calling theposttest helper method, we callxhr(alias forxml_http_request). It simulates an Ajax’ed call to the sameadd action. 
     
  2. We don’t want to be redirected after the action, but instead get a normal 200 HTTP Success response.

To make ouraddcontroller action work correctly with Ajax calls, we need to modifyapp/controllers/cart_controller.rbslightly:

def add
  @book = Book.find(params[:id])

  if request.xhr?
    @item = @cart.add(params[:id])
    flash.now[:cart_notice] = "Added <em>#{@item.book.title}</em>"
    render :action => "add_with_ajax"
  elsif request.post?
   
@item = @cart.add(params[:id])
    flash[:cart_notice] = "Added <em>#{@item.book.title}</em>"
    redirect_to session[:return_to] || {:controller => "catalog"}
 
else
    render
  end
end

Withrequest.xhr?, we can check if the request was made by Ajax, just as we can check whether a request was done usingPOSTorGET. What we do is pretty much the same as withPOSTrequests. However, since we want the flash message to be accessible only to the current action (and not the next one), we use theflash.nowhash instead of justflash.

The most important difference is that instead of redirecting, we now render a template calledadd_with_ajax. But we’re not rendering a normal.rhtmltemplate. Instead, we now use an.rjstemplate. These templates debuted in Rails 1.1 and are a great way to do JavaScript-driven changes to a web page in pure Ruby. Create a new file,app/views/cart/add_with_ajax.rjsand add the following code to it:

page.replace_html "shopping_cart", :partial => "cart"
page.visual_effect :highlight, "cart_item_#{@item.book.id}", :duration => 3 page.visual_effect :fade, 'cart_notice', :duration => 3

pageis an object provided to the template that represents the web page in question. All the changes done to the page are normally done through it.page.replace_htmltakes as its arguments first the DOMidof the element being replaced (here, for example<div id="shopping_cart">) and the value it’s being replaced with. As you can see, you can use partials there just as withrendercalls, as well as simple strings.

page.visual_effectis a method that calls thescript.aculo.us Effect.*JavaScript methods to do some visual effects for elements in the page. In our case, we highlight thecart_itemelement for a newly created item for three seconds, using the so-called Yellow Fade Technique. In addition, we slowly fade out the notice text element from the page.


Tip  You can read more about the Yellow Fade Technique athttp://www.37signals.com/svn/ archives/000558.php.


Our application is now ready to receive Ajax requests for adding new cart items. However, we need to change the links in our pages to actually use Ajax. Rails makes this extremely easy. All you need to do is to replace alink_tocall with a similarlink_to_remotecall. However, as we want to maintain the backward-compatibility of our application, we specify the optionalhrefattribute for thelinktag, so that it will behave as a normal link if the users don’t have JavaScript enabled or supported in their browser.

Using all the attributes makes thelink_to_remotecall a bit messy, so we create our own helper method for this inapp/helpers/application_helper.rb. For the sake of brevity, let’s create similar helpers for removing a book from the cart and clearing the whole cart at the same time, as shown in Listing 5-3. We will use them in the later sections of this chapter.

Listing 5-3. Shopping Cart Helpers

module ApplicationHelper
  def add_book_link(text, book)

    link_to_remote text, {:url => {:controller => "cart",
              :action => "add", :id => book}},
              {:title => "Add to Cart",
               :href => url_for( :controller => "cart",
                          :action => "add", :id => book)}
  end

  def remove_book_link(text, book)
    link_to_remote text, {:url => {:controller => "cart",
                       :action => "remove",
                       :id => book}},
                       {:title => "Remove book",
                         :href => url_for( :controller => "cart",
                                     :action => "remove", :id => book)}
  end

  def clear_cart_link(text = "Clear Cart")
    link_to_remote text,
            {:url => { :controller => "cart",
                      :action => "clear" }},
            {:href => url_for(:controller => "cart",
                              :action => "clear")}
  end
end

Now, instead of using the wholelink_to_remotecall in our views, we can make ourcatalog/_books.rhtmlpartial look as clean as this:

<dl id="books">
  <% for book in @books %>
    <dt><%= link_to book.title, :action => "show", :id => book %></dt>
    <% for author in book.authors %>
      <dd><%= author.last_name %>, <%= author.first_name %></dd>
    <% end %>
      <dd>
        <strong>
         <%= add_book_link("+", book) %>
        </strong>
      </dd>
      <dd><small>Publisher: <%= book.publisher.name %></small></dd>
  <% end %>
</dl>


blog comments powered by Disqus
RUBY-ON-RAILS ARTICLES

- Ruby-on-Rails Faces Second Security Flaw in ...
- Ruby 2.0 Prepped for February 2013 Release
- Why LinkedIn Switched from Ruby on Rails
- Adding Style with Action Pack
- Handling HTML in Templates with Action Pack
- Filters, Controllers and Helpers in Action P...
- Action Pack and Controller Filters
- Action Pack Categories and Events
- Logging Out, Events and Templates with Actio...
- Action Pack Sessions and Architecture
- More on Action Pack Partial Templates
- Action Pack Partial Templates
- Displaying Error Messages with the Action Pa...
- Action Pack Request Parameters
- Creating an Action Pack Registration Form

Watch our Tech Videos 
Dev Articles Forums 
 RSS  Articles
 RSS  Forums
 RSS  All Feeds
Write For Us 
Weekly Newsletter
 
Developer Updates  
Free Website Content 
Contact Us 
Site Map 
Privacy Policy 
Support 

Developer Shed Affiliates

 




© 2003-2018 by Developer Shed. All rights reserved. DS Cluster - Follow our Sitemap
Popular Web Development Topics
All Web Development Tutorials