白银Ruby程序员认证考试攻略

Hisea
May 07, 2012

Ruby Association Certified Ruby Programmer Silver

考完之后给了张纸,感兴趣的可以在这里看

前言

官方网站

早就听说过这个认证,因为Ruby Association在日本,一直以为考试很麻烦,直到看到了ruby-china上这个帖子, 才发现原来Prometric负责考试。

网上之前已经很多人讨论认证值不值的问题,我个人没指望这个可以多涨工资,完全是出于对Ruby的兴趣,想看看考试的内容。

考试的价格是150美金,还能承受,去Prometric网站一看,我们家楼下就有考点,于是就报了名。

考试准备

考试之前,我有一个星期的时间准备。

我搜索了一下,关于这个考试的信息几乎没有,无论是学习资料,图书,或者介绍都很有限。

唯一找到的一点信息就是一位西语系的朋友的博客,说对API侧重比较多。

其他就是他们官方网站上的考试大纲,每个要点几乎只有一个单词,相当的笼统。

于是我基本上就是自己摸索着准备的这次考试。 复习主要用的资料就是两个:

斧头书大体掠过一边,觉得这本书适合入门,不适合准本考试,细节程度不太够,而且有故事情节的长篇幅例程太多。

所以后来主要专注喜鹊书,喜鹊书的语言稍现生硬,不过都是红果果的Ruby,适合准备考试用。

考试

考试形式是50道题,有单选也有多选,每个题都会告诉你有几个正确答案。

总共100分, 75分考过。

考试是完全1.8.7的内容,所以不要混淆了1.9的内容,尤其是有些API不一样了。

一共一个半小时,电脑答题,我选的是英文考题,不知道国内有什么语言可以选择。

时间够用,我大概50分钟做完,做完建议好好的检查一边,我做完因为时间还多,从头到尾又过了一边,发现了很多粗心的错误。

我整个做下来的感觉是并不简单,考点都是很基本的一些语法和API,如果只是写Rails的话,很多东西还是会忽略。

如果有兴趣考这个认证的朋友一定要耐下心来把喜鹊书外加API好好看一遍。

总结

个人觉得这个考试并不是特别难,但是很容易就被轻视,如果你写过很多年的Rails,可能考这个认证还是需要准备的。

如果你用Ruby写了多年的系统维护脚本,可能挑战就相对小一些了。

我个人觉得内容大概就是基本的语法,方法的定义,一些基本的程序流程的语句,这里要特别关注一下rescue和ensure的用法,除此之外,ruby作为程序的流程,比如exit,也要搞清楚,这些是写Rails接触不到的。

大量的API的细节,Array, Hash, String, Regex, File, Dir都考到了,而且是很多很细节的东西,尤其是File的IO等相关的,写Rails相对较少用到。

API的复习一定要细致到位,比如更改自身的方法与不更改自身的方法,很多时候并不是带!的才是修改自身的,例如 "string".concat() 不带!却会修改原来的object。

另一个例子就是 Array.reject!Array.delete_if 虽然一个有!一个没有,但却是一个效果。

再就是一定要熟悉每一个方法的参数,返回值,是否修改reciever.

比如"string".delete,可以传入多种参数,比如起始点加距离,或者是一个index的range,还有一个是类似正则又不是正则的一种规则。

例如 "abcdefghijklmn".delete("^b-cf-h")

delete返回是一个新的去掉参数所规定后的string, 跟delete相比slice和[]在参数上类似,只不过返回的是参数规定去掉的那部分。

围绕这些这些方法的异同,可以出很多tricky的问题。

这夜是我感觉这个考试跟北美的考试风格相比一个最大的不同,北美的考试很容易蒙。

这个考试有很多的陷阱,要特别的小心,感觉可能日本人弄的考试跟中国考试风格类似,粗心大意的孩子很容易吃亏。

比如 5 * "abc" 跟 "abc" * 5的区别, 还有之前说的那些方法的异同。

总之最安全就是所有的API细节都记熟。

