Rails 4 天进阶


作者:John McCreesh (http://rails.homelinux.org) 翻译:Ysl@1ster..! (http://www.1steam.cn/1ster/) 1 版权信息 ................................................................................................................................................3 Rails 简介 .............................................................................................................................................3 Rails 第一天 .........................................................................................................................................4 做个 ToDo 列表应用 ......................................................................................................................4 让你的 Rails 跑起来 ....................................................................................................................4 给你的 Web 服务器添加程序 ........................................................................................................5 在主机文件中定义程序 ........................................................................................................5 在 Apache 中配置文件 ..........................................................................................................5 使用 fastcgi .........................................................................................................................5 看看你的 Rails 跑起来了没 ................................................................................................5 安装数据库 ....................................................................................................................................5 创建分类表 ............................................................................................................................6 MySQL 定义 .....................................................................................................................6 数据模块 ........................................................................................................................6 脚手架 ............................................................................................................................................6 Rails 第二天 .........................................................................................................................................8 模块 ................................................................................................................................................8 创建数据验证规则 ................................................................................................................8 控制器 ............................................................................................................................................9 默认的控制器 ........................................................................................................................9 制作默认的控制器 ..............................................................................................................11 视图 ..............................................................................................................................................11 图层 ......................................................................................................................................12 模板 ......................................................................................................................................12 用数据模块显示错误信息 ..........................................................................................13 用最少的代码创建一个表单 ......................................................................................13 创建超级连接 ..............................................................................................................14 修订默认的 Edit 视图 ........................................................................................................14 修订默然的 List 视图 ........................................................................................................14 处理 HTML 字符 ............................................................................................................15 使用 Ruby 来格式化日期和时间 ................................................................................15 建立一个基于 Javascript 的确认对话框 ................................................................15 Rails 第三天 ......................................................................................................................................16 Items 表 .......................................................................................................................................16 MySQL table defintion.......................................................................................................16 The Model............................................................................................................................16 链接可用性 ..................................................................................................................17 验证输入 ......................................................................................................................17 视图的其它要点 ..................................................................................................................17 在模板和层间共享数据 ..............................................................................................17 ToDo 列表界面 ...........................................................................................................18 点击清除已经完成的条目...................................................................................19 点击列首改变排列顺序.......................................................................................19 添加一个辅助器...................................................................................................20 用 JS 实现导航按钮 ............................................................................................21 子模板 ..................................................................................................................21 格式化日期 ..........................................................................................................22 作者:John McCreesh (http://rails.homelinux.org) 翻译:Ysl@1ster..! (http://www.1steam.cn/1ster/) 2 控制在查找中丢失的值.......................................................................................22 新增 ToDO 条目展示 ............................................................................................................22 为日期输入框创建一个下拉列表.......................................................................23 从查询字段创建一个下拉框...............................................................................24 由常量创建一个下拉框.......................................................................................24 创建一个复选狂框...............................................................................................24 控制器 ..........................................................................................................................................24 锦上添花 ......................................................................................................................................25 制定样式 ..............................................................................................................................25 总结 ......................................................................................................................................25 Rails 第四天 .......................................................................................................................................26 Notes 表........................................................................................................................................26 Model....................................................................................................................................26 使用一个 Model 维护外链接......................................................................................26 视图层 ..................................................................................................................................27 在控制器之间传递 ......................................................................................................27 使用 Session 变量来保存和重现数据 ......................................................................31 整理导航栏 ..........................................................................................................................32 创建主页 ......................................................................................................................34 链接到主页 ..................................................................................................................35 下载这个程序 ......................................................................................................................35 写在最后 ..............................................................................................................................35 关于译者: ..........................................................................................................................35 作者:John McCreesh (http://rails.homelinux.org) 翻译:Ysl@1ster..! (http://www.1steam.cn/1ster/) 3 版权信息 作者:John McCreesh (http://rails.homelinux.org) 翻译:Ysl@1ster..! (http://www.1steam.cn/1ster/) 申明:版权所有,欢迎转载,转载时请保留以上信息! ---------------------------------------------------- 写在前面的话: 自己正在学习ROR,鉴于关于ROR的中文资料比较少,虽然自己E语不怎么样,但是每次看到E 文资料都尽力翻译过来。一来方便自己日后回头看这些资料比较方便;二是这样可以使自己看 的比较认真,仔细揣摩原文的意思;还有一点就是尽量为后来的学习者提供一些关于ROR的中 文资料。 一边看一边翻译记录,没有仔细校核,如有不妥,还望海涵,如能指出,不甚感激! Rails简介 关于Rails的神话已经有很多了,例如在OnLAMP.com上就有文章声称“用Rails开发WEB应用程序 比用传统的JAVA框架至少要快十倍...”。那文章接着展示了如何在PC机上使用Rails和Ruby而 不需要任何的编码就可以创建一个虚拟的“脚手架”程序。 这给人留下深刻的印象,“真正”的WEB应用程序都知道,这个太虚幻了,因为他们认为“真正” 的程序不可能这么简单的,那么在这个表象下面到底隐含着什么样的东西呢?开发一个“ 真正” 的应用程序到底有多难呢? 探索问题的根源或许会使得生活更加充实。Rails的线上文档很完善-事实上,对于一个初学者 来说,这也许太完善了,超过30.000字的线上手册,使得你在写自己的Rails时按照里面的参考 链接,也会使你常常迷失自己。 这个文档正是用来填补这个缺陷的,它假设你已经在自己的PC机上安装好了Ruby和Rails(如果 你还没有安装好,回去参考Curt的文章),接下来就要进入第一天的学习了。 “第二天” 开始深入看上去很玄幻的背后,通过“脚手架”的代码你可以领悟一些东西。新 特征都会被加亮加粗来提示你参考,或者链接到另外的文档中,这样你就可以了解更多。 “第三天”采用“脚手架”来创建一些看上去像“真正”应用的东西,在这个过程中,你可以 创建积累自己的Rails工具箱,最重要的是,你可以很轻松的参照线上文档来继续你的探险。 “第四天”添加另外一个表,并且处理其它的一些比较复杂的关系,最后,你可以得到一个可 以工作的应用,有足够的工具来开始你的项目,也使得你有足够的技巧来获取更多的帮助。 还记得开始我们说的比JAVA快十倍么,到底是不是真的这么快,四天以后,你就可以自己来评 价了。 作者:John McCreesh (http://rails.homelinux.org) 翻译:Ysl@1ster..! (http://www.1steam.cn/1ster/) 4 Rails第一天 做个ToDo列表应用 这个文档接下来将创建一个简单的“ToDo列表”应用-基于列表条,分类,并且有可以选择的 备注信息的应用用来管理你PDA上的事情(怎么,想看看界面如何的,没问题,你可以到17页看 到这个应用的屏幕截图)。 让你的Rails跑起来 这个例子是在MS-Windows的PC机上开发的,我的程序目录在c:\www\webroot下,为了方便,我 把他标志成w:,你也可以这样作,如下操作; 运行rails ToDo将在ToDo:\目录下创建下面的目录结构: app 存放针对这个程序的所有代码 app\controllers 存放驱动业务逻辑的控制器 app\models 存亡描述数据结构、验证和完整性规则等的模块 app\views 存放所有生成HTML文档的模板文件,也可以存放样式表、图片等一些放在public目录里面 的东西. app\helpers 存放视图辅助类(一些常用的代码段) config 存放Apache,数据库或者其它的依赖文件的配置文件. lib 存放应用程序运行过程中需要的其它的类库,这些东西都不属于控制器、视图和辅助类; 另外这个路径被自动加载。 log 存放程序运行过程中产生的日志文件,注意:development.log会记录Rails的所有操作以 使得开发者追踪错误! public 这个目录存放Apache可以访问的路径,包含一些图片,JS代码以及样式目录。 script 一些很有用的脚本用来启动或生成代码. test C:\> subst w: c:\www\webroot C:\> w: W:\> rails ToDo W:\> cd ToDo W:\ToDo> 作者:John McCreesh (http://rails.homelinux.org) 翻译:Ysl@1ster..! (http://www.1steam.cn/1ster/) 5 单元和功能测试 vendor 一些程序所依赖的外部的类库,也是自动加载的。 给你的Web服务器添加程序 我在自己的开发机器上跑了Apache,MySQL等等,下面两步需要给我们的程序起个友好的名 字。 在主机文件中定义程序 C:\winnt\system32\drivers\etc\hosts (excerpt) 127.0.0.1 todo 在Apache中配置文件 使用fastcgi 如果你耐心很好( 或 者有个特别牛的PC机),否则,你就应该使用fastcgi来跑你的程序, 配置如下: public\.htaccess # Change extension from .cgi to .fcgi to switch to FCGI and to .rb to switch to mod_ruby RewriteBase /dispatch.fcgi 看看你的Rails跑起来了没 现在你的服务应该跑起来了,把浏览器定位到:http://todo/就能看到效果。 安装数据库 我已经在MySQL里创建了一个新的数据库叫做todos,连接数据库的文件是: config\database.yml。 Apache2\conf\httpd.conf ServerName todo DocumentRoot /www/webroot/ToDo/public Options ExecCGI FollowSymLinks AllowOverride all Allow from all Order allow,deny 作者:John McCreesh (http://rails.homelinux.org) 翻译:Ysl@1ster..! (http://www.1steam.cn/1ster/) 6 创建分类表 分类表(categories)用来保存分类信息,在下面的例子中将要用到,我们在这里需要创建它。 MySQL定义 关于一些命名规则如下: 名字中的下划线,在Rails中可能会被替换成空格,比如‘human friendly’。 注意大小写……在某些Rails中对大小写是敏感的。 每个表都需要添加一个主键id,在MySQL中,是很容易自动增长的。 链接到其它数据表的外键应该遵照‘_id’规则。 Rails可以自动维护created_at/created_on or updated_at/updated_on这些字段,所以最好把 他们添加到Documentation: ActiveRecord::Timestamp。 有用的提醒:如果你创建一个多人系统(不是说这个例子),如果你添加一个表lock_version (默认为0),Rails就可以对它自动的上锁,而你需要做得就是在你提交的表单中包含 lock_version这个隐藏域。 数据模块 生成一个空文件,如下: W:\ToDo>ruby script/generate model Category 这样就可以创建一个app\modules\category.rb文件。 脚手架 控制器是整个应用的核心。运行控制器生成器来生成它,如下: Running the generate controller script W:\ToDo>ruby script/generate controller category 它会创建两个文件和两个空的目录,分别为: app\controllers\category_controller.rb app\config\database.yml (excerpt) development: adapter: mysql database: todos host: localhost username: foo password: bar Categories table CREATE TABLE `categories` ( `id` smallint(5) unsigned NOT NULL auto_increment, `category` varchar(20) NOT NULL default '', `created_on` timestamp(14) NOT NULL, `updated_on` timestamp(14) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `category_key` (`category`) ) TYPE=MyISAM COMMENT='List of categories'; 作者:John McCreesh (http://rails.homelinux.org) 翻译:Ysl@1ster..! (http://www.1steam.cn/1ster/) 7 app\helpers\category_helper.rb app\views\categories app\views\layouts 如果你以前没有看过那些入门教程(比如Rolling with Ruby on Rails),那么现在就来试试 吧,它展示的是使用两行代码就可以创建一个WEB应用,肯定让你大为惊奇的。 把你的浏览器指向http://todo/category,你将发现Rails到底有多么的聪明,看下图1: 图1 app\controllers\category_controller.rb class CategoryController < ApplicationController model :category scaffold :category end 作者:John McCreesh (http://rails.homelinux.org) 翻译:Ysl@1ster..! (http://www.1steam.cn/1ster/) 8 Rails第二天 仔细想想,我们想知道在这个表层下面到底发生了什么事情。在“脚手架”的动作里面,Rails 自动生成了所有它需要的代码。如果我们运行“脚手架”脚本,就可以得到这些代码,这样我 们就可以研究它,进而还可以把它们整合到我们自己的应用中。 运行“脚手架”脚本:W:\ToDo>ruby script/generate scaffold Category 这个脚本生成程序需要的以大堆的代码,包括控制器、视图、样式以及层。 app\controllers\categories_controller.rb app\helpers\categories_helper.rb app\views\categories\edit.rhtml app\views\categories\list.rhtml app\views\categories\new.rhtml app\views\categories\show.rhtml app\views\layouts\categories.rhtml public\stylesheets\scaffold.css 注意下这里奇怪的命名约定(我们由单数转为复数),所以现在要使用新代码,你需要把你的 浏览器指向:http://todo/categories 模块 模块是用来存放数据关系的,包括数据校验和完整性检测。意思就是你可以定义一次,然 后Rails程序会读取数据的时候自动使用它们。 创建数据验证规则 Rails提供给你许多免费的错误控制器,为 了说明这个问题,我们需要添加一些校验规则到 这个空的Category模型中。 这些代码可以自动的进行:(参考:Documentation: tiveRecord::Validations::ClassMethods) validates_length_of: 不能为空也不能太长 validates_uniqueness_of: 不能重复 为了测试这个,我们可以试着添加一些相同的记录,(如图2),效果就在你面前,你还有什 么不满意的呢?毕竟这是个免费的呀? 提示:在前面的那个版本中做相同的测试,你会发现,不能够防止这样的事情发生,为了安 全,你最好把以前的那个.rb文件(app\controllers\category_controller.rb和 app\helpers\category_helper.rb)删掉. app\models\category.rb class Category < ActiveRecord::Base validates_length_of :category, :within => 1..20 validates_uniqueness_of :category, :message => "already exists" end 作者:John McCreesh (http://rails.homelinux.org) 翻译:Ysl@1ster..! (http://www.1steam.cn/1ster/) 9 图2 控制器 是时候来看看控制器了,控制器控制着整个程序的业务逻辑,它由视图和用户交互,并且 用模型和数据库打交道.你可以看看控制器的代码来搞明白它是怎么把这个程序连系起来的. 默认的控制器 默认的控制器是由"脚手架"自动生成的,它的代码如下: 作者:John McCreesh (http://rails.homelinux.org) 翻译:Ysl@1ster..! (http://www.1steam.cn/1ster/) 10 一个控制器的默认action会和模板中相同名字的模板进行匹配,例如这里的list 动作会取得 @categories实例变量,然后控制器取得list.rhtml. render_template允许你返回不同的模板,例如:index动作可以运行list的代码,然后返回 list.rhtml,而不返回index.rhtml. \app\controllers\categories_controller.rb class CategoriesController < ApplicationController def index list render_action 'list' end def list @categories = Category.find_all end def show @category = Category.find(@params['id']) end def new @category = Category.new end def create @category = Category.new(@params['category']) if @category.save flash['notice'] = 'Category was successfully created.' redirect_to :action => 'list' else render_action 'new' end end def edit @category = Category.find(@params['id']) end def update @category = Category.find(@params['category']['id']) if @category.update_attributes(@params['category']) flash['notice'] = 'Category was successfully updated.' redirect_to :action => 'show', :id => @category.id else render_action 'edit' end end def destroy Category.find(@params['id']).destroy redirect_to :action => 'list' end end 作者:John McCreesh (http://rails.homelinux.org) 翻译:Ysl@1ster..! (http://www.1steam.cn/1ster/) 11 redirect_to 用来转向 更多资料参考:Documentation: ActionController::Base 控制器使用ActiveRecord 中的方法,比如find,find_all,new,save,update_attributes以及 destroy来存取数据到数据库. 更多资料参考:Documentation: ActiveRecord::Base 注意这个过程被分成几段来作,比如,当一个用户选择"edit" 控制器就会返回他需要编辑的 内容,当用户编辑完成,编辑视图就激活更新动作,这个动作进行更新,然后再展示出来. 制作默认的控制器 作为我个人来说,我不喜欢Rails每次更新完都还把我送到编辑后的界面上去,我希望直接到列 表界面上去;也就是说展示界面不是必须的,另外,最好还能显示一个消息说我们的编辑完成 了. 这里的这个flash消息会被缓存,然后在下个页面上显示出来,如图2显示的 更多资料参考Documentation: ActionController::Flash). 可是奇怪的是,尽 管 flash消息拥有它自己的css标签,但是由"脚手架"生成脚本并不知道它, 这里需要我们额外来解决. 视图 视图是和用户的接口界面,Rails可以从一下三个组件中生成和用户交互的界面: Layout in app\views\layouts\ default: application.rhtml app\controllers\categories_controller.rb (excerpt) def update @category = Category.find(@params['category']['id']) if @category.update_attributes(@params['category']) flash['notice'] = 'Category was successfully updated.' redirect_to :action => 'list' else render_action 'edit' end end public\stylesheets\scaffold.css (excerpt) .notice { color: red; } 作者:John McCreesh (http://rails.homelinux.org) 翻译:Ysl@1ster..! (http://www.1steam.cn/1ster/) 12 or .rhtml Template in app\views\\ default: .rhtml Partials in app\views\\ default _.rhtml 图层 Rails的命名规则中说,如果在app\views\layouts\下的一个模板的名字和当前的控制器名字一 样就把它作为这个控制器的层来自动加载,除非你强制指定. 那个叫做application.rhtml和application.rxml的层会被默认为控制器的层,如果没有没有和 这个控制器相同名称且没有另外指定的层的时候. 用"脚手架"来生成层的看上去如下: 这里大部分都是HTML代码,自然还有些比较特殊的东西,加粗的地方就是Rails的处理部分. controller_name 和action_name是ActionController的方法,用来返回地址栏中URL的部分内 容,详细参考:Documentation: ActionController::Base. @content_for_layout允许其它单独的层来动态返回具体的动作(比如edit,new,list),这些 动态内容来源于模板. 详细资料参考:Documentation: ActionController::Layout::ClassMethods. 模板 Rails命名规则中,模板存放在app\views\categories\目录下,且按照action的名字+rhtml 来保存,例如,由"脚手架"生成的edit.rhtml内容如下: app\views\layouts\categories.rhtml Scaffolding: <%= controller.controller_name %>#<%= controller.action_name %> <%= @content_for_layout %> 作者:John McCreesh (http://rails.homelinux.org) 翻译:Ysl@1ster..! (http://www.1steam.cn/1ster/) 13 这些代码就产生了所有的操作请求,其秘密就在上面的黑体部分所代表的意思. 用数据模块显示错误信息 error_messages_for 返回在前个过程中出现的错误信息,如果一个或者多个错误产生了,那么 这个HTML代码可能就是这样的: 注意:css标签都是由"脚手架"产生的,都是相对应的. 更多信息参考:Documentation: ActionView::Helpers::ActiveRecordHelper 用最少的代码创建一个表单 表单是Rails中最经济的,会给出一个Active Record Object,产生一个表单实体,例如下面的 代码: 就会创建下面的HTML代码: 这个看上去也许不是很友好,但是他确实能够工作,而且你能更加快速, app\views\categories\edit.rhtml

Editing category

<%= error_messages_for 'category' %> <%= form 'category', :action => 'update' %> <%= link_to 'Show', :action => 'show', :id => @category.id %> | <%= link_to 'Back', :action => 'list' %>

n errors prohibited this user from being saved

There were problems with the following fields:

  • field_1 error_message_1
  • ... ...
  • field_n error_message_n
<%= form 'category', :action => 'update' %>


作者:John McCreesh (http://rails.homelinux.org) 翻译:Ysl@1ster..! (http://www.1steam.cn/1ster/) 14 参考:Documentation: ActionView::Helpers::ActiveRecordHelper 创建超级连接 link_to 创建一个链接--HTML文件中最常用的部分. 修订默认的Edit视图 由于form的辅助类产生的HTML代码是功能型的,如果我们想更好的控制层,我们需要更多的操 作HTML,可以编辑rhtml文件,在这个例子中,我们向使用一个表来规划用户的输入界面,但是 这个并不违背Rails的特性. 修订默然的List视图 app\views\categories\edit.rhtml

Rename Category

<%= error_messages_for 'category' %>
<%= hidden_field "category", "id" %>
Category: <%= text_field "category", "category", "size" => 20, "maxlength" => 20 %>

<%= link_to 'Cancel', :action => 'list' %> hidden_field, text_field用来快速建立HTML实体.更多参考:Documentation: ActionView::Helpers::FormHelper. 作者:John McCreesh (http://rails.homelinux.org) 翻译:Ysl@1ster..! (http://www.1steam.cn/1ster/) 15 处理HTML字符 在允许用户输入的地方还可能存在一个问题,用户的输入再显示在屏幕上可能偶然的(或者恶 意的)输入一些字符来损坏显示效果,例如,想像看如果用户在category这里输入‘’ 会出现什么后果. 为了消除这个问题,我们需要对用户输入的东西进行HTML字符转换,也就是说输入的 会被转换成 </table>,而这个是没有危害的. Rails处理这个很简单,只需要添加一个h,例如:<%=h category["category"] %> 使用Ruby来格式化日期和时间 我可以使用Ruby的strftime()函数来格式化日期和时间域.更多资料参考:Ruby Documentation: class Time 建立一个基于Javascript的确认对话框 你也许已经看到了,在 link_to中可以使用:confirm 使得删除前弹出对话框让你确认是不是真 的操作.参考:Documentation: ActionView::Helpers::UrlHelper 这就是我们第二天的全部内容,我们创建了一个管理分类的系统,并且控制了"脚手架"生成 的代码. app\views\categories\list.rhtml

Categories

<% if @flash["notice"] %> <%= @flash["notice"] %> <% end %> <% for category in @categories %> <% end %>
Category Created Updated
<%=h category["category"] %> <%= category["created_on"].strftime("%I:%M %p %d-%b-%y") %> <%= category["updated_on"].strftime("%I:%M %p %d-%b-%y") %> <%= link_to 'Rename', :action => 'edit', :id => category.id %> <%= link_to 'Delete', { :action => 'destroy', :id => category.id }, :confirm => 'Are you sure you want to delete this category?' %>

<%= link_to 'Add new category', :action => 'new' %> 作者:John McCreesh (http://rails.homelinux.org) 翻译:Ysl@1ster..! (http://www.1steam.cn/1ster/) 16 Rails 第三天 现在是时候来看看整个应用的核心部位-控制器的时候了,我们这里使用Items表来保存我们 的”todos”列表内容,里面的每个” Item”都属于我们在第二天创建的一个分类(Categories), 另外,每个Item还可以有一个备注信息(Note),存放这个备注信息的表我们放在明天来 讲.另外,每个表都需要有一个主键"id",以用来在表之间建立联系,如下图: 我们来成生成更多的"脚手架"代码.我们需要生成的Items和Notes这两个表,但是我们今 天还不准备把Notes一起搞定,为 了 防止因为没有实现Notes表而产生一些错误,我们打算在 这里把脚手架代码都生成好.就像盖房子一样,脚手架允许你先建好一个墙而把其它的以 后再来实现. W:\ToDo>ruby script/generate scaffold Item W:\ToDo>ruby script/generate scaffold Note 注意:这个操作会清空先前的item和note而没有任何后悔的余地. Items表 MySQL table defintion Items表中的各个字段的意思分别如下: • done - 1 意味着这件事情已经完成 • priority – 1 (高) to 5 (低) • description – 简述这个任务 • due_date – stating when it is to be done by • category_id – 外键指向category的一个分类 • note_id – 外键指向note表. • private – 1标识这个任务是私有的. The Model 作者:John McCreesh (http://rails.homelinux.org) 翻译:Ysl@1ster..! (http://www.1steam.cn/1ster/) 17 链接可用性 使用 item_id 字段来和 Category 表建立 belongs_to 和 validates_associated 关系. 参考:Documentation: ActiveRecord::Associations::ClassMethods 验证输入 • validates_presence_of 保证限制的字段不能为空. • validates_format_of 使用正则表达式来匹配输入. • Rails 会把用户输在数字字段里面的东西转换成数字,如果你向得到用户的具体输入,你可以 使用_before_type_cast 来验证. • validates_inclusion_of 检查用户的输入是不是在指定的范围内. • validates_length_of 检查长度是不是在指定范围内. 参考:Documentation: ActiveRecord::Base 视图的其它要点 在模板和层间共享数据 到目前,我们可以看到所有的模板开头都包含着相同的代码,所以最好是把它们写在一个公用 的模板层中,删除 app\views\layouts\下所有的.rhtml 文件,使用一个公用的 application.rhtml 来 替换它们. app\models\item.rb class Item < ActiveRecord::Base belongs_to :category validates_associated :category validates_format_of :done_before_type_cast, :with => /[01]/, :message=>"must be 0 or1" validates_inclusion_of :priority, :in=>1..5, :message=>"must be between 1 (high) and5 (low)" validates_presence_of :description validates_length_of :description, :maximum=>40 validates_format_of :private_before_type_cast, :with => /[01]/, :message=>"must be 0 or 1" end app\views\layouts\application.rhtml <%=h @heading %>

<%=h @heading %>

<% if @flash["notice"] %> <%= @flash["notice"] %> <% end %> <%= @content_for_layout %> 作者:John McCreesh (http://rails.homelinux.org) 翻译:Ysl@1ster..! (http://www.1steam.cn/1ster/) 18 为了明显起见,我这里把 public/stylesheets/acaffold.css 这个文件名字改为 ToDo.css,在这里面我 们可以定义颜色,表的形式等喜欢的样式.另外,回到原来的话题,留意下在两个文件间是怎 么共享变量 heading 的,也就是说,你可以在这个模板上动态显示你需要显示的模板信息. ToDo 列表界面 这里我们想得到的是一个基于 PalmPilot 或者 PDA 上看到的样式,如下图 4 展示的那样: 要点如下: • 点击随上面的那个"勾号"会清除所有标记为对号的条目. • 可以通过点击‘Pri’, ‘Description’, ‘Due Date’, and ‘Category’ 的列首来排序. • ‘Done’的值(0/1)可以转化成对号. • 超过due date 的被标识成红色. • note 状态标识成 ‘note’ 图标. • ‘Private’ 字段的值(0/1)被转换成锁的符号. • 每个条目都可以点击右边的那个"垃圾箱"标识进行删除. • 用比较好看的格式进行显示. • 可以单击下面的‘New...’按钮来新增条目. 图 4 'To Do' screen 显示这个格式的模板如下(The template used to achieve this is built up as follows:) app\views\items\edit.rhtml (excerpt) <% @heading = "Edit To Do" %> <%= error_messages_for 'item' %> <%= form 'item', :action => 'update' %> 作者:John McCreesh (http://rails.homelinux.org) 翻译:Ysl@1ster..! (http://www.1steam.cn/1ster/) 19 点击清除已经完成的条目 用link_to_image创建一个可以被点中的图片,link_to_image会在 pub/images目录下寻找一个.png 后缀的图片,点击这个图片会运行一个指定的方法. 添加:confirm 参数能自动实现一个弹出对话框. 参考:Documentation: ActionView::Helpers::UrlHelper 选择弹出对话框的"OK"按钮,就会调用这个 purge_completed 方法,当然这个方法需要造控 制器中自己定义.如下: Item.destroy_all 方法删除 Items 表中所有 done 值为 1 的记录,并返回到 list 动作. 参考:Documentation: ActiveRecord::Base 点击列首改变排列顺序 点击 Pri 图标将调用 list_by_priority,和前面一样,这个方法也需要你在控制器中自己来定义, app\views\items\list.rhtml <% @heading = "To Do List" %>
<%= render_collection_of_partials "list_stripes", @items %>
<%= link_to_image "done", {:controller => 'items', :action =>"purge_completed"}, :confirm => "Are you sure you want to permanently delete completed To Dos?" %> <%= link_to_image "priority",{:controller => 'items', :action =>"list_by_priority"}, "alt" => "Sort by Priority" %> <%= link_to_image "description",{:controller => 'items', :action =>"list_by_description"}, "alt" => "Sort by Description" %> <%= link_to_image "due_date", {:controller => 'items', :action => "list"},"alt" => "Sort by Due Date" %> <%= link_to_image "category", {:controller => 'items', :action =>"list_by_category"}, "alt" => "Sort by Category" %> <%= show_image "note" %> <%= show_image "private" %>    

app\controllers\items_controller.rb (excerpt) def purge_completed Item.destroy_all "done = 1" redirect_to :action => 'list' end 作者:John McCreesh (http://rails.homelinux.org) 翻译:Ysl@1ster..! (http://www.1steam.cn/1ster/) 20 代码如下: 我们定义了一个默认的 list 方法,然后还创建了一个 list_by_priority 方法. 注意:find_all 的第一个参数指定条件(就想 SQL 中的 WHERE 语句)--我们这里想查找所有 的记录,所以把这个参数设置为 nil. 参考:Documentation: ActiveRecord::Base 注意到我们使用 render_action 'list'来调用了一个实际上并不存在的 list_by_priority 模板. 添加一个辅助器 我们展现的样式中,Note 和 Private列首是一个不能点击的图标,所以我打算写个小方法 show_image(name)来实现它. 一旦这个辅助器被控制器加载,如下显示加载: app\controllers\items_controller.rb (excerpt) def list @items = Item.find_all (nil,'due_date,priority') end def list_by_priority @items = Item.find_all (nil,'priority,due_date') render_action 'list' end app\helpers\items_helper.rb module ItemsHelper def self.append_features(controller) controller.ancestors.include?(ActionController::Base) ? controller.add_template_helper(self) : super end def show_image(src) img_options = { "src" => src.include?("/") ? src : "/images/#{src}" } img_options["src"] = img_options["src"] + ".png" unless img_options["src"].include?(".") img_options["border"] = "0" tag("img", img_options) end end app\controllers\items_controller.rb (excerpt) class ItemsController < ApplicationController helper :Items def index list render_action 'list' end 作者:John McCreesh (http://rails.homelinux.org) 翻译:Ysl@1ster..! (http://www.1steam.cn/1ster/) 21 就可以在视图中来使用它了. 参考:Documentation: ActionView::Helpers 用 JS 实现导航按钮 onClick 是和标准的 JS 技术,用来获取鼠标动作以实现导航到另外一个页面.Rails 处理这样的 URL 很方便,使用 url_for 动作就可以返回这个 URL. 参考:Documentation: ActionController::Base 子模板 我想另外制作 items 条的样式,然后再把它包含进来.Partials 可以达到这个效果,它们用 render_partial 进行调用,如下: 或者使用另外一个更加简洁的格式 render_collection_of_partials:,如下: 此外还有一种办法可以,使用另外一种命名规则,来包含_list_stripes.rhtm 这个模板,并把它显 示出来. 参考:Documentation: ActionView::Partials Rails 同时传递一个序列到子模板中,可以依据这个来隔行显示不同的颜色,一个最简单的办法 就是奇数行显示比较亮的颜色;而偶数行显示比较暗的颜色. 一个子模版看上去和模板是差不多的,如下:/**A sub-template looks very similar to a template:**/ <% for item in @items %> <%= render_partial "list_stripes", item %> <% end %> render_collection_of_partials "list_stripes", @items app\views\items\_list_stripes.rhtml "> <%= list_stripes["done"] == 1 ? show_image("done_ico.gif") : " " %> <%= list_stripes["priority"] %> <%=h list_stripes["description"] %> <% if list_stripes["due_date"].nil? %>   <% else %> <%= list_stripes["due_date"] < Date.today ? '' : "" %><%=list_stripes["due_date"].strftime("%d/%m/%y") %> <% end %> <%=h list_stripes.category ? list_stripes.category["category"] : "Unfiled"%> <%= list_stripes["note_id"].nil? ? " " : show_image("note_ico.gif")%> <%= list_stripes["private"] == 1 ? show_image("private_ico.gif") : " "%> <%= link_to_image("edit", { :controller => 'items', :action => "edit", :id =>list_stripes.id }) %> <%= link_to_image("delete", { :controller => 'items', :action => "destroy",:id => list_stripes.id }, :confirm => "Are you sure you want to delete this item?")%> 作者:John McCreesh (http://rails.homelinux.org) 翻译:Ysl@1ster..! (http://www.1steam.cn/1ster/) 22 用来判断是奇数行(lt_gray)还是偶数行(dk_gray)的 Ruby 代码很少,其实就一句话,如下: list_stripes_counter.modulo(2).nonzero? ? "dk_gray" : "lt_gray" 这句代码的意思就像它表现出来的一样,list_stripes_counter 除以 2 后得到的是不是 0 呀? 参考:Ruby Documentation: class Numeric 如果问号前面的语句是真,则返回冒号前面的,否则就返回冒号后面的. 参考:Ruby Documentation: Expressions dk_gray 和 lt_gray 这两个标签需要在样式文件中定义,如下: 注意:使用 if then else 语句来判断 list_stripes["done"]是不是为 1,如果是则显示一个"对号" 标识,否则就显示空白,代码如下: list_stripes["done"] == 1 ? show_image("done_ico") : " " 格式化日期 对指定的日期进行加亮显示也是很容易的,例如,针对过去的时间可以这样: list_stripes["due_date"] < Date.today ? '' : '' 另外,这里也需要编写样式 past_due. 控制在查找中丢失的值 如果用户删掉了一个在 ToDo 条中使用的分类信息,我们想系统可以把这个显示成‘Unfiled’, 我们可以这样做: 新增 ToDO 条目展示 如果用户按下"新增"按钮,将会发生什么呢?需要我们编码来是实现了,如下图: public\stylesheets\ToDo.css (excerpt) .lt_gray { background-color: #e7e7e7; } .dk_gray { background-color: #d6d7d6; } list_stripes.category ? list_stripes.category["category"] : 'Unfiled' 作者:John McCreesh (http://rails.homelinux.org) 翻译:Ysl@1ster..! (http://www.1steam.cn/1ster/) 23 为日期输入框创建一个下拉列表 date_select 会自动的为输入框产生一个下拉框,如下代码: date_select "item", "due_date", :use_month_numbers => true 参考:Documentation: ActionView::Helpers::DateHelper app\views\items\new.rhtml <% @heading = "New To Do" %> <%= error_messages_for 'item' %>
<% @item.priority = 3 %>
Description: <%= text_field "item", "description", "size" => 40, "maxlength" => 40%>
Date due: <%= date_select "item", "due_date", :use_month_numbers => true %>
Category:
Priority: <%= select "item","priority",[1,2,3,4,5] %>
Private? <%= check_box "item","private" %>
Complete? <%= check_box "item", "done" %>

作者:John McCreesh (http://rails.homelinux.org) 翻译:Ysl@1ster..! (http://www.1steam.cn/1ster/) 24 然而不幸的是,它会产生 2 月 31 这样的日期,这对 Rails 来说,当它把这个值插到一个”date” 的字段时,就会出现问题,当然,我们可以使用 rescue 来捕获这个异常,如下: 参考:Ruby Documentation: Exceptions, Catch, and Throw 从查询字段创建一个下拉框 还有一个我们几乎天天都会碰到的情况,在下面这儿个例子中: options_from_collection_for_select @categories, "id", "category", @item.category_id options_from_collection_for_select 从categories表中读取所有的字段,并且以这样的格式返回.这些记录将以匹配 @item_category_id作为待选择的选项.但是这还不足够,我们还可以使用html_escapes来处理它. 参考:Documentation: ActionView::Helpers::FormOptionsHelper 由常量创建一个下拉框 这是前面说的那个方法的简化版,我们可以在编码中写死这些等待选择的条目-这貌似不是个 好主义,因为保存着唉数据表中的数据是很容易修改的,但是写死在代码中就不容易修改了.但 是作为一个方法,我们还是提出来吧,你可以这样做: select "item","priority",[1,2,3,4,5] 同时你需要主义下怎么来预定这些值. 参考:Documentation: ActionView::Helpers::FormOptionsHelper 创建一个复选狂框 另外一个很常见的元素,复选框是这样实现的: check_box "item","private" 参考:Documentation: ActionView::Helpers::FormHelper 控制器 下拉框中的值来源于别的地方,这是控制器控制的. app\controllers\items_controller.rb (excerpt) def create begin @item = Item.new(@params['item']) if @item.save flash['notice'] = 'Item was successfully created.' redirect_to :action => 'list_by_priority' else @categories = Category.find_all render_action 'new' end rescue flash['notice'] = 'Item could not be saved.' redirect_to :action => 'new' end end 作者:John McCreesh (http://rails.homelinux.org) 翻译:Ysl@1ster..! (http://www.1steam.cn/1ster/) 25 锦上添花 制定样式 经过我们前面的这些操作和编码,我们的 ToDo 列表和新建按钮都可以正常工作了,但是为了 显示我们想要的风格,我们还需要如下这些修改. 总结 第三天,我们完成了 ToDo 列表,也完成了新建任务.但是还有一个让我比较苦恼的事情, 这个留给读者作为一个小练习去完成吧,所以,现在轮到你表现了. 看看我们的成果,我们使用 Rails 的工具创建了这个看上去和脚手架一点都不像的东西,可以 感觉到,这还是蛮简单的. app\controllers\items_controller.rb (excerpt) def new @categories = Category.find_all @item = Item.new end public\stylesheets\ToDo.css body { background-color: #c6c3c6; color: #333; } h1 { font-family: verdana, arial, helvetica, sans-serif; font-size: 14pt; font-weight: bold; } table { background-color:#e7e7e7; border: outset 1px; border-collapse: separate; border-spacing: 1px; } td { border: inset 1px; } .notice { color: red; background-color: white; } .lt_gray { background-color: #e7e7e7; } .dk_gray { background-color: #d6d7d6; } .hightlight_gray { background-color: #4a9284; } .past_due { color: red } 作者:John McCreesh (http://rails.homelinux.org) 翻译:Ysl@1ster..! (http://www.1steam.cn/1ster/) 26 Rails 第四天 Notes 表 Model 这个表包含 ToDo 列表的每个条目的备注信息,当然,这些信息也是可以存放在 Items 表中的, 但是你如果把它按照我这里分开来放,你会学到更多的 Rails 知识:) 首先来创建表 notes,DLL 如下: 这个表对应的模型也没有什么好说的,都是我们以前接触过的知识,如下: 但是需要注意的是,别忘了在 Items 模型中添加链接,如下: 使用一个 Model 维护外链接 我们将要写的代码的作用是允许用户针对每个 Item 添加一个备注(Note)。但是你想过没有,当 用户删掉一个具有备注信息的 Item 时,发发生什么样子的事情呢?很显然,我们需要使用特定 的方法来找到相应的备注信息,并且把它删掉,也就是说,我们不能留下孤立的备注信息。 在 MVC 模式里,起这样作用的代码应该放在 Model 中,你也许会问为什么?想像看,在上面 例子中,我们可以点击 Item 后面的“垃圾箱”图标来删除这个 Item,我们也可以通过点击“ 号”来清除被标记为“对号”的 Item。所以,我们把上面的代码放在 Model 中,不管你怎样 删除这些 Item,它都能很好的工作,不是吗? Notes table CREATE TABLE notes ( id smallint(6) NOT NULL auto_increment, more_notes text NOT NULL, created_on timestamp(14) NOT NULL, updated_on timestamp(14) NOT NULL, PRIMARY KEY (id) ) TYPE=MyISAM COMMENT='Additional optional information for to-dos'; app\models\note.rb class Note < ActiveRecord::Base validates_presence_of :more_notes end app\models\item.rb (excerpt) class Item < ActiveRecord::Base belongs_to :note 作者:John McCreesh (http://rails.homelinux.org) 翻译:Ysl@1ster..! (http://www.1steam.cn/1ster/) 27 这段代码的意思是,当你准备删除一个 Item 前,首先在 Notes 表中寻找和和它的 Note_id 相同 的 id 那条备注,并且把它删掉,如果找不到,那自然就算了,呵呵。 参考:Documentation: ActiveRecord::Callbacks 视图层 在控制器之间传递 尽管我们可以使用“脚手架”来生成完整的 CRUD 操作代码,但我们可不想用户如此直接的使用 这个功能。于是,我们想让用户在 ToDo 编辑界面时,点 击“ 备注”标识来增加相应的备注信息。 插图 6 :在编辑界面上创建一个备注 另外,一旦一个备注被成功创建了,我们还可以点击相应的按钮来编辑和删除它,如下图 7: app\models\item.rb (excerpt) def before_destroy unless note_id.nil? Note.find(note_id).destroy end end 作者:John McCreesh (http://rails.homelinux.org) 翻译:Ysl@1ster..! (http://www.1steam.cn/1ster/) 28 插图 7: 编辑或者删除备注信息 首先来看看 ToDo 的编辑界面的代码是怎样的,特别要注意的是如何根据备注的油污来选择不同 的按钮,同时注意如何在控制器之间传递信息,代码如下: 作者:John McCreesh (http://rails.homelinux.org) 翻译:Ysl@1ster..! (http://www.1steam.cn/1ster/) 29 app\views\items\edit.rhtml <% @heading = "Edit To Do" %> <%= error_messages_for 'item' %>
<%= hidden_field "item", "id" %> <% if @item.note_id.nil? %> <% else %> <% end %>
Description: <%= text_field "item", "description", "size" => 40, "maxlength" => 40 %>
Date due: <%= date_select "item", "due_date", :use_month_numbers => true %>
Category: <%= link_to_image 'edit_button', :controller => 'categories', :action =>'list' %>
Priority: <%= select "item","priority",[1,2,3,4,5] %>
Private? <%= check_box "item","private" %>
Complete? <%= check_box "item", "done" %>
Notes: None <%= link_to_image "note", :controller => "notes", :action => "new", :id =>@item.id %><%=h @item.note.more_notes %> <%= link_to_image "edit_button", :controller => "notes", :action => "edit",:id => @item.note_id %> <%= link_to_image "delete_button", {:controller => "notes", :action =>"destroy", :id => @item.note_id }, :confirm => "Are you sure you want to delete this note?" %>

作者:John McCreesh (http://rails.homelinux.org) 翻译:Ysl@1ster..! (http://www.1steam.cn/1ster/) 30 先别急着去看 Notes 的界面是怎么实现的,我们还需要在编辑的时候初始下拉框的值呢,如下: 编辑存在的备注信息也很简单,如下: 当我们编辑完成或者删除备注信息后,我们希望再回到 ToDo 界面,可以由如下代码控制: app\controllers\items_controller.rb (excerpt) def edit @categories = Category.find_all @item = Item.find(@params['id']) end def update @item = Item.find(@params['item']['id']) if @item.update_attributes(@params['item']) flash['notice'] = 'Item was successfully updated.' redirect_to :action => 'list' else flash['notice'] = 'Item NOT updated.' redirect_to :action => 'list' end end app\views\notes\edit.rhtml <% @heading = "Edit Note" %> <%= error_messages_for 'note' %>
<%= hidden_field "note", "id" %>
Note:
<%= text_area "note", "more_notes", "cols" => 60, "rows" => 20 %>

作者:John McCreesh (http://rails.homelinux.org) 翻译:Ysl@1ster..! (http://www.1steam.cn/1ster/) 31 使用 Session 变量来保存和重现数据 大家注意到没,我们的这个创建操作有点麻烦了,按照我们的意思,是这样的: n 把新的备注放在 Notes 表中 n 在 Notes 表中找到最新创建的这个记录的 id n 把这个 id 值记录下来并返回给 notes_id,最后在 Items 表中记录 notes_id。 首先,我们来创建这个备注信息,并且把我们正在编辑的 Item 的 id 带过去,如下: Notes控制器的new方法会根据保存着在session中的id信息来创建一个新的备注,如下: 新建备注的模板也没有上面好奇怪的,如下: app\controllers\notes_controller.rb (excerpt) def update @note = Note.find(@params['note']['id']) if @note.update_attributes(@params['note']) flash['notice'] = 'Note was successfully updated.' redirect_to :controller => 'items', :action => 'list' else render_action 'edit' end end def destroy Item.find_by_note_id(@params['id']).update_attribute('note_id',NIL) Note.find(@params['id']).destroy redirect_to :controller => 'items', :action => 'list' end app\views\items\edit.rhtml (excerpt) <%= link_to_image "note", :controller => "notes", :action => "new", :id =>@item.id %> app\controllers\notes_controller.rb (excerpt) def new @session['item_id'] = @params['id'] @note = Note.new end 作者:John McCreesh (http://rails.homelinux.org) 翻译:Ysl@1ster..! (http://www.1steam.cn/1ster/) 32 这里的 create 方法再次取出 session 变量,按照它在 Items 表中寻找对应的记录,然后把刚刚 创建的的备注记录的 id 作为 note_id 存放在 Items 表中,最后返回 Items 控制器,实现代码如 下: 参考:Documentation: ActionController::Base 整理导航栏 到目前,我们这个系统剩下的事情不怎么多了,到我们来整理整理以前创建的模板,并加入导 航栏的时候了,分别如下: <% @heading = "New Note" %> <%= error_messages_for 'note' %>
<%= hidden_field "note", "id" %>
Note:
<%= text_area "note", "more_notes", "cols" => 60, "rows" => 15 %>

app\controllers\notes_controller.rb (excerpt) def create @note = Note.new(@params['note']) if @note.save flash['notice'] = 'Note was successfully created.' @item = Item.find(@session['item_id']) @item.update_attribute('note_id', @note.id) redirect_to :controller => 'items', :action => 'list' else render_action 'new' end end 作者:John McCreesh (http://rails.homelinux.org) 翻译:Ysl@1ster..! (http://www.1steam.cn/1ster/) 33 app\views\categories\new.rhtml <% @heading = "Add new Category" %> <%= error_messages_for 'category' %>
Category: <%= text_field "category", "category", "size" => 20, "maxlength" => 20 %>

app\views\categories\list.rhtml <% @heading = "Categories" %>
<% for category in @categories %> <% end %>
Category Created Updated
<%=h category["category"] %> <%= category["created_on"].strftime("%I:%M %p %d-%b-%y") %> <%= category["updated_on"].strftime("%I:%M %p %d-%b-%y") %> <%= link_to_image 'edit', { :action => 'edit', :id => category.id } %> <%= link_to_image 'delete', { :action => 'destroy', :id => category.id },:confirm => 'Are you sure you want to delete this category?' %>

作者:John McCreesh (http://rails.homelinux.org) 翻译:Ysl@1ster..! (http://www.1steam.cn/1ster/) 34 最后的业务逻辑入下图 8 显示,其它任何的“脚手架”代码――例如 show.rhtm 文件,都可以 删掉了,虽然它们是很好的代码,但是我们这里它不起作用了,那就应该删掉了。 图 8 整个程序的逻辑 创建主页 最后一步,我们需要修改用户输入 http://todo.显示的默认“Welcome to Rails”界面,有三步, 分别如下: n 修改 Apache 的 rewrite 规则 public\.htaccess (excerpt) app\views\categories\edit.rhtml <% @heading = "Rename Category" %> <%= error_messages_for 'category' %>
<%= hidden_field "category", "id" %>
Category: <%= text_field "category", "category", "size" => 20, "maxsize" => 20 %>

作者:John McCreesh (http://rails.homelinux.org) 翻译:Ysl@1ster..! (http://www.1steam.cn/1ster/) 35 # Enable this rewrite rule to point to the controller/action that should serve root. # RewriteRule ^$ /controller/action [R] RewriteRule ^$ /items/list n 把 public\index.html 修改为 public\index.html.orig。 n 浏览 http://todo 就能看到效果。 注意:Rails 0.10 增加了路由规则-可以制定 URL 规则。如果你使用的是 Rails 0.10 以上版本, 你还需要在路由文件中添加你的主页定义。 链接到主页 还有一点需要提出来的是,当你使用 URL 路由到 items/list 的时候,Rails 会丢失整个内部逻辑。 如果你简单的使用一个 link_to 格式-例如 link_to_image "done","purge_completed"等,你会发现 这个链接并不能正确工作。自然,如果你明确的指出控制器-例如 link_to_image "done", {:controller => 'items', :action =>'purge_completed'},自然是可以正常工作了。你只需要在你的首 页和首页局部模板中考虑这个问题,因为一旦你导航到其它地方,系统就会正确建立联系。 下载这个程序 如果你想下载一份这个程序,可以在 http://rails.homelinux.org 上找到下载链接,接下来你需要: n 使用 Rails 创建目录结构(参考第三页的 Running the Rails script) n 下载 todo_app.zip 包并把它放在新创建的 ToDo 目录中 n 修改 public\index.html 为 public\index.html.orig n 如果你想用数据库,那么执行:mysql -uroot -p < db/ToDo.sql 写在最后 希望这个文档对你有所帮助-我也希望收到你的反馈,无论好坏,Email 给 我 : jpmcc@users.sourceforge.ne 希望你使用 Rails 快乐的编码。。 关于译者: Name :Ysl@1ster..! Email: 1ster.Ysl@gmail.com BLog: http://www.1steam.cn/1ster/ config\routes.rb (excerpt) map.connect '', :controller => 'items', :action => 'list'
还剩34页未读

继续阅读

下载pdf到电脑,查找使用更方便

pdf的实际排版效果,会与网站的显示效果略有不同!!

需要 15 金币 [ 分享pdf获得金币 ] 1 人已下载

下载pdf

pdf贡献者

vb2005xu

贡献于2012-02-10

下载需要 15 金币 [金币充值 ]
亲,您也可以通过 分享原创pdf 来获得金币奖励!
下载pdf