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

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

热度:1066   发布时间:2012-10-08 19:54:56.0
Agile Web Development with Rails 4E 学习笔记1

?

Agile Web Development with Rails 4E 2011.03 ??

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

?

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

Chapter6. Task A: Creating the Application

1.1 创建Rails项目depot:

> rails new depot?

1.2 切换到depot目录下:

> cd depot?

使用Rails3默认的数据库SQLite3

1.3 创建项目的脚手架:

> rails generate scaffold Product?

title:string description:text image_url:string price:decimal

生成数据库脚本: db/migrate/create_products.rb 如果想查看表结构:db/schema.rb

Model: app/models/product.rb

View: app/views/products/*.html.erb

Controller: app/controllers/products_controller.rb

test: test/unit/product_test.rb ?test/functional/products_controller_test.rb

(注意:Model是单数,以及Model的单元测试test/unit/product_test.rb

复数的有:View:products/?

Controller:products_controller.rb 以及Controller的Fucntional测试:products_controller_test.rb) ?

1.4 修改Product对应的表的某个字段的长度和精度,修改后需要和原先的数据库进行migrate:

-> db/migrate/create_products.rb

class CreateProducts < ActiveRecord::Migration

t.decimal :price, :precision => 8, :scale => 2

end

> rake db:migrate ?

1.5 启动Rails内置服务器WEBrick,访问以上URL,查看Products列表:

> rails server

@ http://localhost:3000/products

点击列表页面的New Product进入http://localhost:3000/products/new

在新增页面输入数据,提交,并返回 http://localhost:3000/products 则可以看到新增的记录

分析CRUD的流程1--页面跳转:

http://localhost:3000/products/ 满足app/controllers/products_controller.rb ProductsController?

的URL命名约定: GET /products 对应的方法是: index?

在index方法里取得所有的products: @products = Product.all

页面指向respond_to -> format.html # index.html.erb 即app/views/products/index.html.erb

(# index.html.erb是注释,因为该方法名为index,所以format.html的值为:index.html.erb)

在index.html.erb中@products.each do |product|取出所有的products循环输出每个Product的信息

数据的获取是通过Json方式,Controller中在指向index.html.erb时获取到了Json数据: format.json { render json: @products }

新增Product,路径为:link_to new_product_path 根据Rails的命名约定满足?

GET /products/new 的URL(link_to: Get; new_product_path: /products/new ). 会调用Controller的new方法并指向new.html.erb

new.html.erb 只有一行代码: <%= render 'form' %> 指向_form.html.erb

_form.html.erb 有两部分组成,一是错误信息的显示,比如提交时数据验证失败显示错误信息;第二部分即是表单的数据了

在新增页面提交表单 最终会调用到Controller的create方法,保存数据...

1.7 测试以上所写的代码是否正确:

> rake test

This is for the unit, functional, and integration tests that?

Rails generates along with the scaffolding. 注意当修改了Controller,Model中的代码时,

运行rake test可能会报错,这时候需要对test/中相应的测试文件进行修改.

1.8 在服务器启动时,进行初始化数据的导入.下面这种方式更加对象化,而不是用insert SQL语句:

-> db/seeds.rb

Product.create(:title=>'sth',:description=>'sth',:image_url=>'spath',:price=>11.22)

> rake db:seed ?

@ http://localhost:3000/products

?

1.9 关于全局Layout:

app/views/layouts/application.html.erb?

所有的views/*/*.html.erb都将继承application.html.erb的布局.可以在这里写每个页面固定的开头和结尾.

Rails keeps a separate file that is used to create a standard page environment for the entire application

?

application.html.erb, be the layout used for all views for all controllers that don’t otherwise provide a layout.

除非Controller有自己的Layout会覆盖掉全局的Layout.否则Application Layout会应用到所有的视图里.

?

We loaded some test data into the database(1.8), we rewrote the index.html.erb file

that displays the listing of products, we added a depot.css stylesheet, and that

stylesheet was loaded into our page by the layout file application.html.erb(1.9).?

?

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

Chapter 7. Task B: Validation and Unit Testing

2.1 给Product Model添加字段验证:

-> app/models/product.rb

class Product < ActiveRecord::Base

validates :title, :description, :image_url, :presence => true

