Iteration E3: Finishing the Cart

Một phần của tài liệu 1934356549 {88177f43} agile web development with rails (4th ed ) ruby, thomas hansson 2011 03 31 (Trang 134 - 140)

We know by now that in order to implement the “empty cart” function, we have to add a link to the cart and modify thedestroymethod in the carts controller to clean up the session. Let’s start with the template and again use thebutton_to method to put a button on the page:

Download depot_h/app/views/carts/show.html.erb

<h2>Your Pragmatic Cart</h2>

<ul>

<% @cart.line_items.each do |item| %>

<li><%= item.quantity %> &times; <%= item.product.title %></li>

<% end %>

</ul>

<%= button_to 'Empty cart', @cart, :method => :delete, :confirm => 'Are you sure?' %>

In the controller, we’ll modify the destroy method to ensure that the user is deleting their own cart (think about it!) and to remove the cart from the session before redirecting to the index page with a notification message:

Download depot_h/app/controllers/carts_controller.rb

def destroy

@cart = current_cart

@cart.destroy

session[:cart_id] = nil respond_to do |format|

format.html { redirect_to(store_url,

:notice => 'Your cart is currently empty') } format.xml { head :ok }

end end

And we update the corresponding test intest/functional/carts_controller_test.rb.

Download depot_i/test/functional/carts_controller_test.rb

test "should destroy cart" do

assert_difference('Cart.count', -1) do session[:cart_id] = @cart.id

delete :destroy, :id => @cart.to_param end

ITERATIONE3: FINISHING THECAR T 135

David Says. . .

Battle of the Routes: product_path vs. product_url

It can seem hard in the beginning to know when to useproduct_pathand when to useproduct_urlwhen you want to link or redirect to a given route. In reality, it’s really quite simple.

When you use product_url, you’ll get the full enchilada with protocol and domain name, likehttp://example.com/products/1. That’s the thing to use when you’re doingredirect_to because the HTTP spec requires a fully qualified URL when doing 302 Redirect and friends. You also need the full URL if you’re redi- recting from one domain to another, alaproduct_url(:domain => "example2.com", :product => product).

The rest of the time, you can happily useproduct_path. This will generate only the/products/1part, and that’s all you need when doing links or pointing forms, likelink_to"My lovely product",product_path(product).

Now the confusing part is that oftentimes the two are interchangeable because of lenient browsers. You can do aredirect_towith aproduct_pathand it’ll probably work, but it won’t be valid according to spec. And you canlink_to aproduct_url, but then you’re littering up your HTML with needless characters, which is a bad idea too.

Now when we view our cart and click the Empty Cart button, we get taken back to the catalog page, and a nice little message says this:

We can also remove the flash message that is automatically generated when a line item is added:

Download depot_i/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) } format.xml { render :xml => @line_item,

:status => :created, :location => @line_item }

Report erratum this copy is(P1.0 printing, March 2011)

Download from Wow! eBook <www.wowebook.com>

ITERATIONE3: FINISHING THECAR T 136

else

format.html { render :action => "new" }

format.xml { render :xml => @line_item.errors, :status => :unprocessable_entity }

end end end

And, finally, we’ll get around to tidying up the cart display. Rather than use

<li>elements for each item, let’s use a table. Again, we’ll rely on CSS to do the styling:

Download depot_i/app/views/carts/show.html.erb

<div class="cart_title">Your Cart</div>

<table>

<% @cart.line_items.each do |item| %>

<tr>

<td><%= item.quantity %>&times;</td>

<td><%= item.product.title %></td>

<td class="item_price"><%= number_to_currency(item.total_price) %></td>

</tr>

<% end %>

<tr class="total_line">

<td colspan="2">Total</td>

<td class="total_cell"><%= number_to_currency(@cart.total_price) %></td>

</tr>

</table>

<%= button_to 'Empty cart', @cart, :method => :delete, :confirm => 'Are you sure?' %>

To make this work, we need to add a method to both the LineItem and Cart models that returns the total price for the individual line item and entire cart, respectively. First the line item, which involves only simple multiplication:

