Ruby 程式语言入门

    Ruby是个美丽、灵巧而且方便又实用的程式语言,而Ruby on Rails正是 Ruby 程式语言爆发成长的催化剂。在了解Ruby on Rails的程式之前,学习Ruby程式语言是最重要的基础功课之一,我们在这一章节将快速带过一些基本的语法,网络上也有Ruby Taiwan社群所翻译的文章可以一读:

    免费的英文资源也很多,我推荐以下三个教学网站作为练习之用:

    除了用C语言实作的官方版本(又叫做CRubyMRI, Matz’s Ruby Interpreter),也有其他不同实作的Ruby环境。这些实作都以RubySpec作为其语法的标准:

    • 是由Java实作的Ruby,运行在高效能、支援系统执行绪及有非常多函数库的Java虚拟机器(JVM)上。JRuby算是目前Ruby要开发跨平台(WindowsMacLinux)桌面软件最好的选择。
    • Rubinuis是用C++RubyLLVM编译器技术实作的Ruby VM,可以在Mac OS XDebian/UbuntuFreeBSDWindows上执行。LLVM可以说是当代最重要的编译器架构,拥有各种编译器最佳化技术。能给Ruby带来多少效能改善幅度,值得关注。

    IRB(Interactive Ruby)

    IRB是一个互动的Ruby环境,可以让我们练习和语法,做些简单的实验。请输入就会进入互动模式:

    irb之中,每行执行完Ruby都会自动帮你puts输出结果。

    不过,一旦程式稍微复杂一点,还是打开文字编辑器吧。让我们编辑一个档案hello.rbRuby脚本附档名的惯例是.rb,内容如下:

    1. puts "Hello, World!!"

    存盘后,输入:

    1. $ ruby hello.rb

    就会执行这个脚本了,它会在萤幕上输出Hello, World!!

    程式语言分类

    根据需不需要事先宣告变量型别,我们可以分类出静态分型(Static typing)与动态分型(Dynamic typing)程式语言,前者例如JavaCC++,后者例如RubyPerlPythonPHP。根据会不会隐性自动转换型别,又可以区分出不会自动转换型别的强分型(Strong typing)与自动转换型别的弱分型(Weak typing),前者例如RubyPerlPythonJava,后者例如PHPCC++是弱分型。让我们举个例吧:

    1. /* PHP */
    2. $i = 1;
    3. echo "Value is " . $i ;
    4. # Value is 1
    5. /* C */
    6. int a = 5;
    7. float b = a;

    以上的PHPC会隐性地自动转型,但是以下的Ruby程式会检查型别不相配而发生错误,这一点从PHP过来的朋友要特别注意。

    1. # Ruby
    2. i = 1
    3. puts "Value is " + i
    4. # TypeError: can't convert Fixnum into String
    5. # from (irb):2:in `+'
    6. # from (irb):2

    另外,通常动态分型的程式语言多半也是直译式(interpreted)程式语言,也就是不需要事先编译,透过直译器(interpreter)执行即可,当然Ruby也不例外。相对的,编译式(compiled)语言则是事先编译成执行档才行执行。总结以上,Ruby是个动态强分型的直译式程式语言。

    整数Integer

    任何整数都是Fixnum物件:

    1. 5
    2. -205
    3. 9999999999
    4. 0

    完整的Fixnum API请参考文件。

    浮点数Float

    中间带有点号的就是浮点数Float物件:

    1. 54.321
    2. 0.001
    3. -12.312
    4. 0.0

    浮点数四则运算范例如下:

    1. puts 1.0 + 2.0
    2. puts 2.0 * 3.0
    3. puts 5.0 - 8.0
    4. puts 9.0 / 2.0
    5. # 3.0
    6. # 6.0
    7. # -3.0
    8. # 4.5

    要注意的是,整数四则运算结果,也会是整数:

    1. puts 1 + 2
    2. puts 2 * 3
    3. puts 5 - 8
    4. puts 9 / 2
    5. # 3
    6. # 6
    7. # -3
    8. # 4

    以下是一个更复杂的四则运算例子:

    1. puts 5 * (12 - 8) + -15
    2. puts 98 + (59872 / (13*8)) * -52

    完整的Float API请参考Ruby doc文件。

    字串String

    使用单引号或双引号括起来的是字串String物件:

    1. puts 'Hello, world!'
    2. puts ''
    3. puts 'Good-bye.'

    字串相加可以使用加号,要注意的是字串不能直接跟数字相加,会发生例外错误:

    1. puts 'I like ' + 'apple pie.'
    2. puts 'You\'re smart!'
    3. puts '12' + 12
    4. #<TypeError: can't convert Fixnum into String>

    更多字串方法示范:

    1. var1 = 'stop'
    2. var2 = 'foobar'
    3. var3 = "aAbBcC"
    4. puts var1.reverse # pots
    5. puts var2.length # 6
    6. puts var3.upcase # AABBCC
    7. puts var3.downcase # aabbcc

    为了方便字串的组合,Ruby也支持内插的方式:

    1. verb = 'work'
    2. where = 'office'
    3. puts "I #{verb} at the #{where}" # 输出 I work at the office

    注意到使用双引号(“)的字串才会进行内插处理。如果换成单引号(‘):

    1. puts 'I #{verb} at the #{where}' # 输出 I #{verb} at the #{where}

    完整的String API请参考文件。

    Ruby完全地物件导向

    你可能已经注意到,在Ruby里每样东西都是物件,包括字串和数字。所有的方法都是对物件呼叫,你不会看到全域函式,例如PHPstrlen("test")用法,在Ruby中是"test".length

    1. # 输出「UPPER」
    2. puts "upper".upcase
    3. # 输出 -5 的绝对值
    4. puts -5.abs
    5. # 输出 Fixnum 类别
    6. puts 99.class
    7. # 输出五次「Ruby Rocks!」
    8. 5.times do
    9. puts "Ruby Rocks!"
    10. end

    区域变量Local Variable

    区域变量使用小写开头,偏好单字之间以底线_来分隔。范例如下:

    1. composer = 'Mozart'
    2. puts composer + ' was "da bomb", in his day.'
    3. my_composer = 'Beethoven'
    4. puts 'But I prefer ' + my_composer + ', personally.'

    如果存取一个尚未初始过的区域变量,会得到以下错误:

    1. NameError: undefined local variable or method `qwer' for main:Object
    2. from (irb):1
    3. from /Users/ihower/.rvm/rubies/ruby-2.1.5/bin/irb:11:in `<main>'

    型别转换Conversions

    刚刚提到数字和字串物件不能直接相加,你必须使用to_s(转成字串)、to_i(转成整数)或to_f(转成浮点数)来手动转型,范例如下:

    1. var1 = 2
    2. var2 = '5'
    3. puts var1.to_s + var2 # 25
    4. puts var1 + var2.to_i # 7
    5. puts 9.to_f / 2 # 4.5

    大写开头的是为常数,范例如下:

    1. Foo = 1
    2. Foo = 2 # (irb):3: warning: already initialized constant Foo
    3. RUBY_PLATFORM # => "x86_64-darwin10.7.0"
    4. ENV # => { "PATH" => "....", "LC_ALL" => "zh_TW.UTF-8" }

    空值nil

    表示未设定值、未定义的状态:

    1. nil # nil
    2. nil.class # NilClass
    3. nil.nil? # true
    4. 42.nil? # false
    5. nil == nil # true
    6. false == nil # false

    注解

    Ruby偏好一律使用单行注解:

    1. # this is a comment line
    2. # this is a comment line

    多行注解比较少见:

    1. =begin
    2. This is a comment line
    3. This is a comment line
    4. =end

    字串符号Symbols

    Symbol是唯一且不会变动的识别名称,用冒号开头:

    1. :this_is_a_symbol
    1. puts "foobar".object_id # 输出 2151854740
    2. puts "foobar".object_id # 输出 2151830100
    3. puts :foobar.object_id # 输出 577768
    4. puts :foobar.object_id # 输出 577768

    objectid方法会回传_Ruby内部的内存配置编号。你会发现两个字串就算内容相同,也是不同的物件。但是Symbol只要内容相同,就是相同的物件。这种特性让Symbol的主要用途是作为杂凑Hash的键(Key),一会就会介绍到。

    阵列Array

    使用中括号,索引从0开始。注意到阵列中的元素是不限同一类别,想放什么都可以:

    如果读取一个没有设定的阵列元素,默认值是nil。更多阵列方法范例:

    1. colors = ["red", "blue"]
    2. colors.push("black")
    3. colors << "white"
    4. puts colors.join(", ") # red, blue, black, white
    5. colors.pop
    6. puts colors.last #black

    使用each方法走访阵列:

    1. languages = ['Ruby', 'Javascript', 'Perl']
    2. languages.each do |lang|
    3. puts 'I love ' + lang + '!'
    4. end
    5. # I Love Ruby!
    6. # I Love Javascript!
    7. # I Love Perl!

    完整的Array API请参考[Ruby Array API(http://www.ruby-doc.org/core/classes/Array.html)文件。

    杂凑Hash

    Hash是一种键值对(Key-Value)的资料结构,虽然你可以使用任何物件当作Key,但是通常我们使用Symbol当作Key。例如:

    1. config = { :foo => 123, :bar => 456 }
    2. puts config[:foo] # 输出 123
    3. config["nothing"] # 是 nil

    Ruby 1.9后支援新的语法,比较简约:

    1. config = { foo: 123, bar: 456 } # 等同于 { :foo => 123, :bar => 456 }

    如果读取一个不存在的值,例如上述范例的nothing,默认值是nil

    使用each方法可以走访杂凑:

    1. config = { :foo => 123, :bar => 456 }
    2. config.each do |key, value|
    3. puts "#{key} is #{value}"
    4. end
    5. # foo is 123
    6. # bar is 456

    完整的Hash API请参考文件。

    流程控制Flow Control

    让我们来看看一些流程控制:

    1. puts 1 > 2 # 大于
    2. puts 1 < 2 # 小于
    3. puts 5 >= 5 # 大于等于
    4. puts 5 <= 4 # 小于等于
    5. puts 1 == 1 # 等于
    6. puts 2 != 1 # 不等于
    7. puts ( 2 > 1 ) && ( 2 > 3 ) # 和
    8. puts ( 2 > 1 ) || ( 2 > 3 ) # 或

    控制结构If

    else if写成elsif

    1. total = 26000
    2. if total > 100000
    3. puts "large account"
    4. elsif total > 25000
    5. puts "medium account"
    6. puts "small account"
    7. end

    另外如果要执行的if程式只有一行,可以将if放到行末即可:

    1. puts "greater than ten" if total > 10

    三元运算子

    三元运算子expression ? trueexpresion : false_expression可以让我们处理简易的_if else条件,例如以下的程式:

    1. x = 3
    2. if x > 3
    3. y = "foo"
    4. else
    5. y = "bar"
    6. end

    改用三元运算子之后,可以缩减程式行数:

    1. x = 3
    2. y = ( x > 3 ) ? "foo" : "bar"

    控制结构Case

    1. case name
    2. when "John"
    3. puts "Howdy John!"
    4. when "Ryan"
    5. puts "Whatz up Ryan!"
    6. else
    7. puts "Hi #{name}!"
    8. end

    循环 while, loop, until, next and break

    while用法范例:

    1. i=0
    2. while ( i < 10 )
    3. i += 1
    4. next if i % 2 == 0 #跳过双数
    5. end

    until用法范例:

    1. i = 0
    2. i += 1 until i > 10
    3. puts i
    4. # 输出 11

    loop用法范例:

    1. i = 0
    2. loop do
    3. i += 1
    4. break if i > 10 # 中断循环
    5. end

    不过你很快就会发现写Ruby很少用到whileuntilloop,我们会使用迭代器。

    记住,只有falsenil是假,其他都为真。

    1. puts "not execute" if nil
    2. puts "execute" if true # 输出 execute
    3. puts "execute" if “” # 输出 execute (和JavaScript不同)
    4. puts "execute" if 0 # 输出 execute (和C不同)
    5. puts "execute" if 1 # 输出 execute
    6. puts "execute" if "foo" # 输出 execute
    7. puts "execute" if Array.new # 输出 execute

    正规表示法Regular Expressions

    Perl类似的语法,使用=~

    1. # 抓出手机号码
    2. phone = "123-456-7890"
    3. if phone =~ /(\d{3})-(\d{3})-(\d{4})/
    4. ext = $1
    5. city = $2
    6. num = $3
    7. end

    方法定义Methods

    使用def开头end结尾来定义一个方法:

    1. def say_hello(name)
    2. result = "Hi, " + name
    3. return result
    4. end
    5. puts say_hello('ihower')
    6. # 输出 Hi, ihower

    方法中的return是可以省略的,Ruby就会回传最后一行运算的值。上述方法可以改写成:

    1. def say_hello(name)
    2. "Hi, " + name
    3. end

    呼叫方法时,括号也是可以省略的,例如:

    1. say_hello 'ihower'

    不过,除了一些方法惯例不加之外(例如putsRails中的redirect_torender方法),绝大部分的情况加上括号比较无疑义。

    我们也可以给参数默认值:

    1. def say_hello(name = "nobody")
    2. result = "Hi, " + name
    3. return result
    4. end
    5. puts say_hello
    6. # 输出 Hi, nobody

    方法名称可以用?!结尾,前者表示会回传Boolean值,后者暗示会有某种副作用(side-effect)。范例如下:

    1. array=[2,1,3]
    2. array.empty? # false
    3. array.sort # [1,2,3]
    4. array.inspect # [2,1,3]
    5. array.sort! # [1,2,3]
    6. array.inspect # [1,2,3]

    物件导向

    物件导向(Object-Oriented Programming)一种将「资料」和「方法」封装到物件的设计方式,我们定义「类别 Class」,然后依此产生出「物件 Object」,类别可说是物件的样板。

    Ruby的类别其实也是一种常数,所以也是大写开头,使用new方法可以建立出物件,例如之前所学的字串、阵列和杂凑,也可以用以下方式建立:

    1. color_string = String.new
    2. color_string = "" # 等同
    3. color_array = Array.new
    4. color_array = [] # 等同
    5. color_hash = Hash.new
    6. color_hash = {} # 等同
    7. time = Time.new # 内建的时间类别
    8. puts time

    来看看如何自定类别:

    1. class Person # 大写开头的常数
    2. def initialize(name) # 建构式
    3. @name = name # 物件变量
    4. end
    5. def say(word)
    6. puts "#{word}, #{@name}" # 字串相加
    7. end
    8. end
    9. p1 = Person.new("ihower")
    10. p2 = Person.new("ihover")
    11. p1.say("Hello") # 输出 Hello, ihower
    12. p2.say("Hello") # 输出 Hello, ihover

    除了物件方法与物件变量,Ruby也有属于类别的方法和变量:

    1. class Person
    2. @@name = ihower # 类别变量
    3. def self.say # 类别方法
    4. puts @@name
    5. end
    6. end
    7. Person.say # 输出 ihower

    资料封装

    为了可以存取到@name,我们必须定义方法:

    1. class Person
    2. def initialize(name)
    3. @name = name
    4. end
    5. def name
    6. @name
    7. end
    8. def name=(name)
    9. @name = name
    10. end
    11. end
    12. p = Person.new('ihower')
    13. => #<Person:0x007fe9e408b8f0 @name="ihower">
    14. p.name
    15. => "ihower"
    16. p.name="peny"
    17. => "peny"
    18. p.name
    19. => "peny"
    20. p
    21. => #<Person:0x007fe9e408b8f0 @name="peny">

    类别Class定义范围内也可以执行程式

    跟其他程式语言不太一样,Ruby的类别层级内也可以执行程式,例如以下:

    1. class Demo
    2. puts "foobar"
    3. end

    当你加载这个类别的时候,就会执行puts "foobar"输出foobar。会放在这里的程式,主要的用途是来做Meta-programming。例如,上述定义物件变量的存取方法实在太常见了,因此Ruby提供了attr_accessorattr_writerattr_reader类别方法可以直接定义这些方法。上述的程式可以改写成:

    1. class Person
    2. attr_accessor :name
    3. def initialize(name)
    4. @name = name
    5. end
    6. end
    7. p = Person.new('ihower')
    8. => #<Person:0x007fe9e3094410 @name="ihower">
    9. p.name
    10. => "ihower"
    11. p.name="peny"
    12. => "peny"
    13. p.name
    14. => "peny"
    15. p
    16. => #<Person:0x007fe9e3094410 @name="peny">

    这里的attr_accessor其实就是一个类别方法。

    方法封装

    类别中的方法默认是public的,宣告privateprotected的话,该行以下的方法就会套用:

    1. class MyClass
    2. def public_method
    3. end
    4. private
    5. def private_method_1
    6. end
    7. def private_method_2
    8. end
    9. protected
    10. def protected_method
    11. end
    12. end

    Rubyprivateprotected定义和其他程式语言不同,都是可以在整个继承体系内呼叫。两着差别在于private只有在物件内部才能呼叫,默认的接收者(receiver)就是物件本身,也就是self。而protected方法除了可以在本身内部呼叫以外,还可以被子类别的物件、或是另一个相同类别的物件呼叫。

    Class 继承

    Ruby使用小于<符号代表类别继承:

    1. class Pet
    2. attr_accessor :name, :age
    3. def say(word)
    4. puts "Say: #{word}"
    5. end
    6. end
    7. class Cat < Pet
    8. def say(word)
    9. puts "Meow~"
    10. super
    11. end
    12. end
    13. class Dog < Pet
    14. def say(word, person)
    15. puts "Bark at #{person}!"
    16. super(word)
    17. end
    18. end
    19. Cat.new.say("Hi")
    20. Dog.new.say("Hi", "ihower")

    输出

    1. Meow~
    2. Say: Hi
    3. Bark at ihower!
    4. Say: Hi

    这个范例中,CatDog子类别复写了Pet say方法,其中的super是用来呼叫被复写掉的Pet say方法。另外,没有括号的super和有括号的super()是有差异的,前者Ruby会自动将所有参数都代进去来呼叫父类别的方法,后者则是自己指定参数。此例中如果Dog say里只写super,则会发生wrong number of arguments的错误,这是因为Ruby会传say("Hi", "ihower")Pet say而发生错误。

    ModuleRuby一个非常好用的功能,它跟Class类别非常相似,你可以在里面定义方法。只是你不能用new来建立它。它的第一个用途是可以当做Namespace来放一些工具方法:

    1. module MyUtil
    2. def self.foobar
    3. puts "foobar"
    4. end
    5. end
    6. MyUtil.foobar
    7. # 输出 foobar

    另一个更重要的功能是Mixins,可以将一个Module混入类别之中,这样这个类别就会拥有此Module的方法。这回让我们拆成两个档案,debug.rbfoobar.rb,然后在foobar.rb中用require来引用debug.rb

    首先是debug.rb

    1. def who_am_i?
    2. puts "#{self.class.name}: #{self.inspect}"
    3. end
    4. end

    然后是foobar.rb

    1. require "./debug"
    2. class Foo
    3. include Debug # 这个动作叫做 Mixin
    4. end
    5. class Bar
    6. include Debug
    7. end
    8. f = Foo.new
    9. b = Bar.new
    10. f.who_am_i? # 输出 Foo: #<Foo:0x00000102829170>
    11. b.who_am_i? # 输出 Bar: #<Bar:0x00000102825b88>

    Ruby使用Module来解决多重继承的问题,不同类别之间但是拥有相同的方法,就可以改放在Module里面,然后include它即可。

    循环走访与迭代器Iterator

    不同于while循环用法,Ruby习惯使用迭代器(Iterator)来走访循环,例如each是一个阵列的方法,它会走访其中的元素,其中的do … endeach方法的参数,称作Code Block,是一个匿名函式(anonymous function)。范例程式如下:

    1. languages = ['Ruby', 'Javascript', 'Perl']
    2. languages.each do |lang|
    3. puts "I love #{lang}!"
    4. end
    5. # I Love Ruby!
    6. # I Love Javascript!
    7. # I Love Perl!

    其中两个直线|中间的lang被称作Block variable区块变量,每次迭代都会被设定成不同元素。其他迭代器范例如:

    1. # 反复三次
    2. 3.times do
    3. puts 'Good Job!'
    4. end
    5. # Good Job!
    6. # Good Job!
    7. # Good Job!
    8. # 从一数到九
    9. 1.upto(9) do |x|
    10. puts x
    11. end
    12. # 多一个索引区块变量
    13. languages = ['Ruby', 'Javascript', 'Perl']
    14. languages.each_with_index do |lang, i|
    15. puts "#{i}, I love #{lang}!"
    16. end
    17. # 0, I Love Ruby!
    18. # 1, I Love Javascript!
    19. # 2, I Love Perl!

    (Code block)的形式除了do … end,也可以改用大括号。通常单行会会用大括号,多行会用do … end的形式。

    1. 3.times { puts "Hello" }

    透过迭代器,我们就比较少用到whileuntilfor等循环语法了。

    其他迭代方式范例

    1. # 迭代并造出另一个阵列
    2. a = ["a", "b", "c", "d"]
    3. b = a.map {|x| x + "!" }
    4. puts b.inspect
    5. # 结果是 ["a!", "b!", "c!", "d!"]
    6. # 找出符合条件的值
    7. b = [1, 2, 3].find_all{ |x| x % 2 == 0 }
    8. b.inspect
    9. # 结果是 [2]
    10. # 迭代并根据条件删除
    11. a = [51, 101, 256]
    12. a.delete_if {|x| x >= 100 }
    13. # 结果是 [51]
    14. # 客制化排序
    15. [2, 1, 3].sort! { |a, b| b <=> a }
    16. # 结果是 [3, 2, 1]
    17. # 计算总和
    18. (5..10).inject {|sum, n| sum + n }
    19. # 结果是 45
    20. # 找出最长字串find the longest word
    21. longest = ["cat", "sheep", "bear"].inject do |memo, word|
    22. ( memo.length > word.length ) ? memo : word
    23. end
    24. # 结果是 "sheep"

    仅执行一次呼叫

    除了迭代,Code block只会执行一次的特性也很有用,例如用来开启档案。往常我们在档案处理完毕之后,会使用close方法关闭:

    1. file = File.new("testfile", "r")
    2. # ...处理档案
    3. file.close

    改用Code block语法之后,Ruby就会在Code block结束后自动关档:

    1. File.open("testfile", "r") do |file|
    2. # ...处理档案
    3. end
    4. # 档案自动关闭

    Code block的这个特性不只让你少打close方法,更可以避免你忘记关闭档案(不然就语法错误了),也有视觉上缩排的好处。

    Yield

    在方法中使用yield可以执行Code block参数:

    1. # 定义方法
    2. def call_block
    3. puts "Start"
    4. yield
    5. yield
    6. puts "End"
    7. end
    8. call_block { puts "Blocks are cool!" }
    9. # 输出
    10. # "Start"
    11. # "Blocks are cool!"
    12. # "Blocks are cool!"
    13. # "End"

    带有参数的Code block

    1. def call_block
    2. yield(1)
    3. yield(2)
    4. yield(3)
    5. end
    6. call_block { |i|
    7. puts "#{i}: Blocks are cool!"
    8. }
    9. # 输出
    10. # "1: Blocks are cool!"
    11. # "2: Blocks are cool!"
    12. # "3: Blocks are cool!"

    可以将Code block明确转成一个变量:

    1. def call_block(&block)
    2. block.call(1)
    3. block.call(2)
    4. block.call(3)
    5. end
    6. call_block { |i| puts "#{i}: Blocks are cool!" }
    7. # 输出
    8. # "1: Blocks are cool!"
    9. # "2: Blocks are cool!"
    10. # "3: Blocks are cool!"
    11. # 或是先宣告出 proc object (有三种写法,大同小异)
    12. proc_1 = Proc.new { |i| puts "#{i}: Blocks are cool!" }
    13. proc_2 = lambda { |i| puts "#{i}: Blocks are cool!" }
    14. proc_3 = -> (i) { puts "#{i}: Blocks are cool!" }
    15. call_block(&proc_1)
    16. call_block(&proc_2)
    17. call_block(&proc_3)
    18. # 分别输出
    19. # "1: Blocks are cool!"
    20. # "2: Blocks are cool!"
    21. # "3: Blocks are cool!"

    传递不定参数

    1. def my_sum(*val)
    2. val.inject { |sum, v| sum + v }
    3. end
    4. puts my_sum(1, 2, 3, 4) # val 变量就是 [1, 2, 3, 4]
    5. # 输出 10

    其中my_sum方法中的val是一个包含所有参数的阵列。

    参数尾巴的Hash可以省略{ }

    1. def my_print(a, b, options)
    2. puts a
    3. puts b
    4. puts options[:x]
    5. puts options[:y]
    6. puts options[:z]
    7. end
    8. my_print("A", "B", { :x => 123, :z => 456 } )
    9. my_print("A", "B", :x => 123, :z => 456) # 结果相同
    10. # 输出 A
    11. # 输出 B
    12. # 输出 123
    13. # 输出 nil
    14. # 输出 456

    例外处理

    使用rescue可以将例外救回来:

    1. begin
    2. puts 10 / 0 # 这会丢出 ZeroDivisionError 的例外错误
    3. rescue => e
    4. puts e.class # 如果发生例外会执行 rescue 这一段
    5. ensure
    6. # 无论有没有发生例外,ensure 这一段都一定会执行
    7. end
    8. # 输出 ZeroDivisionError

    使用raise可以手动触发例外错误:

    1. raise "Not works!!"
    2. # 丢出一个 RuntimeError
    3. # 自行自定例外物件
    4. class MyException < RuntimeError
    5. end
    6. raise MyException

    Metaprogramming用程式写程式

    Metaprogramming是很进阶的技巧,这里示范define_method方法可以动态定义方法:

    1. class Dragon
    2. define_method(:foo) { puts "bar" }
    3. ['a', 'b', 'c', 'd', 'e', 'f'].each do |x|
    4. define_method(x) { puts x }
    5. end
    6. end
    7. dragon = Dragon.new
    8. dragon.foo # 输出 bar
    9. dragon.a # 输出 a
    10. dragon.f # 输出 f

    Introspection反射机制

    Ruby拥有许多反射方法,可以动态知道物件的资讯:

    1. # 这个物件有什么方法
    2. Object.methods
    3. => ["send", "name", "class_eval", "object_id", "new", "singleton_methods", ...]
    4. # 这个物件有这个方法吗?
    5. Object.respond_to? :name
    6. => true

    其他常见惯例

    如果resultnilfalse的话,将a指派给result,如果不是的话,什么都不做。以上这段程式等同于

    1. result || ( result = a )

    Ruby 应用

    除了本书介绍的Ruby on Rails之外,Ruby也有各式各样的应用,以下兹举一些使用Ruby发展的专案:

    • Sinatra:轻量级的Web框架
    • 网页设计
      • :CSS Pre-Processor
      • Less:CSS Pre-Processor
      • :CSS 设计框架
      • Middleman: 静态网站产生工具
      • : 静态网站和Blog产生工具
    • 自动化测试
      • Cucumber:BDD 测试框架
      • :自动化浏览器测试工具
    • DevOps
      • Chef:服务器部署工具
      • :服务器部署工具
      • Vagrant:虚拟机(VM)工具
    • iOS/Mac
      • :Objective-C 的套件管理工具
      • RubyMotion是由_Objective-C实作的Ruby,运作在iOSMac OS X作业系统上,也可以在_App Store*上架。这个平台需要商业收费。
    • :专案管理系统

    你可以在The Ruby Toolbox找到更多推荐的Ruby套件和应用。