validates :price, :numericality => {:greater_than_or_equal_to => 0.01}

validates :title, :uniqueness => true

validates :image_url, :format => {

:with => %r{\.(gif|jpg|png)$}i,

:message => 'must be a URL for GIF, JPG or PNG image.'

}

end

访问http://localhost:3000/products/new 提交时会对输入域进行验证 如果验证失败则会在页面顶部显示错误信息.

2.2 给Model添加验证后的单元测试:

> rake test

运行测试脚本,会发现报错test/functional/products_controller_test.rb:

test_should_create_product test_should_update_product?

很显然当我们增加了Model的验证代码,测试部分也需要更改:

在测试脚本文件中ProductsControllerTest 的setup方法里初始化一个Product对象@update

并在2个报错的方法里把product: @product.attributes 改为 :product => @update指向初始化的product对象

setup方法第一行: @product = products(:one) 获取test/fixtures/products.yml中的数据

这是测试文件初始化加载的数据,正如seeds.rb是项目启动时初始化加载的数据

2.3 test类型有2种:

unit针对Model测试 test/unit/product_test.rb?

class ProductsControllerTest < ActionController::TestCase

可以运行:> rake test:units

functional针对Controller测试 test/functional/products_controller_test.rb?

class ProductTest < ActiveSupport::TestCase?

可以运行命令:>rake test:functionals

rake test 会测试整个项目是否有错误

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

Chapter 8. Task C: Catalog Display

3.1 创建前台Product展示的控制器和页面:

> rails generate controller store index

创建StroeController和对应的Controller的index方法

和rails generate scaffold不同的是创建脚手架会创建Model,Controller,View,数据库脚本,测试文件等

而generate controller则只创建Controller:

app/controllers/store_controller.rb

app/views/store/index.html.erb

test/functional/store_controller_test.rb

还有一点区别是用generate scaffold创建的View和Controller是复数形式,Model是单数;

而generate controller则是单数.

3.2 上面的命令创建了StoreContoller以及index方法,通过URL:?

http://localhost:3000/store/index则会调用对应的方法.进入views/store/index.html.erb

-> config/routes.rb?

root :to => 'store#index', :as => 'store'

3.3 删除原先根目录下对应的文件:

> rm public/index.html?

当访问 http://localhost:3000 等价于上面的http://localhost:3000/store/index

进入的页面是: views/store/index.html.erb

3.4 访问规则测试:

http://localhost:3000/products 进入后台ProductList的显示页面views/products/index.html.erb

http://localhost:3000/products/index 则会报错:

ActiveRecord::RecordNotFound in ProductsController#show Couldn't find Product with id=index

http://localhost:3000/store 报错:Routing Error No route matches [GET] "/store"

由此可见通过generate scaffold,URL:/products代表访问的是ProductsController的index方法.

但是不能这样子访问:/products/index.

而通过generate controller,contoller中定义了哪些方法,

要访问该方法对应的页面则在controller后加上/方法名即可对应.

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

Chapter 9. Task D: Cart Creation

4.1 创建购物车对象脚手架.但是没有给Cart指定字段:

> rails generate scaffold cart

> rake db:migrate

-> app/controllers/application_controller.rb

class ApplicationController < ActionController::Base

protect_from_forgery

private

def current_cart

Cart.find(session[:cart_id])

rescue ActiveRecord::RecordNotFound

cart = Cart.create

session[:cart_id] = cart.id

cart

end

?

4.2 创建购物项的脚手架:

> rails generate scaffold line_item product_id:integer cart_id:integer

> rake db:migrate

4.3 目前为止的数据库脚本(schema.rb):

create_table "carts"

end

?

create_table "line_items"

t.integer ?"product_id"

t.integer ?"cart_id"

end

?

create_table "products"

t.string ? "title"

t.text ? ? "description"

t.string ? "image_url"

t.decimal ?"price", ? ? ? :precision => 8, :scale => 2

end

由此可见line_items可以当做carts和products的中间表.

?

4.3 Connecting Products to Carts: 将Product和Cart互相关联

-> app/models/cart.rb

Cart?

has_many :line_items

-> app/models/line_item.rb

LineItem?

belongs_to :product?

belongs_to :cart

-> app/models/product.rb

Product?

has_many :line_items

Cart 1---->N LineItem N<----1 Product ...Order

一个Cart可能有多个LineItem,一个Product也可能有多个LineItem(多个LineItem可能对应一个Product)

?

4.4 line_items涉及到的级联删除:

?

4.5 给前台的每个Product添加一个购买按钮:

-> app/views/store/index.html.erb

<%= button_to 'Add to Cart', line_items_path(:product_id => product) %>

button_to line_items_path(:product_id => product) 是如何调用到LineItemsController的create方法?

button_to : POST ; line_items_path : /line_items

LineItemsController中满足POST /line_items的只有create方法

如果是link_to line_items_path. link_to : GET line_items_path 则对应的方法为index

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

#...

end

为什么是LineItemsController.create而不是CartsController.create.

而且ApplicationController中Cart.create是在哪里定义的?(这个稍后解释)

把Product加入购物车中,即创建了一个购物项.所以使用的是LineItemsController.create.

find the shopping cart for the current session(@cart = current_cart),?

add the selected product to that cart(@cart.line_items.build), and display the cart contents.

pass that product we found into @cart.line_items.build.?

This causes a new line item relationship to be built between the @cart object and the product.

4.6 添加一个购物项后指向Cart页面,而不是购物项本身:

在LineItemsController.create方法中,

if @line_item.save #保存后

format.html { redirect_to @line_item.cart }

Since the line item object knows how to find the cart object,?

all we need to do is add .cart to the method call

那么调用的是CartsController的哪个方法index? show? 换句话说进入的是哪个页面?

@line_item.cart 通过LineItem得到Cart对象,所以实际上是传递了cart_id这个参数.

所以URL的形式应该是/carts/cart_id 类型为GET.满足的方法是show

可以这么理解:将一个购物项加入购物车中,而且这个购物车是Session中唯一的.

如果是/casts即所有的购物车List.显然在我们的应用中是说不通的.

所以app/views/carts/show.html.erb 通过Cart取出当前购物车中所有的购物项

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

<li><%= item.product.title %></li>

<% end %>

4.7 添加购物项的Functional测试:

因为LineItemsController修改的是create方法.接收参数:product_id

所以test/functional/line_items_controller_test.rb 修改的也是对应的create方法并传递需要的参数:

post :create, :product_id => products(:ruby).id?

products(:ruby)是test/fixtures/products.yml中的ruby:对应的记录.

测试重定向: assert_redirected_to cart_path(assigns(:line_item).cart)

cart_path:/carts/ assigns(:line_item).cart把购物项对应的购物车对象的id取出来并设置值,即cart_id

运行测试命令: rake test:functionals

有点不明白这里的Functional测试,在前台Product展示页面添加一个购物项到购物车中,

这里能调用到测试的代码吗??

4.8 测试Session:

在Window上跑Ruby有点慢.有时候启动服务器,刷页面,但是没有反应.

还有更奇怪的问题是,有时候点击Add To Cart,浏览器的地址不是:http://localhost:3000/carts/3

而是http://localhost:3000/line_items?product_id=1 页面会报错说没有line_items这个方法.

这时候可以对服务器Ctrl+C终止下. 然后他就会报一堆的错,但页面却恢复正常了. 这里估计没有完全中止服务器.

测试Session,可以这样子做.关掉浏览器.Session中保留的cart_id消失.

重新访问http://localhost:3000. 点击某个Product购买,URL变为: http://localhost:3000/carts/4?

这里的4是新生成的Session中的cart_id.因为如果Session中找不到cart_id?

则会重新创建一个新的Cart对象,并把cart_id放入Session中.

同时购物车中的数据只会显示当前添加的Product.

关掉浏览器之前的那次会话的数据(sessionId=3)是不会存在在当前的这个Session中(sessionId=4).

4.9 查看别的数据:

如果想看LineItems,即所有Cart对应哪些Product. 访问:http://localhost:3000/line_items

似乎有点难以理解. 因为一般来说一个浏览器在当前只会有一个Cart,即一个Session中只能有一个Cart.

不过这个结果可以理解为另一种概念: 浏览器的访问历史记录.

如果访问http://localhost:3000/carts 则是看不到数据的.

但是会有多个Show Edit Destroy链接.不过这样的URL没有什么意义了.

?

  相关解决方案