`
jarry-li
  • 浏览: 43524 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

Ruby On Rails的第一个应用(七)--更智能的购物车

阅读更多

V 任务E:更智能的购物车

 

·个性数据库模式(schema)与现有数据

·诊断和处理错误

·闪存

·日志

 

一、迭代E1:创建更智能的购物车

 

1.由于购物车中每个产品都有一个关联的计数器,这就要求个性line_items表。

Administrator@JARRY /e/works/ruby/depot (master)
$ rails generate migration add_quantity_to_line_items quantity:integer
      invoke  active_record
      create    db/migrate/20130326064705_add_quantity_to_line_items.rb

  

rails有两种匹配模式:add_XXX_to_TABLE和remove_XXX_to_TABLE,这里XXX被忽略,的是出现在迁移名之后的字段及其类型的清单。

rails无法判断字段的默认值,一般默认值为null,但是我们把已有的购物车的默认值设置为1。在应用迁移前,要先修改/db/migrate/20130326064705_add_quantity_to_line_items.rb

class AddQuantityToLineItems < ActiveRecord::Migration
  def self.up
    add_column :line_items, :quantity, :integer, default: 1
  end
  def self.down
    remove_column :line_items, :quantity
  end
end
 

  

2.执行迁移

Administrator@JARRY /e/works/ruby/depot (master)
$ rake db:migrate
==  AddQuantityToLineItems: migrating =========================================
-- add_column(:line_items, :quantity, :integer, {:default=>1})
   -> 0.0469s
==  AddQuantityToLineItems: migrated (0.0625s) ================================
 

 

3.现在Cart中需要一个聪明的add_product方法,该方法用来判断商品清单中是否已包含了想要添加的产品:如果是的话,那就增加数量;如果不是的话,就生成一个新的LineItem。修改/app/models/cart.rb

class Cart < ActiveRecord::Base
  has_many :line_items, dependent: :destroy
  
  def add_product(product_id)
    current_item = line_items.find_by_product_id(product_id)
    if current_item
      current_item.quantity += 1
    else
      current_item = line_items.build(product_id: product_id)
    end
    current_item
  end 
end
 

 

这里调用了find_by_product_id方法,但是没有定义过。Active Record模块注意到调用未定义的方法,并且发现在其名称是以字符串find_by开始和字段名结束,于是ActiveRecord模块动态地构造了查询器方法,并将其添加到类中。

 

4.为了使用add_product方法,还要修改商品项目控制器的create方法

/app/controllers/line_items_controller.rb

  def create
    @cart = current_cart
    product = Product.find(params[:product_id])
    @line_item = @cart.line_items.add_product(product.id)
 
    respond_to do |format|
      if @line_item.save
        format.html { redirect_to @line_item.cart, notice: 'Line item was successfully created.' }
        format.json { render json: @line_item, status: :created, location: @line_item }
      else
        format.html { render action: "new" }
        format.json { render json: @line_item.errors, status: :unprocessable_entity }
      end
    end
  end
 

 

5.为了使用新信息,最后还需要修改show视图

/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>
 

 

6.再次点击add to cart 添加已买过的商品,如图:

 

7.

Administrator@JARRY /e/works/ruby/depot (master)
$ rails generate migration combine_items_in_cart
 
      invoke  active_record
      create    db/migrate/20130326072305_combine_items_in_cart.rb
 

 

8.现在rails完全推断不出想做什么了,所以,这次完全由我们来填写self.up方法/db/migrate/20130326072305_combine_items_in_cart.rb:

  def self.up
    # replace multiple items for a single product in a cart with a single item
    Cart.all.each do |cart|
      # count the number of each product in the cart
      sums = cart.line_items.group(:product_id).sum(:quantity)
      
      sums.each do |product_id, quantity|
        if quantity > 1
          # remove individual items
          cart.line_items.where(product_id: product_id).delete_all
          
          # replace with a single item
          cart.line_items.create(product_id: product_id, quantity: quantity)
        end
      end
    end
  end

 

先从迭代每个购物车开始;对于每个购物车及其每个相关联的商品项目,按照字段product_id进行编组,得出各字段数量之和,计算结果将是字段product_ids和数量对的有序列表;然后迭代每一组之和,从每一个组中提取product和quantity;对于数量大于1的组,将删除与该购物车和该产品相关联的所有单个的商品项目,然后用正确数量的单行商品来替代它们。

 

9.应用迁移

Administrator@JARRY /e/works/ruby/depot (master)
$ rake db:migrate
==  CombineItemsInCart: migrating =============================================
==  CombineItemsInCart: migrated (0.4531s) ====================================
 

 

10.查看购物车查看结果

 

11.迁移的一个重要原则是每一步都是可逆的,所以,还要实现了一个self.down方法。这种方法用于查找数量大于1的商品项目:为该购物车和产品添加一个新的商品项目,一个数量增加一行,最后删除该商品项目多余的行。该操作的代码如下/db/migrate/20130326072305_combine_items_in_cart.rb:

  def self.down
    # split items with quantity>1 into multiple items
    LineItem.where("quantity>1").each do |line_item|
      # add individual items
      line_item.quantity.times do
        LineItem.create cart_id:line_item.cart_id, product_id: line_item.product_id, quantity: 1
      end
      
      # remove original item
      line_item.destroy
    end
  end
 

 

11.回滚迁移,并查看购物车来验证结果。

Administrator@JARRY /e/works/ruby/depot (master)
$ rake db:rollback
==  CombineItemsInCart: reverting =============================================
==  CombineItemsInCart: reverted (0.2812s) ====================================

 

12.重新应用迁移使用命令rake db:migrate

 

二、迭代E2:错误处理

有种攻击:通过传递带错误参数的请求到web应用程序。购物车的链接看起来像carts/nnn,其中nnn是内部的购物车id。感觉这个不是很好,直接在浏览器上输入这个请求,并传个字符串wibble。应用程序将出现如下错误信息:

这里暴露了太多的应用程序的信息,看上去很不专业,因此我们要使应用程序有更强的韧性。

1.上图中可以看到:

app/controllers/carts_controller.rb:16:in `show'

