级别: 中级 Bruce Tate (bruce.tate@j2life.com), 总裁, J2Life, LLC
2006 年 10 月 30 日 Ruby on Rails 是一种基于 Ruby 编程语言的高效的 Web 开发环境。Streamlined 是基于 Ruby on Rails 的一个快速发展的开放源码框架。Streamlined 综合了 Ajax、元编程、代码生成以及 Ruby on Rails 的强大功能,把 Rails 的生产力带到了一个新层次。
我生平首次参加马拉松培训。马拉松培训最有趣的方面——实际上,也是惟一的方面——就是提高不断叠加所带来的影响。有时,我为了提高效率而进行专门为了改进身体条件而设计的长短跑。有时,在跑步过程中,我学习避免小的错误,避免重复多余的姿势(多余的姿势对单个步幅没有太大影响,但却会在整个 26.2 英里的跑步过程中浪费能量或伤害到我)。我每周都有提高,可每周之间的区别并不显著。但是一个训练计划周期过后,我会从最初只能跑 4 英里提高到能跑 26.2 英里。软件开发也与此类似。如果持续进行小的改进,消除多余的重复,您就会不断地累积提高,从而在今后的每个项目中都会做得更好。
 |
关于本系列
在
跨越边界
系列中,作者 Bruce Tate 提出这样一个观点:当今的 Java 程序员通过学习其他方法和语言会得到其他思路。自从 Java 明显成为所有开发项目的最佳选择以来,编程前景已经改变。其他的框架正在影响搭建 Java 框架的方式,从其他语言学到的概念可以影响您的 Java 编程。您编写的 Python(Ruby、Smalltalk……)代码可以改变您处理 Java 编码的方式。
本系列介绍了与 Java 开发有根本不同但是却直接适用的编程概念和技术。在某些情况下,需要集成这些技术以利用它。在其他情况下,则可以直接应用这些概念。比起其他语言和框架能够影响 Java 社区的开发人员、框架甚至基本方法这一概念,单独的工具不是那么重要。
|
|
在这篇包含两部分的文章中,我把重点放在 Ruby on Rails 搭建上,这是一个能够在早期开发阶段削减重复的 Rails 特性。第 1 部分介绍 Rails 搭建的限制和 Streamlined,Streamlined 是个代码生成器,它高效地应用了元编程技术来消除更高层次上的重复。第 2 部分将进一步深入 Streamlined 的元编程模型及其定制特性。
低级重复与高级重复
在整个
跨越边界
系列中,我介绍了通过降低重复和提高效率实现反复改进的语言和框架:
- 具备诸如 duck typing 这类特性的编程语言,通过使用更少的类型定义、减少纯粹为了支持编译器所需要的代码数,能够提高灵活性和减少重复。
- 框架试图通过处理核心任务(例如持久性或事务)来提高效率和消除重复,这样就不必为每个新的应用程序编写代码。
- Ruby on Rails 通过利用公共规范消除重复配置,允许框架推断您的意图,而不是强迫您配置应用程序特性(例如应用程序中特定的数据库表名称和列名称)。
就像所有高效的语言和框架必须做的那样,这些措施都把重点放在每个步骤上,或低级重复上。但是一旦搭建了一个有效的基础,就可以把目标放得更高。Rails 的搭建特性试图通过公共应用程序类型(数据库支持的 Web 应用程序)消除重复。
 |
