打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
EasyTime - Ruby学习笔记 | Agile Web Development with Rails 翻译(十四)

Agile Web Development with Rails 翻译(十四)

2006年4月17日更新

8.3 循环 C1: 创建个购物车

读者可能注意到了我们的分类目录列表“视图”已经包含了一个Add to Cart连接给每个产品列表。


<%= link_to ‘Add to Cart‘,

{:action => ‘add_to_cart‘, :id => product },

:class => ‘addtocart‘ %><br/>

这个连接点由store内“控制器”的add_to_cart()“动作”支持,并会传递产品的id做为表单的参数。[:id=>product,是:id=>product.id的习惯缩写。两者将产品的id传回给“控制器”。]从这里我们看到了我们“模型”内的id字段是如何的重要了。Rails通过它们的id字段标识“模型”对象(及相应的数据库的行)。如果我们传递一个idadd_to_cart(),我们会添加具有唯一标识的产品。

现在,让我们实现add_to_cart()方法。它需要为当前“会话”(如果没有则创建一个)找到购物车,并添加选择的产品到这个购物车中,然后显示此购物车中的内容。由于细节并不很麻烦,让我们只写出抽象级代码。我们将在app/controllers/store_controller.rb文件内创建一个add_to_cart()方法。它使用参数对象来从请求中获得id参数,然后找出相应产品,并使用我们先前创建的find_cart()方法来找到此“会话”内的购物车,并添加产品给此购物车。当给“控制器”添加add_to_cart()方法时要小心。因为它被作为一个“动作”来调用,它必须是public的,所以必须被添加到privatefind_cart()方法的上面。

def add_to_cart

product = Product.find(params[:id])

@cart = find_cart

@cart.add_product(product)

redirect_to(:action => ‘display_cart‘)

end

很明显,这个代码也不会运行:我们没有创建Cart类,并且我们也没对display_cart()功能的任何实现。

让我们开始创建Cart类和它的add_product()方法。因为它存储应用程序数据,它是我们“模型”的逻辑部分,所以我们将创建文件cart.rb在目录app/models内。虽然,它与数据库表没有联系,因此它不是ActiveRecord::Base的子类。

class Cart

attr_reader :items

attr_reader :total_price

def initialize

@items = []

@total_price = 0.0

end

def add_product(product)

@items << LineItem.for_product(product)

@total_price += product.price

end

end

这很直截了当。我们基于产品创建了一个新的商品项并添加它到列表中。当然,我们也没有一个方法来创建一个基于某个产品信息的商品项,所以让我们现在调整一下。我们会打开app/models/line_item.rb文件,并添加一个类方法for_product()给它。创建这类级别的方法是为了让你的代码整洁易于阅读。

class LineItem < ActiveRecord::Base

belongs_to :product

def self.for_product(product)

item = self.new

item.quantity = 1

item.product = product

item.unit_price = product.price

item

end

end

现在我们创建了一个Cart类来保持我们的商品项,并且我们在“控制器”内实现了add_to_cart()方法。并依次调用新的find_cart()方法,这个方法确保我们保持“会话”内的购物车对象。

我们还需要实现display_cart()方法和相应的“视图”。同时,我们已经写了这么多代码却没有尝试,让我们加入一些模拟数据(stub)来看看怎么样。在store “控制器”中,我们将实现一个“动作”方法来处理引入的请求。

def display_cart

@cart = find_cart

@items = @cart.items

end

app/views/store目录内,我们会为相应的“视图”创建个display_cart.rhtml文件。

<h1>Display Cart</h1>

<p>

Your cart contains <%= @items.size %> items.

</p>

 

 

 

 

我们已准备好了所有东西,现在让我们在浏览器内看看我们的商店。导航到http://localhost:300/stroe调用我们的分类目录页。单击每个产品的Add to Cart连接。[如果你没有看到产品列表,你将需要退回到应用程序的管理一节。]我们期望看到购物车显示页面,但我们看到的却是惨不忍睹的页面。

首先,我们可能会想到我们拼错了“动作”方法的名字或者是“视图”的名字,但事实不是这样。这不是Rails的错误消息它来自于WEBrick。想找出原因,我们需要看看WEBrick的控制台输出。进入WEBrick的运行窗口,你会看到登录和跟踪消息。跟踪指出了应用程序内错误的原因。(技术上说,这是个stack backtrace,它显示了应用程序阻塞点调用的方法链。)可以很容易地通过回卷来找出错误。在开始前,你将看到一个错误信息。

#<ActionController::SessionRestoreError: Session contained

