- Ruby程式语言入门
- Ruby 程式语言入门
- 各种Ruby实作
- IRB(Interactive Ruby)
- 程式语言分类
- 整数Integer
- 浮点数Float
- 字串String
- Ruby完全地物件导向
- 区域变量Local Variable
- 型别转换Conversions
- 常数Constant
- 空值nil
- 注解
- 字串符号Symbols
- 阵列Array
- 杂凑Hash
- 流程控制Flow Control
- 正规表示法Regular Expressions
- 方法定义Methods
- ?与!的惯例
- 物件导向
- 循环走访与迭代器Iterator
- 传递不定参数
- 参数尾巴的Hash可以省略{ }
- 例外处理
- Metaprogramming用程式写程式
- 其他常见惯例
- Ruby 应用
- 课程投影片
Ruby程式语言入门
Ruby 程式语言入门
Actually, I’m trying to make Ruby natural, not simple. Ruby is simple in appearance, but is very complex inside, just like our human body. - Matz, Ruby 发明人
Ruby 是个美丽、灵巧而且方便又实用的程式语言,而 Ruby on Rails 正是 Ruby 程式语言爆发成长的催化剂。在了解 Ruby on Rails 的程式之前,学习 Ruby 程式语言是最重要的基础功课之一,我们在这一章节将快速带过一些基本的语法,网络上也有 Ruby Taiwan 社群所翻译的文章可以一读:
- Ruby 中文官方网站:Ruby简介
- Ruby 中文官方网站:二十分钟Ruby体验
- Ruby 中文官方网站:从其他程式语言到Ruby
- Ruby使用手册
免费的英文资源也很多,我推荐以下三个教学网站作为练习之用:
- Introduction to Programming with Ruby by Tealeaf Academy 和 Object Oriented Programming with Ruby 除了入门的内容,还包括完整练习题和解答
- Learn Ruby the Hard Way 以习题为主的教材
- Codecademy 从做中学的线上互动教学
各种Ruby实作
除了用 C 语言实作的官方版本 Ruby(又叫做 CRuby 或 MRI, Matz’s Ruby Interpreter),也有其他不同实作的 Ruby 环境。这些实作都以 RubySpec 作为其语法的标准:
- JRuby 是由 Java 实作的 Ruby ,运行在高效能、支援系统执行绪及有非常多函数库的 Java 虚拟机器 (JVM) 上。 JRuby 算是目前 Ruby 要开发跨平台(Windows 、 Mac 和 Linux)桌面软件最好的选择。
- Rubinuis 是用 C++ 、 Ruby 和 LLVM 编译器技术实作的 Ruby VM ,可以在 Mac OS X 、 Debian/Ubuntu 、 FreeBSD 、 Windows 上执行。 LLVM 可以说是当代最重要的编译器架构,拥有各种编译器最佳化技术。能给 Ruby 带来多少效能改善幅度,值得关注。
IRB(Interactive Ruby)
IRB 是一个互动的 Ruby 环境,可以让我们练习和语法,做些简单的实验。请输入irb就会进入互动模式:
$ irb
irb: Interactive Ruby
irb(main):001:0>
irb(main):001:0> 1 + 1
=> 2
在irb之中,每行执行完 Ruby 都会自动帮你puts输出结果。
不过,一旦程式稍微复杂一点,还是打开文字编辑器吧。让我们编辑一个档案 hello.rb 。 Ruby 脚本附档名的惯例是 .rb ,内容如下:
puts "Hello, World!!"
存盘后,输入:
$ ruby hello.rb
就会执行这个脚本了,它会在萤幕上输出 Hello, World!! 。
程式语言分类
根据需不需要事先宣告变量型别,我们可以分类出静态分型 (Static typing)与动态分型 (Dynamic typing) 程式语言,前者例如 Java 、 C 、 C++ ,后者例如 Ruby 、 Perl 、 Python 和 PHP 。根据会不会隐性自动转换型别,又可以区分出不会自动转换型别的强分型 (Strong typing) 与自动转换型别的弱分型 (Weak typing) ,前者例如 Ruby 、 Perl 、 Python 、 Java ,后者例如 PHP 、 C 、 C++ 是弱分型。让我们举个例吧:
/* PHP */
$i = 1;
echo "Value is " . $i ;
## Value is 1
/* C */
int a = 5;
float b = a;
以上的 PHP 和 C 会隐性地自动转型,但是以下的 Ruby 程式会检查型别不相配而发生错误,这一点从 PHP 过来的朋友要特别注意。
## Ruby
i = 1
puts "Value is " + i
## TypeError: can't convert Fixnum into String
## from (irb):2:in `+'
## from (irb):2
另外,通常动态分型的程式语言多半也是直译式 (interpreted) 程式语言,也就是不需要事先编译,透过直译器 (interpreter) 执行即可,当然 Ruby 也不例外。相对的,编译式 (compiled) 语言则是事先编译成执行档才行执行。总结以上, Ruby 是个动态强分型的直译式程式语言。
整数Integer
任何整数都是 Fixnum 物件:
5
-205
9999999999
0
完整的 Fixnum API 请参考 Ruby doc 文件。
浮点数Float
中间带有点号的就是浮点数 Float 物件:
54.321
0.001
-12.312
0.0
浮点数四则运算范例如下:
puts 1.0 + 2.0
puts 2.0 * 3.0
puts 5.0 - 8.0
puts 9.0 / 2.0
## 3.0
## 6.0
## -3.0
## 4.5
要注意的是,整数四则运算结果,也会是整数:
puts 1 + 2
puts 2 * 3
puts 5 - 8
puts 9 / 2
## 3
## 6
## -3
## 4
以下是一个更复杂的四则运算例子:
puts 5 * (12 - 8) + -15
puts 98 + (59872 / (13*8)) * -52
完整的 Float API 请参考 Ruby doc 文件。
字串String
使用单引号或双引号括起来的是字串 String 物件:
puts 'Hello, world!'
puts ''
puts 'Good-bye.'
字串相加可以使用加号,要注意的是字串不能直接跟数字相加,会发生例外错误:
puts 'I like ' + 'apple pie.'
puts 'You\'re smart!'
puts '12' + 12
#<TypeError: can't convert Fixnum into String>
更多字串方法示范:
var1 = 'stop'
var2 = 'foobar'
var3 = "aAbBcC"
puts var1.reverse # pots
puts var2.length # 6
puts var3.upcase # AABBCC
puts var3.downcase # aabbcc
为了方便字串的组合, Ruby 也支持内插的方式:
verb = 'work'
where = 'office'
puts "I #{verb} at the #{where}" # 输出 I work at the office
注意到使用双引号(“)的字串才会进行内插处理。如果换成单引号(‘):
puts 'I #{verb} at the #{where}' # 输出 I #{verb} at the #{where}
完整的 String API 请参考 Ruby String API 文件。
Ruby完全地物件导向
你可能已经注意到,在 Ruby 里每样东西都是物件,包括字串和数字。所有的方法都是对物件呼叫,你不会看到全域函式,例如 PHP 的strlen("test")用法,在 Ruby 中是"test".length。
## 输出「UPPER」
puts "upper".upcase
## 输出 -5 的绝对值
puts -5.abs
## 输出 Fixnum 类别
puts 99.class
## 输出五次「Ruby Rocks!」
5.times do
puts "Ruby Rocks!"
end
区域变量Local Variable
区域变量使用小写开头,偏好单字之间以底线_来分隔。范例如下:
composer = 'Mozart'
puts composer + ' was "da bomb", in his day.'
my_composer = 'Beethoven'
puts 'But I prefer ' + my_composer + ', personally.'
如果存取一个尚未初始过的区域变量,会得到以下错误:
NameError: undefined local variable or method `qwer' for main:Object
from (irb):1
from /Users/ihower/.rvm/rubies/ruby-2.1.5/bin/irb:11:in `<main>'
型别转换Conversions
刚刚提到数字和字串物件不能直接相加,你必须使用to_s(转成字串)、to_i(转成整数)或to_f(转成浮点数)来手动转型,范例如下:
var1 = 2
var2 = '5'
puts var1.to_s + var2 # 25
puts var1 + var2.to_i # 7
puts 9.to_f / 2 # 4.5
常数Constant
大写开头的是为常数,范例如下:
Foo = 1
Foo = 2 # (irb):3: warning: already initialized constant Foo
RUBY_PLATFORM # => "x86_64-darwin10.7.0"
ENV # => { "PATH" => "....", "LC_ALL" => "zh_TW.UTF-8" }
空值nil
表示未设定值、未定义的状态:
nil # nil
nil.class # NilClass
nil.nil? # true
42.nil? # false
nil == nil # true
false == nil # false
注解
Ruby 偏好一律使用单行注解:
## this is a comment line
## this is a comment line
多行注解比较少见:
=begin
This is a comment line
This is a comment line
=end
字串符号Symbols
Symbol 是唯一且不会变动的识别名称,用冒号开头:
:this_is_a_symbol
为什么不就用字串呢?这是因为相同名称的 Symbol 不会再重复建构物件,所以使用 Symbol 可以执行的更有效率。范例如下:
puts "foobar".object_id # 输出 2151854740
puts "foobar".object_id # 输出 2151830100
puts :foobar.object_id # 输出 577768
puts :foobar.object_id # 输出 577768
object _id_ 方法会回传_Ruby 内部的内存配置编号。你会发现两个字串就算内容相同,也是不同的物件。但是 Symbol 只要内容相同,就是相同的物件。这种特性让 Symbol 的主要用途是作为杂凑 Hash 的键(Key),一会就会介绍到。
阵列Array
使用中括号,索引从0开始。注意到阵列中的元素是不限同一类别,想放什么都可以:
a = [ 1, "cat", 3.14 ]
puts a[0] # 输出 1
puts a.size # 输出 3
a[2] = nil
puts a.inspect # 输出 [1, "cat", nil]
a[99] # nil
inspect方法会将物件转成适合给人看的字串
如果读取一个没有设定的阵列元素,默认值是nil。更多阵列方法范例:
colors = ["red", "blue"]
colors.push("black")
colors << "white"
puts colors.join(", ") # red, blue, black, white
colors.pop
puts colors.last #black
使用each方法走访阵列:
languages = ['Ruby', 'Javascript', 'Perl']
languages.each do |lang|
puts 'I love ' + lang + '!'
end
## I Love Ruby!
## I Love Javascript!
## I Love Perl!
完整的 Array API 请参考 _[Ruby Array API(<http://www.ruby-doc.org/core/classes/Array.html>)_文件。
杂凑Hash
Hash 是一种 键值对(Key-Value) 的资料结构,虽然你可以使用任何物件当作 Key ,但是通常我们使用 Symbol 当作 Key 。例如:
config = { :foo => 123, :bar => 456 }
puts config[:foo] # 输出 123
config["nothing"] # 是 nil
在 Ruby 1.9 后支援新的语法,比较简约:
config = { foo: 123, bar: 456 } # 等同于 { :foo => 123, :bar => 456 }
如果读取一个不存在的值,例如上述范例的nothing,默认值是nil。
使用each方法可以走访杂凑:
config = { :foo => 123, :bar => 456 }
config.each do |key, value|
puts "#{key} is #{value}"
end
## foo is 123
## bar is 456
完整的 Hash API 请参考 Ruby Hash API 文件。
流程控制Flow Control
让我们来看看一些流程控制:
比较方法
puts 1 > 2 # 大于
puts 1 < 2 # 小于
puts 5 >= 5 # 大于等于
puts 5 <= 4 # 小于等于
puts 1 == 1 # 等于
puts 2 != 1 # 不等于
puts ( 2 > 1 ) && ( 2 > 3 ) # 和
puts ( 2 > 1 ) || ( 2 > 3 ) # 或
控制结构If
else if 写成elsif:
total = 26000
if total > 100000
puts "large account"
elsif total > 25000
puts "medium account"
else
puts "small account"
end
另外如果要执行的if程式只有一行,可以将if放到行末即可:
puts "greater than ten" if total > 10
三元运算子
三元运算子expression ? true _expresion : false_expression_ 可以让我们处理简易的_if else 条件,例如以下的程式:
x = 3
if x > 3
y = "foo"
else
y = "bar"
end
改用三元运算子之后,可以缩减程式行数:
x = 3
y = ( x > 3 ) ? "foo" : "bar"
控制结构Case
case name
when "John"
puts "Howdy John!"
when "Ryan"
puts "Whatz up Ryan!"
else
puts "Hi #{name}!"
end
循环 while, loop, until, next and break
while 用法范例:
i=0
while ( i < 10 )
i += 1
next if i % 2 == 0 #跳过双数
end
until 用法范例:
i = 0
i += 1 until i > 10
puts i
## 输出 11
loop 用法范例:
i = 0
loop do
i += 1
break if i > 10 # 中断循环
end
不过你很快就会发现写 Ruby 很少用到 while 、 until 、 loop ,我们会使用迭代器。
真或假
记住,只有false和nil是假,其他都为真。
puts "not execute" if nil
puts "not execute" if false
puts "execute" if true # 输出 execute
puts "execute" if “” # 输出 execute (和JavaScript不同)
puts "execute" if 0 # 输出 execute (和C不同)
puts "execute" if 1 # 输出 execute
puts "execute" if "foo" # 输出 execute
puts "execute" if Array.new # 输出 execute
正规表示法Regular Expressions
与 Perl 类似的语法,使用=~:
## 抓出手机号码
phone = "123-456-7890"
if phone =~ /(\d{3})-(\d{3})-(\d{4})/
ext = $1
city = $2
num = $3
end
方法定义Methods
使用def开头end结尾来定义一个方法:
def say_hello(name)
result = "Hi, " + name
return result
end
puts say_hello('ihower')
## 输出 Hi, ihower
方法中的return是可以省略的, Ruby 就会回传最后一行运算的值。上述方法可以改写成:
def say_hello(name)
"Hi, " + name
end
呼叫方法时,括号也是可以省略的,例如:
say_hello 'ihower'
不过,除了一些方法惯例不加之外(例如puts和 Rails 中的redirect_to、render方法),绝大部分的情况加上括号比较无疑义。
我们也可以给参数默认值:
def say_hello(name = "nobody")
result = "Hi, " + name
return result
end
puts say_hello
## 输出 Hi, nobody
?与!的惯例
方法名称可以用?或!结尾,前者表示会回传 Boolean 值,后者暗示会有某种副作用 (side-effect) 。范例如下:
array=[2,1,3]
array.empty? # false
array.sort # [1,2,3]
array.inspect # [2,1,3]
array.sort! # [1,2,3]
array.inspect # [1,2,3]
物件导向
物件导向 (Object-Oriented Programming) 一种将「资料」和「方法」封装到物件的设计方式,我们定义「类别 Class」,然后依此产生出「物件 Object」,类别可说是物件的样板。
Ruby 的类别其实也是一种常数,所以也是大写开头,使用new方法可以建立出物件,例如之前所学的字串、阵列和杂凑,也可以用以下方式建立:
color_string = String.new
color_string = "" # 等同
color_array = Array.new
color_array = [] # 等同
color_hash = Hash.new
color_hash = {} # 等同
time = Time.new # 内建的时间类别
puts time
来看看如何自定类别:
class Person # 大写开头的常数
def initialize(name) # 建构式
@name = name # 物件变量
end
def say(word)
puts "#{word}, #{@name}" # 字串相加
end
end
p1 = Person.new("ihower")
p2 = Person.new("ihover")
p1.say("Hello") # 输出 Hello, ihower
p2.say("Hello") # 输出 Hello, ihover
注意到双引号里的字串可以使用
#{var}来做字串嵌入,相较起用加号+相加字串可以更有效率。
除了物件方法与物件变量, Ruby 也有属于类别的方法和变量:
class Person
@@name = “ihower” # 类别变量
def self.say # 类别方法
puts @@name
end
end
Person.say # 输出 ihower
资料封装
所有的物件变量(@开头)、类别变量(@@开头),都是封装在类别内部的,类别外无法存取:
class Person
def initialize(name)
@name = name
end
end
p = Person.new('ihower')
p.name # 出现 NoMethodError 错误
p.name = 'peny' # 出现 NoMethodError 错误
为了可以存取到@name,我们必须定义方法:
class Person
def initialize(name)
@name = name
end
def name
@name
end
def name=(name)
@name = name
end
end
p = Person.new('ihower')
=> #<Person:0x007fe9e408b8f0 @name="ihower">
p.name
=> "ihower"
p.name="peny"
=> "peny"
p.name
=> "peny"
p
=> #<Person:0x007fe9e408b8f0 @name="peny">
类别Class定义范围内也可以执行程式
跟其他程式语言不太一样, Ruby 的类别层级内也可以执行程式,例如以下:
class Demo
puts "foobar"
end
当你加载这个类别的时候,就会执行puts "foobar"输出 foobar 。会放在这里的程式,主要的用途是来做 Meta-programming 。例如,上述定义物件变量的存取方法实在太常见了,因此 Ruby 提供了attr_accessor、attr_writer、attr_reader类别方法可以直接定义这些方法。上述的程式可以改写成:
class Person
attr_accessor :name
def initialize(name)
@name = name
end
end
p = Person.new('ihower')
=> #<Person:0x007fe9e3094410 @name="ihower">
p.name
=> "ihower"
p.name="peny"
=> "peny"
p.name
=> "peny"
p
=> #<Person:0x007fe9e3094410 @name="peny">
这里的attr_accessor其实就是一个类别方法。
方法封装
类别中的方法默认是public的,宣告private或protected的话,该行以下的方法就会套用:
class MyClass
def public_method
end
private
def private_method_1
end
def private_method_2
end
protected
def protected_method
end
end
Ruby 的 private 和 protected 定义和其他程式语言不同,都是可以在整个继承体系内呼叫。两着差别在于 private 只有在物件内部才能呼叫,默认的接收者 (receiver) 就是物件本身,也就是 self 。而 protected 方法除了可以在本身内部呼叫以外,还可以被子类别的物件、或是另一个相同类别的物件呼叫。
在物件导向的术语中,
object.call _method_的意思是 _object 收到执行 call _method 的指令,也就是 object 是 call_method 方法的接受者 (receiver) 。因此,你甚至可以改写成object.__send__(:call_method)
Class 继承
Ruby 使用小于<符号代表类别继承:
class Pet
attr_accessor :name, :age
def say(word)
puts "Say: #{word}"
end
end
class Cat < Pet
def say(word)
puts "Meow~"
super
end
end
class Dog < Pet
def say(word, person)
puts "Bark at #{person}!"
super(word)
end
end
Cat.new.say("Hi")
Dog.new.say("Hi", "ihower")
输出
Meow~
Say: Hi
Bark at ihower!
Say: Hi
这个范例中,Cat和Dog子类别复写了 Pet say 方法,其中的super是用来呼叫被复写掉的 Pet say 方法。另外,没有括号的super和有括号的super()是有差异的,前者 Ruby 会自动将所有参数都代进去来呼叫父类别的方法,后者则是自己指定参数。此例中如果 Dog say 里只写super,则会发生 wrong number of arguments 的错误,这是因为 Ruby 会传say("Hi", "ihower")给 Pet say 而发生错误。
Module
Module 是 Ruby 一个非常好用的功能,它跟 Class 类别非常相似,你可以在里面定义方法。只是你不能用 new 来建立它。它的第一个用途是可以当做 Namespace 来放一些工具方法:
module MyUtil
def self.foobar
puts "foobar"
end
end
MyUtil.foobar
## 输出 foobar
另一个更重要的功能是 Mixins ,可以将一个 Module 混入类别之中,这样这个类别就会拥有此 Module 的方法。这回让我们拆成两个档案, debug.rb 和 foobar.rb ,然后在 foobar.rb 中用require来引用 debug.rb :
首先是 debug.rb
module Debug
def who_am_i?
puts "#{self.class.name}: #{self.inspect}"
end
end
然后是 foobar.rb
require "./debug"
class Foo
include Debug # 这个动作叫做 Mixin
end
class Bar
include Debug
end
f = Foo.new
b = Bar.new
f.who_am_i? # 输出 Foo: #<Foo:0x00000102829170>
b.who_am_i? # 输出 Bar: #<Bar:0x00000102825b88>
Ruby 使用 Module 来解决多重继承的问题,不同类别之间但是拥有相同的方法,就可以改放在 Module 里面,然后include它即可。
循环走访与迭代器Iterator
不同于while循环用法, Ruby 习惯使用迭代器 (Iterator) 来走访循环,例如each是一个阵列的方法,它会走访其中的元素,其中的do … end是each方法的参数,称作 Code Block ,是一个匿名函式 (anonymous function) 。范例程式如下:
languages = ['Ruby', 'Javascript', 'Perl']
languages.each do |lang|
puts "I love #{lang}!"
end
## I Love Ruby!
## I Love Javascript!
## I Love Perl!
其中两个直线|中间的 lang 被称作 Block variable 区块变量,每次迭代都会被设定成不同元素。其他迭代器范例如:
## 反复三次
3.times do
puts 'Good Job!'
end
## Good Job!
## Good Job!
## Good Job!
## 从一数到九
1.upto(9) do |x|
puts x
end
## 多一个索引区块变量
languages = ['Ruby', 'Javascript', 'Perl']
languages.each_with_index do |lang, i|
puts "#{i}, I love #{lang}!"
end
## 0, I Love Ruby!
## 1, I Love Javascript!
## 2, I Love Perl!
(Code block) 的形式除了do … end,也可以改用大括号。通常单行会会用大括号,多行会用do … end的形式。
3.times { puts "Hello" }
透过迭代器,我们就比较少用到while、until、for等循环语法了。
其他迭代方式范例
## 迭代并造出另一个阵列
a = ["a", "b", "c", "d"]
b = a.map {|x| x + "!" }
puts b.inspect
## 结果是 ["a!", "b!", "c!", "d!"]
## 找出符合条件的值
b = [1, 2, 3].find_all{ |x| x % 2 == 0 }
b.inspect
## 结果是 [2]
## 迭代并根据条件删除
a = [51, 101, 256]
a.delete_if {|x| x >= 100 }
## 结果是 [51]
## 客制化排序
[2, 1, 3].sort! { |a, b| b <=> a }
## 结果是 [3, 2, 1]
## 计算总和
(5..10).inject {|sum, n| sum + n }
## 结果是 45
## 找出最长字串find the longest word
longest = ["cat", "sheep", "bear"].inject do |memo, word|
( memo.length > word.length ) ? memo : word
end
## 结果是 "sheep"
<=>是比较运算子,当两个数字相等于回传0,第一个数字较大时回传1,反之回传-1
仅执行一次呼叫
除了迭代, Code block 只会执行一次的特性也很有用,例如用来开启档案。往常我们在档案处理完毕之后,会使用close方法关闭:
file = File.new("testfile", "r")
## ...处理档案
file.close
改用 Code block 语法之后, Ruby 就会在 Code block 结束后自动关档:
File.open("testfile", "r") do |file|
# ...处理档案
end
## 档案自动关闭
Code block 的这个特性不只让你少打close方法,更可以避免你忘记关闭档案(不然就语法错误了),也有视觉上缩排的好处。
Yield
在方法中使用yield可以执行 Code block 参数:
## 定义方法
def call_block
puts "Start"
yield
yield
puts "End"
end
call_block { puts "Blocks are cool!" }
## 输出
## "Start"
## "Blocks are cool!"
## "Blocks are cool!"
## "End"
带有参数的Code block
def call_block
yield(1)
yield(2)
yield(3)
end
call_block { |i|
puts "#{i}: Blocks are cool!"
}
## 输出
## "1: Blocks are cool!"
## "2: Blocks are cool!"
## "3: Blocks are cool!"
Proc object
可以将 Code block 明确转成一个变量:
def call_block(&block)
block.call(1)
block.call(2)
block.call(3)
end
call_block { |i| puts "#{i}: Blocks are cool!" }
## 输出
## "1: Blocks are cool!"
## "2: Blocks are cool!"
## "3: Blocks are cool!"
## 或是先宣告出 proc object (有三种写法,大同小异)
proc_1 = Proc.new { |i| puts "#{i}: Blocks are cool!" }
proc_2 = lambda { |i| puts "#{i}: Blocks are cool!" }
proc_3 = -> (i) { puts "#{i}: Blocks are cool!" }
call_block(&proc_1)
call_block(&proc_2)
call_block(&proc_3)
## 分别输出
## "1: Blocks are cool!"
## "2: Blocks are cool!"
## "3: Blocks are cool!"
传递不定参数
def my_sum(*val)
val.inject { |sum, v| sum + v }
end
puts my_sum(1, 2, 3, 4) # val 变量就是 [1, 2, 3, 4]
## 输出 10
其中 my_sum 方法中的val是一个包含所有参数的阵列。
参数尾巴的Hash可以省略{ }
def my_print(a, b, options)
puts a
puts b
puts options[:x]
puts options[:y]
puts options[:z]
end
my_print("A", "B", { :x => 123, :z => 456 } )
my_print("A", "B", :x => 123, :z => 456) # 结果相同
## 输出 A
## 输出 B
## 输出 123
## 输出 nil
## 输出 456
例外处理
使用 rescue 可以将例外救回来:
begin
puts 10 / 0 # 这会丢出 ZeroDivisionError 的例外错误
rescue => e
puts e.class # 如果发生例外会执行 rescue 这一段
ensure
# 无论有没有发生例外,ensure 这一段都一定会执行
end
## 输出 ZeroDivisionError
使用 raise 可以手动触发例外错误:
raise "Not works!!"
## 丢出一个 RuntimeError
## 自行自定例外物件
class MyException < RuntimeError
end
raise MyException
Metaprogramming用程式写程式
Metaprogramming 是很进阶的技巧,这里示范define_method方法可以动态定义方法:
class Dragon
define_method(:foo) { puts "bar" }
['a', 'b', 'c', 'd', 'e', 'f'].each do |x|
define_method(x) { puts x }
end
end
dragon = Dragon.new
dragon.foo # 输出 bar
dragon.a # 输出 a
dragon.f # 输出 f
Introspection反射机制
Ruby 拥有许多反射方法,可以动态知道物件的资讯:
## 这个物件有什么方法
Object.methods
=> ["send", "name", "class_eval", "object_id", "new", "singleton_methods", ...]
## 这个物件有这个方法吗?
Object.respond_to? :name
=> true
其他常见惯例
result ||= a
如果result是nil或false的话,将a指派给result,如果不是的话,什么都不做。以上这段程式等同于
result || ( result = a )
Ruby 应用
除了本书介绍的 Ruby on Rails 之外, Ruby 也有各式各样的应用,以下兹举一些使用 Ruby 发展的专案:
- Sinatra:轻量级的 Web 框架
- 网页设计
- 自动化测试
- DevOps
- iOS/Mac
- CocoaPods:Objective-C 的套件管理工具
- RubyMotion 是由_Objective-C 实作的 Ruby ,运作在 iOS 和 Mac OS X 作业系统上,也可以在_App Store*上架。这个平台需要商业收费。
- Redmine:专案管理系统
你可以在 The Ruby Toolbox 和 Awesome Ruby 找到更多推荐的 Ruby 套件和应用。
