Ruby on Rails 實戰聖經

使用 Rails 5.0+ 及 Ruby 2.3+

電子書製作中,歡迎留下 E-mail,有消息將會通知您。若您有任何意見、鼓勵或勘誤,也歡迎來信給我。願意贊助支持的話,这是我的支付宝微信 和乙太幣 ETH 地址
0x232b7245EBE02900c21682be1e6Ad4e839751F6a

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套件和應用。

課程投影片

》回到頁首