Ruby编程语言是由日本人松本行弘开发,是一种简单快捷的面向对象编程语言,今天主要跟大家聊聊ruby框架——ruby on rails(http://www.maiziedu.com/course/ruby/),可能有些朋友不喜欢ruby,但是作为一门语言,我觉得 Ruby 还是很值得掌握和学习的。
先说说学好 Ruby 有什么好处吧。
Ruby on Rails 的最大优势我觉得有四个:
1. 语言灵巧轻便,适合快速开发和部署。
2. 各种本身自带的 “magic” 以及可以通过 gem 或者 vendor code 加载的 magic。让代码美观简洁。
3. 很容易与各种前端 JS 框架集成(包括Backbone,Ember,React等等)
4. 有一个 Rails console 可以让 test 和 debug 变得异常轻松。
如果你不相信我说的话,可以去各个中小公司走走看看,很大一部分最原始的第一个代码库都是基于 Ruby on Rails 的。尽管后期会有各种拆分和重写,对原来的 Ruby 代码能够很好的把握也是一项极为重要的技能。我早年是写 C++ 和 Python 的,最近三年多,却是有百分之六七十的时间写的是 Ruby,偶尔写写 Java 和 Scala,但是 Ruby 依然是我写的最顺手的语言,没有之一。因为 Square 和 Airbnb 两家公司 Ruby 都用的很多,所以对 Ruby 熟练也成为我在工作上的一个小小的优势。
虽然很喜欢 Ruby,但是却除了上面四点不想写太多废话来解释 Ruby 为什么好。不如说点有用的,了解并如何尽可能的避免 Ruby on Rails 里面的坑。
Ruby on Rails 最大的问题可能就是这个语言写代码开发起来快,维护起来却很困难。什么原因呢?怎么避免呢?
首先,Ruby 是动态类型的。什么意思大家都懂,就是一个变量没有类型声明,run time 的时候给它什么值它就是什么类型。语言的官方说法叫做 duck typing:
“ 如果一个对象和鸭子一样走路一样说话,那么Ruby就很欣然地认为这就是一只鸭子。”
没有类型申明的代码写起来简洁方便,尤其是不同对象间有可继承重用的关系的时候,如果换了 Java 这样的强类型语言就必须使用 interface 或者 abstracted class 来严格按照 design pattern 来处理。而在 Ruby 里面,你只要心中想要给一个变量什么值,那么只要后面赋值正确,这个变量就可以有什么值。这就必然引来两个麻烦:
1. 不小心赋错值,Ruby 依然将计就计,也不抱怨。只不过在实际运行中让你找不着北,得到一个完全出乎意料的结果。
2. 需要 refactor 代码,也就是代码重构的时候。这几乎可以说是一场灾难。试想下你有一百把钥匙一百把锁,有类型申明的语言可能对于不同型号的钥匙和锁有所区分,而无类型申 明的语言所有的钥匙和锁几乎都长的一样。代码重构就好像把所有配对打乱,又要以新的形式组合。最后结果就是看着重构后利索无比的新代码,一不小心就给你 exception 了。
一个很有效的解决办法是,对一些重要的,尤其是 API 的参数类型进行强制类型检查和断言。比如在 Airbnb,我们会用一个 validation gem 对所有需要的变量去用代码来检测其类型,类似于 “validate count is_a Integer” 这样的断言。
第二,很多人对 Ruby on Rails 的 ActiveRecord 一知半解。ActiveRecord 可能是 Rails 里最强大的 magic 之一(或者没有之一)了。ActiveRecord 是对代码中数据库访问和业务逻辑访问的一个混合体。如果写的好,可以事半功倍,写出 DB 访问效率高且干净利落的数据库访问相关的代码,比如它的 scope 和 association 的运用。然而,如果对于 ActiveRecord 怎么实现似懂非懂,加上对 MySQL 的 execution plan 一知半解,很可能写出的代码一到 production 就会各种崩溃。举三个常见的例子:
1. 老实说,我觉得不知道 N+1 Query 是啥的程序员都应该好好去了解下。N+1 Query 几乎是我见过的引发 production 问题的最常见的错误之一(也许没有之一)了。
2. Rails 的association。最便利的 wrap 对象间 1对1、1对多、多对多等关联的 magic。很多别的语言应该都没有这个概念。Rails 的官方文档上有,强烈推荐深入了解。这也是平常见到的被错用最多的 magic 之一了。
3. Rails 中的一些 job 需要在数据库中找到满足某些条件的 records 并进行处理。Rails 常用的有 find、find each、find in batch 等等,虽然不一样的查询语句都能给你一样的结果,但是因着他们对应的 SQL 语句的转化不一样,很多的时候效率天差地别。这也是 Ruby 代码中可以看出一个程序员对 Rails 的了解的一个常见的点。
例子还很多,坐下来可以慢慢写,这里也只是顺手写几个最先想到的。所以我们也不用一边写着低效的 Ruby 代码一边骂 Ruby 这不好那不好了。我琢磨 Ruby 可能觉得更怨。
第三,因着 Rails 中这样那样的 magic,写好 Ruby 的 RSpec 测试例可能是一件比写好 Ruby 代码还难的事了。写好 RSpec 其实有很多的技巧,最常用的:
1. 各种 stubbing,包括对 FactoryGirl 和 Fixture 等的应用,好的 Rspec 其实可以帮助避免很多 Rails 里面的坑。怎么写好 Rspec,这个也不是一篇能说清的,有兴趣的可以留言,以后我如果有好的资料可以分享给大家。
2. 这一条也适用于所有的程序语言的测试例的编写。是自上而下还是自下而上,怎么保证覆盖率,先写代码还是先写 test,很多好书。别的语言的写 test 的思想到这里也是适用的。
老实说我学写 RSpec 可能当初花的时间和写 Rails 代码本身花的时间一样多。即使是现在,我也不敢说我能把 RSpec 写到很好,只能说我愿意花时间精力努力把 spec 写好罢了。
第四,Rails 本身已经是一门语法极其精炼的语言了,没有必要为了把代码变得更紧凑而使用一些很炫的偏僻的语法。记得我刚开始写 Ruby 的时候,喜欢干的一件事就是把整个函数我用一行 Ruby 各种技巧就写出来了。然后呢?不仅可读性差,有的时候时间长了,自己都忘了是怎么玩的了。后来越写越老实,怎么容易懂就怎么写。有一个写程序的原则我觉得 实用所有的语言:好的程序是不应该需要太多注释的。这包括结构的清晰和最合适的命名。这个原则对 Ruby 尤其重要,因为 Rails 本身已经各种 magic,很容易把代码写的出神入化,然后可读性极差。尤其是即使如 RubyMine 这样的 IDE 在跳转到函数定义或函数应用的这一功能上也不可能做到准确,因此可读性差的代码几乎注定早晚就成了个坑。所以当你写了一段 Ruby 代码你觉得有必要加一段注释来说明你想干什么的时候,可能应该先想想是不是应该重写这一段代码了。
其实还有第五第六第七……, 因为一门语言可以聊的东西太多了。记得以前 Square 的时候每周都有 Rails 的专门的研讨班,每周聊都聊不完,何况一篇文章?Square 两年于我最大的收获也是学会写 Ruby,记得我还曾半开玩笑的和朋友说过:“工作做的越久,越是什么技能都不敢往简历上写了,因为不断见到真的牛人。以后我找工作,简历上只写我会写一 点 Ruby 代码吧。” 因为很喜欢 Ruby,我的微信号都取了在 Mac 上写 Ruby 代码的含义。当然,Java 和 Scala 我也很喜欢。