本文发表于《程序员》杂志2009年10月刊。可能由于编辑的工作繁忙,发表的不是此最终版本。杂志发表版本中有些不恰当表述,对此造成
的困扰,深表歉意。
Rails之美,我总结的有这样几点:简洁 、透明、自由、开放、轻灵、丰富和优美。可能你已经感觉到,这些词汇大多展现的是感性的一面。没
错,Rails开发的每一天都是那么“畅快”,畅快背后其实就是这些生动的感触。笔者希望从这些简单的感触出发,结合实际的例子,来展示Rails真实的
美。
可能很多人在推荐别人使用Rails的时候,都会列举一个理由:简洁。的确,简洁是促使很多人开始学习和使用Rails的原因。那到底什么是简洁?简洁可
能代表少,简洁可能代表没有重复,简洁当然也代表复杂的对立面。
Rails是基于ruby语言的。动态语言带来的好处之一是代码量的急剧减少。有一个鲜活的例子,有一次跟客户进行pair,把曾经用Java实现
的一个900多行的类,缩减到了100行。客户很是惊讶。当然,纯粹量的减少可能并不代表什么,但至少带来了清晰和易读这两个对代码来说非常重要的特性。
因为动态语言的良好支持,Rails框架使重复的配置工作减少到了极致。比如在Java世界的大量OR
Mapping配置文件,在Rails里面不再需要。虽然现在Java世界的配置量也在不断地精简,但还是占据了一定的工作量。重复工作的减少,亦即工作
效率的提升。
作为Web开发领域的DSL,Rails提供的各种机制在各个层面极大地简化了开发的工作量和难度。比如ActionView提供的
FormHelper,简化了页面上form的生成;比如ActiveRecord提供的Association,简化了模型之间关联的维护。
举个association的例子,来看一下Rails的简洁之处。下面这两个模型是一对多的关系:一个lightbox有很多images。
class Lightbox < ActiveRecord::Base
end
class Image < ActiveRecord::Base
end
要删除一个lightbox,以及它的所有images,需要这样写:
@images = Image.find_by_lightbox_id(@lightbox.id)
@images.each do |image|
image.destroy
end
@lightbox.destroy
接下来,让我们给它们声明正确的关联关系:
class Lightbox < ActiveRecord::Base
has_many :images, :dependent => :destroy
end
class Image < ActiveRecord::Base
belongs_to :lightbox
end
则删除操作就变得简单了,且在语义和逻辑上更加明确和清晰:
@lightbox.destroy
DSL的一个目的是使某个领域的开发变得更加具体、简单和清晰。Rails框架是从一个现实项目中提炼出来的,这同时也证明了一句话:好的框架都不
是凭空想象出来的。
Rails还有很多其它方面可以体现它的简单。简单,就是美。
透明
项目开发过程中,让我觉得很痛快的一件事情是:基本上不需要借助任何外部的文档。
因为Rails本身是透明的,这首先是动态语言提供的好处。当需要了解任何一个方法的功能或者实现时,只需要跳到那个方法查看源代码即可。
同时,对大多数方法,Rails都提供了详尽的文档以及具体的示例。
开放的源代码,以及详尽的注释,让开发人员得以在一个“透明的环境”上进行开发。开发中可以彻底地了解所用工具的习性,这不可谓不是一件痛快的事
情。
自由
对一个问题,Rails往往都提供了多种解决方案。我们可以根据问题的场景,自由地选择合适的方案。
比如对于页面中form的生成,我们可以选择使用form_tag方法。但当这个form跟对象关联的时候,更好的选择是使用form_for方
法。结合text_field等helper方法,使页面上的元素跟对象的属性更加紧密地结合。
下面再举一个association的例子,来看看Rails如何表现自由的精神。声明多对多关系时,一般是这样的:
class Teacher
has_and_belongs_to_many :students
end
class Student
has_and_belongs_to_many :teachers
end
但有时我们需要利用中间表,并让它映射到一个模型。那么,可以这样来声明模型之间多对多的关联:
class Teacher
has_many :relations
has_many :students, :through => :relations
end
class Relation
belongs_to :teacher
belongs_to :student
end
class Student
has_many :relations
has_many :teachers, :through => :relations
end
选择的多样化带来的是自由。但根据需要和场合选择正确的方案,更加重要。
开放
Rails所体现的一点极其重要的精神是:开放。因为Rails从来不限制你去做任何事情。
有个项目是建立在一个遗留数据库上,并且大多数数据库表结构和遗留数据因为有些原因不能更改。但问题是表结构并不满足Rails的约定,下面列举一
些问题和解决办法来窥探一下Rails的开放精神所在。
问题1:单数表名
根据Rails的约定,表名都是复数形式的。比如一个User模型,对应的表名是users。而遗留数据库上的表名是单数形式:user。
解决方案
在environment.rb文件的配置初始化里,关闭默认的复数表名配置:
config.active_record.pluralize_table_names = false
问题2:type字段不代表单表继承
根据Rails的约定,type字段是单表继承的保留字段。当从数据库读取数据并实例化成对象时,它会根据type字段的内容来寻找相应的子类型。
但这里type字段并不代表单表继承。
解决方案
在模型里面声明另一个字段代表单表继承,比如:
set_inheritance_column :clazz
问题3:type字段的值不满足单表继承的约定
又出现另一个问题,type字段用来表示单表继承,但是它的值并不满足单表继承的约定。根据Rails的约定,type的值应该是类名。比如
ShoppingCart继承自ImageCollection,那么type的值应该是"ShoppingCart"。但在遗留数据库里,使用的
是"shopping_cart"。
解决方案
这里涉及到两个问题,一是在存储一个对象时要设置正确的type值,二是实例化成对象时,需要根据type值找到正确的子类型。通过查看源代码,发
现计算type值和子类型的分别是ActiveRecord上的sti_name和computer_type方法。大家都应该想到了解决方法,就是覆盖
这两个方法。对于ShoppingCart类,解决方案可以这样:
def sti_name
super.underscore.upcase
end
def compute_type(type_name)
super(type_name.downcase.camelize)
end
通过上面的例子,我们已经了解了Rails的开放。Rails有很多约定,但不代表强制。而且Rails的开放不仅限于此,比如你可以打开任何一个
类,往里面添加方法(当然,这是Ruby给予的权力)。举个例子,比如我们可以通过打开NilClass,来实现Null
Object模式(在有些情况下这种做法比较极端):
NilClass.class_eval do
def your_method
...
end
end
有些语言在天性上对程序员防备多于信任,他们总觉得赋予程序员过多的权力,会容易带来破坏。但其实防止破坏靠的应该是程序员的修炼和自我约束。语言,应该
以一种更加开放的态度赋予程序员更多的权利和自由。
轻灵
很多人都觉得Rails是一个庞然大物。但其实Rails并不庞大,DHH在迷思系列里面也解释过。而且,Rails可以轻松剔除任一可选组件。比
如要去除ActionController的benchmark组件,只要注释掉include
ActionController::Benchmarking,并删除相应的文件即可。
在这背后,其实有一个神奇的方法,叫做alias_method_chain。这个方法非常有用,它的设计理念很好地支持了Rails轻灵的特性,
因为它让Rails的各个可选组件都只是很“轻巧”地挂载在上面。继续用Benchmarking这个例子来了解一下它。
Benchmarking的功能是度量action的性能,并把结果输出到日志。这其实是对perform_action方法的增强。从
Benchmarking源代码中可以看到如下的代码:
alias_method_chain :perform_action, :benchmark
以及perform_action_with_benchmark方法的实现。
其实,alias_method_chain跟下面的实现是等价的:
alias_method :perform_action_without_benchmark, :perform_action # 为原来的方法建立别名
alias_method :perform_action, :perform_action_with_benchmark # 重定向原来的方法名到功能增强之后的方法
这种方式其实就是AOP的工作方式:ActionController::Base声明了perform_action方
法,但它对benchmarking一无所知,只要把Benchmarking模块包含进去,就获得了benchmark的功能
。
Rails通过这种方式实现了低耦合,我们可以轻松地选择去除非必要的所有可选组件。Rails并非庞大,它是轻灵的,但我们需要了解它。
丰富
发展到今天,
Ruby和Rails社区已经非常的活跃和强大。千万开源爱好者在不停地贡献着各种各样的Rails插件
和Ruby库。
比如认证系统插件:restful_authentication,分页插件:will_paginate等等,这些插件的出现帮助我们节省了很多
工作。并且,这些插件的实现都非常的优美,并不断地在优化和演进。
强大的社区支持,丰富的插件,让Rails开发变得更加容易。
优美
Rails的优美体现在很多地方,比如它本身就是一个REST风格的WEB架构。
但REST就代表着优美么?这不足以让人信服,还是举个例子吧。
假定有一个InvoicesController,现在需要生成一个invoice的pdf。我们可能会想到给controller添加一个叫做
download_pdf的action:
def download_pdf
invoice = Invoice.find(params[:id])
send_data(generate_pdf(invoice), :filename =>
"#{invoice.no}.pdf", :type => "application/pdf")
end
但从另一个角度看,其实pdf只是invoice这个资源的一种表现形式。而展现一个资源,更适合让show
action来做。用REST风格实现invoide的pdf下载:
def show
@invoice = Invoice.find(params[:id])
respond_to do |format|
format.html
format.pdf { render :pdf => generate_pdf(@invoice) }
end
end
用正确的方式做正确的事情,就是优美的体现。
反思
介绍了这么多Rails开发的优点,肯定有人不禁要提问:Rails开发难道就没有欠缺的地方么?
笔者也一样,在开发过程中不停地反思。比如为Rails创造最大声誉的“快速开发”,就值得谨慎看待。用Rails搭建的系统在代码量上确实少了很
多,但纯粹用代码量来衡量开发效率是不准确的。有几点思考:
1. 当今程序员不再是纯文本编辑时代,强大的IDE极大地提高了程序员的开发效率。但作为一种动态语言,Ruby目前还很难享受到这种好处。
2. 对于任何语言,要写出简洁优美高效的代码,都需要精雕细琢。
3. 随着语言的进步,开发效率真正的瓶颈越来越多地体现在业务逻辑上,而不是代码的编写上,特别是对于复杂系统而言。
从上文列举的那些例子中我们可以看到,Rails很美,但只有在正确使用它时才会很美。初学者可能会因为经验的缺乏落入一个又一个陷阱。不可否
认,Rails的性能问题一直是大家担心的。但系统的性能问题真的是Rails本身引起的么?看似优美的代码,背后是否做着一些“丑陋”的事情?希望能在
下篇讲述性能优化故事的文章中跟大家再次分享和探讨这些问题。
分享到:
相关推荐
让你知道在rails中如何使用路由,路由与URL是如何对应的。
rails server命令启动web服务器的默认端口号为3000,当然我们也可以自定义指定端口号。
Ruby for Rails中文版》.(美)David Black ).zip
( 《Web开发敏捷之道应用Rails进行敏捷Web开发(原书第4版)》.((美)Sam Ruby)
经典《程序员》杂志(2009年9月版) ...ThoughtWorks咨询师讲述Rails之美 技术改变世界 创新驱动中国 – 《程序员》官网 软件工程师的十个“不职业”行为 学做程序经理 云计算对21世纪IT人才的挑战 架构实践
Rails样式指南榜样很重要。 -军官Alex J. Murphy / RoboCop 小费您可以在找到本指南的精美版本,并对其导航进行了改进。 本指南的目的是为Ruby on Rails 4开发提供一组最佳实践和样式说明。 它是对现有社区驱动的的...
糖Sugar是一个现代的开源论坛,针对性能和可用性进行了优化,以Ruby on Rails编写。依存关系魔力安装如果您想破解Sugar,最简单的启动和运行方法是使用Docker Compose: $ docker-compose run rails bin/setup$ ...
报告套件 通过ReportsKit,您可以轻松创建漂亮的交互式图表和表格。 有关示例和文档,请参阅 。 资源资源 文献资料 该文档托管在,它是使用生成的。 如果您想改善文档,请随时打开PR! ...ReportsKit已针对PostgreSQL...
didww-v3-rails-sample 这是一个简单的Rails应用程序,演示了 gem集成。 有关获取DIDWW API密钥的详细信息,请访问 看到它在上实时运行或本地运行确保已安装 , 和 。 git clone git@github....
PingCRM在Rails上 一个使用Ruby on Rails和Vue.js构建的演示应用程序,用于说明工作方式。 这是的到Ruby on Rails的端口。 现在,无需安装PHP即可潜入美丽的Inertia.js世界;-) 在可以找到此演示的托管安装。 登录...
Rails样式指南简介角色模型很重要。 —官员Alex J. Murphy / RoboCop提示《 Rails样式指南》简介榜样很重要。 —官员Alex J. Murphy / RoboCop提示您可以在https://rails.rubystyle.guide上找到本指南的精美版本,并...
简陋 Crummy是一种将面包屑添加到Rails应用程序的简单而美味的方法。安装只需将依赖项添加到您的Gemfile中: gem "crummy" , "~> 1.8.0"例子在控制器中,您可以像before_filter一样在方法中添加add_crumb,也可以在...
:flexed_biceps: 针对开发人员的简单而强大的Ruby on Rails CMS :flexed_biceps: APIQ是采用模块化方法的现代,灵活的Ruby on Rails内容管理系统。 它利用了Rails和PostgreSQL最新功能(例如json列类型)。 主要受...
bootstrap_form是一个Rails表单构建器,可以非常轻松地将Bootstrap v4样式的表单集成到Rails应用程序中。 它提供了表单助手,可以增强Rails表单助手。 bootstrap_forms的表单助手会生成form字段及其标签以及正确...
基于配置,免维护,可扩展的Ruby on Rails管理员 Avo是一个美丽的下一代框架,它使开发人员可以为Ruby on Rails应用创建出色的管理面板,并随着您的成长而灵活地满足您的需求。 开始吧 网址: 文档: 推特: 社区...
bootstrap_form bootstrap_form是一个Rails表单构建器,可以非常轻松地将Bootstrap v4样式的表单集成到Rails应用程序中。 它提供了表单助手,可以增强Rails表单助手。 bootstrap_forms的表单助手会生成form字段及其...
使用Rails和Vue.js重塑Pong 该应用程序的目的是演示如何使用渐进式JavaScript框架和Rails ActionCable构建可在网络上玩的两人乒乓球游戏。 演示版 该演示中仅玩了500多个游戏。 我决定在比赛结束时要求捐款1美元。...
第22章 案例研究——基于Ruby on Rails架构的抵押贷款计算器 附录A 习题参考答案 附录B XPath参考 附录C XSLT参考 附录D XML文档对象模型 附录E XML Schema元素与属性参考手册 附录F XML Schema数据类型 附录G SAX ...
Peek的真正美在于它是一个可扩展的平台。 如果需要一些性能指标,但在Peek上不可用,则可以在可用的列表中找到它们并将它们集成到Peek中。 即使您在Peek Views上找不到想要的内容,也可以随时。安装将此行添加到您...