Ruby On Rails的第一个应用(五)--商品目录显示
III 任务C:商品目录显示
任务列表:
·编写用户视力
·使用页面布局来装点页面
·集成CSS
·使用帮助程序
·编写功能测试程序
客户接下来想先看下从买家的角度,这个应用程序是什么样子。那我们要做的就是创建简单的商品目录显示网页。
一、迭代C1:创建商品目录清单
前面创建了商品(product)控制器,卖家可以用来管理depot程序。现在我们需要创建另外一个控制器,用来与消费者互动,称为store(商店)
(我现在使用aptana studio3工具开发,以下执行的命令是在这上面执行的。基本和dos一样,但是更像是linux)
Administrator@JARRY /e/works/ruby/depot (master) $ rails generate controller store index SECURITY WARNING: No secret option provided to Rack::Session::Cookie. This poses a security threat. It is strongly recommended that you provide a secret to prevent exploits that may be possible from crafted cookies. This will not be supported in future versions of Rack, and future versions will even invalidate your existing user cookies. Called from: d:/dev/RailsInstaller/Ruby1.9.3/lib/ruby/gems/1.9.1/gems/actionpack-3.2.1/lib/action_dispatch/middleware/ session/abstract_store.rb:28:in `initialize'. create app/controllers/store_controller.rb route get "store/index" invoke erb create app/views/store create app/views/store/index.html.erb invoke test_unit create test/functional/store_controller_test.rb invoke helper create app/helpers/store_helper.rb invoke test_unit create test/unit/helpers/store_helper_test.rb invoke assets invoke coffee create app/assets/javascripts/store.js.coffee invoke scss create app/assets/stylesheets/store.css.scss
这里创建了控制器(/app/controllers/store_controller.rb文件下的StoreController)
设置store/index为根网址
编辑/config/routes.rb文件
Depot::Application.routes.draw do get "store/index" resources :products # ... # You can have the root of your site routed with "root" # just remember to delete public/index.html. # root :to => 'welcome#index' root to: "store#index", as: "store" #add # ... end
注释中提示了要删除public/index.html,那么删除它:
Administrator@JARRY /e/works/ruby/depot (master) $ rm public/index.html
在浏览器中尝试http://localhost:3000/
需要得到商品清单而非源自数据库,且把它提供给显示清单的视图代码,这样需要修改/controllers/store_controller.rb。要在合适的抽象层面上编程,这样就先假定可以从模型中得到可销售的商品清单:
class StoreController < ApplicationController def index @products = Product.all end end
先确定下商品的显示顺序,这里用字母顺序来显示。打开模型Product添加方法default_scope,默认范围(scopes)会作用于该模型的所有查询。
/app/models/product.rb
class Product < ActiveRecord::Base default_scope order: 'title' # validates stuff ... end
接下来编写视图模板。修改/app/views/store/index.html.erb:
<% if notice %> <p id="notice"><%= notice %></p> <% end %> <h1>Your Pragmatic Catalog</h1> <% @products.each do |product| %> <div class="entry"> <% image_tag(product.image_url) %> <h3><%= product.title %></h3> <%= sanitize(product.description) %> <div class="price_line"> <span class="price"><%= product.price %></span> </div> </div> <% end %>
这里要指出的是商品属性所用的方法sanitize:它允许安全地添加HTML风格代码,使客户对这一商品属性描述内容更有兴趣。(这样会潜在安全漏洞,但是在这里商品描述都是由公司员工所创建,所以认为风险是很小的。)
刷新页面显示:
页面实在是不美观,要添加样式,但是这就需要专门的网页设计人员设计了。如需要看到比较漂亮的标题和侧边栏。
二、迭代C2:增加页面布局
通过网站页面会共享相似的页面布局--设计人员将已生成的标准模板用来填写内容。下面的工作是将这个网页装饰添加到商店的每个网页上。将这个页面布局命名为application.html.erb,在没有其他页面布局的情况下,所有控制器的视图都将使用这个布局。由于只使用单个布局,因此只需要编辑该文件就可以修改整个网页的界面外观。这里先设置一个占位页面布局,具体需要专门设计。
这个布局放到/app/views/layouts目录中,application.html.erb:
<!-- START:head --> <!DOCTYPE html> <html> <head> <!-- START_HIGHLIGHT --> <title>Pragprog Books Online Store</title> <!-- END_HIGHLIGHT --> <%= stylesheet_link_tag "application", media: "all" %> <%= javascript_include_tag :defaults %><!-- <label id="code.jlt"/> --> <%= csrf_meta_tags %><!-- <label id="code.csrf"/> --> </head> <!-- END:head --> <body class="store"> <!-- START_HIGHLIGHT --> <div id="banner"> <%= image_tag("logo.png") %> <%= @page_title || "Pragmatic Bookshelf" %><!-- <label id="code.depot.e.title"/> --> </div> <div id="columns"> <div id="side"> <ul> <li><a href="http://www....">Home</a></li> <li><a href="http://www..../faq">Questions</a></li> <li><a href="http://www..../news">News</a></li> <li><a href="http://www..../contact">Contact</a></li> </ul> </div> <div id="main"> <!-- END_HIGHLIGHT --> <%= yield %><!-- <label id="code.depot.e.include"/> --> <!-- START_HIGHLIGHT --> </div> </div> <!-- END_HIGHLIGHT --> </body> </html>
更改application.css
/* * This is a manifest file that'll be compiled into application.css, which will include all the files * listed below. * * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets, * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path. * * You're free to add application-wide styles to this file and they'll appear at the top of the * compiled file, but it's generally better to create a new file per style scope. * *= require_self *= require_tree . */ /* START_HIGHLIGHT */ #banner { background: #9c9; padding: 10px; border-bottom: 2px solid; font: small-caps 40px/40px "Times New Roman", serif; color: #282; text-align: center; } #banner img { float: left; } #notice { color: #000 !important; border: 2px solid red; padding: 1em; margin-bottom: 2em; background-color: #f0f0f0; font: bold smaller sans-serif; } #columns { background: #141; } #main { margin-left: 17em; padding: 1em; background: white; } #side { float: left; padding: 1em 2em; width: 13em; background: #141; } #side ul { padding: 0; } #side li { list-style: none; } #side a { color: #bfb; font-size: small; } /* END_HIGHLIGHT */
刷新浏览器观看效果:
现在页面并不美观,但是有个大概的样子,可以给客户看了。
三、迭代C3:用帮助函数来调整价格格式
细看上图发现一个小问题,商品价格在数据库中都是以数字形式保存的。现在我RMB形式显示如价格12.34,就显示为¥12.34;13则显示为¥13.00
Ruby提供了sprintf函数,可以用来对价格进行格式化。可以直接在视图中使用:
<span class="price"><%= sprintf("¥%0.02f",product.price) %></span>
但是这样做以后想国际化这个应用就有维护的问题了。比如 要用美元显示了。
如果有个帮助函数把价格转换成货币格式就好了。恰好Rails就有这样的内置函数:number_to_curreny
参考:http://api.rubyonrails.org/
http://blog.chinaunix.net/uid-25231750-id-153630.html
在视图中如下使用:
<span class="price"><%= number_to_currency(product.price, unit: "¥") %></span>
刷新页面
四、迭代C4:控制器功能测试
在添加新功能之后应该为它编写和运行测试。特别是在添加逻辑到模型的行为之后。
首先确定修改之后是否破坏了什么。我们运行一下测试:
rake test
一切正常,说明增加修改没有破坏任何东西,但是接下来需要测试刚刚加上的功能。
之前所做的模型单元测试看上去是相当清楚的。调用方法,且比较返回值和期待值。但是当前在浏览器中所涉及的不仅是服务器处理讲求,而且用户也注视着响应。我们需要功能测试,这能验证模型、视图、控制器是否在一起正常工作。
先看下rails生成了什么。
/test/functional/store_controller_test.rb:
require 'test_helper' class StoreControllerTest < ActionController::TestCase test "should get index" do get :index assert_response :success end end
这个should get index测试得到了索引并断言期待成功响应。但是还想验证响应是否包含了布局、商品信息和数字格式,看如下代码:
# encoding:utf-8 require 'test_helper' class StoreControllerTest < ActionController::TestCase test "should get index" do get :index assert_response :success assert_select '#columns #side a', minimum: 4 assert_select '#main .entry', 3 assert_select 'h3', 'Programming Ruby 1.9' assert_select '.price', /\¥[,\d]+\.\d\d/ end end
上面添加的4行代码是通过css选择器表示法,查看所返回的HTML代码。
第一个选择测试寻找的是名为a的元素,在id值为side的元素中包含了该元素,而在id值为columns的元素中又包含这个id值为side的元素。这个测试是验证所有满足以上条件的这样的元素中最小值为4.
第二个验证在页面的主要部分有3个类名为entry元素
第三个验证存在具有Ruby书名的h3元素
第四个验证价格格式是否正确。
这些断言都是基于静态测试中的测试数据:
# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/Fixtures.html one: title: MyString description: MyText image_url: MyString price: 9.99 two: title: MyString description: MyText image_url: MyString price: 9.99 ruby: title: Programming Ruby 1.9 description: Ruby is the fastest growing and most exciting dynamic language out there. If you need to get working programs delivered fast, you should add Ruby to your toolbox. price: 49.50 image_url: ruby.png #END:ruby
验证和功能测试都仅测试控制器的行为:它们还会影响任何对象,因为该对象已经在数据库或静态测试中。在之前两个商品有相同的标题。这样的数据将不会导致任何问题,而且将不会察觉到修改和保存这些记录,还会对现有的对象门生任何影响。
现在运行功能测试:
rake test:functionals
上一篇: 单例模式之线程安全解析
下一篇: java_面相接口(抽象)编程
推荐阅读