Download depot_i/app/models/line_item.rb

def total_price

product.price * quantity end

We implement theCartmethod using Rails’ niftyArray::summethod to sum the prices of each item in the collection:

Download depot_i/app/models/cart.rb

def total_price

line_items.to_a.sum { |item| item.total_price } end

ITERATIONE3: FINISHING THECAR T 137

Figure 10.5: Cart display with a total

Then we need to add a small bit to ourdepot.cssstylesheet:

Download depot_i/public/stylesheets/depot.css

/* Styles for the cart in the main page */

#store .cart_title { font: 120% bold;

}

#store .item_price, #store .total_line { text-align: right;

}

#store .total_line .total_cell { font-weight: bold;

border-top: 1px solid #595;

}

For a nicer-looking cart, see Figure10.5.

What We Just Did

Our shopping cart is now something the client is happy with. Along the way, we covered the following:

• Adding a column to an existing table, with a default value

• Migrating existing data into the new table format

• Providing a flash notice of an error that was detected

• Using the logger to log events

• Deleting a record

• Adjusting the way a table is rendered, using CSS

Report erratum this copy is(P1.0 printing, March 2011)

Download from Wow! eBook <www.wowebook.com>

ITERATIONE3: FINISHING THECAR T 138 But, just as we think we’ve wrapped this functionality up, our customer wan-

ders over with a copy ofInformation Technology and Golf Weekly. Apparently, there’s an article about a new style of browser interface, where stuff gets up- dated on the fly. “Ajax,” she says, proudly. Hmmm...let’s look at that tomorrow.

Playtime

Here’s some stuff to try on your own:

• Create a migration that copies the product price into the line item, and change the add_product method in the Cart model to capture the price whenever a new line item is created.

• Add unit tests that add unique products and duplicate products. Note that you will need to modify the fixture to refer to products and carts by name, for exampleproduct: ruby.

• Check products and line items for other places where a user-friendly error message would be in order.

• Add the ability to delete individual line items from the cart. This will require buttons on each line, and such buttons will need to be linked to thedestroyaction in theLineItemsController.

(You’ll find hints athttp://www.pragprog.com/wikis/wiki/RailsPlayTime.)

In this chapter, we’ll see

• using partial templates,

• rendering into the page layout,

• updating pages dynamically with Ajax and RJS,

• highlighting changes with Script.aculo.us,

• hiding and revealing DOM elements, and

• testing the Ajax updates.

Chapter 11

Task F: Add a Dash of Ajax

Our customer wants us to add Ajax support to the store. But just what is Ajax?

In the old days (up until 2005 or so), browsers were treated as really dumb devices. When you wrote a browser-based application, you’d send stuff to the browser and then forget about that session. At some point, the user would fill in some form fields or click a hyperlink, and your application would get woken up by an incoming request. It would render a complete page back to the user, and the whole tedious process would start afresh. That’s exactly how our Depot application behaves so far.

But it turns out that browsers aren’t really that dumb (who knew?). They can run code. Almost all browsers can run JavaScript. And it turns out that the JavaScript in the browser can interact behind the scenes with the application on the server, updating the stuff the user sees as a result. Jesse James Gar- rett named this style of interactionAjax (which once stood for Asynchronous JavaScript and XML but now just means “making browsers suck less”).

So, let’s Ajaxify our shopping cart. Rather than having a separate shopping cart page, let’s put the current cart display into the catalog’s sidebar. Then, we’ll add the Ajax magic that updates the cart in the sidebar without redis- playing the whole page.

Whenever you work with Ajax, it’s good to start with the non-Ajax version of the application and then gradually introduce Ajax features. That’s what we’ll do here. For starters, let’s move the cart from its own page and put it in the sidebar.

Download from Wow! eBook <www.wowebook.com>

ITERATIONF1: MOVING THECAR T 140

Một phần của tài liệu 1934356549 {88177f43} agile web development with rails (4th ed ) ruby, thomas hansson 2011 03 31 (Trang 134 - 140)

Tải bản đầy đủ (PDF)

(457 trang)