Ruby on Rails 實戰聖經

使用 Rails 5.0+ 及 Ruby 2.3+

電子書製作中,歡迎留下 E-mail,有消息將會通知您。若您有任何意見、鼓勵或勘誤,也歡迎來信給我。願意贊助支持的話,這是我的微信 QR Code,謝謝。

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實作

除了用C語言實作的官方版本Ruby(又叫做CRubyMRI, Matz’s Ruby Interpreter),也有其他不同實作的Ruby環境。這些實作都以RubySpec作為其語法的標準:

  • JRuby是由Java實作的Ruby,運行在高效能、支援系統執行緒及有非常多函數庫的Java虛擬機器(JVM)上。JRuby算是目前Ruby要開發跨平台(WindowsMacLinux)桌面軟體最好的選擇。
  • Rubinuis是用C++RubyLLVM編譯器技術實作的Ruby VM,可以在Mac OS XDebian/UbuntuFreeBSDWindows上執行。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.rbRuby腳本附檔名的慣例是.rb,內容如下:

puts "Hello, World!!"

存檔後,輸入:

$ ruby hello.rb

就會執行這個腳本了,它會在螢幕上輸出Hello, World!!

程式語言分類

根據需不需要事先宣告變數型別,我們可以分類出靜態分型(Static typing)與動態分型(Dynamic typing)程式語言,前者例如JavaCC++,後者例如RubyPerlPythonPHP。根據會不會隱性自動轉換型別,又可以區分出不會自動轉換型別的強分型(Strong typing)與自動轉換型別的弱分型(Weak typing),前者例如RubyPerlPythonJava,後者例如PHPCC++是弱分型。讓我們舉個例吧:

/* PHP */
$i = 1;
echo "Value is " . $i ;
# Value is 1

/* C */
int a = 5;
float b = a;

以上的PHPC會隱性地自動轉型,但是以下的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裡每樣東西都是物件,包括字串和數字。所有的方法都是對物件呼叫,你不會看到全域函式,例如PHPstrlen("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很少用到whileuntilloop,我們會使用迭代器。

真或假

記住,只有falsenil是假,其他都為真。

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'

不過,除了一些方法慣例不加之外(例如putsRails中的redirect_torender方法),絕大部分的情況加上括號比較無疑義。

我們也可以給參數預設值:

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_accessorattr_writerattr_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的,宣告privateprotected的話,該行以下的方法就會套用:

class MyClass

  def public_method
  end

  private

  def private_method_1
  end

  def private_method_2
  end

  protected

  def protected_method
  end

end

Rubyprivateprotected定義和其他程式語言不同,都是可以在整個繼承體系內呼叫。兩著差別在於private只有在物件內部才能呼叫,預設的接收者(receiver)就是物件本身,也就是self。而protected方法除了可以在本身內部呼叫以外,還可以被子類別的物件、或是另一個相同類別的物件呼叫。

在物件導向的術語中,object.call_method的意思是object收到執行call_method的指令,也就是objectcall_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

這個範例中,CatDog子類別覆寫了Pet say方法,其中的super是用來呼叫被覆寫掉的Pet say方法。另外,沒有括號的super和有括號的super()是有差異的,前者Ruby會自動將所有參數都代進去來呼叫父類別的方法,後者則是自己指定參數。此例中如果Dog say裡只寫super,則會發生wrong number of arguments的錯誤,這是因為Ruby會傳say("Hi", "ihower")Pet say而發生錯誤。

Module

ModuleRuby一個非常好用的功能,它跟Class類別非常相似,你可以在裡面定義方法。只是你不能用new來建立它。它的第一個用途是可以當做Namespace來放一些工具方法:

module MyUtil

  def self.foobar
    puts "foobar"
  end

end

MyUtil.foobar
# 輸出 foobar

另一個更重要的功能是Mixins,可以將一個Module混入類別之中,這樣這個類別就會擁有此Module的方法。這回讓我們拆成兩個檔案,debug.rbfoobar.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 ... endeach方法的參數,稱作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" }

透過迭代器,我們就比較少用到whileuntilfor等迴圈語法了。

其他迭代方式範例

# 迭代並造出另一個陣列
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

如果resultnilfalse的話,將a指派給result,如果不是的話,什麼都不做。以上這段程式等同於

result || ( result = a )

Ruby 應用

除了本書介紹的Ruby on Rails之外,Ruby也有各式各樣的應用,以下茲舉一些使用Ruby發展的專案:

  • Sinatra:輕量級的Web框架
  • 網頁設計
    • Sass:CSS Pre-Processor
    • Less:CSS Pre-Processor
    • Compass:CSS 設計框架
    • Middleman: 靜態網站產生工具
    • Jekyll: 靜態網站和Blog產生工具
  • 自動化測試
    • Cucumber:BDD 測試框架
    • Watir:自動化瀏覽器測試工具
  • DevOps
    • Chef:伺服器部署工具
    • Puppet:伺服器部署工具
    • Vagrant:虛擬機(VM)工具
  • iOS/Mac
    • CocoaPods:Objective-C 的套件管理工具
    • RubyMotion 是由Objective-C實作的Ruby,運作在iOSMac OS X作業系統上,也可以在App Store*上架。這個平台需要商業收費。
  • Redmine:專案管理系統

你可以在The Ruby ToolboxAwesome Ruby找到更多推薦的Ruby套件和應用。

課程投影片

》回到頁首