再就是,这个考试并不包含或包含很少下面的内容:

  • Metaprogramming 相关的概念
  • Class 跟 Module的组织
  • OO相关的一些概念
  • Rails或者其他的Gem

毕竟这只是RA Ruby认证的第一级, 白银圣斗士的考试。

应该之后黄金跟白金圣斗士可能会考这些深一些的内容吧。

这两个考试目前还在开发当中。

Back to Emacs with Rails: Emacs Rails 开发配置

Hisea
Mar 26, 2012

现介绍下我的工具历程。

上大学的时候,学校的教授都是Functional Programming的狂人,很多课都是Haskell学的。外加那时候用pvs工具.

大学四年基本上一直用的emacs.

那时候喜欢瞎折腾,Gentoo什么的都是整天装了卸卸了装,外加那时候也没有这么github这么好用的东东,配置文件没有好的备份的地方,emacs一直都是最简配置。那四年一直都是emacs自带的功能和命令行工具结合。

不过依然这样写过C,写过Java,写过Haskell,写过汇编。

工作头1年些测试框架,受同事影响用了一年的Vim,之后工作用Java就用eclipse. 后来Ruby/Rails就一直用Textmate/Sublime。

=========================

废话结束:

前两个星期,跟@zw963 讨论了半天emacs. 小拇手指突然很痒。

于是又开始配置emacs,经过一个一个周末,终于弄了一套开发Rails的环境。

操作系统: Mac OS (用其他系统的朋友可以根据喜好调整键盘位置)

Emacs: Emacs For Mac OS X 24.0.94 Pretest,

我是个喜欢尝试新东东的人,下载

本来想图省事,用ELPA,后来发现很多package都比较老了,比如rinari还停留在script/server阶段,最后能找到github项目的package,我基本上都用的github的。

plugin/mode列表:

ruby-mode,

inf-ruby,

ruby-compilation

Ruby相关的东东。

yari Yet Another RI

在buffer里面看ri文档了。

ido InteractivelyDoThings, 互交的做东东

我以前不是很喜欢ido现在textmate mode跟rinari都用ido,用了一下还不错。

rinari Rinari is not a rails ide

提供很多finder,可以再rails项目文件之间转换,提供了一个类似textmate的rinari-find-file-in-project,我个人还是习惯使用textmate.el带的功能。 除此之外,rinari还可以运行rake,web server, capistrano之类的。

另外一个rinari的功能我很喜欢的是TAGS功能。用ctags生成项目ruby文件的tags,这样在一个方法的调用上,点M-. 就会跳到方法定义的文件/位置。

yasnippet 外加 yasnippets-rails

就是默认yasnippet自带的外加textmade rails相关的导出的一部分。 除了def ->基本上都有了。

autocomplete我没有装。个人感觉不需要弹出个框框来。

textmate.el

不多解释,看下面的功能就知道为什么装上了。

