锻造坊 - Ruby on Rails 3

作者:Reuven M. Lerner

我仍然记得几年前我和一位朋友的即时通讯对话。他问我是否听说过 Ruby on Rails,我回答说虽然我听说过,但我还没有机会使用它。几个月后,我有了这样的机会,不久之后我就迷上了 Ruby 语言和 Rails 框架。

事实证明,我并不是唯一一个被 Rails 迷住的人。在过去的几年里,Rails 改变了 Web 开发的面貌。它吸引了许多人(包括我自己)使用 Ruby 语言,同时也影响、启发和激励了其他语言的 Web 开发框架。

当人们问我为什么喜欢使用 Rails 工作时,我告诉他们没有一个答案,因为我不认为 Rails 的任何一个部分是革命性的。相反,开发人员发现了数百甚至数千个在其他 Web 框架中传统上困难或令人恼火的事情,并试图磨平那些尖锐的边缘。例如,数据库迁移,它允许您分块定义和创建数据库模式,随着时间的推移逐步构建它,这为我节省了巨大的麻烦。Active Record 为数据库中的列自动生成 getter 和 setter 方法也是如此。还有控制器过滤器——这样的例子不胜枚举。

因此,我带着一些忐忑不安的心情开始了去年年底发布的 Rails 3 的开发。毕竟,Rails 2 中的大多数东西对我来说都运行良好——为什么要破坏一件好东西呢?此外,与用 Ruby 编写的竞争性 Web 框架 Merb 的开发合并,并没有给我留下特别必要的印象。诚然,Merb 更开放地接受替代的 ORM、HTML 和 JavaScript 库,但在 Rails 中使用这些替代方案并没有太痛苦。我们真的需要一次重大改革才能享受这些好处吗?

我很高兴地说,答案是,这确实是值得的。Ruby on Rails 3 与其前身相比变化不大;它看起来、感觉起来和行为起来仍然像我们熟悉和喜爱的 Rails。大多数变化都很小且微妙,但在那些不小的变化中,您可以理解它们背后的原因并快速适应这些变化。我很惊讶地发现我如此容易地适应了 Rails 3 的做事方式。

本月,我将对 Ruby on Rails 3 进行一次旋风式的巡览。我将描述哪些地方变化很大,哪些地方变化很小,哪些地方根本没有变化——至少对于使用它的开发人员来说是这样。在底层,Rails 已经发生了很大的变化,添加了许多新的类、模块和抽象,使其成为比以前更模块化的框架。这意味着,例如,如果您想实现一个新的数据库存储适配器,Rails 3 将为您提供工具,使其实施和集成相当容易。

没有改变的内容

在我深入探讨 Rails 3 中发生了哪些变化之前,可能值得先说一下哪些内容保持不变。首先,Rails 仍然忠实于模型-视图-控制器 (MVC) 范式,重点是“胖”模型——也就是说,您使用模型定义的类应该包含大部分业务逻辑,以及充当 ORM 和数据库连接。控制器仍然充当中间人的角色,接受来自用户的连接并以任何适当的格式呈现输出。视图包含 HTML 和逻辑(尽可能少的代码)的组合,使您可以显示从个性化菜单和好友列表到 XML 甚至 PDF 输出的所有内容。

Rails 的其他经典领域,旨在支持您的主要 MVC 设计,也仍然存在。助手方法(Helpers),允许您从视图中删除代码的方法,仍然存在。lib 目录也是如此,您可以(并且应该)仍然将自定义类和模块放入其中,包括那些将被多个控制器或模型使用的类和模块。public 目录仍然是静态资源的存储库,从 JavaScript 和 CSS 文件到可以按原样提供的图像和 Flash。

Rails 3 当然比以前更开放地接受替代方案。当您创建应用程序时,您可以指定如果您喜欢使用其他东西代替 Active Record、Prototype 和/或 test-unit,则可以删除它们。但这些都是选项,这意味着对于想要使用与 Rails 2 中相同的系统的程序员来说,无需进行任何更改。

虽然我没有亲自对 Rails 3 与 Rails 2 的性能进行基准测试,但毫无疑问,它感觉比 Rails 2 更快。如果您使用 Ruby 1.9.2 运行 Rails 3,则尤其如此,该语言的最新版本在许多方面都比 Ruby 1.8.7 快得多。在使 Rails 3 更模块化和比 Rails 2 快得多方面,人们付出了大量的努力,这很明显。

小变化

