Agile Web Development with Rails, Edition 4

Agile Web Development with Rails, Edition 4

12.2 Iteration G2: Atom Feeds 11.5 Iteration F5: Testing AJAX changes

12.1 Iteration G1: Capturing an Order

rails generate scaffold Order name:string address:text email:string pay_type:string
      invoke  active_record
      create    db/migrate/20100606143729_create_orders.rb
      create    app/models/order.rb
      invoke    test_unit
      create      test/unit/order_test.rb
      create      test/fixtures/orders.yml
       route  resources :orders
      invoke  scaffold_controller
      create    app/controllers/orders_controller.rb
      invoke    erb
      create      app/views/orders
      create      app/views/orders/index.html.erb
      create      app/views/orders/edit.html.erb
      create      app/views/orders/show.html.erb
      create      app/views/orders/new.html.erb
      create      app/views/orders/_form.html.erb
      invoke    test_unit
      create      test/functional/orders_controller_test.rb
      invoke    helper
      create      app/helpers/orders_helper.rb
      invoke      test_unit
      create        test/unit/helpers/orders_helper_test.rb
      invoke  stylesheets
   identical    public/stylesheets/scaffold.css
edit db/migrate/20100606143729_create_orders.rb
  def self.up
    create_table :orders do |t|
      t.string :name
      t.text :address
      t.string :email
      t.string :pay_type, :limit => 10
 
      t.timestamps
    end
  end
rails generate migration add_order_id_to_line_item order_id:integer
      invoke  active_record
      create    db/migrate/20100606143733_add_order_id_to_line_item.rb
rake db:migrate
mv 20100606143729_create_orders.rb 20100301000006_create_orders.rb
mv 20100606143733_add_order_id_to_line_item.rb 20100301000007_add_order_id_to_line_item.rb
(in /home/rubys/svn/rails4/Book/util/work-193/depot)
==  CreateOrders: migrating ===================================================
-- create_table(:orders)
   -> 0.0019s
==  CreateOrders: migrated (0.0020s) ==========================================
 
==  AddOrderIdToLineItem: migrating ===========================================
-- add_column(:line_items, :order_id, :integer)
   -> 0.0010s
==  AddOrderIdToLineItem: migrated (0.0011s) ==================================
 
sqlite3 db/development.sqlite3 .schema
CREATE TABLE "carts" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "created_at" datetime, "updated_at" datetime);
CREATE TABLE "line_items" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "product_id" integer, "cart_id" integer, "created_at" datetime, "updated_at" datetime, "quantity" integer DEFAULT 1, "order_id" integer);
CREATE TABLE "orders" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar(255), "address" text, "email" varchar(255), "pay_type" varchar(10), "created_at" datetime, "updated_at" datetime);
CREATE TABLE "products" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "title" varchar(255), "description" text, "image_url" varchar(255), "price" decimal(8,2), "created_at" datetime, "updated_at" datetime);
CREATE TABLE "schema_migrations" ("version" varchar(255) NOT NULL);
CREATE UNIQUE INDEX "unique_schema_migrations" ON "schema_migrations" ("version");
edit app/models/order.rb
class Order < ActiveRecord::Base
  has_many :line_items, :dependent => :destroy
end
edit app/models/line_item.rb
edit app/views/carts/_cart.html.erb
<div class="cart_title">Your Cart</div>
<table>
  <%= render(cart.line_items) %>
 
  <tr class="total_line">
    <td colspan="2">Total</td>
    <td class="total_cell"><%= number_to_currency(cart.total_price) %></td>
  </tr>
 
</table>
<%= button_to "Checkout", new_order_url, :method=>:get %>
<%= button_to 'Empty cart', cart, :method => :delete,
    :confirm => 'Are you sure?' %>
edit app/controllers/orders_controller.rb
  def new
    if find_or_create_cart.line_items.empty?
      redirect_to store_url, :notice => "Your cart is empty"
      return
    end
 
    @order = Order.new
 
    respond_to do |format|
      format.html # new.html.erb
      format.xml  { render :xml => @order }
    end
  end
edit app/views/orders/new.html.erb
<div class="depot_form">
  <fieldset>
    <legend>Please Enter Your Details</legend>
<%= render 'form' %>
  </fieldset>
</div>
edit app/views/orders/_form.html.erb
<%= form_for @order do |f| %>
  <% if @order.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@order.errors.count, "error") %>
        prohibited this order from being saved:</h2>
      <ul>
      <% @order.errors.full_messages.each do |msg| %>
        <li><%= msg %></li>
      <% end %>
      </ul>
    </div>
  <% end %>
 
  <div>
    <%= f.label :name, "Name:" %>
    <%= f.text_field :name, :size => 40 %>
  </div>
 
  <div>
    <%= f.label :address, "Address:" %>
    <%= f.text_area :address, :rows => 3, :cols => 40 %>
  </div>
 
  <div>
    <%= f.label :email, "E-Mail:" %>
    <%= f.text_field :email, :size => 40 %>
  </div>
 
  <div>
    <%= f.label :pay_type, "Pay with:" %>
    <%=
      f.select :pay_type,
                   Order::PAYMENT_TYPES, 
                  :prompt => "Select a payment method"
    %>
  </div>
 
  <%= f.submit "Place Order" %>