这里抛出了异常,即这行:

    @cart = Cart.find(params[:id])

如果无法找到购物车,ActiveRecord模块会抛出一个RecordNotFound的异常,显然我们需要处理这个异常。

Rails提供了方便的处理错误和报告错误的方法。它定义了称为闪存(flash)的结构。闪存是一个桶(bucket,实际上更像个散列),当处理请求时,可以在其中存储东西。对于同一会话的下次请求,在自动地删除闪存内容之前,闪存中的内容都是有效的。

通常情况下闪存是用来收集错误信息的。在视图中可以用flash存取器方法(accessor method)来访问闪存的信息。

闪存数据存储在会话中,以使其能在请求与请求的中间被访问。

现在修改show方法来拦截无效的产品id并报告问题:

/app/controllers/carts_controller.rb

 

  def show
    begin
      @cart = Cart.find(params[:id])
    rescue ActiveRecord::RecordNotFound
      logger.error "Attempt to acces invalid cart #{params[:id]}"
      redirect_to store_url, notice: 'Invalid cart'
    else
      respond_to do |format|
        format.html # show.html.erb
        format.json { render json: @cart }
      end
    end
  end
 

  

2.刷新http://localhost:3000/carts/wibble,没有出现错误信息了,显示了目录网页。如图:

 

另外从/log/development.log可以找到Attempt to acces invalid cart wibble日志信息。

 

三、迭代E3:对购物车的最后加工

现在还有个问题,没有办法清空购物车。

清空购物车要在购物车中添加个链接和修改购物车控制器中的destroy方法来清理会话。

1.先从模板开始,并再次用button_to方法给页面添加个按钮:

/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?' %>

 

2.在控制器中修改destory方法,以确保用户只是删除他自己的购物车,并在重定向到索引页面之前(带有通知消息),从会话中删除该购物车:

/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: 'Yout cart is currently empty!' }
      format.json { head :ok }
    end
  end
  

 

3.然后更新对应的测试/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
 
    assert_redirected_to store_path
  end

 

4.点击页面Empty Cart按钮,查看效果:

 

5.添加新的商品项目时,也可以删除那个自动生成的闪存消息:

/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 } # here! remove the notice.
        format.json { render json: @line_item, status: :created, location: @line_item }
      else
        format.html { render action: "new" }
        format.json { render json: @line_item.errors, status: :unprocessable_entity }
      end
    end
  end
 

 

6.用表格来整理下购物车页面,并用css制作样式:

/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, unit: "¥") %></td>
</tr>
<% end %>
<tr class="total_line">
<td colspan="2">Total</td>
<td class="total_cell"><%= number_to_currency(@cart.total_price, unit: "¥")%></td>
</tr>
</table>
 
<%= button_to 'Empty Cart', @cart, method: :delete, confirm: 'Are you sure?' %>

 

7.要让这个代码运行起来,要分别在LineItem和Cart模型中添加方法计算总价。

/app/models/line_item.rb:

  def total_price
    product.price * quantity
  end 

 

/app/models/cart.rb:

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

   

然后再在修改/app/assets/stylesheets/store.css.scss,在.store{}里面添加:

/* Styles for the cart in the main page */
 
.cart_title{
  font: 120%;
  font-weight: bold;
}
 
.item_price, .total_line{
  text-align: right;
  padding: 0 0 0 1em;
}
 
.total_line .total_cell{
  font-weight: bold;
  border-top: 1px solid #595;
}
总价计算显示效果:



 
分享到:
评论