话虽如此,但一旦开始使用 Rails 3,就会立即注意到许多小变化。首先,Rails 不再在 script 目录中有一堆不同的命令。相反,您使用全局 rails 命令,并带有一个参数来指示您想要做什么。因此,要创建一个新的 Rails 应用程序,您可以使用rails new appname。但是,要进入控制台,您需要输入rails console或者,为了照顾打字慢的人,rails c。同样,您可以使用rails generate来创建一个新的控制器、模型、资源或支架(以及其他东西)。而且,您可以使用rails dbconsole来打开与当前环境中定义的数据库的客户端连接。

我必须承认,我从来没有遇到过 script 目录中的旧程序的问题。与此同时,这意味着您的 Rails 应用程序目录在很大程度上可以是空的,因为脚本将来自全局 Rails 安装。

一个虽小但意义重大的变化是值在视图中显示的方式。在以前版本的 Rails 中,(默认)ERb 模板系统允许您按如下方式将值插入到生成的 HTML 中

<h1>Welcome back, <%= current_user.name -%>!</h1>

<% %> 括号表示应该评估 Ruby 代码,而 <%= %> 括号告诉 Rails 将该评估的结果值插入到呈现的 HTML 中。这种方法的问题是,用户在注册时不仅可能输入文本,还可能输入 <script> 标签和 JavaScript,从而导致从烦恼到 XSS(跨站脚本)攻击的所有问题。

在 Rails 2 中,解决方案是使用 “h” 助手方法,该方法将潜在的恶意标签和 JavaScript 转换为静态文本。这意味着有兴趣消除 XSS 攻击的开发人员必须在每组可能包含用户生成输入的 <%= %> 括号中使用 “h” 方法。

Rails 3 颠倒了这个默认值。现在,所有字符串在显示给用户之前都经过清理和处理。如果您想允许显示 HTML 标签(以及潜在的 JavaScript),您需要使用 “raw” 助手方法,该方法执行与 “h” 以前执行的操作相反的操作

<h1>Welcome back, <%= raw current_user.name -%>!</h1>

另一个变化,但稍微重要一点的是,从诸如 remote_form_for 之类的助手方法中切换到非内联 JavaScript。相反,Rails 在 HTML 标签上设置属性;然后,这些属性可以被回调函数用来调用 JavaScript。转向非侵入式 JavaScript 是一个受欢迎的变化,它将在很大程度上消除视图中不必要的 JavaScript 代码。

Active Record

Rails 王冠上的宝石仍然是 Active Record,它是一个 ORM,允许您使用对象进行思考和编码,同时(在您的脑海深处)知道每个对象实例都被存储到关系数据库表中并从中检索出来。

Active Record 中最大的变化是您用来指定查询的语法。在 Rails 2 中,您会这样写

@people = Person.all(:conditions => "created_at > '2010-jul-14'",
       :order => "last_name ASC, first_name ASC",
       :limit => 10)

Rails 3 引入了 “Active Relation”,这是一个 Active Record 使用的库,它改变了您构建这些查询的方式。从表面上看,这些变化可能看起来很烦人和琐碎。您将把上面的查询重写为

@people = Person.where("created_at => '2010-jul-14'")
               .order("last_name ASC, first_name ASC")
               .limit(10)
               .all

您首先会注意到的是,查询已被分解为几个方法。您会注意到的第二件事是 .all 调用位于末尾。这是因为在您调用 .all 之前,数据库中不会执行任何操作。这意味着您可以混合和匹配方法调用,为您的查询添加额外的条件和复杂性,直到您最终决定调用它。因此,您可以随着时间的推移构建查询,将其作为参数传递给方法,从方法返回它或有条件地添加到它——所有这些都在数据库上调用查询之前。

除此之外,Active Record 似乎(再次,从表面上看)变化不大。现在可以使用稍微不同的语法调用验证,突出显示您要验证的属性的名称,而不是验证本身。因此,您可以这样说,而不是这样说

validates_presence_of :email
validates_uniqueness_of :email

您现在可以这样说

validates :email, :presence => true, :uniqueness => true

我仍然不确定我更喜欢哪种语法。幸运的是,至少目前,旧语法仍然有效,并且不会引发弃用警告。

在底层,Active Record 发生了很多变化。Active Relation 可能是最大和最明显的,但另一个是 Active Record 的模块化,这样验证现在位于一个单独的模块中。这可能不会影响大多数人,但这意味着如果您开发不同的 ORM,或者如果您正在实现非关系型 (NoSQL) 数据库(例如 MongoDB)的接口,您现在可以使用 Active Record 中的验证系统,而无需重新发明轮子。

