程序员2007精华本(下)
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

Ruby on Rails创业实践

□ 策划 / 欧阳璟

最近一年多来,高呼“做网站,赚大钱”的人越来越多,伴随 着Web 2.0大潮,大量互联网创业者也迅速涌现出来。有人将这次互联网创业的高潮称为“第二次互联网泡沫时期”,之所以被称作“泡沫”,大多原因是很多喊着Web 2.0口号的厂商从风险投资公司拿到了投资,但其中不少创业项目却正在走向末路。

就在这个互联网创业如火如荼的时候,Ruby on Rails(以下简称RoR)技术也在短短几个月时间就迅速席卷了整个开发者社区。尤其是对Java开发者社区,RoR的影响甚大。一般认为,原因在于RoR卓越的开发效率为开发人员提供了更好的选择,更紧密地结合了技术与业务之间千丝万缕的关系。

1993年诞生的Ruby是一种面向对象的动态脚本语言,正因如此,其开发效率很高。然而创始人松本行弘(Yukihiro Matsumoto)最初并没有为Ruby提供英文的文档支持,因此一直未能得以流行。

2004年7月,一位刚刚毕业的丹麦学生为37Signal公司创造了一个用纯Ruby写就的Web开发框架,此后,这位后来大名鼎鼎的黑客将该框架命名为Rails。通过Rails近两年时间的推广和完善,D.H.H(David Heinemeier Hansson)的大名响彻了Web开发领域,而RoR的名字也被印在Web开发的历史上。

随后,我们看到37Signal大胆采用RoR技术快速开发出许多新颖的Web应用,这不禁让国内的开发人员蠢蠢欲动。很快,一批采用RoR技术开发的Web创业项目在中国初露端倪了,毕竟RoR技术最显著的特点就是其高效的开发速度。

经过对中国RoR互联网创业项目的一番筛选,我们选择了其中的8个互联网创业项目和一个产品项目,邀请其创业者从技术角度畅谈RoR能够提供的帮助,同时分享他们在创业项目中如何应用这项技术的心得,将敏捷与快速开发结合到实践中来。

值得高兴的是,我们看到这些创业者正在充分利用RoR的敏捷特性快速发展着,他们是采用新技术最先吃螃蟹的人。当然,螃蟹的味道是否鲜美,我们现在无从判断,但是相信未来这些互联网创业项目的发展将会给我们一个明确的答案。

借助这次组稿特别策划的机会,我们有幸采访了Ruby语言的创始人松本行弘先生,并聆听他为Ruby开发人员带来的启示。希望这组文章能为正在准备创业的软件技术人员带来帮助,也为其他奋斗在软件开发战线上的朋友提供一顿丰盛的技术实践大餐。

Ruby创始人松本行弘访谈录

□ 记者 / 欧阳璟

《程序员》:近两年来Ruby迅速发展,已经成为目前开发者社区当中最火爆的编程语言,甚至比更“年长”的动态语言Perl和Python都更受人关注,请您谈谈这当中最关键的因素是什么?

Matz.:近些年来,Rails已经为Ruby带来了很大的市场。很多软件开发者是通过Rails框架才引发他们对Ruby开发语言的兴趣的。刚开始,人们只是把Ruby当作Rails的一种开发工具。但是,随着他们对Ruby的学习,很多人都认为Ruby是他们最喜欢的一门动态语言。此外,Ruby也有它自身的一些优势使得它能够受到开发者的青睐。相比较Perl语言,Ruby更简洁。而从面向对象的角度来说,Ruby又比Python语言更抽象些。而且,Ruby的设计初衷也是为了提高开发人员(像我自己一样)的工作效率,并增加他们的编程乐趣。

《程序员》:您认为Ruby是否对传统的Java、.NET技术发出了挑战?在哪些层面上对这些技术提出了挑战?Ruby什么时候能够被“主流”接纳?

Matz:Ruby将(和其他动态语言如Python一起)在某些方面取代Java或者.NET的地位,如在灵活性和开发效率上。但是,现在Ruby程序员还很少。不像找Java开发者一样容易,我们很难找到很多的Ruby程序员。这恐怕是Ruby要成为主流而面临的瓶颈。坦白说,我并不在意Ruby是否会成为主流。我很高兴Ruby为灵活的动态编程语言注入了新的活力,也给许多开发者(包括我自己在内)带来了乐趣。

《程序员》:今天,我们看到很多互联网站开始采用RoR技术进行创业,请您谈谈采用Ruby技术进行Web开发的优势与劣势。

Matz. :开发时效高是用Ruby进行Web开发的优势所在。使用Ruby和Rails,我们可以在几分钟之内就创建Web服务。而且,在适应需求的变化上,采用Ruby语言远比使用Java及其相关技术要容易得多。用Ruby进行Web开发的劣势在于它的运行效率不够高。我们不能期望它能达到像C++或Java所能达到的最高性能。不过,我个人认为,网络和数据库是目前应用程序的发展瓶颈,所以Ruby在性能上的问题也无大碍。在业务逻辑中性能的重要性正在减弱。

《程序员》:很多人都觉得Ruby非常棒,但是太慢了。请问您什么时候能把Ruby变成一种“飞快”的语言?

Matz. :Ruby不会成为最快的编程语言,它也不需要成为这样一门语言。Ruby注重的是开发效率而不是性能效率。不过,随着一些Ruby项目如YARV、JRuby和Rubinius的实施和完成,在接下来的几年里我们还是可以期待Ruby在性能方面取得一些大的改进。

《程序员》:有观点认为,Ruby的兴起是因为有以ThoughtWorks为代表的敏捷开发的软件企业为RoR大力宣传,请问您怎样看待这个问题?请您谈谈Ruby on Rails与敏捷开发之间的关系。

Matz. :虽然Ruby比Rails和敏捷潮流更早出现,但是它们在本质上具有很多共同的地方。例如,它们都以人为本,强调开发的灵活性和敏捷性。我想这也是为什么Martin选择Ruby来开发其项目的原因之一吧。

《程序员》:是Rails成就了Ruby,还是Ruby成就了Rails?除了Web开发,Ruby还适合于做哪些工作?

Matz.:如我回答前面的问题一样,是Rails帮助Ruby得到了很多开发者的接受和认可。除了Web开发之外,Ruby其实可以做任何事情,只要在其性能许可范围之内。Ruby速度较慢,但是很多开发者能够满足于它的速度,只要他们可以通过扩展来弥补Ruby的瓶颈。有些开发者通过写一些FORTRAN扩展程序从而来用Ruby开发用于地球和气象研究的数字运算程序。此外,在生物信息领域,Ruby也得到了用武之地。

《程序员》:现在Ruby已经成为焦点,很多人都声称要挑战Ruby和Rails。您是否感到压力?您打算做些什么来将Ruby推向更大的成功?

Matz. :更多的用户和更多的建议只会帮助我们把Ruby语言设计得更好。我会一如既往地尽最大努力来做好Ruby。我想这也是开源软件取得成功的关键所在吧。

《程序员》:请给中国的软件开发者提点建议。

Matz. :我就送他们一句话吧——享受编程的乐趣。我希望Ruby能够在你们的开发生涯中减轻你们的脑力负担。

RoR:Web创业最佳实践——JavaEye2.0

□ 文 / 范凯

