Ruby 程式語⾔言
綜覽簡介
ihower@gmail.com
2013/4
我是誰?
• 張⽂文鈿 a.k.a. ihower
• http://ihower.tw
• http://twitter.com/ihower
• Ruby Developer since 2006
• CTO, Faria Systems Ltd.
• The organizer of RubyConf Taiwan
• http://rubyconf.tw
• http://ruby.tw
Agenda
• 什麼是 Ruby
• 基本語法
• Ruby 的幾個特點
• Ruby 的語⾔言實作
• 各式應⽤用
• ⽣生態圈
什麼是 Ruby?
• 開放原碼、物件導向的動態直譯式
(interpreted)程式語⾔言
• 簡單哲學、⾼高⽣生產⼒力
• 精巧、⾃自然的語法
• 創造者 Yukihiro Matsumoto, a.k.a. Matz
• 靈感來⾃自 Lisp, Perl, 和 Smalltalk
• 設計的⺫⽬目的是要讓程式設計師 Happy
Matz@RubyConf Taiwan 2012
⾺馬斯洛需求層次理論
⽣生理
安全
社交
尊重
⾃自我實現
Happy?
DHH(Rails creator)
http://redmonk.com/sogrady/2012/09/12/language-rankings-9-12/
Stackoverflow 和
Github 的熱⾨門排名
irb: Interactive Ruby
⾺馬上動⼿手練習
irb(main):001:0>
irb(main):001:0> 1 + 1
=> 2
irb(main):002:0>
PUTS 螢幕輸出
• 打開編輯器,編輯 hello.rb
• 執⾏行 ruby hello.rb
puts "Hello World!"
Ruby 是動態強分型語⾔言
• 動態 Dynamic v.s. 靜態 Static typing
• Ruby/Perl/Python/PHP v.s. Java/C/C++
• 強 Strong v.s. 弱 Weak typing
• Ruby/Perl/Python/Java v.s. PHP/C/C++
什麼強?弱?分型
i=1
puts "Value is " + i
#TypeError: can't convert Fixnum into
String
#	 from (irb):2:in `+'
#	 from (irb):2
$i = 1;
echo "Value is " + $i
# 1
PHP code: Ruby code:
int a = 5;
float b = a;
C code:
1. Ruby 基本語法
整數 Integer
5
-205
9999999999
0
浮點數 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
# -1
# 4
更多運算
puts 5 * (12-8) + -15
puts 98 + (59872 / (13*8)) * -52
字串 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
puts var3.downcase
Ruby 完全地物件導向
每樣東⻄西都是物件,包括字串和數字。
# 輸出 "UPPER"
puts "upper".upcase
# 輸出 -5 的絕對值
puts -5.abs
# 輸出 Fixnum
puts 99.class
# 輸出 "Ruby Rocks!" 五次
5.times do
puts "Ruby Rocks!"
end
⽅方法呼叫 Methods
• 所有東⻄西都是物件(object),可以呼叫物
件的⽅方法,例如字串、整數、浮點數。
• 透過逗點 . 來對物件呼叫⽅方法
變數Variable
⼩小寫開頭,偏好單字之間以底線 _ 分隔
composer = 'Mozart'
puts composer + ' was "da bomb", in his day.'
my_composer = 'Beethoven'
puts 'But I prefer ' + my_composer + ', personally.'
型別轉換 Conversions
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
Foo = 1
Foo = 2 # (irb):3: warning: already initialized constant Foo
RUBY_PLATFORM
ENV
nil
表⽰示未設定值、未定義
nil # nil
nil.class # NilClass
nil.nil? # true
42.nil? # false
nil == nil # true
false == nil # false
註解 #
偏好均使⽤用單⾏行註解
# this is a comment line
# this is a comment line
=begin
This is a comment line
This is a comment line
=end
陣列 Array
a = [ 1, "cat", 3.14 ]
puts a[0] # 輸出 1
puts a.size # 輸出 3
a[3] = nil
puts a.inspect # 輸出字串 [1, "cat", nil]
更多陣列⽅方法
colors = ["red", "blue"]
colors.push("black")
colors << "white"
puts colors.join(", ") # red, blue, black, white
colors.pop
puts colors.last #black
雜湊 Hash
(Associative Array)
config = { "foo" => 123, "bar" => 456 }
puts config["foo"] # 輸出 123
字串符號 Symbols
唯⼀一且不會變動的識別名稱
config = { :foo => 123, :bar => 456 }
puts config[:foo] # 輸出 123
流程控制
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 ) # and
puts ( 2 > 1 ) || ( 2 > 3 ) # or
控制結構 If
if account.total > 100000
puts "large account"
elsif account.total > 25000
puts "medium account"
else
puts "small account"
end
控制結構 If
if account.total > 100000
puts "large account"
elsif account.total > 25000
puts "medium account"
else
puts "small account"
end
Perl Style
三元運算⼦子
expression ? true_expresion : false_expression
x = 3
puts ( x > 3 )? "大於三" : "小於或等於三"
# 輸出 小於或等於三
控制結構 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
i = 0
loop do
i += 1
break if i > 10 # 中斷迴圈
end
i=0
while ( i < 10 )
i += 1
next if i % 2 == 0 #跳過雙數
end
i = 0
i += 1 until i > 10
puts i
# 輸出 11
真或假
只有 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
⽅方法定義 Methods
def 開頭 end 結尾
def say_hello(name)
result = "Hi, " + name
return result
end
puts say_hello('ihower')
# 輸出 Hi, ihower
字串相加
⽅方法定義 Methods
def 開頭 end 結尾
def say_hello(name)
result = "Hi, " + name
return result
end
puts say_hello('ihower')
# 輸出 Hi, ihower
字串相加
return 可省
略,最後⼀一⾏行
就是回傳值
? 與 ! 的慣例
⽅方法名稱可以⽤用?或!結尾,前者表⽰示會回傳 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]
類別 Classes
⼤大寫開頭,使⽤用 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
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
類別 Class
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
建構式
類別 Class
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
建構式
物件變數
類別 Class
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
建構式
物件變數
字串相加
類別 Class
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
建構式
物件變數
字串相加
⼤大寫開頭的
常數
類別 Class (續)
class Person
@@name = “ihower”
def self.say
puts @@name
end
end
Person.say # 輸出 Hello, ihower
類別 Class (續)
class Person
@@name = “ihower”
def self.say
puts @@name
end
end
Person.say # 輸出 Hello, ihower
類別變數
類別 Class (續)
class Person
@@name = “ihower”
def self.say
puts @@name
end
end
Person.say # 輸出 Hello, ihower
類別變數
類別⽅方法
資料封裝
• 所有的物件變數(@開頭)、類別變數(@@開頭),
都是封裝在類別內部,類別外無法存取。
• 需透過定義 public ⽅方法才可以存取到
class Person
def initialize(name)
@name = name
end
end
p = Person.new('ihower')
p.name
=> NoMethodError
p.name='peny'
=> NoMethodError
class Person
def initialize(name)
@name = name
end
def name
@name
end
def name=(name)
@name = name
end
end
p = Person.new('ihower')
p.name
=> "ihower"
p.name="peny"
=> "peny"
⽅方法封裝
預設是 public 公開
class MyClass
def public_method
end
def private_method
end
def protected_method
end
public :public_method
private :private_method
protected :proected_method
end
class MyClass
def public_method
end
private
def private_method
end
protected
def protected_method
end
end
類別 Class body 也可以執⾏行程式
attr_accessor, attr_writer, attr_reader
class Person
attr_accessor :name
end
class Person
def name
@name
end
def name=(val)
@name = val
end
end
等同於
Class 繼承
class Pet
attr_accessor :name, :age
end
class Cat < Pet
end
class Dog < Pet
end
Module (1) Namespace
module MyUtil
def self.foobar
puts "foobar"
end
end
MyUtil.foobar
# 輸出 foobar
Module(2) Mixins
module Debug
def who_am_i?
"#{self.class.name} (##{self.object_id}): #{self.to_s}"
end
end
class Foo
include Debug # 這個動作叫做 Mixin
# ...
end
class Bar
include Debug
# ...
end
ph = Foo.new("12312312")
et = Bar.new("78678678")
ph.who_am_i? # 輸出 "Foo (#330450): 12312312"
et.who_am_i? # 輸出 "Bar (#330420): 78678678"
Module(2) Mixins
module Debug
def who_am_i?
"#{self.class.name} (##{self.object_id}): #{self.to_s}"
end
end
class Foo
include Debug # 這個動作叫做 Mixin
# ...
end
class Bar
include Debug
# ...
end
ph = Foo.new("12312312")
et = Bar.new("78678678")
ph.who_am_i? # 輸出 "Foo (#330450): 12312312"
et.who_am_i? # 輸出 "Bar (#330420): 78678678"
Ruby 使⽤用
Module 來解決
多重繼承問題
⾛走訪迴圈
each method
languages = ['Ruby', 'Javascript', 'Perl']
languages.each do |lang|
puts 'I love ' + lang + '!'
end
# I Love Ruby
# I Love Javascript
# I Love Perl
迭代器 iterator
• 不同於 while 迴圈⽤用法,each 是⼀一個陣
列的⽅方法,⾛走訪其中的元素,我們稱作
迭代器(iterator)
• 其中 do .... end 是 each ⽅方法的參數,稱
作匿名⽅方法( code block)
最簡單的迭代器
3.times do
puts 'Good Job!'
end
# Good Job!
# Good Job!
# Good Job!
code block
⼀一種匿名⽅方法,或稱作 closure
{ puts "Hello" } # 這是一個 block
do
puts "Blah" # 這也是一個 block
puts "Blah"
end
code block
內部迭代器(iterator)
# 處理陣列 people
people = ["David", "John", "Mary"]
people.each do |person|
puts person
end
# 反覆五次
5.times { puts "Ruby rocks!" }
# 從一數到九
1.upto(9) { |x| puts x }
code block
內部迭代器(iterator)
# 處理陣列 people
people = ["David", "John", "Mary"]
people.each do |person|
puts person
end
# 反覆五次
5.times { puts "Ruby rocks!" }
# 從一數到九
1.upto(9) { |x| puts x }
所以我們將
很少⽤用到
while, until, for 等迴圈
code block
其他迭代⽅方式
# 迭代並造出另一個陣列
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]
code block
當作判斷條件
# 迭代並根據條件刪除
a = [ "a", "b", "c" ]
a.delete_if {|x| x >= "b" }
# 結果是 ["a"]
# 客製化排序
[2,1,3].sort! { |a, b| b <=> a }
# 結果是 [3, 2, 1]
code block
有沒有 functional programming 的 fu?
# 計算總和
(5..10).inject {|sum, n| sum + n }
# 找出最長字串find the longest word
longest = ["cat", "sheep", "bear"].inject do |memo,
word|
( memo.length > word.length )? memo : word
end
code block
僅執⾏行⼀一次呼叫
file = File.new("testfile", "r")
# ...處理檔案
file.close
File.open("testfile", "r") do |file|
# ...處理檔案
end
# 檔案自動關閉
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!" }
# 或是先宣告出 proc object
proc_1 = Proc.new { |i| puts "#{i}: Blocks are cool!" }
proc_2 = lambda { |i| puts "#{i}: Blocks are cool!" }
call_block(&proc_1)
call_block(&proc_2)
# 輸出
# "1: Blocks are cool!"
# "2: Blocks are cool!"
# "3: Blocks are cool!"
傳遞不定參數
def my_sum(*val)
val.inject(0) { |sum, v| sum + v }
end
puts my_sum(1,2,3,4)
# 輸出 10
參數尾 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
例外處理
raise, begin, rescue, ensure
begin
puts 10 / 0
rescue => e
puts e.class
ensure
# ...
end
# 輸出 ZeroDivisionError
raise "Not works!!"
# 丟出一個 RuntimeError
# 自行自定例外物件
class MyException < RuntimeError
end
raise MyException
2. Ruby 的特點
Ruby supports multiple programming paradigms
1. Object-Oriented
物件導向程式設計
Everything in Ruby is
object, even class.
Ruby Object Model
class A
end
class B < A
end
obj = B.new
obj.class # B
B.superclass # A
B.class # Class
obj B
Object
Class
A
class class
super
super
class object is an object
of the class Class
obj B
Object
Class
A
class class
super
super
class
class
class
class A
end
class B < A
end
obj = B.new
obj.class # B
B.superclass # A
B.class # Class
class A
end
module B
end
module C
end
class D < A
include B
include C
end
D.ancestors
=> [D, C, B, A, Object, Kernel, BasicObject]
module
obj D
A
Mixin
B,C
class
super
super
class A
def foo
end
end
obj1 = A.new
obj2 = A.new
what’s metaclass?
obj2
Object
A
class
super
obj2
class
obj2
Object
A
class
super
obj2’s
metaclass
obj2
class
super
class A
def foo
end
end
obj1 = A.new
obj2 = A.new
def obj2.bar
# only obj2 has bar method,
# called singleton method
end
# another way
class << obj1
def baz
#only obj1 has baz method
end
end
P.S. well, number and symbol have no metaclass
metaclassalso known as singleton, eigenclass, ghost class, virtual class.
every object has his own metaclass
A
A’s
metaclass
super
Class
class
B
class
class A
# way1
def self.foo
end
# way2
class << self
def bar
end
end
end
# way3
def A.baz
end
A.foo
A.bar
A.baz
class object has its metaclass too.
so the singleton method is class
method!!
動態型別 (duck typing)
會聒聒叫的就是鴨⼦子
# 鴨子
class Duck
def quack
puts "quack!"
end
end
# 野鴨 (不用繼承)
class Mallard
def quack
puts "qwuaacck!! quak!"
end
end
Class 不是 Type
⼀一個物件可以做什麼才是重點
birds = [Duck.new, Mallard.new, Object.new]
# 迭代陣列,並呼叫方法 (無須擔心型別)
birds.each do |duck|
duck.quack if duck.respond_to? :quack
end
Duck Typing(from wikipedia)
• duck typing is a style of dynamic typing in
which an object's methods and properties
determine the valid semantics, rather than
its inheritance from a particular class or
implementation of a specific interface
prototype-based programming
Like JavaScript, Ruby can do, too
Animal = Object.new
def Animal.run
end
def Animal.sit
end
Cat = Animal.clone
Cat.run
Dog = Animal.clone
Dog.run
class-oriented programming
v.s.
object-oriented programming
http://stackoverflow.com/questions/546670/what-is-the-difference-between-class-
oriented-and-object-oriented-programming
“With Ruby, you can think in terms of
classes or in terms of objects.With
Javascript, you can think in terms of
prototypes or in terms of objects.With
Java and C++, classes are your only option.”
by Giles Bowkett
2. Functional Programming
函數式程式設計
http://www.slideshare.net/ihower/fp-osdc2012v2
https://github.com/JuanitoFatas/Ruby-Functional-Programming
(1) Higher-Order Functions
tickets = { "a" => 1100, "b" => 900, "c" => 800 }
highest_price = 0
tickets.each do |ticket, price|
if price < 1000
highest_price = price if price > highest_price
end
end
highest_price # 900
Higher-Order Functions
(cont.)
tickets = { "a" => 1100, "b" => 900, "c" => 800 }
tickets.map{ |x| x[1] }.select{ |x| x < 1000 }.max # 900
pre- and Post-processing
usage example
f = File.open("myfile.txt", 'w')
f.write("Lorem ipsum dolor sit amet")
f.write("Lorem ipsum dolor sit amet")
f.close
# using block
File.open("myfile.txt", 'w') do |f|
f.write("Lorem ipsum dolor sit amet")
f.write("Lorem ipsum dolor sit amet")
end
Dynamic Callbacks
Sinatra usage example
get '/posts' do
#.. show something ..
end
post '/posts' do
#.. create something ..
end
put '/posts/:id' do
#.. update something ..
end
delete '/posts/:id' do
#.. annihilate something ..
end
Dynamic Callbacks
server = Server.new
server.handle(/hello/) do
puts "Hello at #{Time.now}"
end
server.handle(/goodbye/) do
puts "goodbye at #{Time.now}"
end
server.execute("/hello")
# Hello at Wed Apr 21 17:33:31 +0800 2010
server.execute("/goodbye")
# goodbye at Wed Apr 21 17:33:42 +0800 2010
class Server
def initialize
@handlers = {}
end
def handle(pattern, &block)
@handlers[pattern] = block
end
def execute(url)
@handlers.each do |pattern, block|
if match = url.match(pattern)
block.call
break
end
end
end
end
(2) Everything is an
expression
if found_dog == our_dog
name = found_dog.name
message = "We found our dog #{name}!"
else
message = "No luck"
end
Everything is an
expression (cont.)
message = if found_dog == my_dog
name = found_dog.name
"We found our dog #{name}!"
else
"No luck"
end
(3) Lazy enumerators
require 'prime'
Prime.to_a # 這樣是無窮數列...
Prime.take(10).to_a
# [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
Prime.lazy.select{ |x| x % 4 == 3 }.take(10).to_a
# => [3, 7, 11, 19, 23, 31, 43, 47, 59, 67]
3. Metaprogramming
⽤用程式寫程式
http://ihower.tw/blog/archives/4279
Two types of meta-
programming
• Code Generation
• eg. Rails scaffold
• Reflection
• eg. Class Macro
Class Bodies Aren’t
Special
class Demo
a = 1
puts a
def self.say
puts "blah"
end
say # you can execute class method in class body
end
# 1
# blah
Introspection (反射機制)
# 這個物件有什麼方法
Object.methods
=> ["send", "name", "class_eval", "object_id", "new",
"singleton_methods", ...]
# 這個物件有這個方法嗎?
Object.respond_to? :name
=> true
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"
Rails example
class Firm < ActiveRecord::Base
has_many :clients
has_one :account
belongs_to :conglomorate
end
# has_many 是 AciveRecord 的 class method
# 其內容是動態定義出 Firm 的一堆 instance methods
firm = Firm.find(1)
firm.clients
firm.clients.size
firm.clients.build
firm.clients.destroy_all
Memorize example
(Class Macro)
class Account
def calculate
@calculate ||= begin
sleep 10 # expensive calculation
5
end
end
end
a = Account.new
a.caculate # need waiting 10s to get 5
a.caculate # 5
a.caculate # 5
a.caculate # 5
memoize method
class Account
def calculate
sleep 2 # expensive calculation
5
end
memoize :calculate
end
a = Account.new
a.calculate # need waiting 10s to get 5
a.calculate # 5
class Class
def memoize(name)
original_method = "_original_#{name}"
alias_method :"#{original_method}", name
define_method name do
cache = instance_variable_get("@#{name}")
if cache
return cache
else
result = send(original_method) # Dynamic Dispatches
instance_variable_set("@#{name}", result)
return result
end
end
end
end
It’s general for any class
class Car
def run
sleep 100 # expensive calculation
“done”
end
memoize :run
end
c = Car.new
c.run # need waiting 100s to get done
c.run # done
Method Missing
car = Car.new
car.go_to_taipei
# go to taipei
car.go_to_shanghai
# go to shanghai
car.go_to_japan
# go to japan
class Car
def go(place)
puts "go to #{place}"
end
def method_missing(name, *args)
if name.to_s =~ /^go_to_(.*)/
go($1)
else
super
end
end
end
car = Car.new
car.go_to_taipei
# go to taipei
car.blah # NoMethodError: undefined method `blah`
XML builder example
builder = Builder::XmlMarkup.new(:target=>STDOUT, :indent=>2)
builder.person do |b|
b.name("Jim")
b.phone("555-1234")
b.address("Taipei, Taiwan")
end
# <person>
# <name>Jim</name>
# <phone>555-1234</phone>
# <address>Taipei, Taiwan</address>
# </person>
DSL
(Domain-Specific Language)
• Internal DSL
• 沿⽤用程式語⾔言本⾝身的 Syntax
• Ruby 有⾮非常好的能⼒力可以作為 Internal
DSL 之⽤用
• External DSL
• ⾃自⾏行發明 Syntax,再⽤用程式語⾔言去解析
4. Encodings
http://ihower.tw/blog/archives/2722
UCS v.s. CSI
• UCS (Universal Character Set)
• 語⾔言內部統⼀一轉換成使⽤用 UTF-16 或
UTF-8,Java/Python/Perl 等⼤大部分語⾔言
都這樣做
• CSI (Character Set Independent)
• Ruby 內建⽀支援 99 種 Encodings,並不
做轉換
pick one encoding, likely Unicode, and works all data in one format? No.
Ruby 1.9 make it possible to work
with data with 99 encoding.
1.9.3-p392 :024 > Encoding.name_list
=> ["ASCII-8BIT", "UTF-8", "US-ASCII", "Big5", "Big5-HKSCS", "Big5-UAO", "CP949",
"Emacs-Mule", "EUC-JP", "EUC-KR", "EUC-TW", "GB18030", "GBK", "ISO-8859-1",
"ISO-8859-2", "ISO-8859-3", "ISO-8859-4", "ISO-8859-5", "ISO-8859-6", "ISO-8859-7",
"ISO-8859-8", "ISO-8859-9", "ISO-8859-10", "ISO-8859-11", "ISO-8859-13",
"ISO-8859-14", "ISO-8859-15", "ISO-8859-16", "KOI8-R", "KOI8-U", "Shift_JIS",
"UTF-16BE", "UTF-16LE", "UTF-32BE", "UTF-32LE", "Windows-1251", "BINARY", "IBM437",
"CP437", "IBM737", "CP737", "IBM775", "CP775", "CP850", "IBM850", "IBM852", "CP852",
"IBM855", "CP855", "IBM857", "CP857", "IBM860", "CP860", "IBM861", "CP861", "IBM862",
"CP862", "IBM863", "CP863", "IBM864", "CP864", "IBM865", "CP865", "IBM866", "CP866",
"IBM869", "CP869", "Windows-1258", "CP1258", "GB1988", "macCentEuro", "macCroatian",
"macCyrillic", "macGreek", "macIceland", "macRoman", "macRomania", "macThai",
"macTurkish", "macUkraine", "CP950", "Big5-HKSCS:2008", "CP951", "stateless-ISO-2022-
JP", "eucJP", "eucJP-ms", "euc-jp-ms", "CP51932", "eucKR", "eucTW", "GB2312", "EUC-
CN", "eucCN", "GB12345", "CP936", "ISO-2022-JP", "ISO2022-JP", "ISO-2022-JP-2",
"ISO2022-JP2", "CP50220", "CP50221", "ISO8859-1", "Windows-1252", "CP1252",
"ISO8859-2", "Windows-1250", "CP1250", "ISO8859-3", "ISO8859-4", "ISO8859-5",
"ISO8859-6", "Windows-1256", "CP1256", "ISO8859-7", "Windows-1253", "CP1253",
"ISO8859-8", "Windows-1255", "CP1255", "ISO8859-9", "Windows-1254", "CP1254",
"ISO8859-10", "ISO8859-11", "TIS-620", "Windows-874", "CP874", "ISO8859-13",
"Windows-1257", "CP1257", "ISO8859-14", "ISO8859-15", "ISO8859-16", "CP878",
"Windows-31J", "CP932", "csWindows31J", "SJIS", "PCK", "MacJapanese", "MacJapan",
"ASCII", "ANSI_X3.4-1968", "646", "UTF-7", "CP65000", "CP65001", "UTF8-MAC", "UTF-8-
MAC", "UTF-8-HFS", "UTF-16", "UTF-32", "UCS-2BE", "UCS-4BE", "UCS-4LE", "CP1251",
"UTF8-DoCoMo", "SJIS-DoCoMo", "UTF8-KDDI", "SJIS-KDDI", "ISO-2022-JP-KDDI",
"stateless-ISO-2022-JP-KDDI", "UTF8-SoftBank", "SJIS-SoftBank", "locale", "external",
"filesystem", "internal"]
All String are Encoded
• In Ruby 1.9 a String is a collection of
encoded characters.
除了 raw bytes,還包括 Encoding 資訊。
>> "中文".encoding.name
=> "UTF-8"
Encoding
object
In Ruby 1.9, String has attached
Encoding object, and works in
characters.
Transcoding
改變編碼
utf8 = "測試"
utf8.bytesize # 6
utf8.bytes.to_a # [230, 184, 172, 232, 169, 166]
big5 = utf8.encode("big5")
big5.encoding.name # ”Big5”
big5.bytesize # 4
big5.bytes.to_a # [180, 250, 184, 213]
Transcoding fails
轉碼失敗
str = "Résumé"
str.encode("big5")
=> Encoding::UndefinedConversionError: "xC3xA9"
from UTF-8 to Big5
	 from (irb):2:in `encode'
	 from (irb):2
	 from /usr/local/bin/irb19:12:in `<main>'
