本文发表于《程序员》杂志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是如何对应的。
《Ruby for Rails中文版》是美国作者David Black所著的一本专为Rails开发者介绍Ruby语言的书籍,旨在帮助读者深入理解Ruby的基础与Rails框架的内在联系。Ruby是一种面向对象的、动态类型的编程语言,它的简洁语法和...
《Web开发敏捷之道:应用Rails进行敏捷Web开发》是由美国知名开发者Sam Ruby撰写的一本经典著作,该书的第四版提供了全面且深入的指导,帮助读者掌握使用Ruby on Rails框架进行敏捷开发的方法和技术。Ruby on Rails...
rails server命令启动web服务器的默认端口号为3000,当然我们也可以自定义指定端口号。
- **David Heinemeier Hansson** 是 **Rails** 的创建者之一,他在演讲中提到:“追求美是提高生产力的关键。”他将美学视为软件开发中不可或缺的一部分。 - **美** 在此语境下指的是代码的优雅性和简洁性,即编写出...
经典《程序员》杂志(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美元。...
《构建车辆维护与改装追踪系统:深度解析...它展示了Rails框架的强大功能,同时也体现了Ruby语言的简洁之美。通过深入学习和使用这款应用,不仅可以提升车辆管理的效率,也可以进一步提升对Rails开发的理解和技能。
Peek的真正美在于它是一个可扩展的平台。 如果需要一些性能指标,但在Peek上不可用,则可以在可用的列表中找到它们并将它们集成到Peek中。 即使您在Peek Views上找不到想要的内容,也可以随时。安装将此行添加到您...