Ruby 程式語言綜覽簡介

Post on 11-Sep-2014

50 views 14 download

Tags:

description

 

Transcript of Ruby 程式語言綜覽簡介

Ruby 程式語⾔言綜覽簡介

ihower@gmail.com2013/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=1puts "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-20599999999990

浮點數 Float後⾯面有 .

54.3210.001-12.3120.0

浮點數四則運算puts 1.0 + 2.0puts 2.0 * 3.0puts 5.0 - 8.0puts 9.0 / 2.0

# 3.0# 6.0# -3.0# 4.5

整數四則運算結果也會是整數

puts 1 + 2puts 2 * 3puts 5 - 8puts 9 / 2

# 3# 6# -1# 4

更多運算

puts 5 * (12-8) + -15puts 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 # 6puts var3.upcaseputs var3.downcase

Ruby 完全地物件導向每樣東⻄西都是物件,包括字串和數字。

# 輸出 "UPPER"puts "upper".upcase # 輸出 -5 的絕對值puts -5.abs

# 輸出 Fixnumputs 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 = 2var2 = '5'

puts var1.to_s + var2 # 25puts var1 + var2.to_i # 7

puts 9.to_f / 2 # 4.5

常數 Constant⼤大寫開頭

foo = 1foo = 2

Foo = 1Foo = 2 # (irb):3: warning: already initialized constant Foo

RUBY_PLATFORMENV

nil表⽰示未設定值、未定義

nil # nilnil.class # NilClass

nil.nil? # true42.nil? # false

nil == nil # truefalse == nil # false

註解 #偏好均使⽤用單⾏行註解

# this is a comment line# this is a comment line

=begin This is a comment line This is a comment line=end

陣列 Arraya = [ 1, "cat", 3.14 ]

puts a[0] # 輸出 1puts a.size # 輸出 3

a[3] = nilputs a.inspect # 輸出字串 [1, "cat", nil]

更多陣列⽅方法colors = ["red", "blue"]

colors.push("black")colors << "white"puts colors.join(", ") # red, blue, black, white

colors.popputs 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 > 2puts 1 < 2puts 5 >= 5puts 5 <= 4puts 1 == 1puts 2 != 1

puts ( 2 > 1 ) && ( 2 > 3 ) # andputs ( 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 = 3puts ( 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 = 0loop do i += 1 break if i > 10 # 中斷迴圈

end

i=0while ( i < 10 ) i += 1 next if i % 2 == 0 #跳過雙數

end

i = 0i += 1 until i > 10puts i# 輸出 11

真或假只有 false 和 nil 是假,其他為真

puts "not execute" if nilputs "not execute" if false

puts "execute" if true # 輸出 executeputs "execute" if “” # 輸出 execute (和JavaScript不同)puts "execute" if 0 # 輸出 execute (和C不同)puts "execute" if 1 # 輸出 executeputs "execute" if "foo" # 輸出 executeputs "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 = $3end

⽅方法定義 Methodsdef 開頭 end 結尾

def say_hello(name) result = "Hi, " + name return resultend

puts say_hello('ihower')# 輸出 Hi, ihower

⽅方法定義 Methodsdef 開頭 end 結尾

def say_hello(name) result = "Hi, " + name return resultend

puts say_hello('ihower')# 輸出 Hi, ihower

字串相加

⽅方法定義 Methodsdef 開頭 end 結尾

def say_hello(name) result = "Hi, " + name return resultend

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.newcolor_array = [] # 等同

color_hash = Hash.newcolor_hash = {} # 等同

time = Time.newputs 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, ihowerp2.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, ihowerp2.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, ihowerp2.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, ihowerp2.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, ihowerp2.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 endend

p = Person.new('ihower')p.name => NoMethodErrorp.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, :ageend

class Cat < Petend

class Dog < Petend

Module (1) Namespacemodule MyUtil

def self.foobar puts "foobar" end end

MyUtil.foobar# 輸出 foobar

Module(2) Mixinsmodule Debug def who_am_i? "#{self.class.name} (\##{self.object_id}): #{self.to_s}" endend

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) Mixinsmodule Debug def who_am_i? "#{self.class.name} (\##{self.object_id}): #{self.to_s}" endend

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 personend # 反覆五次

5.times { puts "Ruby rocks!" }

# 從一數到九

1.upto(9) { |x| puts x }

code block內部迭代器(iterator)