Force Transcoding
改變編碼,但不改 byte data
utf8 = "測試"
big5 = utf8.encode("big5")
big5.valid_encoding?
=> true
big5.force_encoding("utf-8")
big5.valid_encoding?
=> false
Force Transcoding fails
編碼不對無法進⼀一步操作
big5.valid_encoding? # false
big5 =~ /123456/
=> ArgumentError: invalid byte sequence in UTF-8
	 from (irb):11
	 from /usr/local/bin/irb19:12:in `<main>'
Encoding.compatible?
例如:ASCII with a bigger Encoding
ascii = "my ".force_encoding("ascii")
utf8 = "Résumé"
# 檢查相容性
Encoding.compatible?(ascii, utf8)
#<Encoding:UTF-8>
# 相加
my_resume = ascii + utf8
puts my_resume # "My Résumé"
puts my_resume.encoding.name # UTF-8
Encoding.compatible?
不相容不能加在⼀一起
big5 = "測試".encode("big5")
utf8 = "Résumé"
# 檢查相容性
Encoding.compatible?(big5, utf8) # nil
# 相加
big5 + utf8
=> Encoding::CompatibilityError: incompatible character
encodings: Big5 and UTF-8
	 from (irb):25
	 from /usr/local/bin/irb19:12:in `<main>'
