EasyTime - Ruby学习笔记 | Agile Web Development with Rails 翻译(十四)
来源:百度文库 编辑:神马文学网 时间:2024/04/29 10:57:25
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‘ %>
这个连接点由store内“控制器”的add_to_cart()“动作”支持,并会传递产品的id做为表单的参数。[说:id=>product,是:id=>product.id的习惯缩写。两者将产品的id传回给“控制器”。]从这里我们看到了我们“模型”内的id字段是如何的重要了。Rails通过它们的id字段标识“模型”对象(及相应的数据库的行)。如果我们传递一个id给add_to_cart(),我们会添加具有唯一标识的产品。
现在,让我们实现add_to_cart()方法。它需要为当前“会话”(如果没有则创建一个)找到购物车,并添加选择的产品到这个购物车中,然后显示此购物车中的内容。由于细节并不很麻烦,让我们只写出抽象级代码。我们将在app/controllers/store_controller.rb文件内创建一个add_to_cart()方法。它使用参数对象来从请求中获得id参数,然后找出相应产品,并使用我们先前创建的find_cart()方法来找到此“会话”内的购物车,并添加产品给此购物车。当给“控制器”添加add_to_cart()方法时要小心。因为它被作为一个“动作”来调用,它必须是public的,所以必须被添加到private的find_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文件。
我们已准备好了所有东西,现在让我们在浏览器内看看我们的商店。导航到http://localhost:300/stroe调用我们的分类目录页。单击每个产品的Add to Cart连接。[如果你没有看到产品列表,你将需要退回到应用程序的管理一节。]我们期望看到购物车显示页面,但我们看到的却是惨不忍睹的页面。
首先,我们可能会想到我们拼错了“动作”方法的名字或者是“视图”的名字,但事实不是这样。这不是Rails的错误消息—它来自于WEBrick。想找出原因,我们需要看看WEBrick的控制台输出。进入WEBrick的运行窗口,你会看到登录和跟踪消息。跟踪指出了应用程序内错误的原因。(技术上说,这是个stack backtrace,它显示了应用程序阻塞点调用的方法链。)可以很容易地通过回卷来找出错误。在开始前,你将看到一个错误信息。
#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有关我们Cart和LineItem类。(83页的注释解释了为什么。)在app/controllers目录内你会找到个名为application.rb的文件。这个文件用于构建应用程序入口的上下文环境。缺省情况下,它包含一个空类ApplicationController的定义。我们需要在其中添加两行来声明我们的新“模型”文件。
class ApplicationController < ActionController::Base
model :cart
model :line_item
end
现在,如果我们刷新我们的浏览器,我们应该看到“视图”显示了。(如图8.2)如果我们使用Back按钮返回分类目录显示,并添加另一个产品给购物车,你将会看到当购物车页被显示时,计数被更新了。看起来们的“会话”工作了。
现在最困难的事情我们已做完了。这确实是我们能够为我们的客户展示什么之前所花费的最久时间。 但是现在我们应该将每个东西正确地连在一起,让我们快速地实现一个简单的购物车显示,以便我们尽快得到客户的反馈。我们用下面的代码来替换原有购物车内的display_cart.rhtml文件内代码。
这个“模板”显示了很多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页的第十二章。
_xyz
2006年4月17日更新
8.3 循环 C1: 创建个购物车
读者可能注意到了我们的分类目录列表“视图”已经包含了一个Add to Cart连接给每个产品列表。
<%= link_to ‘Add to Cart‘,
{:action => ‘add_to_cart‘, :id => product },
:class => ‘addtocart‘ %>
这个连接点由store内“控制器”的add_to_cart()“动作”支持,并会传递产品的id做为表单的参数。[说:id=>product,是:id=>product.id的习惯缩写。两者将产品的id传回给“控制器”。]从这里我们看到了我们“模型”内的id字段是如何的重要了。Rails通过它们的id字段标识“模型”对象(及相应的数据库的行)。如果我们传递一个id给add_to_cart(),我们会添加具有唯一标识的产品。
现在,让我们实现add_to_cart()方法。它需要为当前“会话”(如果没有则创建一个)找到购物车,并添加选择的产品到这个购物车中,然后显示此购物车中的内容。由于细节并不很麻烦,让我们只写出抽象级代码。我们将在app/controllers/store_controller.rb文件内创建一个add_to_cart()方法。它使用参数对象来从请求中获得id参数,然后找出相应产品,并使用我们先前创建的find_cart()方法来找到此“会话”内的购物车,并添加产品给此购物车。当给“控制器”添加add_to_cart()方法时要小心。因为它被作为一个“动作”来调用,它必须是public的,所以必须被添加到private的find_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文件。
Display Cart
Your cart contains <%= @items.size %> items.
我们已准备好了所有东西,现在让我们在浏览器内看看我们的商店。导航到http://localhost:300/stroe调用我们的分类目录页。单击每个产品的Add to Cart连接。[如果你没有看到产品列表,你将需要退回到应用程序的管理一节。]我们期望看到购物车显示页面,但我们看到的却是惨不忍睹的页面。
首先,我们可能会想到我们拼错了“动作”方法的名字或者是“视图”的名字,但事实不是这样。这不是Rails的错误消息—它来自于WEBrick。想找出原因,我们需要看看WEBrick的控制台输出。进入WEBrick的运行窗口,你会看到登录和跟踪消息。跟踪指出了应用程序内错误的原因。(技术上说,这是个stack backtrace,它显示了应用程序阻塞点调用的方法链。)可以很容易地通过回卷来找出错误。在开始前,你将看到一个错误信息。
#
to require classes for all objects kept in the session. The
session has been deleted.>
当Rails试图加载来自于浏览器cookie的“会话”信息时,它会遍历一些它还不知道的类。我们必须告诉Rails有关我们Cart和LineItem类。(83页的注释解释了为什么。)在app/controllers目录内你会找到个名为application.rb的文件。这个文件用于构建应用程序入口的上下文环境。缺省情况下,它包含一个空类ApplicationController的定义。我们需要在其中添加两行来声明我们的新“模型”文件。
class ApplicationController < ActionController::Base
model :cart
model :line_item
end
现在,如果我们刷新我们的浏览器,我们应该看到“视图”显示了。(如图8.2)如果我们使用Back按钮返回分类目录显示,并添加另一个产品给购物车,你将会看到当购物车页被显示时,计数被更新了。看起来们的“会话”工作了。
现在最困难的事情我们已做完了。这确实是我们能够为我们的客户展示什么之前所花费的最久时间。 但是现在我们应该将每个东西正确地连在一起,让我们快速地实现一个简单的购物车显示,以便我们尽快得到客户的反馈。我们用下面的代码来替换原有购物车内的display_cart.rhtml文件内代码。
Display Cart
<%= item.quantity %> | <%= h(product.title) %> | <%= item.unit_price %> | <%= item.unit_price * item.quantity %> |
这个“模板”显示了很多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页的第十二章。
_xyz
EasyTime - Ruby学习笔记 | Agile Web Development with Rails 翻译(十四)
EasyTime - Ruby学习笔记 | Agile Web Development with Rails 翻译(一)
EasyTime - Ruby学习笔记1
EasyTime - Ruby学习笔记 | 第二章 简单数据(九) 日期计算
ONLamp.com -- Rolling with Ruby on Rails
Ruby on Rails 学习资料
Ruby on Rails 学习资料
Integrate FCKEditor with your Ruby on Rails application
Fork with spawn - RailsOnWave Ruby on Rails w...
Integrate FCKEditor with your Ruby on Rails application
一个.NET程序员为什么学习Ruby on Rails?(转 InfoQ)
Ruby off the Rails
什么是Ruby on Rails
Ruby on Rails
什么是Ruby on Rails
RUBY ON RAILS:WEB2.0世界新生的创造力 - The Way We Web
What Every Webmaster and Web Developer MUST Know About Ruby on Rails and AJAX
ruby笔记
Ruby on Rails Cheat Sheet - Ruby On Rails - ILoveJackDaniels.com
[Ruby on Rails]Vim 开发 ruby on rails 完全配置
Web Service学习笔记之-
What Is Ruby on Rails
Simplify Ajax development with jQuery
Managing .NET Development with NAnt