跨越边界:Streamlined,第1部分

我生平首次参加马拉松培训。马拉松培训最有趣的方面——实际上,也是惟一的方面——就是提高不 断叠加所带来的影响。有时,我为了提高效率而进行专门为了改进身体条件而设计的长短跑。有时,在跑 步过程中,我学习避免小的错误,避免重复多余的姿势(多余的姿势对单个步幅没有太大影响,但却会在 整个 26.2 英里的跑步过程中浪费能量或伤害到我)。我每周都有提高,可每周之间的区别并不显著。但 是一个训练计划周期过后,我会从最初只能跑 4 英里提高到能跑 26.2 英里。软件开发也与此类似。如 果持续进行小的改进,消除多余的重复,您就会不断地累积提高,从而在今后的每个项目中都会做得更好 。

在这篇包含两部分的文章中,我把重点放在 Ruby on Rails 搭建上,这是一个能够在早期开发阶段削 减重复的 Rails 特性。第 1 部分介绍 Rails 搭建的限制和 Streamlined,Streamlined 是个代码生成 器,它高效地应用了元编程技术来消除更高层次上的重复。第 2 部分将进一步深入 Streamlined 的元编 程模型及其定制特性。

低级重复与高级重复

在整个 跨越边界 系列中,我介绍了通过降低重复和提高效率实现反复改进的语言和框架:

具备诸如 duck typing 这类特性的编程语言,通过使用更少的类型定义、减少纯粹为了支持编译器所 需要的代码数,能够提高灵活性和减少重复。

框架试图通过处理核心任务(例如持久性或事务)来提高效率和消除重复,这样就不必为每个新的应 用程序编写代码。

Ruby on Rails 通过利用公共规范消除重复配置,允许框架推断您的意图,而不是强迫您配置应用程 序特性(例如应用程序中特定的数据库表名称和列名称)。

就像所有高效的语言和框架必须做的那样,这些措施都把重点放在每个步骤上,或低级重复上。但是 一旦搭建了一个有效的基础,就可以把目标放得更高。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  endend

把文件 app/controllers/trails_controller.rb 编辑成像清单 2 一样:

清单 2. TrailsController 中的搭建

class TrailsController < ApplicationController  scaffold :trailend

输入 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 :locationendclass Location < ActiveRecord::Base  has_many :trailsend

把 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"  endend

输入 rake migrate 运行迁移。(要查看关于迁移的更多内容,请参阅 跨越边界: Rails 迁移。)

一下子就有了这么多设置。现在可以深吸一口气,总结以下到目前为止构建的内容:

有了一个针对赛道的数据库表和另一个针对地点的数据库表。

有了 Ruby 模型对象,对象之间还有 Rails 关系。

模型现在在赛道和地点之间有多对一关系。

有了处理模式中的变化的策略,也可以收回目前为止两个主要模式变化中的任何一个。

有了原始用户界面。

虽然可能想添加一些验证,但模型对象是适合生产应用的第一级 Rails 对象。许多 Rails 模型对象 之所以简单,是因为属性都是用元编程动态添加的。为了演示现在的关系,通过控制台添加一些数据。输 入 script/console 启动控制台,并输入清单 5 中的命令:

清单 5. 把数据添加到赛道和地点

>> trail = Trail.new=> #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=> #nil, "state"=>nil}, @new_record=true>>> location.city = "Durango"=> "Durango">> location.state = "Co"=> "Co">> location.trails < [#>, @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=> #"Hermosa Creek", "location_id"=>"1",  "id"=>"1", "description"=>"22 miles of mostly downhill singletrack.",  "difficulty"=>"easy"}>>> hc.location=> #"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 以其产品级质量的应用程序生成器超越了搭建。与使用搭建时一样,您可能需要扩展生成的 代码,但初始的应用程序从它本身来说,其功能性令人惊讶。

请下载初始 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 运用各种技术,例如在表格周围 使用 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,第1部分

相关文章:

你感兴趣的文章:

标签云: