凌华 さんのプロフィール张凌华フォトブログリストその他 ツール ヘルプ

ブログ


11月26日

Ruby on Rails 和 J2EE:两者能否共存?

两个 Web 应用程序框架的比较


developerWorks

文档选项


将此页作为电子邮件发送

将此页作为电子邮件发送

级别: 初级

Aaron Rustad (arustad@gmail.com), 技术架构师, Anassina, Inc.

2005 年 8 月 11 日

Ruby on Rails 是一个相对较新的 Web 应用程序框架,构建在 Ruby 语言之上。它被宣传为现有企业框架的一个替代,而它的目标,简而言之,就是让生活,至少是 Web 开发方面的生活,变得更轻松。在本文中,Aaron Rustad 将对 Rails 和传统的 J2EE 框架在架构上的一些关键特性进行比较。

Ruby on Rails 是一个 Web 应用程序框架,它的目标是为应用程序开发提供一条易行道。实际上,框架的支持者们声称 Ruby on Rails 开发人员的生产率最多是使用传统 J2EE 框架的 10 倍。(请阅读“Rolling with Ruby on Rails”一文,以获得关于这一声明的更多内容;请参阅 参考资料)。虽然这句话造成了 Rails 和 J2EE 社区相当大的争议,但争论中却很少谈及如何比较 Rails 和 J2EE 架构。本文将使用企业应用程序中常见的开源工具,对 Rails 框架和典型的 J2EE 实现进行比较。

什么是 Ruby on Rails?

要想找到用一句话描述 Rails 的简单说明,只需查看项目的 主页 即可:

Rails 是一个用 Ruby 编写的全栈的(full-stack)、开源的 Web 框架,可以使用它来轻松编写实际的应用程序,所需的代码也要比大多数框架花在处理 XML 上的代码少。

虽然我不能保证框架确实会提供它所承诺的轻松快乐,但是上面这句话确实很好地总结了 Rails 的品质。全栈包括:Web 服务器、处理 HTTP 请求和响应的框架,以及方便地把数据持久存储到关系数据库的框架。Rails 通过消除复杂的 XML 配置文件,使用 Ruby 语言的动态性质,帮助把静态类型语言中常见的许多重复代码减少到最少,努力使开发工作变得更容易。

Rails 和典型的 J2EE Web 堆栈

图 1 比较了 Rails 堆栈和典型的 J2EE Web 堆栈(包括 Tomcat servlet 容器、Struts Web 应用程序框架和 Hibernate 持久性框架)。

图 1. Rails 和 J2EE 堆栈的比较
stack

可以看到,Rails 堆栈和构成普通的基于 J2EE 的 Web 应用程序的组件之间的基本区别很小。两者都有用来执行应用程序代码的容器;都有帮助分离应用程序的模型、视图和控件的 MVC 框架;以及持久存储数据的机制。

MVC 框架

模型-视图-控制器(MVC)是应用时间相当长、应用面相当广的一个设计模式。它源自 Smalltalk;如今,几乎所有的 GUI 框架,包括 Web 和胖客户端,都以该框架为基础。MVC 有三个部分:模型,负责业务逻辑,包括应用程序状态和将在这个状态上执行的动作;视图,用来渲染和向用户呈现模型(在 Web 应用程序中,视图一般渲染为 HTML);控制器,定义应用程序的行为。有关 MVC 模式的详细解释,请参阅 参考资料

前端控制器

Struts 的 ActionServlet 和 Rails 的 DispatchServlet 都是前端控制器模式的例子;所以,它们提供了相同的功能。它们接受 HTTP 请求,解析 URL,把请求的处理转发给适当的动作。在 Struts 中,动作是扩展自 Action 的类;对于 Rails,动作是扩展自 ActionController 的类。两个前端控制器之间的主要区别是它们如何决定处理具体请求的动作。

使用 Struts,开发人员需要把特定请求的映射外部化到 XML 配置文件中的 Action 类。当首次装入 ActionServlet 时,它将解析这个文件,并准备接受请求。根据约定,以 .do 结束的请求被重定向到 ActionServlet,由 ActionServlet 分派到适当的 Action图 2 的 XML 是一个典型的映射。它告诉 ActionServlet 把叫作 deleteOrder.do 的请求转发到 controllers.order.DeleteOrderAction 作进一步处理。