3. Ruby 的實作
程式語⾔言和程式語⾔言的實作,
是兩回事
(雖然很多語⾔言只有⼀一種實作)
程式語⾔言本⾝身沒有效能快慢,程式語⾔言的實作才有
CRuby / MRI (Matz's Ruby Interpreter)
• 1.8.7 is dead
• 1.9.3
• 2.0 is released at 2/24/2013 (⼆二⼗十週年)
JRuby
Rubinius
Concurrency models
的爭論
• process-based
• CRuby has GVL (globalVM lock)
• thread-based
• reactor pattern
• eventmachine (Ruby library)
• node.js (JavaScript)
IronRuby
MacRuby
RubyMotion
mruby
4. Ruby 的應⽤用
how ruby change the world!
Web framework
• MVC
• ORM
• URL Routing
• View Template
Web Designer Tools
• Sass/Less/Haml
• Compass
• Middleman
Testing/BDDRuby community loves testing
• RSpec
• Cucumber
http://ihower.tw/blog/archives/5438
http://ihower.tw/blog/archives/5983
What’s BDD?
• An improved xUnit Framework
• Focus on clearly describe the expected
behavior
• The emphasis is Tests as Documentation
rather than merely using tests for
verification.
Terminology changed
New paradigm: Executable Specification
• “Test” becomes “Spec”
• “Assertion” becomes “Expectation”
• “test method” becomes “example” (RSpec)
• “test case” becomes “example group” (RSpec)
class OrderTest < Test::Unit::TestCase
def setup
@order = Order.new
end
def test_order_status_when_initialized
assert_equal @order.status, "New"
end
def test_order_amount_when_initialized
assert_equal @order.amount, 0
end
end
Test::Unit 寫法
describe Order do
before do
@order = Order.new
end
context "when initialized" do
it "should have default status is New" do
@order.status.should == "New"
end
it "should have default amount is 0" do
@order.amount.should == 0
end
end
end
RSpec 寫法
DevOps
• Chef
• Puppet
• Vagrant
Mac
Mac
Blogging
(static HTML generator)
Redmine
5. Ruby ⽣生態圈
1. Editors & IDEs
Vim for Rails
Emacs for Rails
2.Web Services &
Monitors
3.Hosting
3.a
PaaS (Platform as a Service)
3.b
⾼高C/P值的VPS (Virtual Private Server)
3.b
IaaS (Infrastructure as a service)
Cloud Hosing
4. Consulting
http://www.workingwithrails.com/
5. Conferences
6. Books
http://www.pragprog.com/
閱讀練習時,⼩小⼼心 Rails 版本差異
1.2, 2.0, 2.1, 2.2, 2.3, 3.0, 3,1, 3.2
4.0 書籍尚未上市
http://guides.rubyonrails.org/
7.Video
http://peepcode.com/
8.Website & Blog
(free)
http://weblog.rubyonrails.org/
http://www.rubyinside.com/
http://ruby5.envylabs.com
http://railscasts.com/
http://www.rubyflow.com/
http://github.com/
9. Ruby Community
in Taiwan
Ruby Tuesday 聚會
ptt.cc #Ruby 版
http://railsfun.tw/
Thank you.
參考資料:
Beginning Ruby 2nd. (Apress)
Programming Ruby (The Pragmatic Programmers)
The Well-Grounded Rubyist (Manning)
Ruby 程式設計 (O’Reilly)
Foundation Rails 2 (friendsof)
http://rubyonrails.org/ecosystem
http://infoether.com/ruby-and-rails-whitepaper
We’re hiring
• Ruby on Rails developer
• Front-end web developer
• http://faria.co

Ruby 程式語言綜覽簡介