总的来说,Active Record 3 继续发光发热。它是我使用 Rails 的最喜欢的原因之一,而且我知道我并不孤单,因为我欣赏它为帮助我的应用程序保持平稳运行所做的一切。

Bundler

您在 Rails 3 中可能遇到的最大变化可能是 Bundler。Ruby 编程最好的事情之一是社区可用的庞大 gems 库或可下载包。这是 Ruby 对 Perl 庞大的 CPAN 库的回应,尽管 Ruby gems 甚至还远不及 CPAN 的规模和范围,但它们正在以惊人的速度开发。

早期版本的 Rails 试图在主配置文件 config/environments.rb 中处理 gems 的包含。在此文件(或其任何特定于环境的文件)中,您将放置一行如下所示的代码

config.gem 'will_paginate"

这样做将确保 will_paginate gem 可用并包含在内。定义了许多 Rake 任务来自动化测试已安装 gems、在系统上安装它们以及在应用程序中安装它们的过程。

Rails 3 改变了所有这一切,转而支持一个名为 Bundler 的程序。其思想是您在 Rails 应用程序的根目录中创建一个名为 Gemfile 的文件。然后您调用bundle install,它会遍历您的 Gemfile,确保所有 gems 都已安装,并创建一个名为 Gemfile.lock 的文件,该文件指定应用程序运行需要安装的精确版本。Gemfile 和 Gemfile.lock 文件共同帮助确保您需要的 gems 安装在您的开发和生产机器上,无论是在系统的 gem 目录中还是在应用程序中私下安装。

我必须承认,我仍然在习惯 Bundler,而且我还没有确信它有多么出色,或者它比我们以前配置 gems 的方式有多么优越。我也很可能忘记输入bundle install在运行我的服务器之前。

话虽如此,我确实看到了将所有 gem 要求和配置(无论环境如何)放在单个文件中的优势。它当然使在 Heroku 等托管系统上部署更容易,并且避免了我在使用不同环境中需要的各种 gems 时遇到的一些困惑。这可能是 Rails 3 唯一一个尚未让我对其优势感到震惊的功能,但切换到 Bundler 的痛苦相对较小,而且我感觉随着时间的推移,我会确信它是有用的,甚至更优越。

路由

我在此处介绍的 Rails 3 中的最后一个主要变化是路由。路由器是 Rails 的一部分,它将 HTTP 请求映射到控制器方法。如果用户发送一个请求GET /people/5,则由路由器确定 people 控制器的 show 方法的 ID 为 5。

多年来,Rails 一直试图通过鼓励使用 REST(表述性状态转移)来简化路由。也就是说,每个 URL 描述一个特定的对象,而 HTTP 动词(GET、POST、PUT、DELETE)描述您想要对该对象执行的操作。如果我只想查看第 5 个人,我可以这样说

GET /people/5

但是,如果我想更新第 5 个人,我会说

PUT /people/5

以及描述应更新属性的名称-值对。

在 Rails 2 中,您将在路由文件 (config/routes.rb) 中用这样一行代码描述一个资源

map.resources :people

在 Rails 3 中,事情变得更简单了。首先,您可以按如下方式声明资源

resources :people

就其本身而言,这不是一个很大的变化。但是,如果您需要添加 Rails 提供的标准七种方法(index、show、new、create、edit、update 和 destroy)之外的其他方法,您可以在一个块中这样做

resources :people do
  post "send_feedback", :on => :collection
end

这在旧语法中当然是可能的,但它有点复杂。

如果您只想添加一个简单的硬编码路由,您可以使用

get "home/faq"

Rails 3 足够智能,知道这意味着您希望将对 /home/faq 的 GET 请求重定向到 home 控制器并调用 faq 方法。

我必须承认,即使在使用了 Rails 路由多年之后,它对我来说似乎仍然有点像黑魔法。Rails 3 提供的简化非常受欢迎,因为我觉得我对它的作用以及它是如何完成它的有了更好的理解。最重要的是,我的 routes.rb 文件变得不那么复杂,也更容易维护,正如我之前提到的,这是使用 Rails 的最大原因之一。

您应该升级吗?

在如此热情地描述了 Rails 3 之后,您可能会期望我告诉您立即出去升级您可能编写的任何 Rails 2 应用程序。而且,确实,如果应用程序简单而小巧,那么这样的升级并不是一个坏主意。