Rails 采用了不同的方式。它没有依赖配置文件把请求映射到某一个动作,而是根据请求的 URL 发现适当的动作。从图 2 可以看到,URL http://localhost/order/delete/4 告诉 Rails 调用 OrderController 实例上的 delete 方法,并将 4 作为可用的实例变量。Rails 足够聪明,知道 /order 将映射到文件 order_controller.rb 中定义的一个控制器类。如果在控制器中定义了 find 方法,那么只要用 find 替代 URL 中的 delete,就可以调用这个方法。

图 2. Rails 和 Struts 中的 URL 映射
mappings

动作和模型

在 Rails 和 Struts 中,动作用来充当前端控制器和模型之间的桥梁。开发人员提供动作的现实,从而提供特定于应用程序的请求处理。前端控制器负责接受请求,并把请求传递到特定动作。图 3 演示了 Rails 和 Struts 基本的动作层次结构。

图 3. Rails 和 Struts 的动作层次结构
actions

动作是模型还是控制器?

ActionActionController 从技术上讲是 MVC 模式的控制器的一部分,因为它们对客户发起的事件进行响应。但是,在小型应用程序中,开发人员通常在这些类中对域或业务逻辑进行编码,所以在这些情况下,也可以把它们看作是模型的一部分。最佳实践建议:应当把域逻辑从控制器中抽象出来,放置在它自己的特定于域的类中。

Struts 要求开发人员扩展 Action 并覆盖 execute(),以处理请求。通常,每个 Action 类都提供了非常具体的工作单元。图 3 演示了三个特定动作:SaveOrderActionDeleteOrderActionListOrdersAction。前端控制器将调用 execute() 方法,传递给它许多有用的对象,其中包括 HTTP 请求和响应对象。ActionForm 是一个类,它可以方便地向视图来回传输并验证与表单有关的输入,ActionMapping 包含映射的配置信息,就像 图 2 的 XML 所描述的那样。

execute() 方法返回 ActionForward 对象,Struts 用这个对象来确定对请求继续进行处理的组件。一般来说,这个组件是一个 JSP 页面,但是 ActionForward 也能指向其他动作。开发人员必须清楚,Struts 创建的是 Action 的单一实例,并允许多个线程调用它的 execute()。这使请求处理变得更快,因为框架处理每个请求时不用频繁地创建新的 Action 实例。但是因为可以在多个线程之间共享单一对象,所以必须遵守适当的线程注意事项,因为其他线程可能会破坏在这个动作中保持状态的实例变量。

在 Rails 中,必须扩展 ActionController::Base,让模型参与到请求处理中。Rails 没有将 ActionController 的实例池化;相反,它为每个请求创建新的实例。虽然这对性能可能有负面影响,但是它可以让开发变得更容易。开发人员不需要关注 Struts 中存在的线程问题,因此,会话、请求、标题和参数都可以作为 ActionController 的实例成员来进行访问。ActionController 还是一个将特定域逻辑的所有处理组合在一起的合理场所。Struts 的 Action 类是细粒度的,它提供了非常具体的工作单元,而 Rails ActionController 则是粗粒度的,它将具体的工作单元模拟为一些方法。

清单 1清单 2 分别演示了典型的 Struts 动作和典型的 Rails 动作

表 1 提供了对两种方法的逻辑流程的比较,并演示了清单 1 和清单 2 的特定行中发生的事情。研究 DeleteOrderActionexecute() 方法和 OrderControllerdelete 方法,可以看出它们基本上是相同的。

表 1. execute() 和 delete 方法比较

步骤
Struts
Rails

框架调用动作
行 03: execute()
行 07: delete

从请求中检索到的 ID
行 06-07:从请求对象中取出
行 08:从所有参数的实例哈希中取出

从数据库删除订单记录
行 09、14-24:调用 delete() 方法,用 Hibernate 删除记录
行 09:用 ActiveRecord 删除记录

重定向到列出剩余订单
行 11:ActionMapping 对象查找将要转发处理的下一个组件。图 2 中的 XML 映射显示,success 将映射到 /listOrders,这是另一个 Action,负责查找剩余订单,并以 JSP 的形式呈现它们
行 10:用将调用的下一动作的哈希来调用 redirect_to 方法;在这种情况下,它只是调用同一控制器的 list 方法



回页首

持久性框架

持久性框架 用来在应用程序层和数据库之间来回移动数据。Hibernate 和 Rails 的持久性框架可以归类为对象/关系映射(ORM)工具,这意味着它们接受数据的对象视图,并将该视图映射到关系数据库内的表中。使用两种框架的目的都是为了减少与关系数据库有关的开发时间。但是,图 4 演示了两者在设计和配置上的一些根本区别。

图 4. Active Record 和 Hibernate 持久性框架的比较
persistence

Hibernate

Hibernate 基于 Data Mapper 模式,在这种模式中,特定的映射器类 Session 负责在数据库中持久存储和检索数据。Hibernate 可以持久存储任何 Java 对象,只要这个对象符合 JavaBean 规范。XML 映射文件描述了如何将类映射到数据库中具体的表,并描述了类与其他类的关系。

清单 3 显示了 Hibernate 映射文件的一个实例。class 标签把 Order 对象映射到 ORDERS 表,还有许多子标签用于描述其属性、ID 订单名称,以及同 models.Item 的一对多关系。清单 4 显示了 Order 类本身。

清单 3. Order.hbm.xml

...
01 <hibernate-mapping>
02    <class name="models.Order" table="ORDERS"
03        dynamic-update="true" dynamic-insert="false"
04        discriminator-value="null">
05
06 	<id name="id" column="id" type="java.lang.Long" 
07             unsaved-value="null">
08             <generator class="identity"/>
09         </id>
10 
11         <set name="items" lazy="false" inverse="false"
12            cascade="none" sort="unsorted">
13             <key column="id"/>
14             <one-to-many class="models.Item"/>
15         </set>
16 
17         <property name="name" type="java.lang.String"
18             update="true" insert="true"
19             access="property" column="name"/>
20     </class>
21 </hibernate-mapping>

清单 4. Order.java

01 public class Order {
02    private Set items;
03     private String name;
04     private Long id;
05 
06     public Long getId() { return id;}
07 
08     public void setId(Long id) { this.id = id;}
09 	
10     public Set getItems() { return items;}
11 	
12     public void setItems(Set items) { this.items = items; }
13 	
14     public String getName() { return name; }
15 
16     public void setName(String name) { this.name = name; }
17 }

Active Record

反射和元编程

Wikipedia 中(请参阅 参考资料)简要地把 反射 定义为“程序在运行的时候检查和修改其高级结构的能力”。在那里,还将 元编程 定义为“编写那些能够编写和操作其他其他程序(或它们自己),将其他程序作为自己的数据的程序,或者编写那些完成其他程序在运行时所做的部分工作的程序。”

以下代码将实现反射:

01 obj = "some_string"
02 if obj.respond_to?('length'):
03   puts "obj length = #{obj.length}" 
03 end
>> obj length = 5

这个代码将实现元编程:

01 class SomeClass
02 end
03 newMethod = %q{def msg() puts "A message!" end}
04 SomeClass.class_eval(newMethod)
05 aClass = SomeClass.new
06 aClass.msg
>> A message!

Rails 的 ORM 框架叫作 Active Record,它基于同名的设计模式。Martin Fowler 将 Active Record 描述为“包装数据库表或视图中数据行的对象,封装数据库访问,在数据上添加域逻辑”。在 Rails 中,每个域对象都将扩展提供 CRUD 操作的 ActiveRecord::Base

与 Hibernate 一样,Active Record 不需要映射文件;实际上,使用 Active Record 的开发人员不需要对 getter 或 setter、甚至类的属性进行编码。通过一些漂亮的词汇分析,Active Record 能够判断出,Order 类将映射到数据库中的 ORDERS 表。使用 Ruby 反射和元编程的组合,表的列可以变成对象的属性。访问器和调整器也添加了进来。

清单 5 显示了 Order 类的完成后的代码。在 Order 类体中有一行代码定义了它与 Item 对象的关系。has_many 是一个静态方法调用,符号 :items 是它的参数。ActiveRecord 用 :items 发现 Item 域对象,然后将这个 Item 对象映射回数据库中的 ITEMS 表。

清单 5. order.rb

01 class Order < ActiveRecord::Base
02	has_many :items
03 end