削减赘负
Rails 所做的削减数量惊人。重复配置、代码中重新阐述的规范,以及其他框架中的无效理念,在这个框架中都消失了。但是仍然存在大量的重复。对于所有的框架来说都是如此。请记住,日本汽车制造商不是靠造一辆车就威胁了奔驰和宝马公司的,而是通过不懈的改进。如果您为了搭建传统 Web 应用程序而在框架中寻找改进,那么仍然会找到大量可以削减的内容。
|
|
多数数据库支持的 Web 应用程序,几乎要为系统中每个主要的表都提供执行 CRUD 操作(创建、读取、更新和删除)的用户界面。 搭建这些用户界面应当自动进行,而不应当重复。 Rails 通过 搭建开始消除这种重复,搭建是一个特性,可以根据数据库表集合的内容构建默认的 CRUD 界面。使用 Rails,只用几个简单步骤,就可以从头开始构建一个搭建完整的应用程序。如果一直跟随 跨越边界 系列,那么以前就看过这些步骤。这次,我再把这些步骤简要介绍一下:
- 输入
rails trails 生成编排山地摩托车赛道的 Rails 应用程序。
- 用选中的数据库引擎创建叫作
trails_development 的数据库,并修改 trails/config/database.yml,以反映选中的配置。
- 切换到 trails 目录,,生成模型和控制器:输入
ruby script/generate model Trail (如果在 UNIX 上运行,可以省略 ruby) 生成叫作 Trail 的模型,输入 script/generate controller Trails 生成叫作 TrailsController 的控制器。
- 把文件 db/migrate/001_create_trail.rb 编辑成清单 1 那样:
清单 1. 初始迁移
class CreateTrails < ActiveRecord::Migration
def self.up
create_table :trails do |t|
t.column :name, :string
t.column :difficulty, :string
t.column :description, :text
end
end
def self.down
drop_table :trails
end
end
|
- 把文件 app/controllers/trails_controller.rb 编辑成像清单 2 一样:
清单 2. TrailsController 中的搭建
class TrailsController < ApplicationController
scaffold :trail
end
|
- 输入
rake migrate,运行迁移。
- 用命令
script/server 启动服务器,并把浏览器指到 localhost:3000/trails/list。
现在就已经得到了一个简单的能够工作的带有数据库支持的 Web 应用程序,可以进行基于 CRUD 的每个操作,如图 1 所示。可以看到主屏幕列出了每个项目和相关的图片,提供了 Ajax 窗口用来创建、读取、更新和删除项目。
图 1. 简单的 Rails 应用程序
到现在,只付出了很少努力,就到达了一个可以把应用程序开发带到更高档次的地步。Rails 演示人员总会展示搭建功能,因为它是如此之炫,而且对于调试和在匆忙之间为客户做演示,都极为有用。可以通过代码生成器生成搭建 —— 在这个示例中输入了 script/generate scaffold Trail Trails —— 或者在控制器中指定 scaffold 元编程标记。每种方式都有自己的用途。
添加关系
搭建确实有一些明显限制:它不处理关系,也没有利用优秀的 Rails Web 服务或 Ajax 支持。为了说明这些限制,要创建带有模型、视图和控制器的 Location。Location 与 Trail 之间存在一对多关系。搭建并不能协助该关系的管理。
创建 location 的模型(script/generate model Location)和控制器(script/generate controller Location Locations)。就像对 TrailsController 所做的那样,把 scaffold :location 添加到 location_controller.rb。要把 Location 和 Trail 编织在一起,两者间需要多对一关系,所以把 belongs_to :location 添加到 Trail,把 has_many :trails 添加到 Location,如清单 3 所示:
清单 3. trail.rb 和 location.rb 间的关系
class Trail < ActiveRecord::Base
belongs_to :location
end
class Location < ActiveRecord::Base
has_many :trails
end
|
把 db/migrate/002_create_locations.rb 编辑成清单 4 那样:
清单 4. locations 表的迁移
class CreateLocations < ActiveRecord::Migration
def self.up
create_table :locations do |t|
t.column :city, :string
t.column :state, :string
end
add_column "trails", "location_id", :integer
end
def self.down
drop_table :locations
remove_column "trails", "location_id"
end
end
|
输入 rake migrate 运行迁移。(要查看关于迁移的更多内容,请参阅
跨越边界: Rails 迁移。)
一下子就有了这么多设置。现在可以深吸一口气,总结以下到目前为止构建的内容:
- 有了一个针对赛道的数据库表和另一个针对地点的数据库表。
- 有了 Ruby 模型对象,对象之间还有 Rails 关系。
- 模型现在在赛道和地点之间有多对一关系。
- 有了处理模式中的变化的策略,也可以收回目前为止两个主要模式变化中的任何一个。
- 有了原始用户界面。
虽然可能想添加一些验证,但模型对象是适合生产应用的第一级 Rails 对象。许多 Rails 模型对象之所以简单,是因为属性都是用元编程动态添加的。为了演示现在的关系,通过控制台添加一些数据。输入 script/console 启动控制台,并输入清单 5 中的命令:
清单 5. 把数据添加到赛道和地点
>> trail = Trail.new
=> #<Trail:0x2446168 @attributes={"name"=>nil, "location_id"=>nil,
"description"=>nil, "difficulty"=>nil}, @new_record=true>
>> trail.name = "Hermosa Creek"
=> "Hermosa Creek"
>> trail.difficulty = "easy"
=> "easy"
>> trail.description = "22 miles of mostly downhill singletrack."
=> "22 miles of mostly downhill singletrack."
>> trail.save
=> true
>> location = Location.new
=> #<Location:0x240d1c4 @attributes={"city"=>nil, "state"=>nil}, @new_record=true>
>> location.city = "Durango"
=> "Durango"
>> location.state = "Co"
=> "Co"
>> location.trails << trail
=> [#<Trail:0x2446168 @errors=#<ActiveRecord::Errors:0x2411c9c @errors={},
@base=#<Trail:0x2446168 ...>>, @attributes={"name"=>"Hermosa Creek", "id"=>1,
"location_id"=>nil, "description"=>"22 miles of mostly downhill singletrack.",
"difficulty"=>"easy"}, @new_record=false>]
>> location.save
=> true
>> hc = Trail.find 1
=> #<Trail:0x147c588 @attributes={"name"=>"Hermosa Creek", "location_id"=>"1",
"id"=>"1", "description"=>"22 miles of mostly downhill singletrack.",
"difficulty"=>"easy"}>
>> hc.location
=> #<Location:0x6cc2f8 @attributes={"city"=>"Durango", "id"=>"1", "state"=>"Co"}
|
清单 5 向数据库添加了一条赛道和一个地点,由从 trails 中的 location_id 列指向 locations 中的 id 列的外键管理。模型对象足够健壮,可以作为应用程序的构建基础。但是,视图就是另一回事了。
关系问题
把浏览器指向 http://localhost:3000/trails/show/1,看到图 2 所示的屏幕:
图 2. Rails 搭建没有关系字段
在这里看不出 trail 和 location 之间的关系。还会注意到,搭建非常原始:它没有图片、没有 Ajax、没有公共标头或侧栏,也没有任何现代 Web 页面中常见的修饰。但重要的是通过 搭建,只花了几分钟就得到了一个相对复杂的应用程序。您可能并不指望这个简单特性能够生成健壮的代码,但是现在您可以把您的期望值抬高一点。
虽然搭建代表着对多数 Web 开发框架技术水平的显著提高,可它仍然有提高的余地,也应当如此。但是如果在此基础上构建,您会发现获益极多。这就像是从 13 英里开始马拉松训练,而不是从 4 英里开始。
搭建,像许多元编程技术一样,就是个运行时代码生成器。Rails 社区中的有些人认为搭建是有局限的,认为搭建还没有丰富到可以处理多数应用程序。其他人则认为搭建很好用,搭建的质量才是基本问题。这完全取决于应用程序的性质。如果正在构建一个重复的模式,那么会从构成搭建基础的元编程技术得到巨大收获。如果模板是充分可调整、充分丰富的,那么在框架中就能在更高层次上减少重复。现在开始介绍 Streamlined。
Streamlined:强化的搭建
自从 Rails 出现以来,各种形式的和各种大小的 Rails 插件一直在提升所有应用程序开发的抽象程度。像登录生成器这样的组件允许生成安全性。其他插件使得在 Rails 中处理 Web 服务更容易。Streamlined 以其产品级质量的应用程序生成器超越了搭建。与使用搭建时一样,您可能需要扩展生成的代码,但初始的应用程序从它本身来说,其功能性令人惊讶。
 | |