avaEye网站(http://www.javaeye.com)成立于2003年9月。2006年8月,我们决定对JavaEye网站进行彻底的升级,网站代码完全使用Ruby on Rails进行重写,把单纯以论坛为主的网站改变为一个融合论坛、博客、专栏文章、技术圈子、招聘和咨询为一体的综合性专业技术网站。在仅仅一个月之后,2006年9月11日,带有鲜明Web 2.0特色的JavaEye 2.0网站就正式上线了。

随后JavaEye网站取得了突飞猛进的发展,截至目前,JavaEye网站每天页面访问量超过30万,每天独立IP访问量有3万多,Alexa排名8000名左右,是国内访问量最大、功能最多、代码量最大的Ruby on Rails成功应用案例。作为Ruby on Rails在国内互联网商业应用的吃螃蟹者,笔者希望能够和大家分享使用Ruby on Rails在Web网站开发和维护当中的经历和感受。

Ruby on Rails奇迹

Ruby on Rails是近两年来Web应用开发领域的一个奇迹,这个诞生于2004年7月的Web开发框架在短短两年多时间内已经成为引领Web应用革命的旗手,在软件开发领域掀起了无数的波澜:

· Ruby on Rails一诞生就号称可以达到J2EE开发速度的10倍,被很多人视为软件开发的银弹;

· 软件开发界的奥斯卡2006年Jolt Award颁奖礼,Ruby on Rails出尽了风头: General Technical Book奖项颁发给了Ruby on Rails经典教科书——《Agile Web Development with Ruby on Rails》,Web Development Tools奖项颁发给了Ruby on Rails 1.0;

· Rails框架的作者David Heinemeier Hansson(以下简称DHH)当选由Google,O'Reilly等业界巨头主办的2005开源软件OSCON年度最佳黑客(Hacker of the Year at OSCON);

· 此外,Ruby on Rails吸引的并不仅仅是开发人员的目光,2006年《连线》杂志使用了DHH的大幅照片作为封面人物,报道了Ruby on Rails所取得的成就。在《连线》的封面上,DHH身穿电影《Matrix》中Neo的连身黑衣,仿佛化身为软件开发领域的The One,以拯救软件领域众生的姿态出现;

· 与此同时,Ruby on Rails社区正在以惊人的速度增长,社区的活跃程度也完全可以用火爆来形容。TIOBE的编程语言流行度调查显示:Ruby语言从2006年初排名第31位的编程语言经过仅仅一年时间就跃升到第10位,位列当今软件行业最流十大编程语言之列。

因此可以说Ruby on Rails的未来方兴未艾。

为什么偏偏是Ruby on Rails

笔者在2005年5月就知道了Ruby on Rails框架,但是当时它还没有能够引起人们足够的关注。在一年之后,2006年5月,笔者开始真正关注Ruby on Rails,那时国外的Ruby on Rails社区早已如火如荼了。当时笔者已经决定彻底重写整个JavaEye网站,但是对于拥有7年Java开发经验的资深程序员的我来说,采用Java开发构想当中的JavaEye 2.0网站,却是十分的踌躇。

因为笔者非常清楚JavaEye 2.0网站开发工作量之大超乎了一般的网站项目:JavaEye2.0当中的论坛、博客、圈子、文集其中的任意一项功能单独抽取出来实现,正常情况下都至少需要1~2个月时间,更不要说所有功能加在一起,而且还要互相融合。如果采用Java开发,根据估计,至少要4~5月才能开发完成,而之后的不断升级维护工作量更是难以估量。

而采用Ruby on Rails开发,根据当时的计算,大致可在2~3月之内完成,由于Ruby on Rails代码量的大幅度减少,网站以后的升级维护工作量也将大大减少。但是笔者面临的一个现实问题就是:Ruby on Rails在国内尚无成功案例,而JavaEye 2.0开发团队的三位开发人员全部都是比较资深的Java程序员,却完全没有接触过Ruby on Rails。放弃最拿手的Java,采用完全陌生的Ruby on Rails,无疑是冒险的举动。为此,笔者也进行过一段时间的反复权衡,最终让我下定决心使用Ruby on Rails的理由有二:

· 笔者一个朋友自己的公司有50%的软件产品已经改用Rubyon Rails开发,并且成功部署了很多客户。虽然他们的软件产品主要是销往国外,在中文化方面还不具备说服力,但是已经足够证明Rubyon Rails的可行性了;

· 作为一个商业互联网站来说,必须永远保持着快速的用户反馈、功能改进和网站升级,因此网站的持续开发和维护成本随着时间的推移和功能复杂度日益增加会变得越来越大,越来越难以控制,相比之下,网站初始的开发成本反而显得微不足道。由于Ruby on Rails的代码量非常少,因此维护和升级的成本也会很小,所以即使由于我们不熟悉Ruby on Rails造成前期开发周期拖延,但是站在整个网站生命周期的角度来看,采用Ruby on Rails一定是越往后越轻松。

最终笔者的冒险决策带来了丰厚的回报: JavaEye 2.0网站仅仅使用了一个多月时间就开发完毕,大大少于估计,并且在随后至今的四个月当中,其维护和升级速度极快,我们基本上可以做到每天都有新的bug修复,每周都有小的功能改进上线,每月都有大的新功能推出。这对于使用Java来说,是不可想象的开发效率。

你可以把一个需要12周的计划分解成12个只需一周完成的小计划;把需要30~40个小时完成的任务细化到6~7个小时能够完成的每日任务。

RoR让你敏捷起来

孕育Ruby on Rails框架的第一个商业应用BaseCamp项目来自于美国的37signals公司,该公司也是互联网Web 2.0方面的先行者。37signals总结了他们的Web 2.0网站创业经验,出版了一本名为《把握现实》(Getting Real)的畅销书(http://gettingreal.37signals.com/)。这本书很好地揭示了Web创业的秘诀……并能尽量让你的Web网站开发,运营敏捷起来,而Ruby on Rails的开发高效率正是实现你敏捷开发的保证。

让你的网站尽快运行起来

一个可以运行的网站是激励你团队最好的兴奋剂,还能让你暂时不去考虑那些无法使用的功能。这就意味着你要从最简单的功能开始,绕开细节的纠缠,用快速的方式去取得阶段性的成功,如果你做到这一点了,你就能够更加精确地控制过程,这远比那些完美的规划、框架以及HTML页面的演示来得实在得多。

使用迭代开发方式

不要期待你开始的设计都是正确可行的。随着你的系统的逐渐完善,它会告诉你如何改进才是正确的,你必须要接受开发期的变化,其实它带来的是系统的进化。因为Web程序不像那些传统的软件,须封版发布,可以在任何时候对你的程序进行调整,一遍一遍地迭代,直到满意为止。系统运行起来之后,用户的反馈将对你的设计和开发更有帮助。

尽早获得用户反馈

在真实的环境里面测试你的程序,获得真实的数据和反馈,再用这些来改进你的程序,因为实验室里的检测永远无法反映出实际的情况。所以要提前让用户体验你的Beta版本,你可以在用户使用的同时持续地完善功能,及时获得用户的反馈才是最重要的事。

缩短计划周期

缩短计划周期,将时间分片。你可以把一个需要12周的计划分解成12个只需一周完成的小计划;把需要30~40个小时完成的任务细化到6~7个小时能够完成的每日任务。同样的理论也适用于问题的解决,你可以把一个大的问题分解成若干个小的部分,然后逐一解决。

根据这些指导原则,我们决定实现开放式的网站开发过程,将网站整个开发过程都向会员开放:

在网站尚未正式开发之前,我们就已经专门建立一个二级域名网站http://beta.javaeye.com,每天将网站设计草图公布出来让大家讨论和反馈,并且不断根据大家反馈进行修改;

从网站开发第一天起,就搭建起来一个最最简陋但完全可以运行的网站。我们专门建立一个二级域名网站http://dev.javaeye.com,每天都将当天修改内容发布出去,让大家来使用,让大家可以每天一点一滴地看着这个网站从无到有,从小到大、一砖 一瓦地逐步完善起来。

那么我们如何才能够实现这种理想的开发模式呢?

首先,须建立一套自动的代码更新、发布机制。我们使用了CVS进行程序的版本管理,在每天凌晨会自动运行一个发布脚本程序,自动从CVS上面取出最新版本的网站程序,自动发布到Web目录下面,自动重起服务器,自动发送管理员邮件通知。这样每天用户都可以访问到最新开发状态下的JavaEye 2.0网站。

其次,须对网站功能进行足够的细分,制订一个一个任务,并且规定每个任务必须在一天之内完成。这个任务实现的功能不要求很全面,界面不要求美观,但是该任务必须能够运行起来。对于这一点,Rubyon Rails提供了强有力的支持:针对每个任务,我们先设计好它的数据库表结构,然后使用Rails的generator生成该任务的model、controller和view页面,此时这个任务已经能够成功运行起来了,虽然功能还没有实现,但是可以运行起来,这就是最重要的。接下来我们不断在controller里面添加代码,在view页面里面添加HTML,让任务的功能点一个一个实现出来。

另外,我们使用BugTrace工具来记录网站的所有的bug,并且分门别类,根据优先级、类别、负责人安排好每个bug的修复周期。另外也可以让程序自动捕捉系统出现的异常,发送管理员邮件,自动添加到buglist里面。例如,我们在application这个控制器的根类里面定义了rescue_ action_in_public方法,即网站程序抛出异常的时候,可以进行相应的处理,除了给出友好的出错提示页面之外,我们还添加了SystemNotifier.deliver_ exception _notification(self, request, exception),其会把错误信息和用户请求信息进行格式化,自动发送管理员邮件进行通知。这样我们就可以尽快安排程序的bug修复工作。

最后,一个很容易忽略的问题是网站的在线更新。我们知道对于一个有一定复杂度的JavaWeb应用来说,重新发布和部署一次这个Web应用,至少需要将近一分钟时间,而这对于一个繁忙的网站来说,每次中断网站访问将近一分钟来更新版本,几乎是不可接受的事情,所以往往只能等到深夜访问量很低的时候重启应用服务器,更新Web应用的版本。但是这样一来,就意味着你不能够对bug提供最快的修复速度,对用户需求进行最快的反馈。

Rails则可以有效地避免版本发布带来的网站中断问题,Capistrano就是进行网站在线更新的利器。通过一些简单的配置,它可以帮你自动从版本服务器上面取出最新版本代码,自动进行老版本的备份,自动发布新版本代码,自动重起Ruby FCGI进程,当发布失败的时候,还可以自动回滚到前一个老版本上。另外,Ruby FCGI进程在重启之前会先执行完毕当前的用户请求,然后重启时,就已经是新的版本了,因此用户完全感觉不到网站的访问曾经被打断过(在重启的几秒钟过程中,用户会感觉到网站访问速度变慢,但是仅仅只有几秒钟时间),在不知不觉之间,网站的在线更新已经完成。

正因为这样,我们在网站正式上线之后,才能够维持快速的bug修复和用户反馈,乃至于功能改进。往往用户在论坛里面提出新的合理修改意见之后不到一个小时,我们就推出了新的改进版本,往往给用户带来意外的惊喜。

JavaEye的统计数据

截至目前为止,JavaEye主要的大的功能模块有:论坛、博客、专栏文集、技术圈子、招聘,小的功能模块有:搜索、站内短信、在线简历、新闻发布、邀请、好友、投票、RSS订阅、调查等等。网站的功能点非常多,在9月11日上线的时候,已经完成主体功能开发,比较使用Java开发同样功能的网站,开发速度至少可以提高4倍以上。

JavaEye 2.0的开发人员是三个资深Java程序员,从未有Rails开发经验,学习Ruby on Rails的周期大致在一个月左右,还可以算是Rails的新手。此外,下面的数据是网站程序文件更进一步的信息统计 :

· JavaEye 2.0程序Ruby代码行数:总共6000行Ruby代码;

· JavaEye 2.0程序Ruby程序文件个数:60个,即平均每个程序文件100行Ruby代码;

· 总共6000行程序代码是一个很惊人的数字,因为对于Java来说,实现一个功能稍微完备一点的Petstore,Java程序和XML配置加起来都不止6000行代码。

此外,有关页面数据的信息统计也具备了相当的说服力:

· JavaEye 2.0所有动态HTML页面代码行数:15000行;

· JavaEye 2.0动态HTML页面个数:400个,即平均每个页面40行HTML;

· 页面的数量很多,但是每个页面都很小,HTML页面代码总共15000行也是惊人地少。因为Rails提供了很好的局部页面模板功能,所以我们把页面很多可以重用的部分提取出来作为局部模板文件进行重用,从而提供了充分的页面组件化能力和重用度。

所有的程序和页面加起来2.1万行代码。对于一个功能完备的综合性网站来说,代码量惊人地少,这样少的代码量意味着:

· 程序的维护量变得很少,即使出现新的bug,也很容易寻找和定位,因此快速修复bug就不再是一个难题;

· 2.1万行代码并没有超出一个程序员的维护能力范围,这意味着一个人就可以负责网站所有维护工作,极大降低了网站维护成本;

· 对网站代码进行局部重构和改进,也变得不那么困难,往往只会涉及到50行代码以内的改动。因此很容易保持网站代码的合理性。

Ruby on Rails应该是你的Web创业选择吗?

Ruby on Rails惊人的开发高效率,以及维护升级上的低成本,无疑是商业互联网网站在技术开发层面上所最期待的,而这些方面又恰恰是Java/PHP等其他Web开发技术所不具备的,因此我认为在技术条件允许的情况下,使用Ruby on Rails是目前最好的技术选择。但是Ruby on Rails也并非十全十美,以下是你应该注意的方面:

Ruby的第三方类库不多,而且现有的第三方类库往往极不成熟,因此有些功能会无法方便地实现。例如Ruby没有一个良好支持CJK的全文检索引擎,使得你没有办法寻求纯Ruby的全文检索解决方案。Ruby类库的缺乏以及不成熟也是目前我们遇到的比较大的障碍,在JavaEye2.0中使用的几个第三方类库,例如:feedtools, file-column, ouput_compression都有不同程度的bug,迫使我们自己不得不动手修改他们的bug。

Ruby onRails只能在Unix平台发挥比较好的性能,不适合跑在Windows服务器上面。目前Rails高效率的运行方式Lighttpd + FCGI都没有良好的Windows版本,如果你一定要采用Windows服务器,那就没有办法获得很好的性能了。

用RoR搭建信息整合平台——Iease的选择

□ 文 / 王雪峰

Web 2.0呼声震天的2006年,互联网的创业者正在不停地寻找新的创业模式。除了传统的论坛、博客、搜索等形式以外,仿佛并没有更多的创意被人们发现。当今这个时代信息爆炸的威力早就已经被人们意识到,但如何直面Web用户,让他们能够有效找到自己需要的信息,这是新一代Web创业者们应该仔细考虑的问题。

随后,分类信息网站开始日益火爆,如酷讯、酷多网等。但我一直认为这样的分类信息只是解决了一部分问题,也就是过去的信息,如租房、买卖物品等等,这些信息的时间点都是过去的。各种各样的信息分散在互联网的各个角落,也有一部分不在互联网上,而是掌握在一些网络用户的手中。如果能提供一个网络平台,把这些信息按照一定的规律组织起来,由所有的用户来共享,就可以成为一个新型的分类信息平台,上面的各种信息按照时间、地点等分类来组织,用户可以非常方便地获取指定时间段内会发生的信息和事件。

JSP、PHP还是Ruby on Rails?

查找了大量资料并参考了一些网站之后,我们决定采用日程表这种载体来实现Iease最初的想法。原因有两点:

· 日程表可以把一类信息按照时间线索组织起来,每个信息与一个时间点绑定在一起,用户查找和选择信息非常方便。

· 选择日程表作为信息载体,我们可以在提供信息平台的同时,为所有的用户提供在线日程表服务,这样用户就可以把自己认为有价值的信息拷贝到自己的日程表中,进而获得信息提醒等相关服务。

开发目标确定之后,最重要的一步就是选择开发平台。初期整个团队只有三个成员,三个人都有丰富的应用程序开发经验(包括数据库的管理),但是都没有任何Web应用的开发经验,对Web开发也只有很少的了解。根据团队人员的实际情况和公司初创的资金情况,我们对于技术平台的选择有一些基本要求:

· 完全免费的软件平台,避免任何不必要的授权问题:微软平台是无法选择了

· 有可扩展能力:随着系统吞吐量的增加,可以相对容易的进行软硬件的扩展

· 有较高的开发效率,入门门槛低:毕竟对创业公司来讲,时间是非常重要的

· 部署简单,有丰富的开源类库

根据以上几点,最先想到的当然是JSP和PHP这两种国内最流行的开发工具,虽然手头没有准确的统计数字,估计这两种语言占领了大部分的Linux平台的网站开发,因此可以说它们几乎是必然的选择。通过Google我们又了解到了Ruby On Rails这个开发Framework的工具(以下简称RoR),有网友称,使用RoR的开发速度是Java的10倍,这个数字对我们这些没有Web开发经验的工程师具有足够的冲击力。团队里也出现了两种声音,一种是选择成熟的JSP,另一种就是选择不成熟但是相对更快速的RoR。

选择的过程总是痛苦的,要考虑的因素实在太多。

从技术层面看,我们对JSP和RoR做了多项对比(见表1)。

表1:JSP与RoR的特性对比

用事实来抉择

在这张表格当中,很多内容并非技术特性,而是要从创业者角度来考虑怎样实现更低的成本。

首先是学习曲线。对于创业者,这一点尤其重要。无论多么好的技术,如果不能在有限的时间内快速掌握并应用,那么都是没有意义的。对比JSP和RoR,我们可以从学习曲线的多个方面对比 :

1. 以一个最简单的例子入手对比。在JSP平台,我们需要配置一组JSP参数,要使用Eclipse,还需要安装不少插件,一个最简单的程序里面就已经包含了多个让初学者头大的配置文件;在RoR中,我们需要做的只是设置数据库的访问参数,然后就可以用Scaffold获得一个非常完善的应用事例。两者相比,RoR对于初学者显然更为适用,也更容易让学习的人产生信心。

2. 数据库的使用是Web开发中非常重要的一部分,因此能否快速的掌握数据库的操作是衡量学习曲线长短的重要标准之一。在JSP开发中,目前常用的数据库操作模块是Hibernate,功能很强大,但是不易被初学者掌握,加上一组新的Jar文件,学习起来并不容易;在RoR中,使用model几乎可以使数据库对开发者透明,如果不需要实现复杂的SQL操作,开发者甚至不需要了解SQL语言。因此,在数据库操作方面,RoR的学习曲线要优于JSP。

3. Ruby和Java。Java语言的语法严谨,强制要求异常处理;Ruby语言语法灵活,更接近自然语言逻辑,几乎是可以猜着编程序。两者对比,Ruby更容易被初学者快速掌握。

其次是开发速度的对比,这也是大家对RoR最为关注的一点。在测试RoR程序的过程中,其开发速度远远高于JSP。体现RoR开发速度的最好的例子莫过于网上流行的15分钟完成Blog,虽然在这个例子本身有点极端,但是已经足够说明RoR开发速度之快,至少我认为采用JSP在15分钟内是很难完成这个开发的。

应用系统的性能是网友对RoR质疑最多的一点。显然Ruby作为脚本语言,执行的速度不能和编译语言的Java相比的。这也是我们最初比较担心的一点,毕竟目标是做一个大系统,如果性能有问题肯定要有麻烦。不过仔细分析一下并不需要过分担心。Web系统在处理一个请求时,开销最大的部分多是在数据库的查询上,和使用什么编程语言没有关系;性能的另一个重点就是处理并发请求,这个可以交给前端的Web Serve来做分发器处理,也就是说虽然RoR处理单个请求可能略慢于JSP,但是在负载能力上没有什么差别。同时,如果Ruby虚拟机能够推出,Ruby的执行速度也会有大幅提高。

我看过的质疑RoR的文章大致可以分为两类,一是RoR应用的性能,这一点我在上面已经做了分析;二是RoR是不是真有那么快,我觉得这个问题并不是靠想就能够得出结论的,一些实际的开发更能说明问题。以Java为例,Ruby的开发效率决不是Java能够相比的,涉及到数据库操作的话,这种差距会更明显。

综合技术方面的比较,对于一个创业团队而言,我们认为与其选择一个相对成熟稳定的平台,不如选择一个风险和机遇并存的平台。在承担潜在风险的同时,快速实现需求。至少我们认为快速、随需应变正是创业团队相对于大公司的少数优势之一。

采用RoR,但不固执

最终我们选择了RoR作为我们的开发平台,目前Iease网站的主体功能已经基本完成,劳动量大约为6个人月,而且整个开发是由一个没有任何Web开发经验的团队在缺少专业美工的情况下完成的。如果整个项目重新开发,并且加入专业美工的情况下,我认为至少可以把开发的劳动量减少到3.5个人月,而且会获得更好的效果。如果采用Java来开发类似系统,时间至少为12个人月。同时,由于是全新的开发项目,需求的变化很有可能导致需要更多的开发时间。

在Iease网站建设完成之后,我们开始考虑提供一个专门的搜索引擎来搜索未来信息,即在使用关键字进行搜索的同时,可以指定搜索信息的时间和城市。当然,这种搜索不是搜索网页,而是搜索具体信息,这些信息都是与时间密切相关的,通过Iease的爬虫分析之后提取出信息的时间和地点信息存储在自己的数据库当中。

举一个例子:假设用户想在新年前后购物,希望获得一些打折促销的信息。如果使用常用搜索引擎,获得的结果都是一些与打折相关的网页,并没有直接的信息,如果使用Iease的搜索则可以指定时间为新年前后的三天,直接获得那几天当中的打折促销信息。

在搜索引擎的开发中,我们选择了使用RoR来实现爬虫工具,但是搜索部分使用了基于Java的Lucene,原因是Lucene是比较成熟的开源搜索引擎,如果重新使用Ruby去开发检索部分,完全是重复发明轮子的过程。Iease现在的搜索地址是:http://search.iease.com.cn

小结

随着Iease项目初期的完成,RoR给我的创业项目留下了深刻印象:

1.RoR的确可以大幅度提高Web开发效率,不过主要体现在后台逻辑的实现和AJAX部分。

2.RoR是网络创业项目开发工具的一个很好的选择,因为对于创业公司,快速的实现想法是最重要的,发现方向有问题就要快速的修改。

3.RoR应用可以完全架构在免费的Linux平台上,为创业团队和公司节省大量的费用,并避免授权麻烦。

4.RoR能够如此火爆,与现在轰轰烈烈的Web 2.0创业有密切关系,它为很多Web2.0创业团队提供一个新的选择,或许也是一个更好的选择。

Wap上的Ruby on Rails

□ 文 / 何一舟

很多用Java开发过Web应用的开发人员曾经有过这样的经历:在应用开发过程中要处处强调面向XX、分层架构、设计模式、测试驱动、每日发布等理念,精心抽象自己的设计层次。然而高级开发人员,尤其是那些“万事通”的开发人员常常觉得,过分强调开发理念会把思考重心从业务模型转移到技术架构上。作为一个创业型的互联网企业,这种做法很危险。一个正在起步的网站不是IT巨头的研发中心,可以花费大量的人力、物力、财力去实践高深的架构和模式。其实,充分利用资源,迅速改进业务才是互联网创业的生存之道。Rails正是在这样的背景下被万众瞩目,被用于很多新兴的互联网项目开发。笔者所正在开发的项目也是如此。在采用Rails开发这个创业的项目过程中,我们的过程很简单: 不谈模式、架构,每一行代码都以快速实现业务作为目标,并坚持以简单和实用为第一原则。

Rails上的Wap网站诞生

2006年6月,正是DogStart社区网站即将进入火热夏季的时候。当时的我们对于这个网站仅有一个构想:它是基于Wap的一个手机游戏娱乐社区,包括大量手机游戏的介绍,试玩和游戏评论。当然,我们希望手机用户尽可能以客观的条件审视时下大部分流行手机游戏,并且通过这个网站便捷地进行交流和互动。

在这样的需求下面, 我们开始动手开发。在接触Rails以前, 我们都是使用Java来做Web网站开发的。不过在仔细考察了Rails以后,我们觉得和Java相比,Rails更加轻便灵活,更加适合未来可能多变的业务逻辑。便选择了使用Rails来进行开发。

网站整体的架构是基于Linux+Mysql +Rails+FastCGI +Lighttpd的,可能有些朋友不太熟悉Lighttpd和FastCGI,这里简单介绍一下 :

· Lighttpd是一个轻量级的Web HTTP服务器。它的功能和大名鼎鼎的APACHE很类似, 不过远远不如前者出名, 不过和APACHE相比,Lighttpd速度非常快, 同时消耗的内存和CPU时间都比Apache少。在功能上Lighttpd也是多个开源HTTP Server中最类似APACHE的,APACHE中常用的模块,Lighttpd都有对应的插件支持;

· FastCGI是CGI(通用网关接口)的改良协议。传统的CGI服务,每一个请求都需要新启动一个CGI进程,处理完毕以后进程结束。这样导致CGI协议无法承担太高的负荷(进程的启动和程序初始化都很消耗资源),而FastCGI则不同, 它采用的方式是一次性启动多个CGI进程, 当有请求时,FastCGI只是把请求分配给某个进程,处理完成以后进程也不销毁,始终处于可复用的状态。Lighttpd对FastCGI的支持也很好,所以目前Rails的应用大部分是基于Lighttpd+FastCGI的组合(见图1)。

图1:DogStart网站整体架构是基于Linux+MySQL+Lighttpd+FastCGI+Rails的

对于整个架构, 一个完整的请求处理流程如下:

· 用户使用手机通过中国移动的Wap网关访问到我们最外层的Lighttpd服务器;

· Lighttpd服务器将请求通过转发到我们的多个FastCGI实例上;

· FastCGI接受到请求以后,运行Rails的Controller处理请求, 访问MySQL和memcached缓存来获取数据;

· Rails把生成的XML页面返回给Lighttpd,然后按原路返回,最终送达用户手机浏览器并展现到屏幕上。

目前,整个网站的功能模块包括论坛、个人空间(blog)、虚拟货币、在线Wap游戏、圈子5个部分,另外还有数据收集统计、自动CMS系统、报警监控、搜索引擎等非业务需求的功能模块。

根据这些的业务需求,我们在开发DogStart网站的时候遇到了很多传统问题。首先遇到的问题就是如何定位这个Web应用项目。DogStart是一个初创的Wap项目,这一点与一般的企业互联网项目以及企业Web项目都有所不同。有以下一些特点:

创业项目的目标不清晰。企业Web项目一般在项目开始之前,需求已经非常固定。由于企业Web项目与企业业务模型相关,所以只要做好详细的需求分析工作,在项目前期,“要做什么”这个问题已经得到了回答。另外一方面,企业互联网项目则可以通过数据分析,购买专业的分析报告等方式获得用户的需求资料。然后集结一大批专业人士通过一个战略会议决定“要做什么”。不管从哪个角度来看,前两者的目标都是非常清晰的,但创业型项目则正好相反,我们常常无法预知这样庞大的互联网用户(在我们看来是手机用户,或者说智能手机用户)有哪些精确的需求,自然也无法获得这些详细需求的分析。从另外一个角度看,起步的公司也很难抽出资源购买研究报告。所以,通常大多数起步的互联网企业都只能以突发奇想的创意来得到:“我们想做什么!”,就投入去做。

创业时网站要拥抱变化。正如前面所提到的,企业Web项目目标明确,有严格的交付日期和项目安排,整个项目经不起,也不需要大规模的改动。在我们的网站开发过程当中遇到的情况则截然相反,因为很多创业网站会打出“永远的beta版”的这张牌,这也决定了项目从一开始就是不断演化自身的。其实个中道理,想必在小型软件公司呆过的开发人员都非常明白,如果没有充分的资源来集中力量找到需求的目标,那么就只有投入更大的精力来拥抱变化。换个角度看,开发人员在这样的环境下成长,自然要比那些呆在研究院里不紧不慢工作的研发人员要艰苦得多,而他们的成长也要快得多。

其实,简而言之,所有的这些不同,都仅仅只是两个字:“资源”。正因为这些不同点的存在,决定了我们无法采用传统的项目开发管理方式,而是必须创新!在这样的背景下,DogStart还是非常顺利地诞生了,今天是它的第N个Beta版发布的日子。

即时上线的开发

在这个项目的初期,我们和其他创业者一样采用迭代进行开发。每一个版本开发1~2天,然后放到测试站点上,产品设计员测试认可以后并不直接升级正式站点,而是积累到一批改动以后再批量上线,这样能够非常直观地让产品设计员快速提出新需求,并快速进入下一轮的迭代。但是这样做的话,很多改动实际上是我们代用户完成决策:什么功能好、什么功能不好,是技术开发人员和产品设计员讨论的结果,这样的项目对于传统互联网来说还是可行的。因为我们有比较了解互联网的设计人员,也正是凭借他们在这个领域的经验,能够让一个起步的网站少走许多弯路。但是对于Wap创业型项目,这样做的效果就很差了。毕竟现在懂得做Wap的人,少之又少。而且Wap技术自身也在不断演化,从最早的Wap拨号到GPRS上网。目前深圳也已经开通了Edge网络,今年也可能会有3G的上线,期间手机终端也是不断的更新换代。所以基本上靠旧经验来做Wap是完全行不通的。

我们做了一次尝试,技术人员和产品设计人员坐在一起讨论和设计产品功能,并完成一份详细的网站开发说明文档。在经过4~5天断断续续地反复讨论后,我们发现由于没有实际的站点可供参考,大家的讨论只能空对空,陷入了“大而全”的功能设计陷阱。很多功能我们都想做,于是就加入到开发文档中,想想看:酷热夏天你坐在凉风习习的空调会议室里,给产品增加一个功能真是太轻松不过了,只需要站起来说“我(还/又)有一个想法……”,你就让产品变得更好了!于是我们的设计文档。在短短几天时间内,就从两页的说明变成了几十页的沉甸甸的文档。表面上看倒是很有成就感,但是实际上呢?我们有了几十个要开发的模块。按照当时的开发速度估算,一年都未必能够将这许多功能开发完。在浪费掉几天宝贵的时间以后,我们立即舍弃了这样的方式,开始采用“摸着石头过河”的办法,先把最直接的功能开发、发布出来,然后所有人参与使用,总结意见。经过逐步修改、慢慢演化。中间每一个用来讨论的模块都是可以立即试用的。采用这个方式开发才过一周,我们就发现那厚厚的几十页设计中有大量不实用甚至是错误的功能需求。幸好我们没有按照它来指挥项目!

采用这样的新方式开发,首先,我们并没有严格区分测试站点和用户站点。很多改动只要没有技术上的bug,我们先不管是否合理,都一概先上线运行。当然,按照严格的流程和规范,这种方式是严重错误的。先做一个测试站点,把改动发布在测试站点上,等内部人士经过试用后再决定是否上线,才是做网站的“正途”。理由是什么呢?“避免因为不合理的改动而流失用户,用户如果形成不好的印象,就算以后改好也不会再来了”。不过,这个理由对于我们来说却是没有用的,才创建的项目没有稳定的用户群。这是创业项目普遍的弱点,但是对于初期的快速开发来说,它却又是一个优点:你可以大胆改进,不用担心用户流失。

这里举一个例子:我们要上线的手机Blog模块,因为手机屏幕和输入限制,我们无法沿用Web网站的编辑输入方式。但是什么方式才能让用户使用起来尽可能方便呢?一开始在设计和开发人员心中也没有正确答案,只能不断尝试。所以我们先设计了一个向导式的Blog撰写页面,分为三步。投入使用并运行了几天以后,从后台的数据统计我们看到了一些非常有价值的信息:有多少用户坚持走完三步写完Blog、多少用户到第二步放弃、多少用户在首页第一步就放弃。回头再考虑,我们确实发现UI的烦琐,也听到有用户抱怨:“啊!怎么写个东西这么麻烦?”尽管确实会有人从此不再访问这个页面,但是这样的用户对于创业项目来说,并不是最主流。此后,我们改进了页面,并实现一步到位,快速发表Blog,而面向高级用户(也就是那些挑剔的用户)则提供一个高级编辑功能,保留三步的向导页面、提供文章中贴图、管理等高级功能。

有朋友也许会问,为什么不能一开始就设计好呢?其实我们也希望如此,不过细节总是出乎你的意料意外。如果企业的资源充足,有大把测试人员上班可以捉住手机不放,也许能尝试走测试—>反馈—>修改—>再测试的流程找到合理的方案,但是对于创业项目,这种方式就显然不可取了。一来因为我们没有大公司雄厚的人力和财力来如此设计产品,二来如前文所述,我们不是非常担心它会流失用户,因为目标是更广大从未使用过我们业务的新用户。我们跳过这个流程,快速演化产品,将想法和可用产品之间的距离拉近。靠这样的方式,我们才可以在短时间做大量的尝试,从中找到真正合适的方案。相反来看:如果因为担心流失用户而拖后产品的改进甚至不敢做大的改进,最终只能导致用户使用体验不好,他们还是会走人的。

灵活重构类库

这里需要对Ruby on Rails做一个单独的注脚。由于是采用Rails开发Wap网站,我们几乎是从头做起,完全不像Java或者.NET,有大量类库可以调用。而完全独立写一套类库来用也是时间上不允许的。得益于Ruby语言的灵活,在运用Rails开发Wap网站的时候,如果临时遇到一个新功能需求,一般都在就近处写一个方法来快速完成需要的功能,等该功能稳定后,再把这个方法抽象出来,放入专门的helper类中,在原方法调用处混入该类。这样,网站的代码只需要非常微小的改动甚至不需要改动,就能得到一套实用(但不完美)的Ruby无线Wap开发包。

如果因为担心流失用户而拖后产品的改进甚至不敢做大的改进,最终只能导致用户使用体验不好。

DSL应用

DSL可以算是最近Ruby语言界的热门话题(也是争议话题)。DSL是领域专用语言(Domain Specific Language)的缩写,它是针对具体应用范围而设计的小型语言。和通用语言相比,DSL只是用来处理特定的算法。比如我们用到的SQL语言、Ant的XML配置等,都可以看作是一个DSL。 DSL可分为两种:

· 外部化的DSL。从零开始发明的新语法,并且有配套的编译器或者解释器。比如SQL。

· 剪裁或者扩展现存通用语言的DSL。比如Java Web开发框架常用的XML配置文件(Ant、HibernateXMLMapping)和Rails中的Migration、Rake等。

第一种DSL我们没有用到。需要谈到的是第二种DSL,它的优点是开发方便,不需要负担生成和调试一门全新语言的成本,就可以快速得到一个可用的DSL;当然,其缺点是DSL受宿主语言的语法和表达能力限制,如果宿主语言本身表达比较烦琐,生成的DSL也是比较难以使用的。比如Java里面大量使用的XML配置文件,XML化的流程逻辑等,其可读性和维护性都不是很好。成熟的产品往往需要在XML之上再写一个操作控制界面来简化。所幸的是,Ruby语言本身非常灵活,在Ruby语言基础上写DSL是非常方便的,产生的DSL也非常简洁自然,相信用过Rails中Rake文件和migration的朋友都深有体会,比起XML来真是不知道方便了多少倍。

在本项目的开发过程中,我们也采用了一些DSL的理念:在开发过程中,对过于复杂的代码进行逐步抽象,慢慢生成出DSL,用来简化构建产品的工作量。比如对用户手机做适配这个阶段,最开始我们的代码如下很多if-else段,它可以算是我们在适配特殊手机过程中不断获得的业务知识的汇总:

      if ua.name==’moto’
      ...
      done=false
      elseif ua.name=-‘nokia’ && ua.version==’s60’
      ...
      done=true
      elseif Done && ua.name==’xxx’
      ...

随着开发的深入,很快我们就有了一个多级的if-else群,看上去复杂且难以维护。最开始,我们想把它整理为一个纯粹的数据型配置文件,但是由于整个适配逻辑还在不断演化当中,不断有新的例外添加进来,旧的逻辑合并在一起,一个平级的数据配置文件没法满足逻辑需要。这时,我们想到了DSL,于是我们开始尝试写出专门的适配API,重新展现if-else群的逻辑。我们把它改成了如下的形式:

      ua.once.match(“nokia”,”s60”) do
      #只匹配一次
      end
      ua.once.match(/xxx[0-9]/) do
      end
      ua.multi.match(“….”,”…”) do
      #匹配多次
      end

通过这个代码,我们一眼就可以看出宏观的逻辑所在,起码认识英文的人都知道once代表一次,multi代表多次,match代表匹配。对我们来说,我们在阅读或修改代码的时侯可以不用在整个if- else群里面找来找去。一眼就可以看出宏观上的逻辑,阅读方便,沟通起来也方便多了,原来的if-else群,基本上只能由一个人修改维护,而现在的代码两三个人修改没有问题。 当然,最终的版本其实也可以算是一种配置文件。如果做成专门的配置文件,管理起来自然更加方便, 不过回头一想,业务还要继续改进。费力写好的配置文件的解析器可能用不了两天又得大改, 由此看来,轻量适度DSL还是最简单实用。

“有多少用户去了那里,有多少用户用过一次以后就再也不来了”等等才是“用脚投票”的本质所在。

一次性原型与优化

在开发项目的过程中,我们写了很多一次性使用的脚本程序。比如:在开发聊天模块时,为了尝试聊天效果,我快速写了两个完全不同的聊天功能原型。一个是我的想法,一个是产品设计的想法。这些脚本不到半天就全部写完了,剩下的时间,我们对这两个聊天原型都进行了上线测试。试用过两个原型以后,通过收集的数据,我们再重新设计开发了完整的聊天模块。回顾起来,整个站点中还有一些模块也是以这样的方式尝试出来的。表面上看,之前开发的程序完全扔掉了,浪费了人力,不如最开始就讨论出好方案效率高,其实却不然。这些脚本都是尝试性的模块,很多模块只是为了探寻一个方向,最终我们的实现可以完全与此无关。所以如果每个方向都要讨论一番,反而更浪费人力。

在系统的开发过程,还涉及到一个优化的问题。Rails目前已经有了丰富的优化方案.全页面静态化,页面片段缓存,数据库缓存(我们就使用了memcached来做数据库的缓存:memcached是一个高性能的,分布式的内存对象缓存系统,算是一个大大的HashMap),互联网上现也有大量的相关文章可以搜索得到。不过真正谈到优化方案,“不要优化”才是第一方案。对于互联网的创业项目开发来说,优化总是最不重要的。因为只有火了的业务,才有优化问题。创业者最大的问题是如何改良业务。没有人访问的业务,再快也没有意义。而且真正的性能瓶颈也只会在真实用户行为下才会浮现,简单的模拟是测试不出来的。当然,如果你的网站现在已经有了海量的访问人数,不是消灭几个性能瓶颈就能解决的话,我想你的业务恐怕也应该火起来了。那么,快去说服你的投资方出钱去Csdn网站上招聘牛人来帮你优化吧。

让用户“用脚投票”

前面提到:“让用户当最终的设计员”,在网站运营期间,我们也收到一些用户对各个功能的评价。当然,这其中不乏有说好的、有说糟糕的、有提建议的、有说要走人的。不过这些评价并不是我们真正的“设计员”,事实上,我们认为用户对任何设计,只能“用脚投票”。所谓“用脚投票”,就是使用Rails的Log,记录每个功能模块的用户使用情况。“有多少用户去了那里,有多少用户用过一次以后就再也不来了”等等才是“用脚投票”的本质所在。通过这些数据的分析,我们可以知道到底什么模块有问题,什么模块受欢迎。值得提醒大家的是:用户直接的评价和建议只能作为参考,因为即使是同样的功能,也可能有人喜欢、有人讨厌。搞个投票功能让用户“用手投票”决定模块好坏是不行的,就像“超级女声”一样,众口难调。这种难以调和的用户意见,管理学上称之为“群体迷失”,在此按下不表。

总结

以上是我们用Ruby on Rails进行创业的一些技术开发经验。事实上,这些经验与其说是技术总结,不如说是业务需要,所以它不一定适合各位的手头的开发环境。但是在这些表层的经验背后,“简单和实用第一”才是真正的原则,这也是Rails社区一贯推崇的开发理念之一。作为业务技术开发人员的我们,第一使命永远不是去追求框架或者模式,而是为我们的客户/公司快速有效的创造业务价值。所以,保持对“简单和实用”的追求,我们才能在有限的资源和环境下面发挥出技术的力量,为客户/公司创造更大的价值,最终也为自己带来价值。这是我们在开发狗狗网(wap.dogstart.com)这个创业项目中最大的体会。

AJAX on Rails最佳实践

□ 文 / 黄亮

近两年,越来越多的公司和个人在自己的网站中引入了AJAX技术,让自己的网站变得更易于使用,给用户更好的体验。AJAX技术其实很早以前就已经出现,只是在近一段时间才这么大范围地被大家所熟知和接受,并在大大小小的网站中扮演着越来越重要的角色。JavaScript语言是用来实现AJAX的首选工具,目前已经有很多成熟的JavaScript框架或者工具箱,它们对开发AJAX应用提供了良好的支持,使得我们免于处理不同浏览器上AJAX调用细节,让我们能够轻松开发自己的AJAX应用。

而Rails框架似乎天生就对AJAX友好,对Rails用户编写AJAX应用提供了良好的支持。Rails内置了对prototype和scriptaculous这两个JavaScript库的支持,用户甚至可以在完全不用书写JavaScript代码的情况下就可以开发出一个AJAX应用。本文尝试从几个成熟的AJAX框架入手,讨论这些框架对于编写AJAX应用程序的支持,进而分析Rails框架对JavaScript和AJAX的支持,讨论在Rails框架中编写AJAX应用的最佳实践。

当然,我们不会忘记测试。AJAX由于其异步性及其所带来的丰富的界面交互效果,大大增加了其测试的难度。然而正是由于这样,对AJAX应用的测试才显得极为重要,没有测试的保障,我们很难确保应用是否能够完整而无错地实现了我们的需求,也很难知道新的改动是否破坏了原有的功能。在本文中我们会详细讨论如何对AJAX应用进行测试,并将测试自动化,使得我们的AJAX应用完整无错。

JavaScript AJAX工具箱

prototype

prototype(http://prototype.conio.net/)是一个比较成熟的JavaScript函数库,它封装了许多常用的JavaScript功能,使用户不需要考虑浏览器差异就可以完成许多功能,同时它也对JavaScript进行了许多增强,例如提供了枚举等支持。prototype当然也对AJAX编程提供了比较完备的支持,除了对不同浏览器进行封装之外,还提供了一整套比较便利的方式给开发人员用来注册回调函数等。本文不准备在此详细介绍prototype库的方方面面,只想简单讨论一下其中对于AJAX的支持。

prototype采用一种非常直观和简单而且面向对象的方式来支持回调函数,用户可以在AJAX调用发生的各个阶段(Uninitialized、Loading、Loaded、Interactive、Complete、Success、Failure等)注册需要被回调的函数,例如我们如果想在XMLHttpRequest调用完成时执行回调函数foo,只需将foo注册给Complete:

      Ajax.Responders.register({
        onComplete: foo
      });

除此之外,我们还可以给HTTP请求的各种返回状态码(200、404等)注册回调函数,这样我们能很方便的对一个AJAX调用的整个过程进行控制和处理。

prototype还提供了对返回的HTTP请求MIME类型的支持,除了支持最常见的text/HTML,还支持text/JavaScript, X-JSON等,如果返回内容的MIME类型是text/JavaScript,则prototype会假设返回的内容是JavaScript代码,从而尝试执行这段代码,对于X-JSON类型的内容也会执行。prototype提供的这个功能正是Rails框架实现AJAX的基础。

prototype同时提供了Ajax.Updater, 专门用于用AJAX方式从服务器端获取文本然后显示在页面上,以及Ajax.PeriodicalUpdater,用于定时发起AJAX请求更新页面内容。所有这些都给我们进行AJAX编程提供了极大的便利。

scriptaculous

scriptaculous(http://script.aculo.us/)是在prototype基础上开发出来的一套JavaScript函数库,提供了大量非常酷的动画效果、JavaScript组件、AJAX控件,还有一个不错的JavaScript单元测试框架。scriptaculous为我们开发Web应用提供了一个非常好的工具箱,同时其本身也是书写良好的JavaScript代码的一个好例子。

scriptaculous为我们提供了非常实用的AJAX控件,其中包括AutoCompletor(自动完成)、InplaceEditor(原地编辑)、Sortable(用户创建可拖动排序列表)等,这些控件为我们开发相关的AJAX功能提供了非常的便利,Rails很好地集成了scriptaculous的这些功能,我们甚至不必关心JavaScript代码就可以在Rails程序中使用这些控件。

AJAX on Rails

Rails作为当今最惹人注目的Web框架,自然没有忽略对AJAX的支持。Rails框架对JavaScript编程提供了出色的支持,通过helper机制使得在Rails中编写客户端动态效果非常简单,保持了Rails代码一如既往的简单和美。同时,Rails通过prototype库中的AJAX功能提供了对AJAX编程的支持,简单而高效。

具体来讲,Rails提供了如下支持:通过JavaScript Helper和JavaScript MacrosHelper提供对JavaScript编程的支持,封装了常用的与JavaScript编程有关的工作;通过Prototype Helper和Scriptaculous Helper封装了prototype和scriptaculous提供的JavaScript函数、AJAX和各种客户端效果,让开发人员可以不用书写JavaScript代码就实现各种客户端视觉效果和生成AJAX调用代码;Rails同时提供了JavaScript Generator,被用于RJS中用来生成AJAX调用的返回代码。

JavaScript Helper

JavaScript Helper提供了对常用的一些与JavaScript处理相关的一些帮助函数,例如link_to_function和button_to_function会生成调用JavaScript代码的连接和按钮,javascript_tag会生成用来包含JavaScript代码的HTML标签,escape_JavaScript会将你的JavaScript代码中的回车和单双引号进行转义,方便在Web上显示。

Rails框架似乎天生就对AJAX友好,对Rails用户编写AJAX应用提供了良好的支持。Rails内置了对prototype和scriptaculous这两个JavaScript库的支持,用户甚至可以在完全不用书写JavaScript代码的情况下就可以开发出一个AJAX应用。

JavaScript MacrosHelper则提供了对某些常用功能的更高层次上的封装,例如auto_complete_field会生成具有自动完成功能的文本输入框,in_place_editor能够生成一段可以被原地编辑的文字,这些都是一些常用的AJAX代码,Rails通过合理的封装使得这些日常工作的处理变得轻松简单。

Prototype Helpers、Scriptaculous Helpers以及AJAX支持

prototype和在其基础上构建的scriptaculou库提供了丰富的功能,而正如大家所期望的,Rails也没有忘记对它们进行集成,Scriptaculous Helper为在Rails框架中使用scriptaculous效果提供了很好的封装, draggable_element,drop_receiving_ element, sortable_element使开发人员可以很容易创建一个可拖放对象和可进行拖动排序的列表;visual_effect让你很容易实现各种scriptaculous动态效果。

Prototype Helper封装了一些常用的AJAX模式。link_to_remote生成一个连接,点击它就会发起一个AJAX调用,periodically_call_remote会生成一段JavaScript代码,这段代码会定时地不断发起AJAX调用, form_remote_tag和form_tag类似,不过点击提交时,form的数据会经过prototype提供的函数进行序列化,然后以AJAX的方式提交到服务器端。

observe_form和observe_field会生成这样的form,当其中的内容发生变化时,就会发起预先定义好的AJAX调用。

当这些AJAX调用提交到服务器端以后,服务器响应这些请求的结果可能会是一段文本或者一段JavaScript代码。返回的文本通常会被动态地以各种方式插入到页面上某个地方,这是AJAX很普遍的一种应用;而如果返回的是JavaScript代码,这段代码就会被执行,其结果可能会给页面带来各种动态效果,改变页面的显示、更新页面内容等等。

然而客户端的JavaScript代码如何知道返回的是可供直接更新的文本,还是需要执行的JavaScript代码呢?这要归功于prototype和Rails的协作。首先,通过prototype中的Ajax.Request发起的AJAX调用在收到相应内容后会判断内容的MIME类型,如果返回内容的MIME类型是text/JavaScript,则说明返回的内容是JavaScript代码,客户端应该执行这段代码,如果是其他的内容例如text/HTML等,可以选择其他的方式来处理。其次,Rails框架提供了一系列的机制来支持对JavaScript内容的生成等,这其中就包含一种新的render方式update和一种新的模板RJS。

render : update

render:update是Rails提供的一种在服务器端动态生成JavaScript内容的方法。通常的调用方法是:

        render:update do |page|
          page.replace_html  'user_list', :partial =>
    'user', :collection => @users
          page.visual_effect :highlight, 'user_list'
        end

render:update生成了一个JavaScript Generator,它就是上面示例代码中的page,这个generator提供了许多方法,可以用来完成许多功能的JavaScript代码的生成,通过调用这个generator的方法,render:update会返回生成的JavaScript代码,并将返回内容的MIME类型设为text/JavaScript,这样在客户端的AJAX请求就可以识别并执行返回的JavaScript代码,完成各种动态的效果。

通过这个Generator,开发人员可以不用接触JavaScript代码就可以实现大部分常用功能。例如,通过[](id)方法,我们可以在DOM中找到元素id等于id的对象,并对该对象进行进一步的操作 :

      page['blank_slate']
      page['blank_slate'].show
      page['blank_slate'].show('first').up

生成的JavaScript代码是 :

      $('blank_slate');
      $('blank_slate').show();
      $('blank_slate').show('first').up();

RJS模板和render:update给我们带来了很大的便利,然而相比于RHTML模板,RJS模板还有些美中不足,例如不支持partial,这使得重构和复用RJS模板变得不那么容易。

更强大的地方在于page的select方法,它能够使用css selector语法来选择页面上的元素,对它们进行操作:

      page.select('div.to_be_hidden').each { |element|
    element.hide }

除此之外Generator还提供了众多的方法来帮助用户生成各种特效,例如:

      page.replace_html  'blank', 'Hello, world!'
        # 替换blank的内容为Hello, world!
      page['foo'].value = 'new value'
    # 将foo元素的值设置为new value
      page.hide 'message'
    # 隐藏message元素

Generator也提供了一些直接操作JavaScript代码的方法:

      page << “alert('hello')”
        # 直接插入alert('hello')代码
      page.assign 'foo', 'value'
        # 给JavaScript变量foo赋值为value
      page.call 'func'
        # 调用JavaScript函数func

RJS模板

render:update已经给我们动态生成JavaScript代码给予了极大的便利,然而它只能被用在controller中,这在需要生常大量JavaScript代码的时候,会使controller变得很臃肿,同时不便于管理和重用。RJS为我们提供了另一个好的选择。

RJS模板和RHTML以及RXML模板类似,都是在controller执行完毕后被render然后发送到客户端,其不同之处在于RJS最后会被render成为JavaScript代码发送到客户端然后被执行,执行的结果往往被用来改变原有的页面内容,不像RHTML模板,会被用来生成新的内容页面。RJS模板通常在客户端的请求是AJAX类型的时候才会被调用,也就是说如果一个action有两个模板:index.rhtml和index.rjs,则如果以非AJAX的方式调用index,其RHTML模板将会被启用,RJS模板会被忽略;相反,如果是AJAX方式的调用,则会使用index.rjs模板,忽略其RHTML模板。

RJS模板的内容即是前面的例子中传递给render:update的block中的内容,Rails会自动生成一个JavaScriptGenerator给RJS模板使用。例如:

          page.replace_html  'user_list', :partial =>
    'user', :collection => @users
          page.visual_effect :highlight, 'user_list'

RJS模板和render:update给我们带来了很大的便利,然而相比于RHTML模板,RJS模板还有些美中不足,例如不支持partial,这使得重构和复用RJS模板变得不那么容易。

还有一种途径可以帮助我们有效的组织我们的JavaScript生成代码,这就是利用Rails的helper。每个controller都有一个对应的helper,我们可以将render:update的block中的内容放到helper中,这为我们复用和重构提供了一个有效的工具。例如:

        module ApplicationHelper
          def update_time
            page.alert 'hello'
          end
        end
        # Controller action
        def spike
          render(:update) { |page| page.alert_hello }
        end

测试AJAX应用

异步性是AJAX应用的一个特点,这给我们测试AJAX应用带来了一些困难。由于服务器性能、网络状况等等原因,我们往往无法估计出一个AJAX调用要多久才能返回结果,一个最典型的例子就是我们从服务器上去获取一段内容,我们必须等到这段内容显示在浏览器中才能确定我们的功能确实满足了需求。AJAX应用往往还包含很多客户端的效果,例如拖动排序等,如何对这些功能进行自动化测试也是很关键的问题。如果能够有一个测试工具帮助我们测试这些功能,我们可以更自信地编写AJAX应用。

目前支持AJAX应用测试的测试工具不多,Selenium则是其中比较成熟的一个。Selenium具有对异步调用进行测试的能力,可以模拟键盘、鼠标事件对被测应用进行各种操作,例如拖动、右键点击、模拟快捷键等,同时提供了许多种方法让用户能够选择页面上的元素进行各种操作和验证,包括验证元素之间的顺序、元素的位置等,为我们测试AJAX应用程序提供了一个很好的选择。

Selenium对AJAX测试的支持

Selenium自0.7版本开始逐渐加入了对AJAX测试的支持,到目前为止的0.8版本,已经提供了比较完善的功能来支持AJAX应用的测试,Selenium对AJAX测试的支持体现在以下几个方面:

对异步调用的测试

对异步调用的测试在AJAX应用程序的测试中占有很大一部分比重,由于异步调用返回结果的时间难以预先估计,所以往往难以测试。Selenium在如下几个方面提供了对异步调用测试的支持。

首先,Selenium对所有的断言都提供了相应的waitFor命令,用来等待某一种条件的满足或者某一种情况的出现。例如,verifyTextPresent有对应的waitForTextPresent命令,大家知道verifyTextPresent是用来验证页面上某一段给定文字是否出现,而相应的waitForTextPresent命令则会在预设的限制时间段内一直等待,直到给定的文本出现在页面上,这使得我们可以对异步调用功能使用Selenium编写功能测试代码。同理,其他常用的断言例如verifyText、verifyAttribute等也有相对应的waitFor命令。

对于不满足于Selenium提供的断言的用户来说,Selenium还提供了waitForCondition命令,该命令允许用户使用JavaScript代码指定等待条件。waitForPageToLoad会一直等待直到页面load完成,而waitForPopUp能够等待一个弹出窗口load完成。

所有的waitFor命令都有一个时间限制,避免无限制的等待,超过了这个限制,Selenium会认为该断言没有被满足,会返回错误。这个时间被默认地设为很长,然后用户可以通过setTimeout命令来改变这个时间长度。

对鼠标键盘事件的模拟支持

AJAX技术在很多方面使得用户体验更加良好,包括可拖动排序列表、自动完成等,同时也使得Web界面的用户交互更加丰富,例如Google的很多产品都支持键盘快捷键的操作。Selenium提供了对各种键盘和鼠标时间的模拟。

Selenium提供了keyDown, keyPress, keyUp命令支持对键盘事件的模拟。它们都带有两个参数,locator和keySequence,locator用来指定键盘时间发生的对象,而keySequence指定按下的键盘按键。

mouseDown, mouseUp, mouseMove, mouseOut, mouseOver提供了对鼠标时间的模拟,只要指定了时间发生的对象,就可以通过这些命令来模拟鼠标左键的动作。另外Selenium还提供mouseDownAt, mouseUpAt, mouseMoveAt,这些命令可以通过像素精确地指定鼠标事件将会发生在某个元素上的位置。

Selenium具有对异步调用进行测试的能力,可以模拟键盘、鼠标事件对被测应用进行各种操作,例如拖动、右键点击、模拟快捷键等,同时提供了许多种方法让用户能够选择页面上的元素进行各种操作和验证,包括验证元素之间的顺序、元素的位置等。

对常用AJAX模式的测试支持

Selenium还提供了一些对常用AJAX功能测试的封装。对于像可拖动列表这样的AJAX模式的测试,可以使用dragdrop命令来拖动某个元素,然后用verifyOrdered命令来验证列表中元素的顺序,或者使用verifyElementIndex来验证某个元素相对于它的父元素的索引。

Selenium也提供了很多便利的断言。例如关于元素的verifyElementHeight, verifyElementWidth,verifyElement PositionLeft, verifyElement PositionRight等,可以用来测试元素的大小和位置。

在Rails框架中使用Selenium

Rails框架本身提供了良好的测试集成,然而默认地并不包含Selenium。在Rails中使用Selenium框架,至少有如下两种比较方便的方法。

Selenium on Rails

Selenium on Rails(http://www.openqa.org/selenium-on-Rails/)是一个Selenium的Rails插件,它在某中程度上提供了一个Selenium的Rails集成,给开发人员在编写和组织Selenium测试提供了很多便利。

Selenium on Rails允许用户使用两种格式来书写Selenium测试,第一种是sel格式的测试文件,比较简单,通过竖线分隔的命令和参数来表示Selenium命令,例如:

      |open|/selenium/setup|
      |open|/|
      |goBack|

Selenium on Rails会在运行测试前将sel格式的文件转换成HTML格式的Selenium测试文件,加载到浏览器中运行。

第二种格式是rsel文件,这种文件的内容很像Ruby代码,例如:

      open '/'
      assert_title 'Home'
      ('a'..'z').each   {|c|   open   :controller   =>
  'user', :action => 'create', :name => c }

Selenium on Rails同样会在运行测试前将rsel格式的文件转换成HTML格式的Selenium测试然后再运行。

第三种格式就是RHTML格式,用户可以像书写RHTML模板一样书写Selenium测试。

Selenium on Rails提供了对partial testcase的支持,就好像partial RHTML模板一样,用户可以将可重用的测试代码放到一个文件中,然后在其他测试中包含这个partial测试文件。例如:

      #_login.rsel
      open '/login'
      type 'name', name
      type 'password', password
      click 'submit', :wait=>true

在其他测试文件中我们可以包含这个文件:

      |includePartial|login|name=John
  Doe|password=eoD nhoJ|

在rsel文件中:

      include_partial   'login',   :name   =>   'Jane
  Doe', :password => 'Jane Doe'.reverse

在RHTML文件中:

      <%= render :partial => 'login', :locals => {:name
  = 'Joe Schmo', :password => 'Joe Schmo'.reverse} %>

Selenium on Rails提供的另一个很方便的功能就是测试fixtures的管理。大家知道,在运行每个测试之前,我们都往往需要预设一些环境包括刷新测试数据库这样的工作。Selenium on Rails提供了setup命令用来帮助用户完成这些操作:

      setup :fixtures => :all
  #导入所有fixtures
      setup :clear_tables => :items
  # 清空items表的内容

Selenium on Rails还提供了一个新的rake task:test:acceptance,该task会自动打开用户指定的浏览器,运行所有的Selenium测试并返回测试的结果。

Selenium rc mode

另一种在Rails中使用Selenium的方式是使用Selenium remote control模式(http://www.openqa.org/selenium-rc/)。采用这种方法,整个Selenium测试从结构上会包含两部分,一部分是我们用Ruby代码编写的Selenium测试,另一部分是Selenium server。当运行Selenium测试的时候,会向Selenium server发送一系列的Selenium命令, Selenium server负责打开浏览器,载入被测应用,执行这些Selenium命令,然后报告结果。

使用这种方式只需要在测试代码中包含一个Selenium的Ruby客户端驱动文件,它是一个Ruby类,负责和服务器通信和解析并发送Selenium命令。只需要和Test:Unit配合,我们就可以使用Ruby来书写Selenium测试。

这种方式有许多方便的地方,例如我们可以很容易地对测试进行重构,可以很方便的利用Rails的fixtures机制来进行数据库环境的准备。而且可以方便地使用Test:Unit库的断言,用户也可以使用其他的测试框架例如rspec等。

Selenium在Rails程序中的应用

Rails框架本身提供了比较完善的测试架构,尽管某些方面受到争议,但是总体来说给我们提供了很大的方便。但是,仅仅依靠Rails本身提供的测试功能,我们往往很难对view进行测试,Rails的functional测试框架虽然提供了assert_tag可以用来测试Response页面中的内容,但是其功能不是十分强大,在Rails的1.2新版本中assert_select已经取代了assert_tag。

Rails提供的测试框架并不能满足用户测试AJAX应用的要求。例如,我们不能测试页面上的动态效果,不能测试异步调用的结果,基本上在这些部分Rails并没有给我们提供太多的支持,Selenium自然而然成为我们的一个选择。如果能够充分利用Selenium的AJAX测试支持,作为Rails测试框架的一个有益补充,能够让我们的测试更加完善,确保我们的AJAX应用稳定无错。

Selenium本身作为一个出色的功能测试和验收测试框架,也可以被用来在Rails框架中编写功能测试和集成测试,承担Rails的功能测试和集成测试的功能。Selenium在测试view方面比Rails框架本身提供的功能测试框架功能要更强大一些,可以满足一些复杂的测试需求,只是在速度方面比Rails测试框架慢一点。由于Rails测试框架并不在实际的浏览器中运行测试,而Selenium提供了对多种浏览器的支持,所以Selenium也可以用来测试AJAX应用的浏览器兼容性。

小结

本文从在Rails中集成的JavaScript框架入手,介绍了Rails中的AJAX实现原理和一些具体实践,分析了Rails对AJAX变成的支持,并对如何在Rails中测试AJAX应用进行了讨论,尝试给读者一个关于在Rails框架中编写AJAX应用的总体印象和脉络。Rails框架毫无疑问已经成为当今最受人注目的web框架,如何用Rails框架快速搭建可靠稳定的AJAX应用是很多人关注的问题。希望这篇文章能给读者带来一些有用的经验和信息。

Rails帮你学英语——Idapted平台

□ 文 / 杨祥吉

李国栋(Adrian)是一名在英国出生并长大的华人,他毕业于剑桥大学经济系,后进入斯坦福大学学习MBA。Adrian一直对语言有浓厚的兴趣,他在校期间学习拉丁语、法语、德语和日语。1997年他第一次来到中国开始学习中文。虽然只有6个月,李国栋在北京学会了日常使用的中文。这种经验为将来的englishquad学习英语口语方式提供了灵感。在斯坦福学习期间,他一直在思考在语言学习领域创业。Adrian意识到他需要一个拥有技术并能实现这些想法的人。于是他搜索互联网,找到了最接近该想法的一个共享软件,其被称之为虚拟记忆卡,是一个通过卡片来学习语言,并允许用户共享学习资源的软件。Adrian给作者写了邮件,询问作者是否有兴趣合作。当该软件的作者Jonathan Palley,斯坦福大学物理系的一个本科生收到这封邮件的时候,留意到Adrian使用@gsb.stanford.edu(斯坦福大学商学院)电子邮件,便回信说,下楼见个面吧,我在你宿舍旁边的一幢宿舍楼里。两人对于语言教育有非常一致的观点,于是Jonathan便和Adrian决定一起创业。他们随后参加了一个语言培训的课题项目。他们与团队中其他MBA成员一起努力收集数据,讨论商业模型,并邀请硅谷一些著名的风险投资家担任项目顾问。最终,他们从35支队伍中脱颖而出,获得风险投资家的青睐(这也是投资者愿意成为课程顾问的原因)。几个月后,Adrian毕业了。团队的其他MBA成员退出后,Adrian和Jonathan决定在风险投资的支持下,成立一家公司正式开始创业。考虑到中国巨大的英语学习市场,良好的技术人才以及更低的创业成本,他们于2006年7月来到北京,开始正式运作这个公司Idapted。

所以,Idapted公司是一家来自美国硅谷的创业公司。公司创办的www.EnglishQuad.com网站定位于使用先进的Web和VoIP技术,让英语学习者可以直接同美国本土的老师进行一对一的英语口语练习,改变人们学习英语口语的方式,提高中国人的口语水平。

技术架构

作为一家初创的互联网企业,Idapted拥有足够的冒险精神。考虑到公司产品定位和市场上以传统培训机构区分定位的情况,公司决定开发基于Web的产品,而非客户端产品。主要考虑了两方面的技术选型:Web技术和VoIP服务器方案。

在Web方面,CTO经过大量考察以及与众多硅谷的同行进行讨论后,认为Ruby On Rails最能高效地开发出想要的产品。Ruby On Rails有许多出色的设计,在2005年,Rails以出奇的简洁和快速席卷了整个Web开发领域。AgileWeb Development With Rails是2006年度Amazon十佳畅销书之一,2006年Jolt Award General Technical Book得主。而Rails作者David Heinemeier Hansson也在2006年8月荣获OSCON(全球开源大会)年度最佳黑客。当然也有不少老牌社区铁杆对Rails进行质疑,并且迄今还没有世界级的Web应用是建立在Rails基础上的。不可否认,将整个公司的产品建立在Rails之上,是有冒险的成分。作为初创企业,在没有任何/优势/包袱的情况下,采取不同的技术和策略,是能够避开传统巨头并生存的必备素质。

在VoIP服务器方面,Asterisk以悠久的历史、丰富的应用和强大的功能,成为公司的理想VoIP服务器端。理想中的VoIP服务器应该实现的功能有:

1)路由语音数据

2)用户数据的动态管理

3)呼叫记录

4)权限和优先级管理(决定最好的Trainer得到最多的业务)

5)记录全部语音数据,并以文件形式保存

6)能够提供接口,与Web进行良好的交互和数据共享