objects where the class definition wasn‘t available. Remember

to require classes for all objects kept in the session. The

session has been deleted.>

Rails试图加载来自于浏览器cookie的“会话”信息时,它会遍历一些它还不知道的类。我们必须告诉Rails有关我们CartLineItem类。(83页的注释解释了为什么。)app/controllers目录内你会找到个名为application.rb的文件。这个文件用于构建应用程序入口的上下文环境。缺省情况下,它包含一个空类ApplicationController的定义。我们需要在其中添加两行来声明我们的新“模型”文件。

class ApplicationController < ActionController::Base

model :cart

model :line_item

end

 

 

现在,如果我们刷新我们的浏览器,我们应该看到“视图”显示了。(如图8.2)如果我们使用Back按钮返回分类目录显示,并添加另一个产品给购物车,你将会看到当购物车页被显示时,计数被更新了。看起来们的“会话”工作了。

 

 

 

 

现在最困难的事情我们已做完了。这确实是我们能够为我们的客户展示什么之前所花费的最久时间。 但是现在我们应该将每个东西正确地连在一起,让我们快速地实现一个简单的购物车显示,以便我们尽快得到客户的反馈。我们用下面的代码来替换原有购物车内的display_cart.rhtml文件内代码。

<h1>Display Cart</h1>

<table>

<%

for item in @items

product = item.product

-%>

<tr>

<td><%= item.quantity %></td>

<td><%= h(product.title) %></td>

<td align="right"><%= item.unit_price %></td>

<td align="right"><%= item.unit_price * item.quantity %></td>

</tr>

<% end -%>

</table>

这个“模板”显示了很多ERb特性。如果我们用-%>(注意有个减号)来结束植入的Ruby语句,ERb将抑止随后的新行。这意味着被植入的Ruby不能产生任何输出。

 

-------------------------------------------------------------------------------

“会话”信息,序列化,和类

Session结构存储浏览器请求间你想保持的对象。要想工作,Rails必须能接受这些对象,并存储它们在一个请求的尾部,当同一浏览器有后续请求时将它们加载回来。要在运行的应用程序外部存储对象,Rails使用了Ruby的序列化机制,它转换对象为可被稍后能重新取回的数据。当我们在一个“会话”内存储一个购物车时,我们存储了类Cart的一个对象。但是当加载回数据时,Rails并不保证加载此点上的原有Cart “模型”(因为Rails只加载它认为它需要东西)。可以使用“模型”声明来强制Rails加载先前用户“模型”类,所以当Ruby加载序列化的“会话”时,它知道它在做什么。

-------------------------------------------------------------------------------

 

刷新浏览器,(假设我们从分类目录中选择了一个产品)我们会看到它显示。

1 Pragmatic Project Automation 29.95 29.95

单击Back按钮,添加另一个产品。

1 Pragmatic Project Automation 29.95 29.95

1 Pragmatic Version Control 29.95 29.95

看起来不错,返回并再选择原有产品一次。

1 Pragmatic Project Automation 29.95 29.95

1 Pragmatic Version Control 29.95 29.95

1 Pragmatic Project Automation 29.95 29.95

这看起来可不好,尽管购物车逻辑上是正确的,但它与我们想的不一样。相反,我们或许应该将两者自动地合并成一个数量为2的单独的行条目。

幸运地,这改起来很容易,通过给Cart “模型”添加add_product()方法。当添加一个新产品时,我们会看到产品已在那个购物车内了。如果是这样话,我们将只增加它的数量,而不添加个新的商品项。记住购物车不是个数据库对象它只是Ruby代码。

def add_product(product)

item = @items.find {|i| i.product_id == product.id}

if item

item.quantity += 1

else

item = LineItem.for_product(product)

@items << item

end

@total_price += product.price

end

我们现在面对的问题是,我们在购物车内已经有个一个带有重复产品的“会话”,这个“会话”与我们浏览器内存储的一个cookie相关联。它不会自动离去,除非我们删除那个cookie[如果你想的话,你可这样做。你可以删除cookie文件。] 幸运的是,当我们想测试我们的代码时有除了点击浏览器按钮以外的方法。Rails的方法是写测试。 但是这是个很大的题目,我们把它单独地放在了一章,132页的第十二章。

 

 

 

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
不定期整理推荐 InfoQ 上面的优秀文章
Ruby on Rails 学习资料
NetBeans IDE Ruby 快速入门教程
Rails 与 XML(一)
Ruby off the Rails
说说Rails吧,config的幕后工作 - 差沙的密码 -- SSHWSFC‘s cod...
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服