Streamlined 由 Relevance LLC 创建,脱胎于进行自动培训(叫作 Code Site )的一组开源工具,而且每次 Relevance 构建商业 Rails 应用程序时,它都会得到改进。最终,此团队生成了通用的 Streamlined 框架,可以解决跨越众多项目的公共问题。
|
|
请下载初始 alpha 版本的 Streamlined .gem 文件(参阅 参考资料)。切换到保存 .gem 的目录,并输入 gem install streamlined。所需要的所有内容都会自动安装。如果出现问题,可以通过 streamlined 的博客得到优秀的支持,也有商业支持可选。
现在是把 Streamlined 投入实践的时候了。首先,输入 script generate streamlined location trail,运行 Streamlined 生成器。当提示是否替换 locations 和 trails 控制器时,回答 y。
把浏览器指向 http://localhost:3000/locations/list 查看图 3 中的结果:
图 3. 默认的 Streamlined 应用程序
可以立即让 Streamlined 生成一个更完整的应用程序。把 Streamlined 列表与 图 1 中的列表比较。区别是惊人的:
- 默认应用程序处理关系,单击其中一个 Edit 链接就可以看到。在下一节会看到更多关于关系的内容,在这篇文章的第 2 部分中甚至会更多。
- 应用程序更好地运用样式表,并生成更复杂的样式表。 Streamlined 运用各种技术,例如在表格周围使用
<div>s,使得每个页面元素更容易进行样式处理。Streamlined 的 alpha 版本的样式处理有限,但是预期未来的版本会突破这个限制。
- 应用程序在左侧有默认的导航侧栏,在顶部有菜单和标头。这些菜单有更完整的默认行为,而且能够定制。
- 表格每一行都有代表编辑、显示或删除行的图片,应用程序还有额外的图片代表创建新条目、导出 CSV 以及把整个表导出为 XML。
这个页面看起来更像默认应用程序,而不太像不完整的搭建。这正是 Streamlined 的亮点。在深入之前,先对 Streamlined 的工作方式做个简单描述。
先决条件
要使用 Streamlined,先要有一个可以工作的数据库模式、一个使用经典 Rails 工具和规范(在这篇文章中已经见到)的模型。然后,用 script/generate streamlined model1, model2, 等等命令生成 Streamlined 界面。Streamlined 观察 Rails 的命名规范,并在处于开发模式时频繁地重新装入应用程序对象,这样只要刷新浏览器,就可以看到最新的代码变化。
像 Rails 搭建一样,Streamlined 是个元编程框架,用元数据构建默认应用程序,构建的程序可以用各种方式定制。框架查询两个元数据源:活动记录模型和每个模型对象的定制元数据文件。默认情况下,Rails 从活动记录内捕获到足够的元数据,构建复杂的用户界面。活动记录查询数据库表,获得表中数据之外的信息,并且维护您所提供的其他信息,例如主键、关系、字段、字段类型、字段大小。Streamlined 利用所有这些信息来提供默认应用程序,但是要调整应用程序,框架还需要更多数据。Streamlined 提供了额外的元数据来源。
快速查看 trails/app 下的目录,可以看到 Rails 的常见目录:models、controllers、views 和 helpers。但是还有第五个目录可用:streamlined。就是在这里指定额外的元数据。streamlined 目录中四个文件快速列表说明了问题:
- location.rb 和 trail.rb 包含同名模型的详细定制信息。
- streamlined_relationships.rb 包含活动记录中指定的关系的更多信息,例如 Streamlined 要如何呈现关系。
- streamlined_ui.rb 包含全局用户界面问题的配置信息,例如是否创建头、尾以及左侧导航栏。
Streamlined 立刻组合了代码生成(它生成可以修改的代码)和真正的元编程(它使用 Ruby 语言在运行时把代码动态地添加到应用程序)开始工作。Streamlined 生成日后可能要修改的静态内容和页面。例如,生成器直接把样式表和图片复制到您的项目。可以用真正的元编程或代码生成来创建视图,视图可能需要修改,也可能不需要修改。
特性
通过操作这个默认应用程序,可以对它提供了多少特性有些感觉。左侧的导航侧栏拥有针对每个所指定模型的链接——针对本文的模型就是赛道和地点。单击链接,会进入每个模型的主页面。标头有管理域内对象的一套默认链接,有上下文敏感帮助,还有关于页面。
在进入表格数据区时,会看到更为复杂的功能。有充当记录过滤器的文本框。要查看它的工作方式,请单击 + 链接添加新赛道,并输入一些数据。然后,在主窗口输入 Her。将看到列表被调整成只有字段中包含指定文本的条目。也可以单击任意一个列名,根据这一列对列进行排序。
继续操作下去,肯定会注意到优秀的 Ajax 功能。在这里的 CRUD 设置中使用 Ajax 的最大好处是在一个主屏幕上就能提供管理表所需要的全部内容,只有很少的弹出框(用来编辑、显示和删除)。Ajax 支持更丰富的用户体验、更简洁的应用程序路径和更好的用户反馈。
最后看看关系管理。请单击左侧侧栏上的 Locations 链接。然后单击 + 图片,添加新地点(试着添加 Moab,Utah)。单击赛道下的 Edit,并选择应当属于这个地点的赛道。请注意 Streamlined 默认记录了属于每个地点的赛道的数量。这个默认行为已经非常丰富了,但是我在第 2 部分还要用更复杂的优化对它进行定制。
与 Java 框架比较
目前为止,最流行的 Java™ 框架都不生成 搭建,更不用说应用程序了。部分原因是在这个领域在根本上缺少驱动创新的竞争。Ruby on Rails 正在改变这种局面。而且,可以假设,在 Web 框架发展了八年之后,应当有人已经构建出了类似的东西。
应用程序生成器在 Java 环境中一直没有成功。它们有一个重要的问题:过多地依赖代码生成器,但在元模型上,却缺乏能够对代码生成进行补充的坚实的元编程框架。这类框架可以提供短期的生产力提升,但是不能在长时间内持续改进。生成的代码通常太脆弱和复杂。除非有足够的能力在每次代码生成之间定制代码,否则时间一长就会失去生产力。Streamlined 确实支持代码生成,但只支持应用程序中不变的那些部分,或者应用程序中简单的可变部分——例如视图和样式表,而这些内容开发人员可以容易地修改和维护。
有两个看起来想正确地混合代码生成和元数据的 Java 框架,它们是 RIFE 和 JMatter(请参阅 参考资料)。我在这个系列中已经多次讨论过 RIFE,但是 JMatter 是新的。JMatter 框架拥有开源许可,也有商业报价。JMatter 基于 Hibernate 和 Swing,它允许根据元编程模型迅速地开发非常复杂的应用程序。Eitan Suez 这位 Java 圈中著名的发言人构建了 JMatter,以帮助快速地启动一项针对医疗实践的两层客户/服务器应用程序的 Java 开发。在将近两年的特化之后,JMatter 惊人地强壮,而且它的特性很容易与 Rails 和 Streamlined 对抗。如果 Jmatter 中的变化步伐能赶上 Ruby 社区的技术水平,那么它今后还会存在。
结束语
在这篇文章中,我介绍了 Rails 搭建、它的限制以及称作 Streamlined 的替代品。Streamlined 搭建得更完整,但到目前为止,它仍然还是搭建。在第 2 部分中,您将获得围绕 Streamlined 的元编程模型的更详细讨论,还将学习如何定制应用程序的关键部分。在这之前,您可以放飞思维、大量实践,继续跨越边界。
参考资料 学习
获得产品和技术
-
Streamlined:下载 Streamlined 应用程序生成器并试用。Streamlined 正在快速发展,所以要下载初始的 alpha 版本。
-
RIFE:基于 Java 的元编程框架,它通过强大的元模型为基于 CRUD 的应用程序提供了非常好的搭建。
-
JMatter:下载新的 JMatter 框架,这是本文作者所看到的针对基于 Swing 和 Hibernate 的两层的应用程序的最佳框架。
-
Ruby on Rails:下载开放源码的 Ruby on Rails Web 框架。
-
Ruby:从该项目的 Web 站点获取 Ruby。
讨论
关于作者  | 
|  | Bruce Tate 居住在得克萨斯州的首府奥斯汀,他是一位父亲,同时也是山地车手和皮艇手。他是 3 本最畅销 Java 书籍的作者,其中包括荣获 Jolt 大奖的 Better, Faster, Lighter Java 一书,最近又出版了 Spring: A Developer's Notebook 一书。他在 IBM 工作了 13 年,现在是 J2Life, LLC 的创始人兼顾问。他潜心研究基于 Java 和 Ruby 的轻量级开发策略和架构。 |
对本文的评价
|