7)多台语音服务器之间的负载均衡以及语音代理服务器功能

经过考察,Asterisk能实现大部分功能,某些不具备的功能,也能通过接口自行添加。

数据库方面很自然地选择了MySQL这个开源领域的头号数据库。

服务器方面,则由于Linux对Ruby、Rails和Asterisk以及MySQL都提供了良好的支持,成为毫无争议的选择。

在Web浏览器中运行的VoIP组件,他们初期认为几个比较知名的VoIP开源项目能满足需求,须重新封装为ActiveX便能使平台功能得以实现。

至此,技术选型完成,很明显,全套都是开源项目。

在公司正式运营6个月后,回头来看,仍有一些当初没有预料到的变化:

1. Asterisk的Queue(路由)不稳定,常导致服务无法进行。解决方案是用Ruby重新写了一个路由模块,使用Asterisk的接口进行操作,成功解决这个问题。总共代码只有区区几百行。

2. 语音ActiveX效果无法令人满意,尤其是网络环境恶劣的情况下,与Skype的品质差异立刻显示出来。令Idapted不得不花费大量的时间从头开始学习VoIP的基本知识,再耗费巨大的时间对VoIP音质进行调优。

在Web开发方面,按照CTO Jonathan的观点,该网站已经是中国国内难度和规模最大的Rails项目了,至少就所知道的而言。Rails的开发效率和稳定性,令公司感到十分惊奇。短短的几个月之间,一个很小的团队,便可以完成过去无法想象的Web功能,远远超出了最好的预期。Rails始终给Idapted提供了极大的帮助,使其可以抽出精力来研究VoIP和反复改进产品。这一切,如果使用传统技术来完成,其工作量是无法想象的。