清单 5 那样编码的 Order 类在运行时提供了一些类和实例方法。表 2 提供了可在 Order 上使用的操作和属性的部分列表:

表 2. 在 Order 上可用的属性和操作

类方法
实例方法
属性

  • find(*args)
  • find_by_sql(sql)
  • exists?(id)
  • create(attributes)
  • update(id, attributes)
  • update_all(updates, conditions
  • delete(id)
  • delete_all(conditions)
  • ...
  • add_items
  • build_to_items
  • create_in_items
  • find_all_in_items
  • find_in_items
  • has_items?
  • items
  • items=
  • items_count
  • remove_items
  • id
  • name



回页首

结束语

虽然 Ruby on Rails 是一个非常新、令人兴奋的框架,并且在 Web 社区中已经引起了人们相当的兴趣,但是它的核心架构仍然遵循在 J2EE 中发现的基本模式。开发把两个框架分开的 Web 应用程序是一种合理的方法。Rails 更喜欢清楚的代码而不是配置文件,而 Ruby 语言的动态性质在运行时生成了大部分管道 代码。大多数 Rails 框架都是作为独立项目创建的,而且应用程序开发能够从一组同类组件受益。相比之下,典型的 J2EE 堆栈倾向于构建在通常独立开发的最好的组件之上,常常用 XML 进行配置并将组件组合在一起。

那么,是否应该考虑对下一个 Web 应用程序使用 Rails 呢?嗯,为什么不呢?它是编写得很好的组件堆栈,它们彼此之间工作得很好,并且基于行业接受的企业模式。Ruby 语言支持快速开发,并通过生产大多数应用程序管道来添加到框架。熟悉 Java 世界中的 MVC 和 ORM 框架的人们在用 Rails 表达自己的思想时没有任何困难。

与 J2EE 一起分发会不会有利于 Rails?绝对不要。J2EE 是一个已经设置好的标准,有许多固定的实现,而且,最重要的是,它是一个经过验证的技术。我建议您下载一份 Rails 的副本,并开始自己钻研它。许多可用的教程都是介绍性的,这些教程可以让您立即开始使用 Rails。再次声明,我并不能保证您会通过使用 Rails 得到快乐,但是我敢打赌您会感到满意。

使用 Ruby on Rails 快速开发 Web 应用程序

基于 Ruby 的框架用于快速开发,使用的是模型-视图-控制器模式


developerWorks

文档选项


将此页作为电子邮件发送

将此页作为电子邮件发送

级别: 初级

David Mertz, Ph.D. (mertz@gnosis.cx), 开发人员, Gnosis Software

2005 年 7 月 04 日

虽然还是测试版本,但 Ruby on Rails 已经成为进行 Web 应用程序开发的一个新途径。Rails 的成功之处在于能够自动化大部分常见类型的 Web 应用程序的创建,而且在您希望添加定制或者有特殊要求时,并不会受到限制。不仅如此,同那些只能完成 Web 应用程序单方面需求的自由软件(Free Software)库相比,Rails 包含了非常完整的一套工具。

Ruby on Rails 正在令整个 Web 开发领域受到震憾。让我们首先了解底层的技术:

  • Ruby 是一门免费的、简单的、直观的、可扩展的、可移植的、解释的脚本语言,用于快速而简单的面向对象编程。类似于 Perl,它支持处理文本文件和执行系统管理任务的很多特性。
  • Rails 是用 Ruby 编写的一款完整的、开放源代码的 Web 框架,目的是使用更简单而且更少的代码编写实际使用的应用程序。

作为一个完整的框架,这意味着 Rails 中的所有的层都是为协同工作而构造的,所以您不必自己再重复,可以完全只使用一门单一的语言。在 Rails 中,所有内容(从模板到控制流再到业务逻辑)都是用 Ruby 编写的。Rails 支持基于配置文件和注释的反射(reflection)和运行时扩展。

本文详细介绍了 Rails 的组成部分,并介绍了它的工作原理。

Rails 介绍

关于 Rails,首先需要理解的是它的模型/视图/控制器(model/view/controller,MVC)架构。虽然这种技术不是 Rails 所特有的 —— 甚至不是 Web 应用程序所特有的(相对于其他程序),但是 Rails 具有非常清晰而专一的 MVC 思维方式。如果您并不使用 MVC 方法,那么 Rails 的用处将大为降低(与遵循其模式的情况相比)。

模型

Rails 应用程序的模型部分主要是它所使用的底层数据库。实际上,在很多情形中 Rails 应用程序正是以一种受管理的方式对关系型数据库管理系统(RDBMS)中的数据执行操作的一个途径。

ActiveRecord 类是 Rails 的一个核心组成部分,它将关系型表映射为 Ruby 对象,使其成为控制器可以操作并能在视图中显示的数据。Rails 应用程序特别倾向于使用广为应用的 MySQL 数据库,不过也有与很多其他 RDBMS 的绑定,比如 IBM? DB2?。

如果您愿意,您可以添加 Ruby 代码来在应用程序模型中执行额外的验证,加强数据关联,或者触发其他操作。应用程序的 app/models/ 目录中的 Ruby 文件能够调用 ActiveRecord 的多种验证方法。不过,您也可以将模型代码留作一个存根,而只是依赖保存数据的 RDBMS 的约束。例如,我在这个示例中所开发的应用程序只包含这个骨架模型代码(至少在开始时是):

清单 1. 骨架模型 app/models/contact.rb

				
class Contact < ActiveRecord::Base
end

控制器

控制器以其抽象形式执行应用程序的逻辑。也就是说,应用程序的 app/controllers/ 目录中的 Ruby 脚本能把模型数据导入为变量,保存回去,或对其进行修改和处理。不过,控制器不关心用户如何适当地显示或者输入数据。在通常的 MVC 模型中,这可以让用户能够以多种方式与同一控制器进行交互:本地 GUI, Web 界面,以及视力较弱的人使用的语音界面都可以与相同的控制器进行交互。

不过,Rails 不像那样非常通用;相反,它仅局限于在 Web 页中提供和收集数据。虽然如此,但是您可以修改那些 Web 页的布局 —— 颜色、字体、表格、样式表单,等等 —— 与控制器代码无关。

视图

Rails 视图是我们编写 Ruby 代码的地方。Rails 包含有一门用于 .rhtml 的非常好的模板语言,它将纯粹的 HTML 与嵌入的 Ruby 代码组合起来。 Rails 应用程序界面的最表层外观通常是由 CSS 样式表单控制的。.rhtml 格式是一种增强的 HTML。实际上,一个简单的 HTML 文件本身也是一个合法的 RHTML 模板,不过,不应该忽略 RHTML 为您提供的脚本控制。

RHTML 是真正的模板格式 —— 不仅是在 HTML 中嵌入代码的方式 —— 这是一种更为有效的方法。如果您熟悉 PHP,那么可以考虑 PHP 本身与 Smarty 模板之间的对照。也就是说,嵌入的脚本只是将代码与未被解释的 HTML 混合在一起;当需要向客户机输出某些内容时,代码部分仍要负责执行 print 语句。

与之不同的是,模板引擎向 HTML 添加了一组定制的标签,让您能够将条件、循环以及其他逻辑作为增强的 HTML 标记的一部分来表示。



回页首

生成代码

Rails 所提供的工具主要是一组代码生成器。相对于那些强迫我使用严格的工作空间和 IDE 的开发环境,我更喜欢这种方法。 Rails 不会妨碍您,但是却会为您省去大部分手工编程的工作 —— 或者,通过提供“可自由获得的”初步(first-pass)支架(scaffolding),至少帮助您轻松将需要手工编码的工作分为多个部分。

支架 概念是 Rails 中的核心概念。非常简单的应用程序可能完全不用编码,让 Rails 在运行时动态地生成客户机 HTML 页面。第一遍生成代码时创建的只是粗略的支架;接下来您可以生成更详细的能够定制的控制器、视图和模型。不过在开始时不需要生成太多。

Rails 对其文件的组织是固定的而且非常普通的,不过这种组织相对严格。如果您试图强行使用其他文件和代码组织方式,那么您可能得付出努力去修改 Rails 环境。再者说,我找不到不使用 Rails 所提供的组织方式的理由;在大部分情况下,它“fits your brain”(Ruby 的支持者喜欢这样讲)。例如,如果您从头开始设计一个框架(至少如果您以“Ruby 方式”思考),那么这些目录名称及其组织可能与您的选择非常接近。



回页首

构建一个简单的应用程序

在 Ruby on Rails Web 站点上有一些教程,可以完整地引导您创建一个简单的 Rails 应用程序(见 参考资料)。这里的示例程序与之类似,因为正确开始构建 Rails 应用程序的方式是确定的。由于此介绍的长度相对较短,所以我 极力 推荐那些较长的教程中的一篇,以使得您能够打好更为全面的基础。

示例应用程序是一个基本的通讯录。它演示了创建应用程序的一般步骤:

  1. 生成模型(在此步骤中创建 MySQL 数据库和表)。
  2. 生成应用程序(包括生成基本代码和目录)。
  3. 启动 Rails(并配置数据库的访问)。
  4. 创建一些内容(包括生成支架模型和控制器,并告知控制器去使用那个支架)。

我们将详细研究每一个步骤。

生成 AddressBook 模型

对于任何应用程序,您需要做的第一件事情是为它创建一个存放数据的数据库。技术上这个步骤不必最先进行,不过需要在早期完成;应该在编写任何应用程序代码(甚至是自动生成的代码)之前创建数据库,这应该是显然的。所以,让我们在 MySQL 数据库中创建一个数据库,并在此数据库中创建第一张表。(阅读其他文档以了解如何安装运行 MySQL 或其他 RDBMS。)

我们假定 MySQL 已经安装并且可用。

清单 2. 创建 MySQL 数据库和表

[~/Sites]$ cat AddressBook.sql
CREATE DATABASE IF NOT EXISTS AddressBook;
USE AddressBook;
CREATE TABLE IF NOT EXISTS contacts (
  id smallint(5) unsigned NOT NULL auto_increment,
  name varchar(30) NOT NULL default '',
  created_on timestamp(14) NOT NULL,
  updated_on timestamp(14) NOT NULL,
  PRIMARY KEY (id),
  UNIQUE KEY name_key (name)
) TYPE=MyISAM COMMENT='List of Contacts';
[~/Sites]$ cat AddressBook.sql | mysql

在这第一张表中有些地方需要注意。最重要的是每一张表都必须拥有一个 id 列,列名称就是 id。Rails 使用主键列 id 来完成各种记录保持和引用任务。域 created_onupdated_on 是不需要的,不过,如果您使用了它们,那么 Rails 会自动地“在后台”维护它们;在大部分情况下使用这些时间戳没有什么不好。所以,您还要添加的“真正” 数据就只是通讯录内容的名称。

另一个稍微古怪的方面是,Rails 为不同的内容使用单数和复数的名称。根据上下文,各种条目会被重命名为单数或复数形式。表的名称应该使用复数格式。我没有使用不规则复数单词的经验;datumdata 等单词可能会令 Rails 出现问题。

生成 AddressBook 应用程序

既然已经拥有了一个能够交互的数据库,就可以创建 AddressBook 应用程序了。第一个步骤是简单地运行 rails 来生成基本目录和支架代码:

清单 3. 生成基本代码和目录

[~/Sites]$ rails AddressBook
create
create  app/apis
create  app/controllers
create  app/helpers
create  app/models
create  app/views/layouts
create  config/environments
create  components
[...]
create  public/images
create  public/javascripts
create  public/stylesheets
create  script
[...]
create  README
create  script/generate
create  script/server
[...]

我删减了运行 rails 的输出;所忽略了那些行只是提醒您已经创建的各种文件和目录。在您的系统上试运行它,浏览生成的所有文件。我已经在代码中显示了一些最重要的文件和目录。

运行 Rails

创建了 AddressBook/ 目录和所需要的子目录后,您需要执行一次惟一的初始配置。首先,通过修改 YAML 配置文件来设置数据库,如下:

清单 4. 配置数据库访问

[~/Sites]$ cd AddressBook
[~/Sites/AddressBook]$ head -6 config/database.yml # after editing
development:
  adapter: mysql
  database: AddressBook
  host: localhost
  username: some_user
  password: password_if_needed

最后,您需要提供数据。Rails 附带了它自己的单一功能的 Web 服务器,即 WEBrick,非常适用于我们的试验。您可能也会遵循 Ruby on Rails Web 站点上的说明来配置 Apache 或者其他服务器,以通过 FCGI(或者普通的 CGI,但是普通的 CGI 将会较慢)向 Rails 应用程序提供服务。

清单 5. 启动 WEBrick 服务器

[~/Sites/AddressBook]$ ruby script/server -d
=> Rails application started on http://0.0.0.0:3000
[2005-03-21 17:57:38] INFO  WEBrick 1.3.1
[2005-03-21 17:57:38] INFO  ruby 1.8.2 (2004-12-25) [powerpc-darwin7.8.0]

创建一些内容

要在 WEBrick 端口上看到一个欢迎页面,先前的步骤就足够了。例如,在我的本地系统中,现在可以访问 http://gnosis-powerbook.local:3000/。不过,为了操作定制数据库,需要生成稍微多一些代码。可以使用脚本 generate 来完成此任务,这个脚本创建在 AddressBook/ 应用程序目录中:

清单 6. 支架模型和控制器代码的生成

[~/Sites/AddressBook]$ ruby script/generate model contact
      exists  app/models/
      exists  test/unit/
      exists  test/fixtures/
      create  app/models/contact.rb
      create  test/unit/contact_test.rb
      create  test/fixtures/contacts.yml
[~/Sites/AddressBook]$ ruby script/generate controller contact
      exists  app/controllers/
      exists  app/helpers/
      create  app/views/contact
      exists  test/functional/
      create  app/controllers/contact_controller.rb
      create  test/functional/contact_controller_test.rb
      create  app/helpers/contact_helper.rb

注意,在相应的表名中,这里应该使用单数的 contact,而不是复数的 contacts

现在需要编辑一个或多个生成的文件(只需稍加编辑)来让控制器去使用支架:

清单 7. 告知控制器去使用支架

[~/Sites/AddressBook]$ cat app/controllers/contact_controller.rb
class ContactController < ApplicationController
  model :contact
  scaffold :contact
end

现在可以通过类似于 http://rails.server/contact/ 的 URL (在我的测试用例中是 http://gnosis-powerbook.local:3000/contact/)来查看和修改数据库的内容。输入一些数据后,它看起来如图 1 和图 2 所示:

图 1. 列出联系人
rails-contact1

图 2. 编辑联系人
rails-edit1



回页首

创建可定制的内容

前面的代码创建了一个查看和修改数据库的功能完全的界面,不过,所有格式化、显示以及业务逻辑(比如本来就有的)都由 Rails 动态完成,没有任何重大修改。为了创建一些更为定制的内容,需要生成更多一些代码。现在我们所需要的是让 Rails 显式地写出它在运行时隐式地生成的所有支架,以使得我们能够修改它。

图 8. 显式控制器和视图代码生成

[~/Sites/AddressBook]$ ruby script/generate scaffold Contact
  dependency  model
      [...]
      create  app/views/contacts
      exists  test/functional/
      create  app/controllers/contacts_controller.rb
      create  test/functional/contacts_controller_test.rb
      create  app/helpers/contacts_helper.rb
      create  app/views/layouts/contacts.rhtml
      create  public/stylesheets/scaffold.css
      create  app/views/contacts/list.rhtml
      create  app/views/contacts/show.rhtml
      create  app/views/contacts/new.rhtml
      create  app/views/contacts/edit.rhtml

现在有了更多一些要做的,所以尝试去修改一些内容。(注意此代码已经重新使用了复数格式 contacts,我不清楚其原因;现在我们需要接受它。)尝试在 CSS 中修改一些颜色和字体:

清单 9. 配置层叠样式表单

[~/Sites/AddressBook]$ head -8 public/stylesheets/scaffold.css
body { background-color: #ffe; color: #338; }
body, p, ol, ul, td {
  font-family: verdana, arial, helvetica, sans-serif;
  font-size:   13px;
}
td { border: 1px solid; }
a { color: #eef; background-color: #446; }
a:hover { color: #fff; background-color:#000; }

您已经拥有了这段代码,那么 contacts_controller.rb 做什么?就其操作而言,它比前面的代码中所出现的 contact_controller.rb 更为显式且可配置。控制器类似如下:

清单 10. 控制器 app/controllers/contacts_controller.rb

class ContactsController < ApplicationController
  def list
    @contacts = Contact.find_all
  end
  def show
    @contact = Contact.find(@params['id'])
  end
  def create
    @contact = Contact.new(@params['contact'])
    if @contact.save
      flash['notice'] = 'Contact was successfully created.'
      redirect_to :action => 'list'
    else
      render_action 'new'
    end
  end

如前所述,控制器的主要任务是将数据导入到变量之中。对象 Contact 是模型所提供的 ActiveRecord 对象-关系映射。变量 @contacts 或者 @contact 是它们的适当方法中所给出的数据。通过 URL 可以访问那些方法本身,比如 http://rails.server/contacts/show/2 (这一个方法显示出 id 为“2”的联系人)。

此示例中的控制器最终连接到了视图,即 RHTML 文件,它们使用的是控制器导入到变量中的数据值。例如,这里是 list 视图的一部分:

清单 11. 列出视图 app/views/contacts/list.rhtml

[...]
<% for contact in @contacts %>
  <tr>
  <% for column in Contact.content_columns %>
    <td><%=h contact.send(column.name) %></td>
  <% end %>
    <td><%= link_to 'Show', :action => 'show', :id => contact.id %></td>
    <td><%= link_to 'Edit', :action => 'edit', :id => contact.id %></td>
    <td><%= link_to 'Destroy', :action => 'destroy', :id => contact.id %></td>
  </tr>
<% end %>
[...]

方法 ContactsController.list 导入变量 @contacts,RHTML 中的流控制标签从数组中取出单个的记录。



回页首

修改模型

初始的模型只包含联系人的名字。不幸的是,本文中我已经没有余地扩展这个模型以使其包含实际的联系人数据,比如电话号码、地址、电子邮件等等。通常,那些数据应该存放在一张子表中,子表的外部关键字关联到表 contacts。Rails 模型会使用类似这样的定制代码来指明关联:

清单 12. 定制代码 app\models\phone.rb

class Phone < ActiveRecord::Base
  belongs_to :contact
end

在结束之前,让我们来对数据模型稍加修改,以查看它如何影响应用程序。首先,添加一列:

清单 13. 向模型添加 first_met 数据

$ cat add-contact-date.sql
USE AddressBook;
ALTER TABLE contacts ADD first_met date;
$ cat add-contact-date.sql | mysql

既然已经修改了底层的模型,http://rails.server/contact/ —— 支架的后台版本 —— 就会直接调整过来,不需要您做什么。控制器和视图是完全自动基于模型的。不过,在 http://rails.server/contacts/ 上应用程序版本使用了我们手工编写的文件,并不是那样自动化的。

list 视图将 Contact.content_columns 作为模板循环的一部分,能够自动查找 所有 的列,不管它们是什么。不过,edit 等其他视图已经被生成了,需要添加新的数据域。例如:

清单 14. 编辑视图 app/views/contacts/edit.rhtml

<h1>Editing contact</h1>
<%= error_messages_for 'contact' %>
<%= start_form_tag :action => 'update' %>
<%= hidden_field 'contact', 'id' %>
<p><label for="contact_name">Name</label><br/>
  <%= text_field 'contact', 'name'  %></p>
<p><label for="first_met">Known Since</label><br/>
  <%= date_select "contact", "first_met", :use_month_numbers => false %></p>
<input type="submit" value="Update" />
<%= end_form_tag %>
<%= link_to 'Show', :action => 'show', :id => @contact.id %> |
<%= link_to 'Back', :action => 'list' %>

那么您手工修改的应用程序看起来如何了呢?与默认的区别不太大,不过在图 3 和 4 中可以看到修改已经生效了:

图 3. 列出联系人,修改后
rails-contact2

图 4. 编辑联系人,修改后
rails-edit2



回页首

结束语

Rails 为您提供了开发灵活的 Web 应用程序的一种极其快速的途径;本篇介绍只是肤浅地涉及了如何使用 Rails。完整的框架包含很多实用的类和方法,能够完成基于 Web 的应用程序使用最多的操作。

Rails 的最大价值在于它孕育了一个成体系的“Rails 思维方式”,因为您所需要的所有支持代码令它变得完整。相对于只是给出要使用的原始材料的其他工具包和框架而言,这是一个巨大的优势。Rails 开发为您提供了将半成形(half-formed)的想法实现为功能完全的 Web 应用程序的一条坦途。