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 %> × <%= 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 %> × <%= 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 %> ×</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; }
相关推荐
- **2004年**:David Heinemeier Hansson 发布了 Ruby on Rails 的第一个版本。这一事件标志着 Rails 的正式诞生,同时也推动了 Ruby 语言的发展。 - **快速发展期**:自从 Rails 发布之后,Ruby 社区经历了快速...
在本文中,我们将深入探讨如何使用Rails敏捷开发技术构建一个购物车系统,特别是在参考《rails敏捷开发第四版》中的示例。Rails 3.2.6是本文的基础框架,它是一个强大的Ruby Web应用程序框架,以其MVC(模型-视图-...
《Ruby on Rails的秘密》是一份关于Ruby on Rails框架的高级介绍资料,由该框架的创始人David Heinemeier Hansson编写。这份资料深入探讨了Rails的核心理念和技术细节,旨在为开发者提供一个全面而深入的理解。自...
Ruby on Rails(简称Rails)是一款基于Ruby语言的开源Web应用程序框架,遵循MVC(Model-View-Controller)架构模式,为开发人员提供了快速开发、简洁代码以及强大的数据库支持。 首先,让我们深入了解Ruby on Rails...
在Web开发领域,Ruby on Rails(简称Rails)是一种流行的开源框架,它基于MVC(Model-View-Controller)架构模式,用于快速构建高效、可维护的Web应用。本压缩包中的"web开发之rails最新调试通过购物车代码"是关于...
在本项目"Ruby-Rails实战之B2C商城开发"中,我们将深入探索使用Ruby on Rails这一强大的Web开发框架来构建一个完整的B2C(Business-to-Consumer)在线商城。Rails是Ruby语言的一个核心框架,以其MVC(Model-View-...
Bootstrap-ShoppingCart是一个基于Ruby on Rails框架开发的购物车应用程序,它充分利用了Twitter Bootstrap的前端设计工具,为用户提供了一个简洁、响应式的界面。这个项目旨在帮助开发者了解如何将Rails的强大功能...
Ruby on Rails(RoR)是一个基于Ruby语言的开源Web应用程序框架,它遵循模型-视图-控制器(MVC)架构模式,旨在提高开发效率和可读性。"e-commerce-on-rails"项目就是一个使用Rails搭建的简单电子商务平台,特别加入...
该书的第一版提供了名为"depot"的源代码示例,旨在帮助读者更好地理解Rails的工作原理以及如何在实际项目中应用敏捷开发方法。"depot"是一个典型的电子商务应用程序,包含了商品管理、购物车和订单处理等功能,是...
在本项目中,我们主要探讨的是如何利用Ruby on Rails框架构建一个购物平台,重点在于实现产品的回购功能。Ruby on Rails(简称Rails)是一款基于Ruby语言的开源Web开发框架,它遵循MVC(Model-View-Controller)架构...
Rails是Ruby语言的一个著名Web开发框架,全称为Ruby on Rails,它遵循MVC(Model-View-Controller)架构模式,旨在提高开发效率和代码可读性。本示例"rails项目起步示例"是一个购物系统,非常适合初学者入门学习。 ...
第十七章讲解了 **Rails 引擎**,这是一种可以被其他 Rails 应用作为插件使用的独立 Rails 应用。 - **Rails 引擎**: - 类似于插件,但更加强大和灵活。 - 可以提供额外的功能,如电子商务模块、论坛系统等。 #...
《Agile Web Development with Rails-Second Edition-Beta》是一本专注于使用Ruby on Rails进行敏捷Web开发的书籍。这本书的第二版beta版提供了关于如何利用Rails框架高效构建动态、响应式网站的深入指导。作者们...