Ruby语言的动态特性

在Rails流行以前,Ruby还是一种弱势的开发语言,与Python、PHP等动态语言相比似乎没有多少优势。但Rails的流行,让Ruby有了新的机会让大家重新学习。纵观Rails的源代码,充满了大量使用Ruby动态特性的技巧。某种程度上说,Rails与Ruby有非常紧密的天然联系,这种联系是建立在Ruby语言的独有特性上的。

例如需要申明三个函数,通常的做法是:

      def student?
        false
      end
      def trainer?
        false
      end
      def admin?
        false
      end

而在Ruby中,可以非常简洁的表达为:

      %w(student trainer admin).each do |type|
            class_eval "def #{type}?; false; end"
      end

Rails出类拔萃的一个特性便是其丰富的Plug-in插件系统。Rails的Plug-in简单清晰。

假设在一个Blog项目中多处用到了搜索数据库多个column的功能。通常情况下,你可以在每个Model中这样写:

      class Post < ActiveRecord::Base
        has_many :comments
        def self.search(query, fields, options = {})
          find :all,
          options.merge(:conditions                  =>
    [[fields].flatten.map { |f|
          "LOWER(#{f})   LIKE   :query"   }.join('   OR
    ' ),{:query => "%#{query.to_s.downcase}%" }])
        end
      end

你可能会在多个Model中重复用到这些代码和逻辑。将其封装为一个Plug-in,只需要这样的步骤。

1. script/generate plugin active_ record_search Rails将自动创建一个空白的Plug-in需要的数个文件。

2. 进入Rails项目目录,在vendor/plugins/active_record_search/lib/下面,修改active_record_search.rb :

      module ActiveRecordSearch
      def search(query, fields, options = {})
      find    :all,    options.merge(:conditions    =>
  [[fields].flatten.map { |f|
      "#{f} LIKE :query" }.join(' OR ' ), {:query =>
  "%#{query}%" }])
      end
      end

修改vendor/plugins/acitve_ record _search/ 目录下的init.rb文件,加入以下代码,使plug-in成为ActiveRecord::Base的基本功能 :

      require 'active_record_search'
      ActiveRecord::Base.extend ActiveRecordSearch

至此,任何一个项目只要在vendor/plugins/中有了以上文件,则任何一个Model都将奇迹般地具有更灵活的Search功能。

如此清晰简单而又强大的插件系统,全靠Ruby语言的动态特性才得以成为现实。

项目的Web架构

www.EnglishQuad.com 的Web需要实现以下几个主要的模块 :

1. 学生Portal:即学生所接触到的网站部分。包括注册、新闻、BBS、在线购买学习卡、课程学习、语音交互、及时的文本聊天、速度检测、语音检测、插件检测和自动安装等。

2. 培训老师Portal:这部分包括关于公司的业务介绍,在线受理成为培训老师的申请。在对申请者进行认证中,加入了手机号码认证,然后系统用语音提问并记录老师的回答,稍后由人工来对这些回答进行评测。此外,还有老师的考试系统。每个老师要通过8次在线的考试,必须全部通过这些考试,才能成为Idapted老师。一旦成为老师以后,这些人将可以在培训老师Portal中使用BBS。在线查询自己的呼叫记录和收入,并予以确认。学习更多的课程和认证,确保每个课程的每个老师都是经过了足够的培训。

3. 内容编辑系统:在后台,有一个功能异常强大的课程内容编辑系统。在这个系统中,可以设定培训的课程。这些课程能够支持图片、音频和视频,并实现了事件脚本系统,使之可以在课程中增加各种定制化功能:如定时、自动弹出、自动时间提醒等。

4. 后台管理:在后台,还可以看到整个系统的状况。包括呼叫次数,培训老师的具体录音,每天有多少学生使用公司的产品,评价如何等。同时,在后台可以管理老师的服务质量,提供对老师网络强大的管理功能。

5. 培训老师的客户端:对于老师,Idapted认为一个功能强大的软件比单纯的网页更适合他们工作。他们可以更改自己的状态,实现本地录音等功能。对于实际的对话,在Trainer Client中使用了IE作为容器,加载了对话的Web页面,所有的业务逻辑仍然是通过Web来实现,使得公司保持了整个项目的一致性。

Rails整合Asterisk + SIP

Rails具有良好的扩展性、开放性和灵活性。Idapted平台的核心业务模型是建立在VoIP技术之上的。如何让Web平台顺利地和VoIP系统进行交流显得尤为重要。在VoIP服务器方面,在做了大量的调查和论证以后,Idapted选择了Asterisk(链接)作为VoIP服务器。

在Idapted的业务模型中,我们需要在Web界面去控制Asterisk。例如在业务中需要对老师进行电话自动面试。当老师填写申请表后,将请老师填入电话号码。Rails获得电话号码后,命令Asterisk通过美国电信网络接口(需要支付一小笔电话费),向目标电话号码通话。当申请者接听电话后,自动刷新Web页面,显示预先设定的画面。同时Asterisk向申请者播放一些录音并提问。申请者在电话中回答这些问题,Asterisk记录下录音,以便公司的招聘人员进行筛选。

然而,如何让Asterisk和Web进行交互,是整个系统的核心技术难点之一。

在实际使用中,Idapted发现Asterisk的队列路由不稳定,常常出现老师空闲,学生尝试去拨入的时候,Asterisk的队列部分却认为老师都是忙音,让学生一直等待。

在认真分析以后,公司决定使用Asterisk的AMI模块,用Ruby来重写队列的路由算法,运用类似图2的结构,让Rails来控制Asterisk对语音的路由。

得力工具

www.englishquad.com使用了大量的AJAX效果和技术。如何调试AJAX就成为高效工作的重要问题。

团队在日常开发中,一定会使用到以下三种工具 :

1. Firefox:其良好的兼容性,高速稳定、友好的HTML代码查看以及对众多的插件的支持,使之成为Web开发第一选择。

2. Web Developer:Web Developer拥有众多的功能。最令人愉悦的功能便是其查看“generated source code”的功能。许多AJAX效果,执行前和执行后HTML代码改变了很多,通过查看执行后的代码,能更快找到问题所在。

3. Fire Bug:通过它,我们可以方便地查看到每一个AJAX请求,发布了什么数据,响应是什么等。是AJAX调试必备工具。

有了以上工具,养成良好的调试习惯,会发现AJAX在Rails中如此简单好用,让人爱不释手。

部署

对于Rails应用,找到合适的方式,部署便可成为乐趣。这来自于Capistrano的强大支持。

Capistrano是一款功能强大灵活的Ruby小程序,主要用于实现自动化的Web应用程序部署。

作用原理:Capistrano通过使用SSH和SVN,使用在部署脚本中设定的用户名和密码,登录到服务器(群)上,自动运行各种命令最终完成部署。当部署出现问题的时候,可以非常轻易的使用Cap rollback来回滚到部署前的版本,从而实现安全部署。

由于每次部署的时候Capstrano都会重新拷贝已有的项目文件夹,然后使用SVN进行更新。如果在项目的Vendor中使用了最新的Rails,则每次拷贝超过1000个Rails的文件,会成为单个耗时最长的操作,如果部署涉及多台Web服务器,那么耗时就特别长。解决的办法之一是将Vendor中Rails也作为一个软链接,每次部署中,将Vendor/rails这个目录指向系统中实际存在Rails的地方,即可大幅缩短部署时间。

只需要在Deploy.rb脚本中的after_ symlink任务中加入以下代码即可提高部署的效率:

      task :after_symlink, :roles => :web do
      run "rm -rf #{release_path}/vendor/rails"
      run  "ln  -nfs  #{deploy_to}/#{shared_dir}/rails
    #{release_path}/vendor/rails"
      end

整个团队使用相同的开发数据

在的实际开发过程中,由于项目越来越大,以致于常常因为没有统一标准数据而导致无法在本地使用很多功能。从经验的角度来看, Idapted建议 :

1. 在初期,所有的程序员都应该使用标准的开发数据。这将显著提高开发效率。标准开发数据来自于所有程序员的共同维护。例如当程序员A修改了User表,加入了激活码的功能,那么A就应该立即加入标准的开发数据,使得团队中的任何人都可以直接使用该项目。

2. 当项目上线,并有了用户后,建议每日备份数据库。每周要求全体程序员直接使用最新的服务器数据作为开发数据。这样的好处是,当服务器端出现bug时,比较容易在本地也能重现这些bug,从而更容易维护。其次,服务器上的数据才是真实的数据,使用真实数据开发,往往能够在开发期就能避免许多错误(如果团队并没有养成测试驱动的开发模式的话)。

Rails学习心得

Rails是如此地超然精妙,能够使用Rails来开发实在是程序员的幸福。以笔者自己学习Rails的经验和教训来看,可以推荐如下三本书 :

1. Agile Web Development with Rails

这本书在Rails界被尊为圣经,可见其影响力之大。事实上,Rails的文档和开发资料相比Java等技术显得匮乏和凌乱。这使得本书显得尤为珍贵。本书并未要求读者需要具备Ruby知识——作者在需要的时候会介绍必要的Ruby知识。该书的更新非常快,建议阅读最新的第二版(即使是第二版,也每几个月便更新一次,并加入新的章节),第一版中不少内容已经不再实用了。

2. Programming Ruby

这是一本关于Ruby的好书。聪明的你虽然不必阅读也能写出很棒的Rails程序,但如果想要写出高效简洁的Rails代码或者Gem、Plug-in等,阅读此书一定会有不少帮助。该书对Ruby进行了细致入微的描写,对于Ruby很多特有的语言特性也花了不少篇幅,被誉为Ruby语言圣经。

3. Rails Recipes

阅读本书将会轻松愉快,全书讲解了几十个最常用的Rails偏方,涉及View、AJAX、数据库、Email等。该书行文简单易懂,是实用主义至上的最佳书籍,相信每个Rails项目都会用到其中的偏方。

善用Plug-in和Gem,同时要敢于修改第三方代码,使其适应自己的项目需要。在这里,笔者认为一定要知道的Plug-in和Gem有:

1. Capistrano:让部署成为乐趣

2. Streamlined:一分钟做好网站后台的好工具

3. Redcloth:如果想做Rich Content,这将是好工具

4. File_column:简化文件上传的插件。虽然谁都可以很快写这样一个小插件,不过直接使用会更简单

5. RMagic:图象处理,与File_column配合处理用户上传图片

6. acts_as_authenticated:帮助产生基本的非常优秀的用户认证代码,省去密码加密等许多麻烦。不过也可能需要手动加入“remember me”,用户角色划分等功能

7. exception_notification:在网站出现可怕的Application Error的一刹那,向你的邮箱发送了错误信息——让人可以安心睡觉

8. globalize:想冲出亚洲?提供了多种语言的支持,使得开发多种语言的网站更简单

9. acts_as_taggable:使用这个插件,几行代码就能让你的程序拥有Tag功能。此外,这个plugin的代码清晰简单,适合初学者DIY

学习Rails的几个主要手段 :

1. 让经典3本书一直陪伴你身边,并经常阅读;

2. 加入到实际项目中,请动手来尝试Rails,这一直是最有效的方式;

3. 从简单的Plug-in开始阅读源代码,与精英对话才能成为精英。最后要亲自阅读Rails的源代码;

4. 加入到社区中去!中文的社区推荐railscn.com和google group中的RoR北京社区,国外的社区推荐参加Google Group的Rails Talk社区;

5. 找个Rails空间,享受成就感!推荐Dreamhost.com,24美元, 200G空间!

6. PodCast!聆听来自前沿技术发源地的声音,体会Web设计的理念和信仰,给自己的思想充电,放松的绝佳时机。

思想、思想、思想!大师之所以为大师,并非编程的技巧高,而在思考问题并指出问题的关键所在。Rails所以为Rails,在于其背后的一些前所未有的理念和思想。作为一个程序员,会思考才是生命常青的关键。

跳出Rails的限制,发挥你的所有才能,你还会发现 :

1.JavaScript的价值:尽管Rails宣称要避免程序员与JavaScript打交道,但如果你确实想与众不同,不想让模仿者轻易窃取创意,JavaScript是不错的技术壁垒;

2. 必要的时候,不要犹豫,与C/C++/Java整合。为了优化性能,请使用C或者C++重写系统中最关键的部分。为了获得强大的业务逻辑,JavaEE是非常好的选择。Rails可以很开放地与这些技术共存。

用户行为测试

当然,Idapted并不认为自己的设计就是完美的。Idapted关心用户的使用感受,并设法满足用户的要求,甚至他们没有留意到的要求。在产品原型开发完毕后,公司便邀请许多渴望提高英语口语能力的用户来试用产品。Idapted每天安排2~3名自愿者,邀请他们到公司的测试房间,然后准备好电脑以及老师就位,并进行测试。系统会记录下用户的录音,以便用时分析。测试结束后,Idapted对每一位测试者都进行谈话,以求了解他们的真实感受。这样的活动持续了一个多月,当对某个功能的设置存在疑问的时候,Idapted会请测试者给与评价,并依据评价来改进。有时候甚至会针对某些刚添加的功能,邀请测试人员使用,并使用摄像机全程记录用户的行为和屏幕。有时结果会让人大吃一惊:原来公司的系统竟然让用户如此不知所措!有了这些数据和用户的建议,配合上Rails强大灵活的技术架构支持,很快就能改进易用性方面的问题。几天后再次邀请其他用户进行测试的时候,录像会显Idapted我们的用户感到轻松自然得多。

51dir初探Rails

□ 文 / 江立波

虽然写程序不是我的职业,但这些年来断断续续地一直在写,也许是好奇心使然,尝试了许多不同的编程语言和运行环境。最早接触的是微软的ASP,我用它编写了一个被称作女网的化妆品销售网站,当时是1999年底,那还是8848网站很火的年代。我的女网运行了大半年之后,很快被淹没在不计其数的B2C网站中。

后来,由于工作调动,让我有机会接触大量的电脑和网络,这时候我第一次安装并使用Linux,发现除了Windows和ASP之外,还有这样一个精彩的开放世界。我开始学习Perl,并且尝试在Apache mod_perl的环境下编写一个新闻发布系统,使用的模版系统是Mason,其实Mason远不止是模版,它有很多创新的想法,甚至和Rails也有某些相似之处。但是Mason没有流行起来,并且也没有上升到框架的高度。没有流行的Web框架,也许是Perl的一大遗憾。但是最近出现了一个Catalyst的Web框架,是Perl版的Rails,相信以Perl语言的成熟度,表现一定不会差。

而我学习Python是因为在办公室里看到一本Python的书。Python语言整齐干净的代码给我印象深刻,它有丰富的库函数,对于Unicode有良好的支持,我用Apache的mod_python编写了一个通用的网站繁简体转换系统,还有就是为Novell的ifolder编写了一个Web层面的用户注册系统。Python还有一个非常有名Web应用服务器叫Zope,它不仅仅是一个Web框架,而是和Java世界里的JBoss类似的应用程序服务器。Zope非常优秀,但始终不温不火,也许是它囊括了Web应用的所有层面,反而变得相对封闭了。

好了,接下来是Java和Ruby以及Rails。因为我的51dir就是先用Java开发,然后改成用Ruby on Rails。

51dir最初是用Java开发的,当时叫做Web文件夹,就像它的名称一样,它允许用户通过Web界面上传文件,然后以树状的目录管理自己的文件,这样用户可以在任何时间,任何地方操作自己的文件,只要那里有网络连接。用Java开发Web程序是大多数人的选择,因为它有很多Web框架可供选择,比如Struts、Webwork等等。

51dir采用Struts开发,Struts作为MVC框架也许不是最好,但比较流行。我花了3、4个月的时间写完这个程序,然后放在互联网上运行,期间又要不断的修改错误,这是一个艰苦的工作。更重要的是经过一段时间的运行,发现用户大多是用51dir在自己的网站和论坛上发布文件的连接,对于文件夹没什么兴趣,也就是说用户的需求非常简单,他们只是上传文件,然后告诉别人这个文件在哪里,不管是通过邮件、论坛、还是QQ和MSN。

这时候,我就有了改写程序的想法,并且考虑Java之外的其他选择。虽然Java是一种完善的、流行的、跨平台的语言,但我个人觉得,也正是它跨平台的特点,使得它和操作系统之间有点隔膜。我不知道怎样表达这种感觉,比方说对比于Perl,Linux系统里面没有Perl的话,几乎无法进行安装,也就是说Perl和系统结合得很紧密。

Java典型的Web应用的是一个Web服务器加上一个数据库、邮件系统等,都是在应用程序层面,对于Linux操作系统层面的操作总不如脚本语言方便,比如sendmail,文件管理等等。当然并不是指Java不能做这些事情。51dir对操作系统的文件有频繁的操作,并且希望采用轻量级的Web服务器来针对大量的下载并发用户,所以计划采用一种脚本语言来开发。

当时我正在使用Python,所以在寻找Python的Web框架。就像刚才提到不采用Java的原因一样,Zope也不适合这个应用,而其它的框架我又都不太熟悉。刚巧这个时候Rails热起来了,并且大家都说用Ruby on Rails编程是一种乐趣,这确实挺吸引人的。于是我开始学习Ruby语言,然后将51dir用Rails重新编写。

Ruby on Rails突然流行起来,一定有其原因。它主页上的一句话很准确地概括了它的特点。“Ruby on Rails is an open-source web framework that's optimized for programmer happiness and sustainable productivity.It lets you write beautiful code by favoring convention over configuration"。主要是两个方面,一方面是针对开发体验,是高效而充满乐趣;另一方面是它提出了采用约定成俗,而不是复杂的配置。对第一方面来说,生产效率高了,可以快速看到开发的成果,当然会有乐趣。反之你没日没夜的加班工作,却迟迟看不到结果,当然是痛苦啦。第二方面,针对配置文件越来越复杂的趋势,Rails反其道而行之,在遵循某种约定的情况下,几乎不需要配置文件。

51dir采用Rails重新开发,花了不到半个月,比起Java真是快了好几倍,而且在你因为编程的枯燥而厌倦之前,你就会看到工作的成果!下面就谈谈用Rails开发Web程序的优势。

首先简要说一下Ruby语言,Ruby从运行速度,强壮程度,可用的库函数角度来看,不如Perl和Python。但是Ruby很有特色,是一个纯粹的面向对象的语言,在Ruby里面连数字都是对象。还有Ruby特有的block、mixin等概念,使得代码编写非常灵活。和Python类似, Ruby也有一个交互的运行环境,对于函数的用法除了查看函数文档之外,在交互环境里验证一下,也是很直观的,至少我很喜欢这种方式。关于Perl、Python和Ruby的情况可以参考一下Google的趋势,从图中可以看出Ruby受关注的程度处于上升阶段。图中蓝色的是Ruby,红色的是Perl,黄色Python。

Rails是一种全堆栈的架构,它已经为你构建了应用程序的所有层面,使你不需要重复自己的工作(DRY),而且Rails是用来构建面向数据库的Web应用程序,在约定成俗的原则下,Rails自动完成数据库中的记录和Ruby对象之间的映射。最简单的情况下,你可以用代码生成器生成整个面向数据库的应用程序,而不用编写一行代码,生成的程序具有对数据库的CRUD(create、read、update、delete)功能。在真实的开发情况下,你可以将代码生成器生成的程序作为原型,在此基础上作修改即可,而程序始终处于可运行的状态,我觉得Rails的高效正是体现在这里:你只改变你想改变的,你不想改的它都已替你做好。这种优势不仅体现在Rails本身提供的功能上,作为开源软件,还有丰富的第三方插件、组件为你的快速开发提供帮助。以51dir的用户管理为例,大多数Web程序都需要用户管理,而且功能相似,当然你可以花时间自己开发一个,提供用户注册、激活码发送、忘记密码的情况下发送密码等功能。但我觉得没有必要,因为用户管理其实是一种基础性的功能,作为Web程序开发的重点是提供给用户使用的功能而不是用户管理本身。51dir就是利用Salted HashLoginGenerator提供的用户管理,只需区区几行代码就集成到自己的Rails程序中。

约定成俗原则体现在Rails的各个方面,除了前面提到的O/R mapping,Rails程序的目录结构也遵循一定规则,与MVC架构相吻合。这种简单明了的方式谁都想得到,但只有Rails这样做。这里特别要提到的是,Rails的URL和Controller、Action以及其他参数的映射关系非常灵活,可以在congfig目录中的routes.rb文件中自由定义。因为routes.rb本身就是Ruby代码,几乎支持所有你想得到的映射方式,而且在HTML页面中用来产生URL的相关函数(url_for)也是以此为依据,相当于反解析。比如http://www.51dir.com/saved_files/get/12345678这个URL,如果在routes.rb有这样的定 义“map.connect ':controller/:action/:id'”,那么就是对应saved_files控制器里的get动作,而params[:id]的值将会是12345678。在routes.rb里面可以定义多种映射关系,但按照定义的顺序,第一个匹配者获胜。大家可以看到51dir也有这样的URL:http://www.51dir.com/12345678,访问这个URL和访问前面的URL其实指向同一个处理。因为在route.rb的map.connect ':controller/:action/:id' 前 面 还 定 义 了 一 个 规 则 :map.connect ':id', :controller => "upload",:action => 'get',:id=>/\d+/,可以直观地看到,凡是 http://www.51dir.com/加上纯数字的URL都会映射成前面一样的URL。

Rails提供了对AJAX的内置支持,在理解AJAX原理的基础上,只要编写少量的JavaScript,就可以实现大多数的AJAX应用,比如51dir的文件上传进程显示就是由AJAX实现的。Rails将复杂的AJAX包含在几个模块中,并将它们混入(mixin)到actionview和actioncontroller中,使得处理AJAX请求感觉就像普通的HTTP请求一样。在同一个Controller里面你可以定义处理普通HTTP请求的Action,也可以定义处理AJAX请求的action。Rails同时提供了在view里面的辅助函数和action里面的辅助函数,两者相互配合最大程度了掩盖了AJAX的复杂性。比如说在一个View页面写入link_to_remote辅助函数:

      <%=link_to_remote      "      I_am_a_remote_link
    ",:url=>{:action=>'test',:controller=>'upload'},
          :condition=>"confirm('再一次可以吗?')",
          :confirm =>"可以吗?"
          %>

那么通过Rails解析之后生成的HTML片段是:

    <a href="#" onclick="if (confirm('可以吗?')) { if
    (confirm(' 再 一 次 可 以 吗 ? '))  {  new  Ajax.Request
    ('/upload/test',  {asynchronous:true,        evalScripts:
    true}); }; }; return false;">I_am_a_ remote_link </a>

现在当你点击I_am_a_remote_link时,AJAX请求就会发送到upload/test,在test这个action函数里面再次利用Rails提供的辅助函数,你不用编写JavaScript就能达到AJAX的效果。比如test函数如下:

    Def test
    Render :update do |page|
          Page.call(“alert”,”hi I’m from remote server!”
    End
    End

在Rails里面,AJAX就是这么简单。此外实现Web Services也是非常方便,这里就不介绍了。

Rails应用程序的发布也非常方便,一般采用Web服务器+FastCGI的方式,51dir就是采用Lighttpd+FastCGI的模式。Rails有一个自动发布应用程序的工具叫做Capistrano,它也是一个用Ruby编写的程序,可以极大地简化Rails程序的发布。它支持CVS和SVN。发布过程很简单,只要进入Rails程序的根目录,然后键入cap -p -a deploy -f config/deploy.rb即可。也许你对Capistrano感到非常惊讶,但就像Ruby、Rails,Capistrano的原理也非常简单。你在配置文件里面修改必要的参数,比如SVN服务器的路径,应用程序的名称,要发布的目标机器的地址等等,当你运行cap时,它其实是用SSH登录到目标服务器,然后在目标服务器上面执行SVN命令,从SVN服务器获得最新的代码,它同时定义了许多标准的动作和许多hook函数,比如after_update_code这个回调函数,你可以在这个函数中定义一些动作,这些动作会在更新代码之后执行,这些动作可以是目标机器上的Shell命令,比如ln、mv等等,做任何你想做的事情。即使发布错了也没有关系,只要Rollback就行了,所以对于51dir这样运行着的网站来说,即使在线发布新的版本,用户也难以察觉。就像Rails让你愉快的编写Web程序, Capstrano让你愉快的发布Rails程序。

Rails快速开发的特性,非常适合小团队的创业。一个想法和创意可以在最短的时间内变成产品。即使你不是全职的程序员,也可以利用有限的时间完成自己对于Web应用的某些奇思妙想。每一个Web程序员都希望将自己的想法付诸现实,但是编程的艰苦又让他们最终放弃。就我本人来说,当我用Java写完51dir的时候,感觉非常非常地累,曾经一度产生了放弃编程的想法。想做一个创新的Web程序和在电脑公司上班编程根本是里两回事情(虽然我并没有电脑公司上班的经历),Web创业失败是正常,成功是意外,所以失败之后是否有勇气重新再来,不仅仅取决于个人的毅力,也取决于经历每一次失败的代价,这个代价太大,就会放弃。如果没有接触Rails,我想我可能真的已经放弃编写程序,安心在家里烧菜、做饭、洗衣服、带孩子了,这些都是很令人愉快的事情。感谢Rails的高效率,让我继续编写程序,并且有空余的时间来做菜。

虽然Rails可以运行在Windows平台下,但建议在Linux系统下使用,因为Linux下有很多的现成代码可供使用,并且还可以和其他脚本语言协作运行,让自己尽可能地少写代码,比如51dir文件上传的目标是一个upload.py,它是一个Python的CGI程序。Linux上面运行的代码已经相当丰富,基本上你想得到的东西都有人曾经编写过。看看Perl的CPAN库,看看SourceForge上的项目,DRY,don’t repeat yourself!

而且你也不必担心Rails应用程序的扩展性,因为有很多高负荷的Rails网站在成功运行,并且随着Rails的流行,许多商业公司也开始提供适合Rails部署的产品,比如Litespeed Web Server,其公司网站的首页就放了一个大大的Rails logo,可见该公司对Rails用户的重视。

编写Web程序的最大乐趣就是有人使用它、喜欢它,每当我收到用户感谢的、赞美的邮件,就会对我的妻子说:你看你的老公不错吧,有很多人写信感谢我呢!你也想拥有这种乐趣吗?试一试Rubyon Rails吧,让你生活编程两不误。

“敏捷”的Rails和“便捷”的发生网

□ 文 / 罗文 余振建

很快就是周末了,你并不打算呆在家里无所事事,又没有开卷的心情,可是周末的城市里有哪些活动可以参加呢?或许你还在BBS上守株待兔,等待有人发个帖子告诉你哪里有活动,或许你等着起床以后买份报纸看看上面推荐什么,亦或上MSN或者QQ问问朋友们准备去哪里?

把做发生网(www.8sheng.com)作为创业的第一步的想法,来源于两个创始人在生活中自己遇到的困扰。那时正是2006年的五一黄金周,习惯了网络生活的我们,找不到一个好方法来知道城市里到底有些什么活动可以参加。我们觉得这可能是一个很好的创业的切入点,于是便开始做调研。从国外来看,提供事件信息的网站相当多,除了专门提供此类服务的upcoming、eventful等,像MySpace这样的综合性网站也开发了单独的频道来满足人们获取活动信息的需要。而在国内,我们也找到了几个以论坛等形式汇聚这类活动信息的站点,但是从功能和专注程度来看都不能很方便地使用。在调研的基础上,我们决定创造一个中国的事件分享型的Web 2.0网站。

一眼爱上Rails

对于网站创业来说,如果说选择做什么是关键的一步,那么选择哪一个开发平台对于创业者来说,则是另一个比较重要的决定。

我们决定选择Rails来开发网站,实际上比我们确定做发生网还要早。余振建在之前用Java开发过多个基于Web的商业系统,有些审美疲劳,想尝试一些新的东西。他给我看的是那张经典的Ruby on Rails和Java学习书籍的对比照片,我也一下子被吸引了。

如同《Agile Web Development with Rails》所说,Rails是一个网络应用的开发、部署和维护框架,而“敏捷”天生是Rails的设计理念之一。Rails的CoC(Convention over Configuration)模式看似人为地设置了很多限制,实际则为开发者提供了极大的便利,它使得开发者从一开始就可以专注于业务逻辑,而不用浪费时间在各种繁琐的配置文件上。同时,通过这种限制它也保证了开发一定会遵循MVC的设计模式。当使用Java开发Web应用的时候,Hibernate已经给我们带来了一次惊喜,而基于CoC的ActiveRecord带来的更多的是开发的乐趣,它使我们乐在其中。Rails框架本身设计相当完整,提供各种便利,开发、测试、管理、部署等都有相应的自动处理的script帮助程序员节省时间。

Ruby语言本身非常优美,比如block语句,它使得回调函数的实现变得简单自然。同时Ruby彻底的面向对象特性也让习惯了Java开发的我们感到亲切,这也是我们没有选择PHP这样的脚本语言的原因。

如果说当初选择Rails还带有那么点点冲动,那么回过头来看,我会用这两个理由来说服其他创业者把Rails作为开发平台 :

· 敏捷的Rails极大地提高开发效率。这个是最重要的一个原因。上线时间、投入成本对于网站创业都是很重要的因素,而开发效率足以决定这两点。从我们开始学习RoR到发生网第一个版本开始内测,只花了大概两个月。Rails 1.2版本的推出,必将能进一步地提高开发效率。

· 更新,更酷,带来更多的关注。现在还有很多人关心一个用PHP或者Java开发的新网站吗?Rails是引领潮流的热点,使用它做开发则让你搭上了时代的顺风车。

发生网的定位和设计

确定了我们要做什么和用什么来做以后,我们开始设计发生网的完整功能。发生网要成为一个大家分享事件、活动、信息的平台,用户可以根据时间、地点、分类以及标签来浏览感兴趣的事件。

如同上图所示,事件是这个网站的核心,围绕着事件的组织和分享,进而有了日程表、群组、好友等等的概念。使得用户可以分享活动信息,找到一帮志同道合的人,还可以进一步创建或者参加各类小组,和关注同样事件的网友进行有意思的讨论等等。如果好友也在使用发生网,还可以知道他正在关注什么。同时,为了更好的满足用户的各种阅读习惯,还提供通过RSS、ICal等方式来订阅事件的功能。从MVC的设计模式上来讲,用户、事件、日程表、群组、场所等等很自然成为Model层的对象。

同时发生网是一个典型的Web2.0应用,由用户的贡献来使得事件组织得更加完善,进而使用户有更好的使用体验。首页的热点事件推荐通过类似Digg的机制筛选,由用户的关注和点击决定事件的流行度。事件之间通过用户关注的行为分析产生联系,从而给用户推荐更多的相关信息。

Rails应用实践

在做功能设计的同时,我们一步一步从零开始学习Ruby和Rails。RoR不断地给我们一些惊喜,使我们坚定了当初的选择。

发生网在添加事件、添加评论等功能上,使用了AJAX来提高用户体验。通过Rails集成的AJAX功能,这些实现都非常简单。而Ical功能的快速上线则借助于Ruby社区的相关类库。国外的Ruby和Rails社区相当活跃,Ruby类库虽然没有Java多,但也相当充分,并在不断出现。

使用Rails的过程中,也遇到了一些细碎的小问题,比如FastCGI的稳定性问题。不过这个小问题在RoR欣欣向荣的成长氛围中很快就得到了解决。现在发生网在Apache+Mongrel的架构下运行地非常稳定。

性能问题是大部分人评估Rails的时候存有顾虑的一点。首先我们看到的是Ruby确实比较慢。有人说它比Java慢10倍,在“Computer Language Shootout Benchmarks”(http://shootout.alioth.debian.org/)网站上的数据显示,对于在特定的CPU上运行的大运算量的程序,Ruby的效率是C++的600分之一,是Java的300分之一,差距确实很明显。那么这对于网站开发的影响到底有多大呢?Ruby的性能问题确实限制了它的应用,但是网站的大部分功能不需要大量CPU运算。虽然单从耗费CPU功能的角度来看,Ruby的性能是基本不可接受的,但对于类似发生网的以事务逻辑为主的网站,页面cache机制是提高性能的首要环节,而Rails提供了灵活方便的cache管理,程序员可以通过良好的设计使动态页面生成的操作大大减少。动态页面生成主要是数据库和IO操作,在这个过程中,RoR就像是粘合剂,轻松地把各个部分串联起来,而自身并不需要太多的复杂运算。如果网站中有需要依赖CPU的操作,可行的方案是单独用其他语言优化,这跟用汇编优化C++代码中的关键部分的做法是一样的。另外,发生网现在所使用的架构,也完全可以很方便地增加Rails服务器,提供了无限扩展的可能。

Rails并没有提供很好的美工和程序员工作分离的方案,页面的逻辑和美工都在RTHML文件里面实现。Rails对于这个问题的解决方案是通过MVC的设计模式以及一些帮助函数使得RHTML中的Ruby代码比较少。实际工作中,发生网的页面没有专门的美工参与设计,完全由我们两个程序员自己完成。因此美工是我们现存的一个问题,有待于今后的解决。

还有一些困扰,当前比较大的一个是Rails 1.2正式发布以后,我们什么时候跟进的问题。Rails并没有像Microsoft和Sun这样的大公司来推动,在每次版本升级的同时往往存在一些bug,同时从老版本升级的应用也存在比较多的不确定性。Rails 1.1升级的过程中,有些常用的Plugin出现了兼容性问题。所以升级实际上会带来比较大的切换成本,需要谨慎考虑。

总结

一句话总结一下我们的感受:Rails天生的敏捷开发模式非常适合网络开发。创业者要善于利用Rails的这个优点,通过快速原型,不断细化,随需应变的策略,来进行网站的开发。第一个公测版本推出后,我拉了一个朋友来试用,她很高兴有这种服务,然后她想去找明天哪里有讲座,结果当时的感觉是没法用。于是我们就把设计一个更好的浏览功能放在所有事情的第一位。而因为用了Rails,所以在开发这个功能上用的时间很短,使用了它,我们就可以把更多时间和精力集中在产品层面,这是真正给用户带来价值的地方,也是我们的服务能赢得用户的必要条件。

关于网络创业,因为刚刚走出第一步,谈不上什么经验,只谈几点感受:一、不要害怕竞争。竞争至少意味着你的创意不是个人的奇思异想,而且竞争是一个互相吸取提高的过程;二、不要害怕失败。网络创业并没有很高的门槛,所以很容易进行各种尝试,失败一次并不可怕,可以从头再来;三、要注重积累技术、人脉、影响力。这些因素都不是一朝一时能够俱备的,只有在创业的同时不断地积累,才能增加成功的可能。

用Rails实现“乐道”构想

□ 文 / 邱海峰

我总觉得,有些优美的歌曲,如果你不去推荐,那么别人可能真的没机会听到。本着这个想法我在2001年建立了“神话年代论坛”,开始推荐喜欢的音乐。这种方式在2001年的时候还很原始,无非就是提供mp3下载,我回复帖子然后下载歌曲,不仅缺乏交流,而且相当混乱。

2004年我在5dblog注册了一个博客,起名为“牧羊人之乐”,继续音乐推荐之旅。当时论坛推荐的混乱是自己无法控制的,然而博客是自己写的,控制起来相对容易一些。我还为在博客上推荐音乐专门编写了两个Flash播放器(一个用于播放音频,这个播放器后来被裁减用于乐道;另一个用于播放视频(见图1)。但是两年的播客推荐却让我发现了音乐播客这种形式的缺点:它更像是一个自言自语的场所,但是如何与他人之间寻找交集,如何与喜欢同一个音乐的人分享自己的感受就变得很困难。播客推荐的音乐通常是围绕他自身的喜好来定的,而我始终认为音乐推荐不应该“以人为本”,而是“以音乐为本 ”,把那些喜欢它的人聚合起来,这样才更佳符合Web 2.0的特点。

于是一个关于多人推荐音乐的播客网站的想法就在我脑海形成了,这是乐道(http://www.likenote.com)最初的构思。后来演变为一种“DJ听众模式”的网站形式,这是一种在现实生活中有近似例子的模式。一些 电台DJ们有自己的音乐推荐栏目, 比如北京音乐台吕游的《浪漫情歌》,伍洲桐的《零点夜话》、志飞《世界歌曲排行榜》等,听众在收听节目的时候可以通过打电话的形式参与,只是这种参与形式比较有限。与之相比,“乐道”的模式要稍微先进一些。但光靠几个DJ很难维持新歌推荐的数量,如何从访客留言中发现“潜在”DJ就成了乐道急需的另外一个功能,于是我开发了“大众推荐”栏目,接连有四五个新人通过“大众推荐”成为正式注册DJ,我自己也从中收益很多,发现了很多从来没听到过的好音乐。

图1:“乐道”上用于播放音频的Flash播放器在上图版本的基础上做了相应裁减

乐道网站的技术

其实我很早就有了关于创建一个音乐推荐网站的想法,但是因为技术上的问题迟迟得不到解决而无法实现,尤为突出的就是前台展现问题。

音乐推荐网站不同于普通的论坛、新闻、CMS系统网站,它是个多媒体平台,也就是说普通HTML、JavaScript、AJAX等都很难满足其要求,只有采用先进的多媒体网页技术。早期Flash版本的脚本能力太过简单,直到带有ActionScript 2脚本语言的Flash 2004出台后,这个开发平台才初步符合要求。它对开发人员的吸引力来自:第一,Flash插件的客户端占有率很大,98%的浏览器都会安装Flash;第二,Flash 2004的Action Script 2脚本语言采用了类似Java的面向对象语法,很适合Java程序员学习和使用;第三,Flash的多媒体创作能力有目共睹,尽管国内用户多把它当作动画工具,而国外却是大量应用于交互式网站。

后台技术的选择颇为费事,事实上,我从2002年开始从事Java网站开发,其中接触了不少的Web框架,但是始终没有一个符合预期要求。JavaEE包含的规范实在太多,而且每个规范都会有各种实现方式(包括各种开源实现)。如何将这些东西组装为一个全功能网站开发工具包,如何选取合适的框架方案始终困扰着我。直到Rails横空出世,才让我感觉到实现想法的时机到了。

选择Rails其实是很自然的事,Java程序员选择Ruby,因为它更面向对象。Rails框架当时是一枝独秀,基于Python的Django框架的正式版本在当时还没有正式推出(现在也没有),而PHP的各种框架还在受困于其自身的问题而极力模仿Rails。其实,对于打算创业的Web技术人员,选择一款敏捷的框架非常重要。对小公司和个人而言,开发效率往往是至关重要的,这些人需要的,只是高效快速地开发,并实现创业想法。

实践Rails

2005年4月,笔者曾写了一篇《Ruby on rails实践》教程,但直到2006年3月间,才觉得不能再拖延实现想法的时机了。于是我向公司提出辞呈,正式采用Rails开发乐道。当时给定的目标是三个月实现这个网站的雏形,5月份开始介入开发,7月底,乐道正式部署上线。

也许有人会问:“既然Rails开发那么简单,为什么开发一个像乐道这样简单网站要花费三个人月?”实际上,笔者真正用在开发上的时间大概是三个多星期,另外还有一个多星期在用于改善和集成Flash播放器,而前期学习相关技术的时间花费了一个多月,这些学习不仅包括研读那本著名的“Rails Book”,还有开源操作系统和数据库服务器的管理。

在开发乐道过程中,我遇到了一些问题,希望对初学的朋友有所帮助 :

数据迁移

在5D博客上推荐音乐时,该Blog服务提供商在成立初期曾经被黑客攻击造成两次数据丢失,这提醒我注意数据安全的重要性。因此我将每次的音乐推荐都按照数字编号保存为文本文件,一方面是为了备份,二来是为了播放器动态加载显示。为了在Flash中显示中文,文本文件的编码采用了UTF-8,编码方式与现在的后台数据库相同。这个看似不得已的操作为后来移植到新数据库提供了方便。

乐道选用的数据库是PostgreSQL ,数据转换程序采用Ruby编写。此前,我曾在一个公司项目中采用Ruby编写过从SQL Server迁移到Oracle数据库的脚本,所以这次将文本文件转移到PostgreSQL的工作相对容易一些。Ruby对MySQL的支持非常好,有好几个c binding的实现,PostgreSQL就没那么幸运了。好在通过DBI方式可以访问ODBC数据库,所以我下载了PostgreSQL的ODBC驱动程序,设置了ODBC源后开始编写脚本转换程序。所有的推荐文本文件都放在一个txt目录下,每个文本文件采用如下格式(歌曲的四类信息以 &xx= 的标准flash变量数值对的形式保存):

      &ctx=歌词…
      &rec=推荐文章…
      &title=歌曲名…
      &artist=歌手…
      转换代码如下:
      require 'rubygems'
      require 'dbi'
      @source_directory = "txt"
      i= 0
      DBI.connect('DBI:ODBC:ledao, ledao, ledao) { | dbh
    |
      Dir.open(@source_directory).each { |file|
        next unless ( file =~ /[.]txt$/ )
        @source_full_name                             =
    "#{@source_directory}/#{file}"
        n = "#{file}".gsub!(/\.txt/,'').split('_')[0]
        text = File.open(@source_full_name).read()
        fields = text.split(/\&.*\=/)
        @song_txt = fields[1]
        @song_rec = fields[2]
        @song_title = fields[3]
        @song_singer = fields[4]
        i += 1
        puts "song : [" + file +  "]" + @song_title
        songsql = "insert into songs (id, category_id,
    title, artist, swf) VALUES (?, ?, ?, ?, ?,?)"
        dbh.prepare(songsql) { |t|
  t.execute(i,n,@song_title,@song_singer,file)
        }
      }
      }

登录和图片上传

Rails的Wiki站上提供了很多login generator,早期Rails的login generator对开发自己的应用缺乏实用性。其实,大部分generator无法满足定制特定系统的要求,但这些generator对学习开发登录系统很有帮助,比如笔者开发这个部分时就参考了typo blog的实现。在网站开发中,用户授权与认证是最麻烦的部分之一,且大部分Web框架都不提供内置的支持,对于一个像乐道这样简单的网站来说相对容易一些,因为它的用户只有认证用户(DJ)和非认证访客(听众)两种。但是对于一个完整的CMS来说,框架集成用户管理系统是非常重要的,在这点上Django就做得很出色。其admin功能模块跟O/R Mapping集成得很密切,而且还拥有一个全功能的用户组管理系统。传闻Rails新版本将集成一个admin管理的插件,使用下一版的用户不妨一试。

另外,图片的上传和裁减对于论坛或博客来说很重要。通常,用户会根据需要上传图片,系统必须对图片做必要的裁减,以防止文件过大或尺寸超出限定范围,造成大量占用磁盘空间和页面显示超出的现象。Ruby通过Rmagick对ImageMagick提供了良好的支持,而且代码非常简单,有兴趣的读者可以下载相关代码来参考。

数字验证码的实现

在开发乐道的留言功能时,我并没有参考其他Rails验证码的实现。此前开发Java项目时,我曾经做过相关工作。很多实现采用了GD库生成数字图片的方式,后来在Rails.cn上也有人分享了类似的实现,但考虑到每次留言生成图片可能会增加服务器的压力,所以我只是简单通过调用随机数字图片的方式来实现验证码。

AJAX的应用

Rails对AJAX提供了良好的支持,最初的时候我在三个地方采用了AJAX:

1. 首页左侧的最新留言最初采用的方式是每隔一分钟去数据库检索最新10条留言。在访问人数少的时候,性能压力不大,如果访问人数多了,可以延长轮询的时间间隔。但是从实际应用角度考虑,大部分人停留在首页的时间不长,这种即时更新的功能就沦为一种摆设。在最新的网站版本当中,已经去掉了相关的功能。

2. 首页最上面用于查询歌曲和歌手的搜索框,最初提供了随着用户的输入文字自动从数据库中检索匹配的歌曲名,并以下拉框的形式加以显示。比如你输入一个“爱”,系统会自动从数据库中检索出“爱的代价”、“让爱自由”等,这种自动补全的AJAX功能效果很好,但是往往会造成服务器性能问题,后来也将它去掉了。最近我再三思索准备重新使用这个功能,因为在实际使用中并发大量搜索的情况并不多见。

3. 留言后不刷新页面显示最新留言。这是现在唯一保留的AJAX应用,而它的确是一个很需要的功能。在最初没有采用AJAX实现的时候,每次听众留言后,页面重新刷新造成歌曲播放中断,严重影响了用户体验。采用AJAX实现后,效果很好,所以功能保留到现在。

JavaScript编辑器

大多数论坛博客的发帖都提供了JavaScript编辑器功能。用户可以通过插入外部图片、Flash链接、改变字体颜色/大小的方式来给帖子“增色”。这种个性化的措施却带来管理上的不便。比如插入外部图片可能会因为断链而显示失败,插入的图片无法控制显示大小,五花八门的字体颜色大小定义会造成网站风格的不统一等等。对于乐道而言还有一个不利之处:在首页的列表视图中,会显示每篇推荐文章前30个字的概要。如果在推荐文章的开头插入图片,那么按照固定字数裁减势必造成图片显示不全,以HTML源码的形式显示出来。另一个问题是,显示编辑器的速度很慢。在乐道开发初期,我采用了TinyMCE,它是当时我能找到的最小巧的JavaScript编辑器。但在实际应用中显示速度还是很慢。所以最终根据实际需要,我没有采用JavaScript编辑器的功能。

Unicode字符编码

乐道网站最初上线的时候出现过字符乱码的问题。开始我想当然地认为PostgreSQL数据库中设置为Unicode,页面编码采用UTF-8,那么显示就应该是正常的。后来却发现页面虽然显示正常,但是用浏览器查看HTML源码确是乱码。根据网上搜索的相关资料做了修改,问题才得到解决。在application.rb中添加如下代码:

      before_filter :configure_charsets
      normalize_params :form => 'KC'
      def configure_charsets
        if request.xhr?
          @headers["Content-Type"] = "text/javascript;
    charset=utf-8"
        else
          @headers["Content-Type"]    =    "text/html;
    charset=utf-8"
        end
        ActiveRecord::Base.connection.execute     'SET
    CLIENT_ENCODING TO UNICODE'
      End

另外,在Config目录中的enviroment.rb的前面要添加:

      $KCODE = 'u'
      require 'jcode'

之后我又增加了一个有趣的功能:鼠标移动到列表页面中的推荐文章上,会弹出前五个留言的概要。见图2:

图2:跟随鼠标的留言信息概要

当提取留言概要信息时,乱码问题再次出现,继而引发了浏览器报JavaScript错误。后来经过学习才发现原因是Rails内置的Jcode中Unicode的支持问题。只有通过安装Unicode插件才能解决。

开发编辑器的选择

很多Rails开发人员受D.H.H的影响,采用Textmate编辑器。但是这款苹果软件对大多数PC机用户是可望不可及的。其实熟悉VIM的用户可以使用Rails.vim得到很好的开发体验,Youtube上还有相关的视频可供参考。乐道上线后的每次进行连线修改时,我都采用这种方式。对于Windows用户来说,PSpad这款内置FTP功能的免费编辑器也是一个不错的选择,它有Ruby on Rails语法颜色支持,虽然功能简单,但是已经足够满足目前开发程序使用了。内置的FTP客户端配合服务器上安装的FTP Server,感觉就像是在编辑本地文件。

Rails的缺点

Rails同样存在一些缺点。比如,它没有项目的概念,所有开发都是应用,也就是说任何一个网站都是一个应用,而下层是靠controller来控制action。由于它不包含项目的概念,这样就会造成同属于一个项目的不同应用之间相互共享复用变得很困难。最近我在公司开始采用的Symfony PHP 5框架做项目,Symfony就有项目、应用、模块的概念。一个网站通常是一个项目,下面可以有各种应用,每个应用又可以有各种模块(类似于Rails的controller)。与之对应的,Python的框架Django也有项目的概念。Rails为了让程序员不必再学新的模板语法,所以在视图层采用了和服务端同样的脚本语言Ruby,这其实是一种重复JSP错误的倒退,虽然现在也涌现了不少可替代的Rails模板方案,但是Erb RHTML始终还是Rails模板的主流。

Rails有组件的概念,在开发乐道中,我就编写了大量的组件,效果还是很理想的。它的组件和Hivemind的组件还有很大的差距。对于一个程序员独自开发网站来说,这些技术可以适用,毕竟项目在个人掌控中,但是如果一个项目由很多程序员和网页制作人员合作开发,就会产生问题。Rails没有好的封装组件的形式,复用性差,不过新的plug-in机制可以完全实现功能性组件的要求。从现在发展看,Rails开发网站的趋势就是将大量基础设施以plug-in的形式实现。当然,早期Rails站点的重构也不可避免,乐道网站初期碰到的中文字符集的问题就是通过插件来解决的。

网站开发的感悟

1. 做一件事情的热情远比完成它所需要的技术更加重要。如果没有热情,就算是碰到一点小困难,你也会放弃;相反,就算是再大的困难你也能克服。

2. 最好的解决问题的方式是自己从Google上搜索。

3. 国内程序员之间喜欢谈论技术的人多,真正埋头做事的人太少;各种论坛上相互争吵辩论的多,写代码的人少。我也曾经是这样的,开始对RoR的顾虑也很多,诸如性能、扩展等问题考虑过细。其实仔细想想,你如果不喜欢一个东西,会找出很多原因,如果喜欢它,一条理由就足够了。

4. 我在开始做网站的时候,界面设计选用了很复杂的模板,后来改版的时候做了很大的简化。我想来乐道网站的人无非是推荐音乐、听音乐、参与评论和搜索,鼓捣花哨概念和技术的想法可能从最开始就是在远离你的用户。

5. AJAX应该限制在某些特定功能的网站上(比如Gmail、Google Map等),整个网站采用它是不合适的,而且浏览器之间兼容性的问题确实存在。RoR提供了非常简单的AJAX使用方式,但是在使用它之前千万要考虑一下,这样做是否会给你的页面带来复杂性和服务器性能上的巨大压力。

6. RoR只是一种技术,众多后台技术中的一种。网站的访问者并不会因为你使用了RoR就给你掏钱,甚至留下个好印象也不会。网站的内容是核心。技术应该是实现想法的工具,如果网站成为技术的试验品,那就失去存在的价值了。

小结

RoR的好处在于:如果你有什么想法,总是能很快地实现。只要你掌握了基本的开发技术,你就可以发挥想象来建设网站。RoR网站开发的灵活性还体现在开发方式上,网站部署成功后,新功能的增加我基本上都是直接连接远程服务器来做的,其中包括调试。Rails的动态性表现在最初的开发环境中不需要重新启动Web服务器,而Ruby代码直接就可以通过刷新浏览器生效,在生产环境中,即便重新启动Web Server,也只需要很少的时间,用户甚至感觉不到重新启动了Web Server。这种灵活性使得随时开发部署开发新功能成为可能。

用RoR发现音乐——YOBO创业回顾

□ 文 / 余振建 李洪涛 王智贤

做一个方便用户听歌、找歌和分享心情的网站,想法由来已久。大家总有这样的经历:想听歌,却不知听什么歌,搜索引擎帮不了忙,排行榜的又听腻了。能不能有这样一个服务:能自动获知和了解我的品味心情,源源不断地给我推送我爱听的新歌?

过去几年里,世界范围内的互联网数字音乐是一个在阵痛中蓬勃发展的活跃地带,初期的P2P音乐分享和免费MP3下载服务在经过几轮对簿公堂后开始向正规版权操作靠拢,AllofMp3被俄罗斯政府判为非法,今天的Napster更像一个iTunes。而同时创业者也在不断积极地探索能为用户带来更大价值又能促进音乐产业健康发展的新模式。其中,Pandora.com和Last.fm称得上典范之作。业界一致认为,以“个性化推荐”为核心的新一代音乐网站会成为未来数字音乐营销的骨干平台。

中国在这方面远远落后,不只有大小网站猖獗地提供盗版下载,更有处于垄断地位的搜索引擎提供MP3下载链接,以致那些正规运营却不能为用户提供足够附加价值的数字音乐网站而面临倒闭。最终受害的是消费者和整个中国音乐行业。

YOBO相信,传统意义的搜索绝不是音乐服务的最好方式,MP3搜索尤其会危害整个产业链,更是害群之马。零成本的高精准网络营销会带动题材多样的音乐内容的持久繁荣,最终使消费者受益。YOBO的服务正是立足于此:

· 操作最简单:最初只需提供一首歌或一个歌手,接下来可闭上眼睛尽情地听。

· 智能推荐:基于内容的推送配合协同滤波的社会推送使用户无需提供任何播放列表。

· 自学习:基于用户收听过程提供的反馈,推送越来越精准——好像聪明的私人DJ。

· 一站式服务:在线找歌,听歌,管理,记录分享,讨论,唯独不提供下载。

· 强大的互动:AJAX配合Flash Remoting页面平滑交互让用户体验最优。

· 最大限度的免费:最好的服务从来都是免费的。

YOBO的开发平台选择考虑

速度对于创业公司是一个核心竞争力。决策、开发、市场反应、提高速度不仅能够节省成本,更是争取市场机会的最佳方式。对于开发团队来说,摆在面前的选择是:增加人力、延长工时、选用高效率工具和平台。很多创业之初的小公司在资源紧缺的情况下选择的是延长工时,一天13小时,一周6天的工作制屡见不鲜。YOBO的团队结合了本土资源和硅谷的理念,希望营造既人性化又高效的工作环境,我们不希望通过无限制的增加程序员工作强度来提高开发速度。这时,高效且Scalable的平台便成为YOBO的选择,而以此为标准,Ruby on Rails为首选。

一个两至三人的Rails团队可以在24小时内完成一个新功能的构思、设计、编码、测试、上线的完整周期。这样的开发速度可以为早期的测试用户提供极大的刺激和信心,也能最快速地依据用户反馈改善产品服务。用户驱动,以用户的需求为核心,在用户实际使用过程中完善产品是YOBO选用Rails为开发框架的另一个主要原因。换作Java,一个用户反馈要体现在产品上很难在两周内完成。在用户驱动的开发流程中,用户的需求不断变化,这要求技术团队实践敏捷开发方法,Rails是适合敏捷开发的首选Web开发框架。简明的MVC结构, convention over configuration,丰富的插件API,以及活跃的社区支持等,Rails的这些特性帮助开发人员集中精力在业务逻辑上,而不用分散太多精力在服务器安装、数据库如何配置等边角问题上。

YOBO的技术架构/Rails架构

YOBO的技术架构如下 :

1. 采用AJAXWeb,Flash Remoting作为前端

2. 用RoR(Ruby On Rails)实现复杂的后台逻辑

3. 数据库:Memcached + MySQL

4. 服务器部署:Linux + Apache + Mod_proxy + Mongrel

Memcached,MySQL

Memcached是一个高性能的分布式的内存对象缓存系统,通过在内存里维护一个统一的巨大的hash表来存储各种格式的数据,包括图像、视频、文件以及数据库检索的结果等。它的缓存是分布式的,允许多个Server通过网络形成一个大的Hash,用户不必关心数据存放在哪,只调用相关接口就可。存放在内存的数据通过LRU算法进行淘汰,同时可以通过删除和设置失效时间等来淘汰存放在内存的数据。这种方法不仅解决了共享内存只能是单机的弊端,同时也解决了数据库检索的压力,最大的优点是提高了访问获取数据的速度!

采用Rails开发的Web系统负担主要在数据库部份,为了减轻数据库检索的压力,我们采用Memcached作为数据库检索的缓存系统。现在Ruby社区针对Memcached已经提供了足够完美的解决方案 :

· Memcached-client:用纯Ruby编写的Memcached客户端,方便使用Ruby语言对存储在Memcached中的Ruby对象进行get/set/delete操作。

· Cached Model:采用继承的方式实现的一个对Model进行缓存的类。继承于ActiveRecord类。

· Acts_as_cached :一个允许在Mem-cached中缓存任何Ruby对象的Rails插件。

· 如果Cached Model和Acts_as_cached还是不能满足您的需求,那可以自己定制一个操作Memcached的程序。

Rails的性能问题

最初YOBO在性能上没有进行特别的优化,上线两个周后Keso在其博客上发表了《东拉西扯:友播和长尾的过滤器》一文,访问量剧增,YOBO上线后第一次宕机。我们对此增加了fragment cache作为缓存(具体方法请参考《AgileWeb Development with Rails》一书)。

随着访问量的增加,最终我们使用了流行的Memcached作为缓存系统。以后就只需要随着访问量的增加,增加服务器了。

YOBO的服务器部署架构

YOBO目前的架构为Apache+ Mod_proxy + Mongrel。Apache作为前端HTTP请求分发的代理和静态页面的处理,动态内容通过Mod_proxy分发给后台的Mongrel去处理。

YOBO的Rails团队协作开发

Startup + Rails = 兵贵神速,所以YOBO的Rails Team每个Member都必须是精英。可以说YOBO Rails团队包括产品设计精英、UI设计精英、AJAX精英、业务逻辑与服务器优化精英。

Rails团队的协作方式一般有两种 :

1.按前端和后端,分别指定开发人员。

2.按任务,开发人员分别负责某一个功能模块从前端到后端的整个开发任务。

YOBO选择的是前者。

后者的协作方式更适用于在开发过程趋于稳定之后,把不同的功能模块划分成多个任务交与多个小组去开发。后者对于开发者的素质要求相当高,要求的高低本质上取决于表示层的交互质量以及业务逻辑算法的优化。如果就一些内部项目或者数据管理界面来说,对界面的要求就会低得多,而YOBO的用户是很懂得享受的,YOBO面对的用户也几乎都是来欣赏音乐的。YOBO不能赤裸上阵,或者衣衫褴褛地示人!作为国内屈指可数的Web2.0音乐网站,YOBO的团队选择了“逻辑层”与“表示层”相对独立的开发方式,这也是Web2.0技术革命中“页面重构”精神的体现。

自从有了RIA的两大阵营Flash RIA与DHTML RIA,“让页面动起来”的想法左右了很多产品设计者的想法,而YOBO在Web表现上坚定地选择了AJAX(Asynchronous JavaScript and XML)。AJAX其实不是什么新鲜的事物,只是RoR的诞生让专注于前台的设计者和开发者连同AJAX本身都一夜成名。RoR与AJAX是天生的一对,AJAX也并非浪得虚名。真正的AJAX应该是跨浏览器兼容的;应该是Content Based Web (基于内容的网页),脱离CSS(Cascading Style Sheets)也可以让浏览者正确理解的文档;应该是设计者与开发者协同配合的润滑剂。

YOBO Web是RoR中型应用的良好典范,Rails的敏捷性并非指Rails本身敏捷,而是Rails可以让前后台配合得很默契。Rails对于前台表示层工程师的要求相当之高,所需工作量往往是业务逻辑的好几倍。因为要让业务逻辑开发者专于数据库以及服务器运行效率的优化,所以YOBO Web从来不会让业务逻辑开发者去花任何多余的时间去考虑怎样显示某个页面,只需要把数据取出来Call前台人员准备好的接口,“把要显示的传过去就行了!”

YOBO Web大致的开发流程如下:

第一步 :通常产品人员会把需求提给界面设计师。

第二步:前台开发人员将需求和界面设计做成Mockup(模拟真实用户可用的界面),实现显示逻辑,Content-Based XHTML、CSS2、JS Class、Cross-Broswer Compaibility……(略去几万字)并通过DHTML和AJAX提供后台可控的显示接口。

第三步就是交给业务逻辑开发者进行数据验证和处理。

看到这里是不是想起了Ajaxian的那篇《Salaries Go Up for AJAX Jobs》?

但是不得不提一点,这样的协作方式虽然高效,但只适用于一开始所说的初级阶段,因为前期任务过于集中,过多前台批处理的方式造成前台代码量过大。更好的办法就是扩大团队,将不同功能模块划分,让后期团队接受培训以及阅读技术文档与规范,此时用之前提到的协作方式2更为合适。YOBO的Rails团队目前的协作方式非常高效,短短1个多月的时间即从Alpha垂直过渡到Beta版本,就足够说明问题了。

总结

Ruby on Rails的出现,改变了互联网公司的开发模式,改变了产品周期。在YOBO,Rails提供的敏捷开发平台正在为用户创造中国互联网最令人激动的用户体验。

(2007年第2期)