但是,如果您像我一样,并且使用一些大型、复杂的应用程序,那么切换不太可能是一个快速或容易的过程。大量查询需要重写以使用 Active Relation。路由表需要更改,Gemfile 需要重写,并且您必须仔细检查您的助手方法是否存在 XSS 攻击。这并不意味着升级是一项艰巨的任务,但这不是一个晚上就能完成的事情。

对于任何考虑升级的人,我首先建议的是确保您的测试,尤其是您的集成测试,到位、通过并为您提供足够的测试覆盖率。一旦您有了这样的测试,您就可以开始升级过程,并在每个阶段检查您可能破坏了什么。

有一个名为 rails_upgrade 的插件可用;它提供了一个 Rake 任务,该任务会检查您的代码和配置,尝试确定您可能遇到问题的地方并将其指出给您。如果您有良好的测试覆盖率并运行rails_upgrade,您可能会知道潜在的问题在哪里,以及当您尝试使您的代码与 Rails 3 对齐时是否破坏了任何东西。

最后,升级到最新版本的 Rails 2(在撰写本文时为 2.3.10)是为升级到 Rails 3 做准备的好方法。如果您通读您的日志,您会发现许多弃用警告,这些警告旨在推动您朝着 Rails 3 兼容性方向发展。即使您不打算立即升级,使您的代码更接近 Rails 3 标准也不是一件坏事。

结论

Rails 3 是对已经很棒的 Web 开发框架的绝佳升级。在它开发期间,我带着一些忐忑不安的心情关注了博客文章和解释,想知道 Rails 3 与以前的版本会有多大的不同。我本不必担心。正如 Rails 在许多方面改进了我作为 Web 开发人员的体验一样,Rails 3 也通过许多小的更改对其前身进行了改进。我通常非常喜欢这些更改,并相信这表明 Rails 仍然有很多优势。在某些情况下,升级可能很棘手,但朝着这个方向努力是值得的,即使需要一段时间。应用程序的执行速度会因此而提高,您的开发速度和代码的可维护性也会提高。

资源

Ruby on Rails 的主页是 rubyonrails.org。该网站有指向下载、文档和社区提供的许多资源的链接。

一个很棒的社区资源是 “Rails Guides” 文档,它可以引导您了解 Rails 中的大量功能。这些指南对于 Rails 开发人员(无论新老)来说都是很好的读物。它们是 Rails 3 的最新指南,并且经常为计划升级的人指出与 Rails 2 的差异。

由 Ryan Bates 制作的每周 “Railscasts” 截屏视频一如既往地是知识和灵感的来源。2010 年的许多 Railscasts 都致力于 Rails 3 以及升级的意义。我在使用 Rails 3 之前多次观看这些视频,它们对我的工作产生了很大的影响。

rails_upgrade 插件可以从其 GitHub 主页 github.com/rails/rails_upgrade 下载和安装。

您可以在 gembundler.com 了解更多关于 Bundler 的信息。Yehuda Katz 还在 yehudakatz.com/2010/09/30/bundler-as-simple-as-what-you-did-before 发布了一篇关于 Bundler 及其对 Rails 开发人员的意义的博文。

最后,一些关于 Rails 3 的书籍已经开始出现。Pragmatic Programmers 最著名的书籍,由 Sam Ruby、Dave Thomas 和 Rails 创建者 David Hannemeier Hansson 编写的 Agile Web Development with Rails 仍然是该框架的可靠入门指南。由 Apress 出版,Cloves Carneiro Jr. 和 Rida Al Barazi 编写的 Beginning Rails 3,是一本不那么密集的入门书,可能是 Web 开发新手的一个很好的起点。

而且,尽管在撰写本文时它尚未出版,但 Manning 出版的 Yehuda Katz 和 Ryan Bigg 编写的 Rails 3 in Action 看起来确实是一本真正的赢家。我特别喜欢它使用 Cucumber 和 RSpec 这一事实。它还提到了生产 Rails 应用程序可能使用的第三方 gems 和服务,例如 Git、Hoptoad 和 paperclip。

Reuven M. Lerner 是一位资深的 Web 开发人员、架构师和培训师。他是西北大学学习科学专业的博士候选人,研究协作式在线社区的设计和分析。Reuven 与他的妻子和三个孩子住在以色列的莫迪因。

加载 Disqus 评论