;; ⌘T - Go to File ;; ⇧⌘T - Go to Symbol ;; ⌘L - Go to Line ;; ⇧⌘L - Select Line (or expand Selection to select lines) ;; ⌘/ - Comment Line (or Selection/Region) ;; ⌘] - Shift Right (currently indents region) ;; ⌘[ - Shift Left (not yet implemented) ;; ⌥⌘] - Align Assignments ;; ⌥⌘[ - Indent Line ;; ⌥↑ - Column Up ;; ⌥↓ - Column Down ;; ⌘RET - Insert Newline at Line's End ;; ⌥⌘T - Reset File Cache (for Go to File)

各种rails中可能用到的mode

(require 'rvm)
(require 'sass-mode)
(require 'haml-mode)
(require 'feature-mode)
(require 'rspec-mode)
(require 'coffee-mode)

除了功能上的,字体设了Menlo另外中文用Hiragino Sans GB,个人看着顺眼点。

另外,添加了几个还看的过去的color theme, solarized跟tango是我个人比较喜欢的两个。

除了上面的这些,添加了些个人喜好的设置,比如关了工具条,关了喇叭的咚咚响的,tab设成了2等等。

好了,感兴趣的可以看看这里:

https://github.com/hisea/emacsd

clone了之后记得运行.

git submodule init      
git submodule update

=========================

后记:

用了两三个小时,小拇指稍微有点酸。。难道真的老了。。。

Ruby和元编程的故事 - 第2回: 类与模块,Ruby的绝代双骄

Hisea
Mar 03, 2012

开篇

上回说到,在Ruby语言中,万物皆为对象。

类class和模块module也不例外,也是对象,只不过方法和用途各有不同。

本回综合的看一下class跟module的一些故事。

Ruby的类,就是一段被执行的代码

如果你用过Java等静态OO语言,当你定义一个类的时候,你是在声明一个数据类型。

public class Test {
}

在Ruby中,并不存在这个声明的过程, 类的定义与其他的代码没有什么不同,完全就是在执行一段代码。

例如 >> class Test >> puts "hello world" >> end hello world => nil

如果不是在声明定义,完全是在执行代码,那么class这个key word到底起到什么作用呢?

第一回中我们说,self在Ruby中是个重要的概念

self是一个特殊的变量,里面保存的是当前的对象,方法的调用/定义,实例变量的解析,接受者都会默认为self对象。self保存的值不能被显式的修改,只有通过特殊途径可以修改self的值。一个是(.)这个符号,当我们调用一个方法时,例如obj.method()之后method的解析和运行就会把self的值切换到obj。

另外一个能修改self值的途径,就是class关键字。

class能够达到下面几个作用, 比如我们有前面的 class Test

  1. 定义一个常量,就是class后面的名字Test
  2. 生成一个新的Class的实例对象并赋值给第一步里面生成的常量
  3. 把self的值换成第二步生成的Class实例对象

以上完成之后,Ruby继续执行class内的代码,直到end.

实际上,如果已经存在一个同名常量,Ruby会重新使用那个常量,如果常量不是class就会报错 >> Test = 2 => 2 >> class Test >> end TypeError: Test is not a class from (irb):2

如果是已存在的类,class会把self转到那个类对象,继续执行后面的代码,包括重新定义方法的代码。 这个行为看上去就像是打开类重新定义新内容,Monkey Patching就是利用了这个特性。

Ruby的类,就是一个普通的对象

上一个部分说了Ruby的类就是在不同的上下文环境(class的self)中执行一段代码。
这个self指向的,class本身,其实就是一个很简单的对象,这个对象是Class类的一个实例,当然Class类也是一个对象。

我们完全可以不用class关键字来定义类。

我们可以用Class.new来生成一个类的对象赋值给一个常量。这样生成的类跟class关键字生成的一样好用。 >> Test = Class.new => Test

从这里可看出来,Test就是Class类的一个对象而已,这个对象有Class的实例方法。

>> Class.instance_methods
=> ["private_class_method", "inspect", "name", "tap", "clone", "public_methods", "__send__", "method_defined?", "instance_variable_defined?", "yaml_tag_read_class", "autoload", "equal?", "freeze", "extend", "send", "const_defined?", "methods", "to_yaml_properties", "ancestors", "module_eval", "hash", "dup", "object_id", "instance_methods", "public_method_defined?", "yaml_as", "instance_variables", "class_variable_defined?", "eql?", "constants", "id", "instance_eval", "singleton_methods", "module_exec", "instance_method", "const_missing", "taint", "autoload?", "instance_variable_get", "frozen?", "to_enum", "private_method_defined?", "public_instance_methods", "display", "instance_of?", "superclass", "to_a", "included_modules", "const_get", "instance_exec", "type", "<", "protected_methods", "<=>", "class_eval", "==", "class_variables", ">", "===", "instance_variable_set", "enum_for", "protected_instance_methods", "protected_method_defined?", "yaml_tag_class_name", "taguri", "respond_to?", "kind_of?", ">=", "method", "public_class_method", "to_s", "<=", "const_set", "allocate", "taguri=", "class", "new", "private_methods", "=~", "tainted?", "__id__", "class_exec", "untaint", "nil?", "private_instance_methods", "to_yaml", "to_yaml_style", "include?", "is_a?"] 

这些实力方法中,有些是大家比较熟悉的,例如#class, #superclass,最关键的是,有一个方法叫做new.

就是说Class的实例对象Test,有一个实例方法叫做new,这个方法可以生成一个Test自己的实例对象。

>> t = Test.new
=> #<Test:0x105e026c0>

如果想往这个类上定义实例方法,可以用Monkey Patch的办法再次打开类,也可以用class_eval.

类的self和类的方法

类有两种方法,一是实例方法,例如:

class Test
  def hello
    puts "hello"
  end
end

这里定义的是一个实例方法,只有通过Test的实例才能调用这个方法。如Test.new.hello

另一种是类方法,类方法就是类似这样的方法: Test.hello 看上去是不是很像Test.new, 我们定义这一类型的方法时其实就是利用的影子类为Test对象创造了一个单例方法。 最简单的办法就是

>> Test = Class.new
=> Test
>> def Test.hello
>> puts "hello"
>> end
=> nil
>> Test.hello
hello
=> nil

上面的例子也能显示Test其实就是一个简单的对象,hello只不过是这个对象的单例方法,关于单例方法,详见第一回

大部分时候,类方法的定义是在类的内部。

class Test
  def self.class_method1
    puts "hello1"
  end
  def Test.class_method2
    puts "hello2"
  end
end

以上时两种在类的内部定义类方法的途径。 class_method2的定义途径跟前面一个例子的一样,利用了class内部只是在执行代码这一特性。 这部分代码在类的内部和外部执行都是一个结果,给类对象添加一个singleton method.

而class_method1的定义途径只是利用了self在class的内部及方法定义外部的时候,其值就是类对象本身。简单的说就是self的值就是Test,所以两个定义方法是等价的。

除非在实例方法定义内,self是实例对象,self在类的内部其他部位就是其本身。
加上前面一个特性,类就是一段被执行的代码,就变的非常的强大了。

这个两个 特性被广泛的应用。例如我们熟悉的has_many

class Post << ActiveRecord::Base
  has_many :post
end

has_many是一个方法,这个方法的接受者receiver是默认的self,也就是类本身,换句话说,has_many这个方法是个类方法。我们这里在类的内部调用了一个类方法。

我们可以用一个非常简单的例子来看has_many是怎么实现的:

>> Test = Class.new
=> Test
>> def Test.has_many(name)
>> puts "Rails style has_many #{name}"
>> end
=> nil
>> class Test
>> has_many "photos"
>> end
Rails style has_many photos
=> nil
>> 

这里如果我们把Test.has_many类方法内的代码,换成能动态生成一系列方法,能include外部的module,或者其他元编程的技巧,就达到了类似Rails中ActiveRecord的效果。这些元编程的具体技巧我们以后在研究。

模块module

module跟class是绝代双骄,因为他们都是对象,而且有很多相似指出。

其实说法不太对。

  • Module是Class的父类: >> Class.superclass => Module

  • module 没有实例变量

  • module 没有new不能生成实例对象

module内可以有常量

>> module Test
>>   PI=3.14
>> end
=> 3.14
>> Test.PI
>> Test::PI
=> 3.14

module的方法有两种,一种是module方法,这类方法可以直接调用。

>> module Test
>>   def Test.test_method
>>     puts "hello from module"
>>   end
>> end
=> nil
>> Test::test_method
hello from module
=> nil

另一种是没有module名字的方法,这种方法不能直接调用,需要mixin到一个类中。

>> module Test
>> def hello
>>   puts "hello"
>> end
>> end
=> nil
>> Test::hello
NoMethodError: undefined method `hello' for Test:Module
    from (irb):23

把module的方法添加到类中有两种方法。
一种是include,方法会被添加到实例方法中。
一种是extend,方法会被添加到类方法中。

继续前面的module Test的例子

>> class Class1
>> include Test
>> end
=> Class1
>> Class1.new.hello
hello
=> nil
>> class Class2
>> extend Test
>> end
=> Class2
>> Class2.hello
hello
=> nil

module常用的一个hook/callback是included方法,这个方法在module被include到一个类中的时候会被调用。

>> module Test
>>   def self.included(cls)
>>     puts "including module in class #{cls.name}"
>>    end
>> end
=> nil
>> class Class1 
>>   include Test
>> end
including module in class Class1
=> Class1

本回完

本回讲了class跟module的一些东西。

本文的例子非常非常的简单,不过很多东西结合起来使用就会很强大,

例如,我们可以定义一个module Test,include Test 的时候extend另外一个module Test::ClassMethods,这时就会给当前类添加很多类方法。

这些类方法又可以来定义跟生成一部分功能。达到类似下面的效果:

class Artist
  include Mongoid::Document
  field :name, type: String
  embeds_many :instruments
end

如果你感兴趣,可以考虑一下mongoid怎么能实现上面的功能。

且听下回分解

还没想好下回讲什么,或者是eval系列,或者是define_method,或者是block/lambda/Proc.new

联系作者

如果你有任何问题,欢迎讨论。

作者: Hisea
web: http://hisea.me
email: zyinghai@gmail.com
weibo: http://www.weibo.com/zyinghai
twitter: https://twitter.com/zyinghai
github: https://github.com/hisea

Hisea.me 版权所有

Ruby和元编程的故事 - 第1回: 色色空空,万物皆为对象

Hisea
Feb 20, 2012

开篇

空即是色,色即是空。 空空色色,色色空空,在Ruby语言中,万物皆为对象。

Ruby是一个面向对象的语言(Object Oriented Language),面向对象的概念比其他语言要贯彻的坚定很多。

Ruby中不存在Java中原始类型数据和对象类型数据之分。大部分Ruby中的的东东都是对象。

所以,想要掌握Ruby和Ruby的元编程,对象就是第一门必修功课。本回就着重研究一下Ruby中的对象.

Ruby中的对象

如果你从其他面向对象的语言转来,一提到得到一个对象你可能会想到建立一个类,然后建立这个类的实例出来产生一个对象。

在Ruby中这完全是可以的,不过这种先建立类才能获得对象的过程,听起来更像是面向类的设计,而不是面向对象的设计。关于类的一些东西放到下回再说。

在Ruby中,不存在原始类型的概念,1, 0.3, true/false 甚至 nil都是对象。比如,你可以在irb中尝试下面的代码:

>> 1.methods
=> ["%", "odd?", "inspect", "prec_i", "<<", "tap", "div", "&", "clone", ">>", "public_methods", "__send__", "instance_variable_defined?", "equal?", "freeze", "to_sym", "*", "ord", "lcm", "+", "extend", "next", "power!", "send", "round", "methods", <…more methods…> "is_a?", "ceil", "[]"]
>> 1.class
=> Fixnum

你可以在irb中尝试一下其他数据类型,看看他们的方法和类等等信息。

不只是各种数据类型,方法在Ruby中也是对象, 比如下列例子:

>> one_plus = 1.method(:+)
=> #<Method: Fixnum#+>
>> one_plus.class
=> Method
>> one_plus.call(2)
=> 3

有意思的是,方法对象也是有方法的:

>> one_plus.arity()
=> 1

对象到底是什么?

到底什么是对象呢?

简单的说,对象就是 状态 + 行为

状态 就是表明当前对象所拥有的属性,每个同类的对象可能有不同的状态,这些状态保存在实例变量里面(Instance Variable).
对象的实例变量可以由instance_variable_set/instance_variable_get来设定/读取:

>> 1.instance_variable_set(:@my_var, "world")
=> "world"
>> 1.instance_variable_get(:@my_var)
=> "world"

行为 行为就是作用在对象上的动作,就是我们常说的方法。Ruby方法的调用,类似于smalltalk或者Objectiv-C,采用消息模式。调用方法相当于对这个对象发送了一个消息。所以对方法的调用也可以这样:

>> 1.send(:+,1)
=> 2

在Ruby中,状态,也就是实例变量是保存在对象里的,而行为或方法则是存在于对象的类或者mixin的module里面。

在静态语言中,编译时就会确定所调用的方法是否存在,不存在会产生编译错误。

Ruby中,当我们在方法调用的运行时,对象会查找他隶属的类,module,父类等,来找到相对应的方法。

Singleton/Meta/Anonymous/Ghost/Shadow Class

  • Singleton Class: 单例类
  • Meta Class:元类
  • Anonymous Class: 匿名类
  • Ghost Class:鬼类
  • Shadow Class: 影子类

上面的这些东东其实说的都是一个东西,我喜欢叫它 影子类。

Ruby中每一个对象都一个一个影子类,这个影子类存在于对象跟它所属的类之间:

对象("obj1") -> 影子类 -> 对象所属的类(String)

当一个对象的方法被调用时,首先查找的是影子类,之后才是它所属的类。

上面讲到实例变量存在于对象内,方法存在于对象的类中。 影子类上的方法,就是只有这一个对象拥有的方法。这个方法通常叫做单例方法(Singleton Method)。

这样的方法只存在于这个对象上,同一个类的其他对象没有这个方法,因为他们的影子类不同,其他对象的影子类上没有这个方法。

>> a = "obj1"
=> "obj1"
>> def a.hello
>> puts "hello world"
>> end
=> nil
>> a.hello
hello world
=> nil
>> b = "obj2"
=> "obj2"
>> b.hello
NoMethodError: undefined method `hello' for "obj2":String
    from (irb):49
>> a.singleton_methods
=> ["hello"]
>> b.singleton_methods
=> []

Self

Ruby里面一切都是对象,self也是对象,确切地说是当前对象的引用。

前文说Ruby的方法调用是消息模式,比如obj.method, 消息的接受者是.之前的对象,.之后的是方法及参数。 如果对象和.没有出现的话,消息会被默认送到self对象。除了作为方法的默认接受者,self也是实例变量的解析对象。

self在ruby一开始的时候,被设定为一个叫做main的对象,再irb里面可以看到:

>> m = self
=> main

self可以被认为是一个特殊的变量,它的特殊性在于,你不能给他赋值:

>> self = "obj"
SyntaxError: compile error
(irb):77: Can't change the value of self
self = "obj"
      ^

有几个办法可以改变self的值,.(obj.method的.)是其中一个,除了.还有class/module关键字。 本回主要关注跟对象相关的.

当我们用obj.method调用方法时,接下来的时间代码的执行就会到相应的方法里,运行的上下文切换到那个对象,self自然也变成了那个对象。用def定义单例方法时,道理也是相通的。 下面的例子可以说明这个self切换的情况。

>> a = "obj"
=> "obj"
>> def a.hello_self
>> puts "hello #{self}"
>> end
>> m = self
=> main
>> a.hello_self
hello obj

对象的复制

前文说对象的存在包括两部分,一是状态/实例变量,另一个是行为,本回专注讲了单例方法和影子类。 Ruby中对象的复制也有两种模式,一个是只复制当前的状态/实例变量 dup。另外一种是连同影子类和引用的对象一起复制,从而把单例方法也复制一份。

>> a = "obj"
>> def a.hello_self
>> puts "hello #{self}"
>> end
>> b = a.dup
=> "obj"
>> b.hello_self
NoMethodError: undefined method `hello_self' for "obj":String
    from (irb):90
>> b = a.clone
=> "obj"
>> b.hello_self
hello obj

其实有本回上述的这些功能,即便是没有class,Ruby也可以作为一种Prototype(类似JavaScript)的面向对象语言了。

你可以建立一个对象,生成默认的实例变量,把行为作为单例方法定以在这个对象的影子类上,然后用clone生成千千万万个实例。当然这样比较麻烦,但却是可行的途径之一。

其他Object API

对象还有很多其他的功能,比如可以freeze,另外dup跟clone也有一些其他的引用上面的区别,dup只复制引用,clone会吧引用的对象也复制。

这些都可以在Object类(Ruby所有对象的父类)API上找到,可以查看apidock.com的文档

例如关于dup .dup() produces a shallow copy of obj—the instance variables of obj are copied, but not the objects they reference. dup copies the tainted state of obj. See also the discussion under Object#clone. In general, clone and dup may have different semantics in descendant classes. While clone is used to duplicate an object, including its internal state, dup typically uses the class of the descendant object to create the new instance.

本回完

本回讲了些对象相关的东西,有的很基础,有的是Ruby自身的一些特性。

其中Ruby对象模型中最具特色的两个特性就是影子类/单例方法和self,最好能深入理解这两个概念。

且听下回分解

下回注重一些关于类的故事。

联系作者

如果你有任何问题,欢迎讨论。

作者: Hisea
web: http://hisea.me
email: zyinghai@gmail.com
weibo: http://www.weibo.com/zyinghai
twitter: https://twitter.com/zyinghai
github: https://github.com/hisea

Hisea.me 版权所有

Ruby和元编程的故事 - 第0回: 欲练神功,必先自废武功

Hisea
Feb 13, 2012

开篇

随着Ruby/Rails的逐渐火热,越来越多的开发人员转向Ruby.
Rails简单易用,很多其他语言开发人员很快就做出简单的Web App.
然而另一方面,Ruby做为一个语言,确需要一些学习和研究才能掌握其方方面面.
本系列就是专注于提供一些关于Ruby语言和其元编程技巧的文章。 在编写这个系列文章的时候,Hisea对读者做了以下的一些假设:

  • 已经熟悉用某种程序些程序
  • 已经熟悉Ruby
  • 已经能够假设Ruby环境并可以运行代码

Ruby是个神马语言

Ruby是一个开源的动态类型,强类型,动态的,面向对象的解释型语言.

看上去貌似很麻烦,这些形容词拿出来一个一个解释就很简单了。

  • 开源的:
    Ruby开源协议1.9.3以前是GPLv2和Ruby, 1.9.3之后是Simplfied BSD和Ruby协议

  • 动态类型:
    类型的检查是运行时进行的, 相对于Java等静态类型语言,类型检查是在编译时进行的

  • 强类型语言:
    不同数据类型在操作和运算中出现混用时,除非显示转换类型,否则会报类型错误,相对于JavaScript等弱类型语言,不存在类似的限制.

  • 动态语言: 动态语言的定义还很模糊,很多人把动态语言跟动态类型语言搞混,对于Hisea来说,动态语言就是类,方法,及其他定义可以在运行时进行改变,元编程就是利用这一特性。由此可见,动态语言跟动态类型语言并不是一个概念.

  • 面向对象:
    不解释

  • 解释型: 区别于编译型语言.

欲练盖世神功,必先自废武功

Ruby的动态特性跟以往的静态类型语言相比,需要很多思维上的转换。
如果带着以往的静态语言思维来学习Ruby,往往需要走些弯路。
对于有其他语言基础的初学者,有些已经练好的武功最好一开始彻底忘掉,才能更好的练成盖世Ruby神功。

下面是一些学习Ruby需要深刻在心的概念,很多可能跟你现有的思维定式相违背。最好的办法就是清空现有想法,不带偏见的从0开始学习Ruby.

0.万物皆是对象

Ruby里的一切,一切一切,无论是色还是空,都是对象。 不象Java,Ruby没有原始类型。1是对象,1.2是对象,nil是对象,true/false是对象。 类是对象,module也是对象。

1.拥抱动态类型

Ruby界有条谚语,如果你扭的像个鸭子,叫的像个鸭子,你就是个鸭子。
这是Ruby Duck Typing的特性。看上去很简单,但是真正把这句话融入到骨髓却不是件易事。如果你在代码中有很多检查类型的判断,例如is_a, kind_of等方法,甚至太多的nil检测,都是没有很好的理解动态类型的特征。

2.忘记静态的类设计

这一条其实是第一条的延续。很多有多年Java或者C++/C#经验的开发人员(包括Hisea自己),在设计复杂的系统时,都会很自然的想到继承和接口(Interface).在Ruby中设计继承时需要认真的思考一下几个问题。

1. 父类在做什么?
例如很多人喜欢这么做:

class 飞行器  
  def 起飞  
    fail "子类包含具体的实现!"   
  end  
end  

class 飞机 < 飞行器  
  def 起飞  
    #飞机起飞实现 
  end  
end  

class 热气球 < 飞行器  
  def 起飞  
    #热气球起飞实现  
  end  
end  

单看以上的例子,父类的作用仅仅是定义了API的存在,这在Java中是非常常见的,可是在Ruby中,这个父类其实存在的意义不是特别大。Ruby更倾向于,任何能起飞(拥有‘起飞’方法)的对象都是飞行器,而不是任何继承飞行器的类的对象。

2. 需要用继承分享代码么?
ActiveRecord需要你继承ActiveRecord::Base,例如:

class Post < ActiveRecord::Base  
end

Hisea本人觉得类似Mongoid的Mixin方式更适合解决类似的问题。 Post从设计逻辑上来讲,跟ActiveRecord::Base没有半毛钱关系。继承关系完全用于分享代码,而在Ruby中,更好的分享代码的办法是用Mixin.
比如第一个例子中,我们可以定义一个飞机的module,实现 起飞 方法,任何include起飞的类,都是某一种飞机。

3.让动态进行到底

Ruby不仅仅是类型是动态的,还有很多其他的也很动态,比如可以动态的include或者extend一个module,可以动态的定义或者重新定义一个类,或者一个方法。这在Java等静态语言来看是非常的无组织无纪律极其自由散漫。Hisea面试Ruby程序员时经常会问到关于Metaprogramming的问题,其实对Ruby元编程这个特性懂多懂少并无所谓,只是拿来刺探Ruby经验多少的一个问题。资历尚浅的Ruby程序员对于元编程总会抱有这样那样的保守想法。

4,忘记UML(或其他)设计大法

这个话题是3的继续,公司新招了一个Ruby程序员,他来了一两个星期后问了个问题,问为什么公司(或者其他Ruby程序员)不爱用UML之类的设计工具,当时我也一时找不出答案,后来开车回家的路上用半个小时想明白了,UML是一个类只见关系的静态表示,Ruby运行时的情况却是不停在变幻。用静态去表示动态,自然捉襟见肘。

5.为什么不用IDE?

很多从Java阵营转来,尤其是有多年eclipse经验的Ruby初学者尤其是经常爱问,Ruby用什么IDE。
得到的回答往往是text mate,vim,sublime text 2等等文本编辑器。
很多人可能纳闷,为什么Ruby/Rails没有一个IDE占领大片江山的情况,为什么Netbeans/Eclipse再Ruby开发阵营中没有其他语言开发占的地位重要。

其实答案很简单。

  1. IDE最讨喜的功能是什么?
    很多用惯了IDE开发的Java程序员甚至XCode程序员,都会说最爱的功能是代码不全,object之后按一下'.'立马生成一个方法列表。转到Ruby用文本编辑器,没有这个功能,很是郁闷。其实道理很简单,如果方法都是动态生成的,在写程序的时候怎么能给出一个列表呢。

  2. Debugger还是必要工具么?
    静态语言开发,调试是居家旅行杀人灭口必备良药。而IDE又是调试的好帮手。 Ruby/Rails的Debugger还没那么成熟,而且如果用logger, raise在适当的地方输出inspect, to_yaml等内容,也可以很容易的找到错误。

  3. Eclipse可以很好的配置Java开发环境。 Ruby/Rails有好用的RVM,RubyGems,Bundler.

所以,不补全,不调试,不配环境,要IDE不也是拿来当文本编辑器用么。

本回完

零零总总的想了些Ruby的主要特性,以及常见的初学者的疑问/误区。
本文打算从这里开始,写点Ruby深层次的一些概念和特性,并且怎么利用这些特性进行Metaprogramming.

且听下回分解

下回注重一些对象Object的故事。

联系作者

如果你有任何问题,欢迎讨论。

作者: Hisea
web: http://hisea.me
email: zyinghai@gmail.com
weibo: http://www.weibo.com/zyinghai
twitter: https://twitter.com/zyinghai
github: https://github.com/hisea

Hisea.me 版权所有