# 處理陣列 people

people = ["David", "John", "Mary"]people.each do |person| puts personend # 反覆五次

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 wordlongest = ["cat", "sheep", "bear"].inject do |memo, word| ( memo.length > word.length )? memo : wordend

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 blockdef 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 objectproc_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 / 0rescue => e puts e.classensure # ...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 Aend

class B < Aend

obj = B.new

obj.class # B B.superclass # AB.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 Aend

class B < Aend

obj = B.new

obj.class # B B.superclass # AB.class # Class

class Aend

module Bend

module Cend

class D < A include B include Cend

D.ancestors=> [D, C, B, A, Object, Kernel, BasicObject]

module

obj D

A

MixinB,C

class

super

super

class A def foo endend

obj1 = A.newobj2 = A.new

what’s metaclass?

obj2

Object

A

class

super

obj2class

obj2

Object

A

class

super

obj2’s metaclass

obj2class

super

class A def foo endend

obj1 = A.newobj2 = A.new

def obj2.bar # only obj2 has bar method, # called singleton methodend

# another wayclass << obj1 def baz #only obj1 has baz method endend

P.S. well, number and symbol have no metaclass

metaclass also known as singleton, eigenclass, ghost class, virtual class.

every object has his own metaclass

AA’s

metaclass

super

Classclass

B

class

class A # way1 def self.foo end # way2 class << self def bar end end end

# way3def A.bazend

A.fooA.barA.baz

class object has its metaclass too. so the singleton method is class

method!!

動態型別 (duck typing)會聒聒叫的就是鴨⼦子

# 鴨子

class Duck def quack puts "quack!" endend # 野鴨 (不用繼承)class Mallard def quack puts "qwuaacck!! quak!" endend

Class 不是 Type⼀一個物件可以做什麼才是重點

birds = [Duck.new, Mallard.new, Object.new]

# 迭代陣列,並呼叫方法 (無須擔心型別)

birds.each do |duck| duck.quack if duck.respond_to? :quackend

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 programmingLike JavaScript, Ruby can do, too

Animal = Object.new

def Animal.runend

def Animal.sitend

Cat = Animal.cloneCat.run

Dog = Animal.cloneDog.run

“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 endend

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 blockFile.open("myfile.txt", 'w') do |f| f.write("Lorem ipsum dolor sit amet") f.write("Lorem ipsum dolor sit amet") end

Dynamic CallbacksSinatra 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 Callbacksserver = 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 2010server.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 } endend

dragon = Dragon.newdragon.foo # 輸出 "bar"dragon.a # 輸出 "a"dragon.f # 輸出 "f"

Rails exampleclass Firm < ActiveRecord::Base has_many :clients has_one :account belongs_to :conglomorateend

# has_many 是 AciveRecord 的 class method# 其內容是動態定義出 Firm 的一堆 instance methods

firm = Firm.find(1)firm.clientsfirm.clients.sizefirm.clients.buildfirm.clients.destroy_all

Memorize example(Class Macro)

class Account

def calculate @calculate ||= begin sleep 10 # expensive calculation 5 end end end

a = Account.newa.caculate # need waiting 10s to get 5a.caculate # 5a.caculate # 5a.caculate # 5

memoize methodclass Account def calculate sleep 2 # expensive calculation 5 end

memoize :calculate end

a = Account.newa.calculate # need waiting 10s to get 5a.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 endend

It’s general for any classclass Car def run sleep 100 # expensive calculation “done” end

memoize :run end

c = Car.newc.run # need waiting 100s to get donec.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 endend

car = Car.new

car.go_to_taipei# go to taipei

car.blah # NoMethodError: undefined method `blah`

XML builder examplebuilder = 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 # 6utf8.bytes.to_a # [230, 184, 172, 232, 169, 166]

big5 = utf8.encode("big5")

big5.encoding.name # ”Big5”big5.bytesize # 4big5.bytes.to_a # [180, 250, 184, 213]

Transcoding fails轉碼失敗

str = "Résumé"str.encode("big5")

=> Encoding::UndefinedConversionError: "\xC3\xA9" 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? # falsebig5 =~ /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 + utf8puts 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 (global VM 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 changedNew 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 endend

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.aPaaS (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 Communityin 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/ecosystemhttp://infoether.com/ruby-and-rails-whitepaper

We’re hiring

• Ruby on Rails developer

• Front-end web developer

• http://faria.co