<% end %>  
edit app/models/order.rb
class Order < ActiveRecord::Base
  PAYMENT_TYPES = [ "Check", "Credit card", "Purchase order" ]
 
  # ...
edit public/stylesheets/depot.css
/* Styles for order form */
 
.depot_form fieldset {
  background: #efe;
}
 
.depot_form legend {
  color: #dfd;
  background: #141;
  font-family: sans-serif;
  padding: 0.2em 1em;
}
 
.depot_form label {
  width: 5em;
  float: left;
  text-align: right;
  padding-top: 0.2em;
  margin-right: 0.1em;
  display: block;
}
 
.depot_form select, .depot_form textarea, .depot_form input {
  margin-left: 0.5em;
}
 
.depot_form .submit {
  margin-left: 4em;
}
 
.depot_form div {
  margin: 0.5em 0;
}
edit app/models/order.rb
class Order < ActiveRecord::Base
  PAYMENT_TYPES = [ "Check", "Credit card", "Purchase order" ]
 
  validates :name, :address, :email, :pay_type, :presence => true
  validates :pay_type, :inclusion => PAYMENT_TYPES
 
  # ...
edit app/controllers/orders_controller.rb
  def create
    @order = Order.new(params[:order])
    @order.add_line_items_from_cart(find_or_create_cart)
 
    respond_to do |format|
      if @order.save
        Cart.destroy(session[:cart_id])
        session[:cart_id] = nil
        format.html { redirect_to(store_url, :notice => 
          'Thank you for your order.') }
        format.xml  { render :xml => @order, :status => :created,
          :location => @order }
      else
        format.html { render :action => "new" }
        format.xml  { render :xml => @order.errors,
          :status => :unprocessable_entity }
      end
    end
  end
edit app/models/order.rb
class Order < ActiveRecord::Base
  PAYMENT_TYPES = [ "Check", "Credit card", "Purchase order" ]
 
  # ...
  validates :name, :address, :email, :pay_type, :presence => true
  validates :pay_type, :inclusion => PAYMENT_TYPES
 
  # ...
 
 
  has_many :line_items, :dependent => :destroy
 
  def add_line_items_from_cart(cart)
    cart.line_items.each do |item|
      item.cart_id = nil
      line_items << item
    end
  end
end
get /orders/new
Please Enter Your Details
post /orders
Please Enter Your Details

5 errors prohibited this order from being saved:

  • Name can't be blank
  • Address can't be blank
  • Email can't be blank
  • Pay type can't be blank
  • Pay type is not included in the list
sqlite3> select * from orders
sqlite3> select * from line_items
        id = 10
product_id = 3
   cart_id = 3
created_at = 2010-06-06 14:36:49.301327
updated_at = 2010-06-06 14:36:49.301327
  quantity = 1
  order_id = 
get /orders/new
Please Enter Your Details
post /orders
You are being redirected.
get http://localhost:3000/
Home
Questions
News
Contact

Thank you for your order.

Your Pragmatic Catalog

Debug

Debug It!

Professional programmers develop a knack of unerringly zeroing in on the root cause of a bug. They can do that because they've written a lot of buggy code and then gained experience fixing it. This book captures all this experience -- use it, and you'll find you write fewer bugs, and the ones you do write will become easier to hunt down.

$34.95
Ruby

Programming Ruby 1.9

Ruby is the fastest growing and most exciting dynamic language out there. If you need to get working programs delivered fast, you should add Ruby to your toolbox.

$49.50
Wd4d

Web Design for Developers

Web Design for Developers will show you how to make your web-based application look professionally designed. We'll help you learn how to pick the right colors and fonts, avoid costly interface and accessibility mistakes -- your application will really come alive. We'll also walk you through some common Photoshop and CSS techniques and work through a web site redesign, taking a new design from concept all the way to implementation.

$42.95
sqlite3> select * from orders
        id = 1
      name = Dave Thomas
   address = 123 Main St
     email = customer@example.com
  pay_type = Check
created_at = 2010-06-06 14:37:41.822634
updated_at = 2010-06-06 14:37:41.822634
sqlite3> select * from line_items
        id = 10
product_id = 3
   cart_id = 
created_at = 2010-06-06 14:36:49.301327
updated_at = 2010-06-06 14:37:41.838448
  quantity = 1
  order_id = 1
edit app/views/line_items/create.js.rjs
page.select("div#notice").each { |div| div.hide }
 
page.replace_html('cart', render(@cart))
page[:cart].visual_effect :blind_down if @cart.total_items == 1
page[:current_item].visual_effect :highlight,
                                  :startcolor => "#88ff88",
                                  :endcolor => "#114411"

12.2 Iteration G2: Atom Feeds 11.5 Iteration F5: Testing AJAX changes