当前位置: 代码迷 >> Web前端 >> Agile Web Development with Rails 4E 学习札记2
  详细解决方案

Agile Web Development with Rails 4E 学习札记2

热度:833   发布时间:2012-10-07 17:28:51.0
Agile Web Development with Rails 4E 学习笔记2

?

?

Agile Web Development with Rails 4E 学习笔记2

=========zqhxuyuan@gmail.com 2011.10=========

声明:本文根据英文电子版Rails3 4E 2011-03-29 重要的地方有中文注释.

采用Notepad++编辑(语言:YAML),发布在ItEye上可能看起来不是很美观.

PS:只是RoR新手(而且尼玛还在win7上编程,羡慕Mac..),如有错误,欢迎指正.?

===========================================

Chapter 10. Task E. A Smarter Cart

5.1 相同购物项显示Product的数量:

> rails generate migration add_quantity_to_line_items quantity:integer

-> db/migrate/add_quantity_to_line_items.rb

class AddQuantityToLineItems < ActiveRecord::Migration

def self.up

add_column :line_items, :quantity, :integer, :default => 1

end

end

> rake db:migrate


create_table "line_items"

t.integer ?"product_id"

t.integer ?"cart_id"

t.integer ?"quantity", ? :default => 1

end

? 给购物项LineItems添加字段quantity,检查购物车中的购物项是否存在将要添加的Product

-> app/models/cart.rb

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

-> app/controllers/line_items_controller.rb

def create

@cart = current_cart

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

#@line_item = @cart.line_items.build(:product => product)

@line_item = @cart.add_product(product.id)

end

-> app/views/carts/show.html.erb

<%= item.quantity %> &times; <%= item.product.title %>

重新访问http://localhost:3000 添加Product到购物车中.如果添加的是相同的Product,

则购物车中显示的是数量,而不是一个一个Product列举出来.比如:http://localhost:3000/carts/5

Your Pragmatic Cart

2 × Programming Ruby 1.9

4 × Debug It!

5.2 数据库提交和回滚操作:

当访问http://localhost:3000/carts/2时 因为cart_id=2的那些操作在5.1之前,所以显示是还是一个一个列举Product.

为了使所有的购物车都运用到5.1中的操作.即看到的都是:如果有相同Product,则显示相同的数量.

我们要做的是migrate the data:

> rails generate migration combine_items_in_cart

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

> rake db:migrate

访问http://localhost:3000/carts/2 这时cart_id=2的购物车中显示的不再是一个一个相同的Product,也是有数量的.

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

> rake db:rollback

在此访问http://localhost:3000/carts/2 则又回到5.1之前的显示.

5.3 错误处理及flash内容显示:

当访问如http://localhost:3000/carts/wibble会报错ActiveRecord::RecordNotFound in CartsController#show

Rails处理错误一种方便的方式是使用flash这种类似hash的结构.(下面的:notice => 'Invalid cart'即hash结构)

A hash is a bucket in which you can store stuff as you process a request.?

The contents of the hash are available to the next request in this session before being deleted automatically.

flash中的内容在同一个Session中被删除之前 可以被下一个URL请求所获取.flash常用于错误信息收集.

比如访问/carts/wibble,show方法检测到传递一个无效的cartid,flash可以存储错误消息,

在重定向到index页面时(Controller的Action:index,对应view的index.html.erb),

可以提起出错误消息显示出来.这里next request即show的下一个请求index可以获取到放置到show里的flash的内容.

The flash information is accessible within the views by using the flash accessor method.

-> app/controllers/carts_controller.rb

def show

begin

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

rescue ActiveRecord::RecordNotFound

logger.error "Attempt to access invalid cart #{params[:id]}"

redirect_to store_url, :notice => 'Invalid cart'

else

#...

end

end

-> views/store/index.html.erb

<% if notice %>

<p id="notice"><%= notice %></p>

<% end %>

CartsController#show 中放置在flash的错误消息:notice => 'Invalid cart'

重定向到store_url即StoreController#index,在views/store/index.html.erb中可以取出来:<%= notice %>

为什么不使用Controller中存在的实例变量来存储错误消息.

因为在我们的应用中(show方法里),使用的是浏览器重定向.重定向:浏览器发送一个新的请求给我们的应用即

StoreController#index.当我们接收另外的这个请求时,之前的请求中存在的实例变量(CartController#show)都会消失.

而flash中的数据是存储在Session中的.在多次不同的请求中数据是都可以获取到的.

5.4 清空购物车:

deleting their own (current) cart,and to remove the cart from the session.

-> app/views/carts/show.html.erb

<%= button_to 'Empty cart', @cart, :method => :delete, :confirm => 'Are you sure?' %>

-> 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 => 'Your cart is currently empty') }

format.xml { head :ok }

end

end

测试时,由于测试环境是木有Session的,所以初始时我们把当前的购物车

(cart_id:@cart.id)放到Session中来模拟Controller中的current_cart

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

remove the flash message that is automatically generated when a line item is added.

-> app/controllers/line_items_controller.rb

format.html { redirect_to(@line_item.cart) }

5.5 显示价格信息:

add a method to both the LineItem and Cart models that returns?

the total price for the individual line item and entire cart

-> app/views/carts/show.html.erb

<% @cart.line_items.each do |item| %>

<%= number_to_currency(item.total_price) %>

<% end %>

<%= number_to_currency(@cart.total_price) %>

-> app/models/line_item.rb

class LineItem < ActiveRecord::Base

belongs_to :product

belongs_to :cart

def total_price

product.price * quantity

end

end

-> app/models/cart.rb

class Cart < ActiveRecord::Base

has_many :line_items, :dependent => :destroy

def total_price

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

end

end

关于Model中方法访问变量:LineItem belongs_to的内容相当于定义了一个变量.

不同的是belongs_to定义的内容是Symbol类型,而在方法中使用的应该是去掉Symbol之前的:的变量.

比如belongs_to :product,则total_price方法中可以访问product.

*quantity.因为在5.1中add_quantity_to_line_items给line_items添加了一个quantity字段.

相当于给Model:LineItem添加了一个属性.所以在Model对象里是可以访问自己的属性的.

也就是说Model方法里可以访问的属性包括:

数据库定义的字段=>对应Model添加的属性.

Model中定义的依赖关系: belongs_to ?has_many 定义的Symbol对应的变量(去掉Symbol前的冒号)


  相关解决方案