相关推荐

    SOA系列:开源框架Ruby on Rails

    - **2004年**:David Heinemeier Hansson 发布了 Ruby on Rails 的第一个版本。这一事件标志着 Rails 的正式诞生,同时也推动了 Ruby 语言的发展。 - **快速发展期**:自从 Rails 发布之后,Ruby 社区经历了快速...

    rails敏捷开发的购物车系统

    在本文中,我们将深入探讨如何使用Rails敏捷开发技术构建一个购物车系统,特别是在参考《rails敏捷开发第四版》中的示例。Rails 3.2.6是本文的基础框架,它是一个强大的Ruby Web应用程序框架,以其MVC(模型-视图-...

    Secrets of ruby on rails

    《Ruby on Rails的秘密》是一份关于Ruby on Rails框架的高级介绍资料,由该框架的创始人David Heinemeier Hansson编写。这份资料深入探讨了Rails的核心理念和技术细节,旨在为开发者提供一个全面而深入的理解。自...

    无线点餐系统的服务端,使用ruby on rails框架

    Ruby on Rails(简称Rails)是一款基于Ruby语言的开源Web应用程序框架,遵循MVC(Model-View-Controller)架构模式,为开发人员提供了快速开发、简洁代码以及强大的数据库支持。 首先,让我们深入了解Ruby on Rails...

    web开发之rails最新调试通过购物车代码

    在Web开发领域,Ruby on Rails(简称Rails)是一种流行的开源框架,它基于MVC(Model-View-Controller)架构模式,用于快速构建高效、可维护的Web应用。本压缩包中的"web开发之rails最新调试通过购物车代码"是关于...

    Ruby-Rails实战之B2C商城开发

    在本项目"Ruby-Rails实战之B2C商城开发"中,我们将深入探索使用Ruby on Rails这一强大的Web开发框架来构建一个完整的B2C(Business-to-Consumer)在线商城。Rails是Ruby语言的一个核心框架,以其MVC(Model-View-...

    Bootstrap-ShoppingCart:使用Ruby on Rails中的引导程序开发的购物车应用程序

    Bootstrap-ShoppingCart是一个基于Ruby on Rails框架开发的购物车应用程序,它充分利用了Twitter Bootstrap的前端设计工具,为用户提供了一个简洁、响应式的界面。这个项目旨在帮助开发者了解如何将Rails的强大功能...

    e-commerce-on-rails:一个非常简单的 Ruby on Rails 电子商务,带有活动管理员

    Ruby on Rails(RoR)是一个基于Ruby语言的开源Web应用程序框架,它遵循模型-视图-控制器(MVC)架构模式,旨在提高开发效率和可读性。"e-commerce-on-rails"项目就是一个使用Rails搭建的简单电子商务平台,特别加入...

    《web开发敏捷之道 应用rails进行敏捷web开发》(第一版)的depot源代码

    该书的第一版提供了名为"depot"的源代码示例,旨在帮助读者更好地理解Rails的工作原理以及如何在实际项目中应用敏捷开发方法。"depot"是一个典型的电子商务应用程序,包含了商品管理、购物车和订单处理等功能,是...

    shop:使用Ruby On Rails购买回购

    在本项目中,我们主要探讨的是如何利用Ruby on Rails框架构建一个购物平台,重点在于实现产品的回购功能。Ruby on Rails(简称Rails)是一款基于Ruby语言的开源Web开发框架,它遵循MVC(Model-View-Controller)架构...

    rails 项目起步示例

    Rails是Ruby语言的一个著名Web开发框架,全称为Ruby on Rails,它遵循MVC(Model-View-Controller)架构模式,旨在提高开发效率和代码可读性。本示例"rails项目起步示例"是一个购物系统,非常适合初学者入门学习。 ...

    Rails 3 in Action

    第十七章讲解了 **Rails 引擎**,这是一种可以被其他 Rails 应用作为插件使用的独立 Rails 应用。 - **Rails 引擎**: - 类似于插件,但更加强大和灵活。 - 可以提供额外的功能,如电子商务模块、论坛系统等。 #...

    Agile Web Development with Rails-Second Edition-Beta一书例子

    《Agile Web Development with Rails-Second Edition-Beta》是一本专注于使用Ruby on Rails进行敏捷Web开发的书籍。这本书的第二版beta版提供了关于如何利用Rails框架高效构建动态、响应式网站的深入指导。作者们...

    Web 开发敏捷之道(应用Rails 进行敏捷Web 开发第三版)

    **Rails**,全称为 **Ruby on Rails**,是一种用于构建 Web 应用的开源框架,由 David Heinemeier Hansson 在 2004 年首次发布。Rails 自诞生以来,便以其独特的设计理念迅速吸引了全球开发者的关注。随着 Web 2.0 ...

    rails敏捷开发,我的成功之路

    ### 知识点四:第一个Rails应用 **标题与描述**:本书通过一个简单的“Hello, Rails”示例来引导读者快速上手,掌握创建新应用的基本流程。 **详细说明**: - **创建新应用**:使用`rails new`命令初始化一个新的...

Global site tag (gtag.js) - Google Analytics