Ruby/Chapter

From YuntechWiki

Jump to: navigation, search

Ruby初步介紹

軟體安裝:
請先至www.ruby-lang.org/en/downloads下載Ruby 1.8.6。完成安裝、設定PATH系統變數並確認可以在Windows的「命令提示字元」執行。

物件:
Ruby中資料的基本單位是「物件」,所有資料都是物件。包括了數字、字串、正規表示式、以及陣列和雜湊等都是物件。

  • 數字物件
1  1.0  -1
  • 字串物件:一般都使用單引號或雙引號來建立字串物件
'one'  "two"
  • 正規表示式物件
/three/

類別:
而每個物件都有所屬的類別,物件該怎樣的運作都是由所屬類別來決定,例如:數字類別提供了加、減、乘、除等動作(方法),來提供數字物件進行運算。而物件的建立等操作會在後面章節在進行介紹,在這邊只需要知道每個物件都有所屬的類別,而它們的動作(方法)則是定義在各自類別裡頭。

Object類別:
Object類別是Ruby最基礎的類別。所有的Ruby的類別都會自動繼承Object類別,所以其他的Ruby類別物件就可以使用Object類別裡的所有動作(方法)、變數等功能。例如:「= =」、class、to_s等方法。註: 繼承是指能夠允許使用被繼承的所有功能(方法、變數等)。如何使用類別所提供的方法,我們只要透過「物件.方法名」就可以使用類別所提供的方法。
第一張圖.jpg
這邊只是列出常用到的類別,想更進一步的了解可以查閱RUBY-DOC,網址為http://www.ruby-doc.org/core/ 。如果遇到不熟的類別或方法也都可以到這個網址查詢。

  • 「==」:判斷兩個物件是否相等,傳回值為true或false
1 == 1		#=>true
1 == 2 #=>false
  • 「class」:可用來判斷物件的所屬類別,回傳值為該物件類別
1.class			#=> Fixnum 
"one".class #=>String
  • 「to_s」:可將物件轉換成字串的方法。
1.to_s		#=> "1"
  • 「to_i」:可將物件轉換成數值的方法。
"10".to_i		#=>10

語法糖衣
這邊同學應該覺得很奇怪,為什麼「= =」這個方法與其他呼叫方式不同。我們在前面有說過,使用方法是透過「物件.方法名」來使用。但為何在使用基本算數運算時,卻是使用我們一般所熟悉方式來進行運算呢!例如:1+1。其實也是可以使用「1.+(1)」方式來進行計算。而這種為了方便人們去使用的方式,我們稱為語法糖衣。

print 1.+(1)	#=>2

方法
方法是撰寫於於類別裡,提供物件來進行操作。這如同我們前面所說的數字類別提供了+、-、*、/等方法提供數字物件進行操作。而一般呼叫方法的方式是以「物件名.方法名」,就可以使用物件所提供的方法。

模組
模組的定義非常像類別,由方法與變數等構成,與類別不同的是,模組無法建立實體(物件)。Ruby只允許單一繼承,所以每個類別都只能夠繼承一個父類別,這個時候就可以透過mixin(混入)將模組混到類別裡頭,使用模組所提供的方法及變數等。而模組的建立等操作也會在後半章節提到,在這只需要知道它與類別相同,能夠提供方法及變數等供其他類別使用,但它無法建立物件。

Kernel模組
Kernel模組是Ruby的內建模組,Object類別引用了Kernel模組;也就是說任何Ruby程式都能使用Object類別,也就可以使用所有屬於Kernel所提供的方法。而常用的Kernel方法,包括:print、puts、gets等。

Ri
如果想要查看類別所提供的實體方法、類別方法、常數等,又或者想要知道方法或者是常數的所屬類別,我們都可以透過Ri來提供相關的資訊。我們只需要在命令提示字元內,透過ri指令就可以得到相關的資訊,表示如下:
第二張圖.jpg

我們透過ri指令來找尋提供pust方法的所屬類別。由上面這張圖看到提供pust這個方法的類別有IO類別、IRB模組的Locale類別等等。這邊是針對方法,如果是針對類別的話,可以將上例的puts改成所要查詢的類別。


此外,也可以查詢方法的說明,如下表示:
第三張圖.jpg

上面這張圖我們查詢了Kernel.print,來了解Kernel模組所提供的print方法的功能為何,除了有說明還有範例可以提供參考。

fxri:
fxri能夠列出並說明所有類別以及它們所提供的方法,也可以透過fxri來搜尋方法的所屬類別和參考文件。
Fxri1.jpg
Fxri2.jpg


基本輸出:
Ruby主要的顯示方法有2種分別為print、puts,它們都是由Kernel模組所提供的方法,而各自差異如下:

  • print:單純印出所要顯示的東西,不會進行換行動作
print "one"
print "two"
 
-----result-----
onetwo
  • puts:印出所要顯示的東西,會進行換行動作
puts "one"
puts "two"
 
-----result-----
one
two

還有一種常用的顯示方法,也是由Kernel模組所提供的p方法,主要用於程式開發過程中來檢查物件的方法:

str="6"
puts str
p str
 
-----result-----
6
"6"

使用一般的顯示方法,我們無法判斷str這個物件是屬於數值還是字串。這時候我就可以透過p方法,就可以清楚知道這個物件是屬於字串類別的物件。

基本輸入:
而我們要輸入字串時,可以透過Kernel模組提供的gets方法。
•gets:能夠在命令提示字元內輸入字串

print "請輸入字串:"
str=gets
print "輸入:",str #=>字串之間的連結使用「,」
 
-----result-----
請輸入字串:123
輸入:123

這邊要注意的一點是,使用gets這個方法所取得的字串會自動加上一個換行符號「\n」,如下表示:

str=gets
p str
 
-----result-----
abc
"abc\n"

而要消除換行符號,String類別所提供的chomp!方法,能夠幫我們去除換行符號。

str=gets.chomp!
p str
 
-----result-----
abc
"abc"

Ruby基本算術運算

  • 數值物件可以進行計算,並顯示出結果。而這些加、減、乘、除方法都是數值(Numeric)類別所提供的方法,這邊先介紹一些基本的數值運算。
print 1+1     #=>加法,結果為2
print 1+1.0 #=>加法,結果為2.0
print 2-1 #=>減法,結果為1
print 2-1.0 #=>減法,結果為1.0
print 3*-2.0 #=>乘法,結果為-6.0
print 5%2 #=>餘數,結果為1
print 5**2 #=>乘冪,結果為25
print 5**0.5 #=>2.236067977

變數Variables
Variable是程式語言的專有名詞。每一個變數都有一個「名字」,這個「名字」對應一個記憶體的「地址」,這個「地址」存放該變數的值。可以對它求出變數的值。而以往的程式通常都必須先宣告值的型別,但Ruby在宣告變數時,不需要指定它的型別,Ruby會自動判斷值替我們加上型別。建立語法如下:

  • 變數名稱 = 物件
number = 100		#不需指定整數型別
str = "one" #不需指定字串型別
puts number
puts str
 
-----result-----
100
one

條件與條件判斷式

條件

介紹條件判斷式前,要先了解條件的一些使用方式,條件的一般運算結果為 true 或 false(nil)的運算式。而要比較值的相對次序時,可以透過「比較運算子」來達成,如:<、<=、>、>=這些比較運算子都是由Comparable模組所提供的。Numberic、String等類別都有讀入Comparable。如下表示:

 
||左邊的數字是否大於右邊
|-
||>=
||左邊的數字是否大於等於右邊
|-
||< = >
||特殊的比較運算子,回傳結果並非T或F<br\>
當左邊小於右邊時,回傳-1<br\>
當兩邊相等時,回傳0<br\>
當左邊大於右邊時,回傳1<br\>
|}
這邊要特別說明的是「<=>」,他是屬於特殊語法並不是Ruby所提供的API,而定義在Comparable模組的<、<=、>、>=、= =這些方法都是透過「<=>」所撰寫出來的方法。
<code lang="ruby">
puts (4<3)
puts (3<=4)
puts (3>1)	
puts (5>=5)
puts (3<=>2)
------Result-------
false
true
true
true
1
</code>
當要判斷的是值的相對次序時,我們可以透過「相等性運算子」來達成,表示方法如下:
{| border="1" style="border-collapse;"
|-
||= =
||左邊的數字是否等於右邊
|-
||! =
||左邊的數字是否不等於右邊
|-
||= ~
||主要是進行樣式的比對(Regexp會再說明)<br\>
當比對成功時會回傳出現在字串中的位置
|-
||! ~
||與「= ~」相反,當「= ~」回傳nil時,則回傳true
|}
<code lang="ruby">
puts (2==1)
puts ("Ruby"=="Ruby")	
puts /Ruby/ = ~ "Ruby"
puts /Ruby/ =~ "01Ruby"
puts /Ruby/ = ~ "Diamond"
puts /Ruby/ ! ~"Diamond"
------Result--------
false
true
0
2
nil
true
</code>
*使用方法作為條件:
除了運算子能夠拿來當條件使用,Ruby也提供了許多的方法來當做條件使用,例如:String類別所提供用來判斷字串是否為空的empty?方法以及由Object這類別所提供判斷兩個物件是否佔用相同記憶體的equal?方法。要注意的是empty?這個方法許多的類別有都有提供,例如:Array、Hash等類別。
<code lang="ruby">
print  "".empty?   		  #=>true
print  "one".empty? 		  # => false
str1="word"
str2=str1
str3="word”
print str1.equal?(str2)		  # => true
print str1.equal?(str3)		  # => false
print str1==(str2)		  # => true
print str1==(str3)		  # => true
</code>
*多個條件合併使用
當有多個條件時,可以透過邏輯運算子「&&」、「||」來合併各個條件。邏輯運算子是內建於語言之中,並非以方法建立而成,使用方法如下:						
{| border="1" style="border-collapse;"
!
!&&
! | | |
|-
||使用方式
||條件1 && 條件2
||條件1 | | 條件2
|-
||說明
||當兩條件為真時,才為真
||當任一條件為真,就為真
|}
<code lang="ruby">
print  "".empty? &&  2==1    			#=> false
print  "".empty? &&  1==1			#=>true
print  5>=5  ||  2==1				#=>true
print  "one".empty?  ||  "two"=="Ruby"	        #=> false
</code>
 
===條件判斷敘述===
條件判斷敘述能夠檢查指定條件是否為true的條件陳述式,然後根據條件判斷為true或false,執行區塊裡的程式碼。Ruby也提供了大致上有三種條件判斷敘述分別是if、unless、case敘述,這邊我們介紹if以及case敘述,而unless可以透過if來達成所以就不多做說明。<br\>
*if敘述:是指當條件成立時,會執行該段程式敘述。表示方式如下:
<code lang="ruby">
if 條件 then			#then允許省略
程式敘述
end
</code>
<code lang="ruby">
a=20
if a>10 
   puts "a > 10"
end 
------Result-----
a > 10
</code>
*使用elsif與else,表示方法如下
<code lang="ruby">
if 條件1 then	 #then允許省略
  程式敘述1
elsif 條件2
  程式敘述2
else
  程式敘述3
end
</code>
當條件1為true時,執行程式敘述1並結束判斷。當為false(nil)時,會進行條件2的判斷。而當條件2不成立時,則會直接執行程式敘述3,如果沒有else的話則會都不執行。
<code lang="ruby">
a=5
if a>10
  puts "a > 10"
elsif a<10
  puts "a < 10"
else
  puts "a = 10"
end
------Result--------
a < 10
</code>
*case敘述:當要比較多個值時,使用case敘述會比較方便。語法如下:
<code lang="ruby">
case 物件1(想要比較的物件)
when 值1 
  程式敘述1
when 值2 
  程式敘述2
else
  程式敘述3
end
</code>
case敘述會依序的將值1、值2與物件1進行比對,當與值1比對為true時則會執行程式敘述1,結束後跳出比對。當比對為false時,會與值2進行比對。而當值2不成立時,則會直接執行程式敘述3,如果沒有else的話則會都不執行。
<code lang="ruby">
a=20
case a
when 10
  print "a=10"
when 20
  print "a=20"
else
  print "比對不到"
end
------Result-------
a=20
</code>
以下是將if敘述所教的範例修改成以case敘述來表示:
<code lang="ruby">
a=5
case
when a>10
  puts "a > 10"
when a<10
  puts "a < 10"
else
  puts "a = 10"
end
------Result--------
a < 10
</code>
==迴圈==
'''迴圈''':<br\>
當要反覆執行某段程式碼時,就可以透過迴圈來達成,而Ruby實現迴圈的方式大致上有兩種,分別為使用「迴圈專用的敘述」,例如:while、until、for以及「透過方法」來達成,,例如each、times方法等。<br\>
<br\>
'''迴圈專用的敘述''':<br\>
*while迴圈敘述
while迴圈是指當條件一直為true時,while迴圈就會一直執行迴圈敘述裡的程式碼,當條件為false時,則會結束迴圈,往下繼續執行或結束。While迴圈的敘述語法如下:
<code lang="ruby">
while 條件 do
  欲重覆執行的程式碼
end
</code>
<code lang="ruby">
i=1
while i<3
  puts i
  i+=1
end
------Result--------
1
2
</code>
*for迴圈敘述
for迴圈會對一個可列舉物件(陣列、範圍物件)所包含的元素進行迭代。每次迭代就會把一個元素指派給一個特定的迴圈變數,以供迴圈的程式碼進行處理。for迴圈的敘述語法如下:
<code lang="ruby">
for 變數 in 可列舉物件 
  欲重覆執行的程式碼
end
</code>
<code lang="ruby">
for var in 1..3
  print var		#=>123
end
</code>
'''用來控制迴圈的指令'''<br\>	
<code lang=ruby>
指令	  功能
break	  停止動作,馬上跳出回圈
next	  直接跳到迴圈的下一圈
redo	  重新進行這一圈
</code>
*break
<code lang="ruby">
for i in 1..9
  if i%2==0
    break
  end
  puts i  
end
-----result-----
1
</code>
我們在迴圈當中加上一個判斷式,當i能夠被2整除時,使用break來停止並跳出迴圈。所以答案只印出了1
*next
<code lang="ruby">
for i in 1..9
  if i%2==0
    next
  end
  puts i  
end
-----result-----
1
3
5
7
9
</code>
我們在迴圈當中加上一個判斷式,當i能夠被2整除時,使用next跳到下個迴圈。所以答案只印出不能被2整除的值
*redo
<code lang="ruby">
i=0
while(i!=5)
  i=i+1
  if i == 3
    redo
  end
  puts i
end
-----result-----
1
2
4
5
</code>
這個範例主要是列印出1到5的數字,中間有個條件判斷當i值等於3時進行redo,所以當遇到3時則重新執行迴圈而沒有印出3。這邊要注意的是,當使用redo可能會造成無窮迴圈,例如我們如果沒有把「i = i + 1」放至於redo之前,則會造成迴圈的i值一直為3,而進行redo的動作。所以使用redo務必要小心。<br\><br\>
 
'''使用方法達成迴圈''':<br\>
Ruby提供了許多方便的方法來達成迴圈,包括了times、upto、downto、each等方法都可以實現迴圈,現在我們就來介紹times以及downto方法。而each方法主要應用於陣列及雜湊,所以我們將each方法留到陣列及雜湊類別章節時在進行介紹。
*times方法
如果只是單純的想要重覆的去處理固定次數的程式碼時,使用times方法會較容易達成。其語法敘述如下:
<code lang="ruby">
想要重覆執行的次數.times { |變數|
   欲重覆執行的程式碼
}
</code>
<code lang="ruby">
3.times{|i|
   print "這是第",i,"圈\n"
}
-----result-----
這是第0圈
這是第1圈
這是第2圈
</code>
當不需要變數時,允許省略變數的撰寫。
<code lang="ruby">
3.times{
    print "*"		#=>***
}
</code>
*downto方法
downto方法是由Integer類別所提供的方法,以遞減的方式來達到迴圈的效果。指定兩個數字來設定數值範圍,由較大的數值開始遞減到較小的數值時停止。downto方法也一樣可以指定變數,供區塊內程式進行操作。其語法敘述如下:
<code lang="ruby">
較大數值.downto(較小數值){ |變數|
   欲重覆執行的程式碼
}
</code>
<code lang="ruby">
3.downto(1){ | i |
   print "這是第",i,"圈\n"
}
-----result-----
這是第3圈
這是第2圈
這是第1圈
</code>
 
==Numeric類別==
'''Numeric類別''':<br\>
[[Image:第四張圖.jpg]]
<br\>
Ruby中所有的數字類別皆為Numeric的實例。而所有的整數類別皆為Integer類別的實例,如果一個整數值的範圍夠存入31位元的儲存空間,則它就是一個Fixnum的實例。當範圍超過32位元時,Ruby會「自動轉換」成Bignum類別。所以寫程式時幾乎不用去在意這些整數類別的差異,Ruby都會自動幫你轉換。Float類別則是用來處理0.1、3.14159等精度在小數點以下的浮點數。除了Integer和Float類別外,數值類別還有用來表示分數的Rational類別、表示複數的Complex類別等類別。
*數值物件的字面常數:
{| border="1" style="border-collapse;"
|-
||字面常數
||說明
|-
||123
||十進位寫法
|-
||0123
||開頭為0時,表八進位寫法(83)
|-
||0x123
||開頭為0x時,表十六進位寫法(83)
|-
||0b1111011
||開頭為0b時,表二進位寫法(123)
|-
||?a
||開頭為?時,可以取得ASCII碼(97)
|-
||?\t
||取得定位字元的ASCII碼(9)
 
|-
||1.23e4
||指數寫法(1.23 X 10 的 4 次方)
|-
||1.23e-4
||指數寫法(1.23 X 10 的 -4 次方)
|-
||1_234
||Ruby會忽略底線,主要是利於判斷
|}
<code lang="ruby">
print 1_234_567	#=>1234567
print 0123		#=>83
print 1_23e4		#=> 1230000.0
</code>
*轉換數字:
要將Integer物件轉換成Float物件,可以使用Integer類別所提供的to_f方法。而要將Float物件轉為Integer物件時,也可以使用Float類別所提供的to_i方法。除了to_f、to_i方法外,Kernel模組也有提供Integer方法轉換為整數、Float方法轉換為浮點數。這邊要注意的是浮點數轉換成整數時,會無條件捨去小數點部分。
<code lang="ruby">
puts	 3.14.to_i		#=>3
puts	 "123".to_i		#=>123
puts	 123.to_f		#=>123.0
puts	 Float(123)		#=>123.0
puts  Integer(1.23)		#=>1
</code>
而to_i、to_f與Kernel模組提供的轉換方法之間的差異,我們透過下面的兩個例子來進行說明:
<code lang="ruby">
print  12d3.to_i		
-----result-----
123
</code>
<code lang="ruby">
print  Integer(12d3)
-----result-----
invalid value for Integer: "12d3" (ArgumentError)
</code>
透過這兩個例子,可以發現到to_i方法會直接轉換數值部分,而將非數值的部分省略掉。而Integer方法則會出現錯誤訊息,所以在使用時,必須更加的小心。
*除法:
使用除法時,要特別的小心。Ruby在做整數除法時,結果中的分數部分會被截斷,但我們卻渾然不覺。
<code lang="ruby">
print 12 / 2	#=>6
print 13 / 2	#=>6
</code>
若要取得具有分數計算的結果,至少要有一個運算值是浮點數才行。
<code lang="ruby">
print 13.0 / 2	#=>6.5
print 13 / 2.0	#=>6.5
</code>
*Math模組:
Math模組是Ruby內建模組之一,提供了一些經常使用的數學運算的方法。要使用模組時,可以透過include來將模組納入,就可以像使用Kernel模組所提供的方法一樣。而Kernel模組不需使用include納入的原因,是Ruby已經是先將Kernel模組include到Object類別。除了include外,也可以直接使用「模組名.方法名(引數)」來完成,而不用將模組納入類別裡頭。Math模組所提供的方法如下:
{| border="1" style="border-collapse;"
|-
||方法名
||說明
|-
||sin(x)、cos(x)
tan(x)、asin(x)
acos(x)、atan(x)
||用來求算三角函數的一些方法(x為弳度值)
|-
||exp(x)
||用來求算指數函數
|-
||log(x)、log10(x)
||用來求算自然對數與常用的對數
|-
||sqrt(x)
||用來求算平方根
|}
<code lang="ruby">
i = 2
print Math.sqrt( i )		#=> 1.4142135623730951 include Math
include Math
print sqrt( i )			#=> 1.4142135623730951
</code>
'''例外處理''':<br\>
這邊我們簡單的介紹一下,Ruby的例外處理。在前面我們可以看到當數值除以零時,就會產生「invalid value for Integer: "12d3" (ArgumentError)」這個ArgumentError錯誤。還有其他許多的例外狀況被定義於Exception這個類別,這邊我們就不多做說明,我們主要是來處理例外狀況的發生。而處理的方法可以透過begin~rescue~end如下表示:
<code lang="ruby">
begin
有可能發生例外的程式碼
rescue => 變數
例外發生時的處理措施
end
</code>
我們可以將可能會發生錯誤的程式碼放至於begin與rescue之間。而當產生例外時,則會執行rescue與end之間用來處理例外時的程式碼。而rescue後方的「=> 變數」當中的這個「變數」是用來存放例外物件,如果在例外處理時無需用到則可以省略。
<code lang="ruby">
puts "請輸入整數:"
begin
  n=Integer(gets)
  print "你輸入的整數為:",n, "\n"
rescue => msg
  print "你輸入的並非整數(",msg.message,")\n"
end
 
-----result-----
請輸入整數:
u
你輸入的並非整數(invalid value for Integer: "u\n") 
</code>
由這個範例可以看到,當發生錯誤時,會執行rescue與end之間的程式。並且使用到msg這個變數。message這個方法是由Exception類別所提供的方法,主要是顯示例外的資訊。常用的方法還有:
*class方法:例外種類
*backtrace方法:例外發生的位置資訊
<code lang="ruby">
puts "請輸入整數:"
begin
  n=Integer(gets)
rescue => msg
  puts msg.class
  puts msg.message
  puts msg.backtrace
end
 
------result-----
請輸入整數:
a
ArgumentError
invalid value for Integer: "a\n"
:1 
</code>
class方法讓我們知道範例發生的是引數錯誤的例外種類,backtrace方法則說明了錯誤發生位置是在於begin與rescue之間的程式碼第一行發生錯誤。<br\>
'''ensure'''<br\>
ensure是保證程式一定執行,不管是發生例外,或者是沒發生例外他一定會執行他所包覆的程式碼區段。執行語法如下:
<code lang="ruby">
begin
 有可能發生例外的程式碼
rescue 
 例外發生時的處理措施
ensure
 無論發生意外一定要執行
end
</code>
<code lang="ruby">
puts "請輸入整數:"
begin
  n = Integer(gets)
ensure
  puts "一定會執行的程式碼"
end
 
----result-----
請輸入整數:
a
:1: invalid value for Integer: "a\n" (ArgumentError)
一定會執行的程式碼 
</code>
'''retry'''<br\>
當發生例外時,透過rescue處理完以後想要繼續重新執行時,我們可以透過retry來達成。
<code lang="ruby">
begin
  有可能發生例外的程式碼
rescue 
  例外發生時的處理措施
  retry
ensure
  無論發生意外一定要執行
end
</code>
<code lang="ruby">
puts "請輸入整數:"
begin
  n=Integer(gets)
  print "輸入值為:",n,"\n"
rescue
  puts "請輸入整數:"
  retry
end
 
-----result-----
請輸入整數:
a
請輸入整數:
3
輸入值為:3 
</code>
這個範例,當我們輸入非整數時,則會透過retry重新執行一次begin與rescue之間的程式碼。
 
==String類別==
'''String類別''':<br\>
文字在Ruby中會被表示成String類別的物件。String類別裡定義了一套有效的運算符號以及方法,可用於提取子字串、插入與刪除文字、搜尋、取代等工作。
*建立字串:
Ruby提供了幾種建立字串的方法,例如透過String類別的new方法、%Q方法、單引號或雙引號等方法。我們這邊介紹較簡便的方式透過單引號、雙引號來建立字串。
<code lang="ruby">
建立方式如下:
     " 字串 "
    ' 字串 '
</code>
<code lang="ruby">
str1 = " 這是字串 "
str2 = ' 這是字串 '
</code>
而單引號與雙引號所建立的字串,差別在於使用單引號所建立的字串並不會去解釋像能夠用來換行的\n轉義字元、能夠嵌入變數或運算式的內嵌運算式等特殊字元。如下表示:
<code lang="ruby">
print  " 字串 \n "
print  ' 字串 \n '
print str1
print str2
-----result-----
字串
字串 \n
</code>
如同上面結果所示,雙引號會去解釋\n換行符號進行換行動作,但單引號所建立的字串並不會去解釋這些轉義字元,而是直接顯示出來。這邊我們列出一些特殊字元,提供大家參考:
{| border="1" style="border-collapse;"
|-
||轉義字元
||說明
|-
||\a
||警示音
|-
||\b
||倒退鍵
|-
||\cx
||Control-x
|-
||\n
||換行字元
|-
||\t
||定位字元(tab)
|}
*嵌入變數
要在字串裡嵌入變數或者是運算式時,可以使用逗號來進行區隔或者是透過內嵌運算式(embedded expressions)「#{ 變數 }」來達成。如下所示:
<code lang="ruby">
number = 10
str1 = " number值為 #{ number } \n"    #=>透過內嵌運算式
str2 = " number值為 ", number , " \n"    #=>透過逗號進行區隔
print str1
print str2
-----result----
number值為 10 
number值為 10  		
</code>
雖然兩種方式都能夠將變數或者是運算式嵌入字串裡頭,但使用內嵌運算式的方式會是較為方便。如上所示,使用逗號的方式,字串與變數之間都必須以逗號來進行區隔,當要嵌入的變數多時可能就容易出錯。
*建立多行字串:
如果要建立多行字串時,我們可以使用here document(即席文件)來達成,它能夠保留行與行之間的換行符號。它是由「<<」符號加上自訂的分界字元所組成的。表示方式如下:
<code lang="ruby">
<< 自訂的分界字元
     所要建立的多行字串
自訂的分界字元
</code>
<code lang="ruby">
str=<<eof	#=>我們設定eof為我們的分界字元
春眠不覺曉
處處聞啼鳥
夜來風雨聲
花落知多少
eof				#=>當出現eof代表字串結束
print str						
-----result-----
春眠不覺曉
處處聞啼鳥
夜來風雨聲
花落知多少
</code>
上面這個範例定義了一個eof的分界字元,當再一次出現時,則是該多行字串結尾的表示符號。
*取得字串長度
要取得字串長度可以使用「String類別」所提供的「length方法」或者是「size方法」。這邊要注意的Ruby的length(size)方法並不是以中文的字數來計算,而是以位元來計算,所以回傳的是位元數。
<code lang="ruby">
print "Hello,Ruby!".size		#=>11
print "這是字串".length		#=>12
</code>
中文字以三個位元來表示一個中文字,所以範例回傳為12。而要如何去正確的回傳中文字數呢!!當然你可以直接除以3來完成,或者是使用String類別所提供的split方法。split方法可以將字串依照指定的「樣式」或「字串」進行切割,並將切割後的字串存入陣列(陣列是Ruby用來存物件的容器,在後面章節將會介紹)而split方法我們將留到正規表示式在進行介紹。 
*串連字串
Ruby準備了許多種可以將字串相互連接起來的方法,例如:String類別所提供的「+」、「concate」或「<<」方法。這邊我們介紹「+」方法,來將字串進行串連。
<code lang="ruby">
str1="Hello,"
str2="Ruby"
str3=str1+str2
print str3    	#=>Hello,Ruby
</code>
'''擷取字串'''<br\>
想要擷取字串裡的部分字串進行操作時,可以透過String類別所提供的「[]」方法。「[]」是slice方法的別名,以下所教的[]方法的操作,都可以使用slice方法進行取代。
*存取單一字元時,格式如下:
<code lang="ruby">
字串 [ 索引 ]
</code>
<code lang="ruby">
str="abcde"
print str[2]		#=>99
</code>
這邊會發現透過索引所取出的並非字串而是字碼。所以想要取得字碼所代表的字串物件時,可以透過Integer類別所提供的chr方法,能夠字元編碼轉換為實際字元。
<code lang="ruby">
str="abcde"
print str[2].chr		#=>c
</code>
索引的算法是從0開始算起,所以上面這個例子a為0、b為1、c為2,所以取得的字元為c。
*取得連續的字元,格式如下:
<code lang="ruby">
字串 [ 起始索引 , 數量 ]
</code>
<code lang="ruby">
str ="我是字串"
print str[0,6]		#=>我是
</code>
這邊我們刻意用中文字來表示,Ruby是以位元來表示,而3個位元表示一個中文字。這邊我們起始索引為0,數量為6表示為6個位元,所以取出字串中的”我是”這個部分字串。<br\>
<br\>
'''字串的搜尋、插入與取代'''<br\>
String類別提供index方法來搜尋指定的字串之索引。以及insert方法透過指定的索引以及所要插入的字串,來將部份字串插入字串內。還有「[ ] =」方法來將字串中的部分字串進行取代。
*index方法:搜尋指定的字串之索引。
<code lang="ruby">
str="我是字串"
print str.index("字串")		#=>6
</code>
上面範例透過index方法來搜尋”字串”的索引,回傳值會是字串兩個字的起始索引,由於3位元表示一個中文字,所以回傳索引為6。index方法也可以指定搜尋的起始位置,如下表示:
<code lang="ruby">
index(欲搜尋的字串,起始位置)
</code>
<code lang="ruby">
str="abcabc"
print str.index("a",2)
-----result-----
3
</code>
由於我們的起始位置從位置2的c開始,並沒有搜尋到位於位置0的a,所以結果為3。
*insert方法:接下來我們使用insert方法來將指定的字串給插入。
<code lang="ruby">
str="我是字串"
str.insert(str.index("字串"),"字串")
print str	#=>我是字串字串
</code>
這邊我們將前一個範例所產生的索引當為insert的第一個參數,作為所要插入的起始索引,而將”字串”兩個字插入str字串中。現在我們來示範使用「[ ]=」方法來進行取代。
<code lang="ruby">
str="我是字串字串"
n=str.index("字串")
str[n,6]="一行"
puts str
 
-----result-----
我是一行字串
</code>
使用「字串名 [ 初始索引 , 取代數量 ] = 新字串」就可以進行取代,上面我們先使用index方法搜尋到第一次出現字串的索引,將它當作初始索引,將新字串”一行”往後取代6byte也就是”字串”兩個字。除了使用[]=方法進行取代外,還可以使用String類別所提供的sub方法,表示方式如下:
<code lang="ruby">
字串名.sub("欲被取代的字串","新字串")
字串名.sub! ("欲被取代的字串","新字串")
</code>
<code lang="ruby">
str1="我是字串字串"
str2=str1.sub("字串","一行")
print str2			
-----result-----
我是一行字串
</code>
使用sub方法進行取代無需索引,sub會從字串頭開始進行搜尋欲被取代的字串。但相對的就無法指定兩筆相同的部分字串該取代哪一個,而是會取代第一筆找到的符合的字串。另外一點是sub方法並不會直接修改原始物件,而是會產生新的物件來儲存修改後的字串。如果想要直接修改原始物件的話,可以直接在sub後面加上「!」。	現在介紹另外一種取代方式gsub方法,它會從頭到尾搜尋字串,將符合的、欲取代的字串進行取代。而gsub與sub一樣擁有另一種破壞性的方法:gsub!。
<code lang="ruby">
str1="我是字串字串"
str2=str1.gsub("字串","一行")
print str2			
-----result-----
我是一行一行
</code>
*破壞性方法: 指方法會改變原本物件的內容,在Ruby以「!」來表示此方法為破壞性方法,所以在使用要特別小心。
<code lang="ruby">
str1="我是字串字串"
str1.gsub!("字串","一行")
print str1
-----result-----
我是一行一行
</code>
我們使用破壞性方法gsub!直接更改了str1的內容,不需將內容在另外指派。但str1因此無法再恢復到更改前的內容。	
*String類別其他方法:
這邊介紹幾個String類別常用的方法,表示如下:
{| border="1" style="border-collapse;"
|-
||方法
||說明
|-
||upcase / upcase!
||將英文字母全部轉換成大寫
|-
||downcase / downcase!
||將英文字母全部轉換為小寫
|-
||swapcase / swapcase!
||將英文字母大寫轉為小寫,小寫轉為大寫
|-
||capitalize / capitalize!
||將第一個字母轉回大寫,其他轉為小寫
|}
<code lang="ruby">
str="abCDe"
puts "原始為#{str},經upcase後為"    + str.upcase
puts "原始為#{str},經downcase後為"  + str.downcase
puts "原始為#{str},經swapcase後為"  + str.swapcase
puts "原始為#{str},經capitalize後為"  + str.capitalize
-----result-----
原始為abCDe,經upcase後為ABCDE
原始為abCDe,經downcase後為abcde
原始為abCDe,經swapcase後為ABcdE
原始為abCDe,經capitalize後為Abcde
</code>
 
==資料儲存結構==
Ruby主要的資料儲存結構有Array以及Hash,Array與Hash都是Ruby的內建類別,能夠儲存不同類型的物件,包含數值、字串、符號等物件。現在我們就來一一介紹它們的功用:<br\>
===Array===
Array是用來將多個物件集合在一起的物件,能夠保存String、Integer、Hash、Array等物件。每個陣列的元素,都要透過索引才能產生關聯或參照。而每個陣列元素會自動產生整數索引;索引的編號由0開始,每增加新的元素,索引也依序加1。
*建立陣列
建立陣列的方法有幾種,我們這邊介紹使用new方法以及[]方法來建立陣列。使用new方法來建立,語法如下
<code lang="ruby">
陣列名稱 = Array.new
</code>
<code lang="ruby">
arr=Array.new
</code>
目前arr的陣列裡沒有任何的元素,我們可以再建立陣列的時候,設定陣列中元素的數量
<code lang="ruby">
arr=Array.new(5)
p arr
-----result-----
[nil, nil, nil, nil, nil]
</code>
由於我們指設定了陣列的元素數量,並沒有指派元素值給它,所以顯示的都為nil。我們可以再建立陣列時給予元素的初始值。
<code lang="ruby">
arr=Array.new (5,"one")
p arr
-----result-----
["one", "one", "one", "one", "one"]
</code>
接下來我們來看Array類別所以提供的另外一種建立陣列的方式[ ] 方法,建立語法如下:
<code lang="ruby">
陣列名稱 = [ ]
</code>
使用這樣的方式就能夠更方便的建立一個空的陣列,但在給予陣列元素初始值或者事先設定元素數量時,還是使用new方法會較為方便。這邊必須注意的是元素與元素之間必須以逗號進行區隔。
<code lang="ruby">
arr=[1, "one",2, "two"]
p arr
------result-----
[1, "one", 2, "two"]
</code>
*存取陣列單一元素
存取單一陣列元素可以使用「陣列名稱 [ 索引 ] 」取得元素,以及使用「陣列名稱 [ 索引 ] = 元素」來存入元素。
<code lang="ruby">
arr=[1, "one",2, "two"]
arr[4]=3
puts arr[0]
puts arr[-1]
puts arr[-2]
-----result-----
1
3
"two"
</code>
*而要存取多個連續元素時,可以使用兩種方式:
1. 陣列名稱 [ 起始索引 , 元素數量]
<code lang="ruby">	
arr=[1, "one",2, "two"]
puts arr[1,3]
-----result-----
one
2
two
</code>
如果要再陣列插入元素時,可以使用:<br\>
(1).陣列名稱 [索引 , 0] = 元素<br\>
(2).陣列名稱 [ 索引 ] = 元素,會覆蓋掉原本的元素。
<code lang="ruby">
arr=[1, "one",2, "two"]
arr[1,0]="three"
p arr
-----result-----
[1, "three", "one", 2, "two"] 
</code>		
2. 陣列名稱 [ 範圍物件 ]<br\>
Range(範圍)物件是以一個開頭值與一個結尾值來代表一串值,範圍會被寫成在頭尾兩值之間放入「兩個或三個點號」。如果使用兩個點號,則此範圍包含結尾的值。如果使用三個點號,則此範圍不包含結尾的值。
<code lang="ruby">
1..4  	#表示1到4
1...4		#表示1到3
</code>
<code lang="ruby">
arr=[1, "one",2, "two"]
puts arr[1..3] ,"\n"	  #取得索引為1到3的元素
puts arr[1…4]		  #取得索引為1到3的元素
-----result-----
one2two
one2two
</code>
下面這練習,我們設計一個陣列能夠儲存姓名、生日。
<code lang="ruby">
arr=[]
i=0
puts "input:"
str = gets.chomp!
while( str! = "q" )
  arr[i] = str
  puts "input:"
  str = gets.chomp!
  i = i + 1
end
p arr
-----result-----
input:
john,7/9
input:
merry,9/5
input:
q
["john,7/9", "merry,9/5"] 
</code>
透過陣列,將姓名以及生日的字串儲存起來,再來我們只要針對每筆字串進行搜尋、列印、刪除、新增的動作等,功能就能更佳的完善了。<br\><br\>
'''逐項處理陣列的元素'''<br\>
如果要逐項處理陣列的元素,可以透過each方法,each方法是(Iterater)迭代器的一種,主要是用來反覆執行某動作。each後面緊接著「{ }」稱為區塊,區塊可以定義多個連續的動作。而區塊的前方有個「|變數|」,each方法會將每一圈逐項取得的陣列元素指派給這個變數,以供區塊內程式的使用。
*each方法用法如下:
<code lang="ruby">
陣列.each {  |變數|
  想要反覆執行的動作
}
</code>
<code lang="ruby">
arr=[1, "one",2, "two"]
arr.each{ |var|
   puts var
}	
-----result-----
1
one
2
two 
</code>
*each_with_index:另外一種,它能夠連索引都一起顯示出來
<code lang="ruby">
陣列.each_with_index {  |value,index|
   想要反覆執行的動作
}
</code>
<code lang="ruby">
arr = [1, "one",2, "two"]
arr.each_with_index{ |value , index|
   print index,":",value,"\n"
}	
-----result-----
0:1
1:one
2:2
3:two 
</code>
接下來這個範例我們搭配著判斷敘述,將數值部分顯示出。
<code lang="ruby">
arr=[1, "one",2, "two"]
arr.each{ |var|
  if var.class == Fixnum
    puts var
  end  
}
-----result-----
1
2 
</code>
透過each方法就能夠對陣列裡的元素逐一進行判斷,對所需要的資料來進行處理。當然也可以使用while等迴圈來進行,但使用each會是較方便的一種選擇。<br\><br\>
'''請開始做練習1、2、3'''<br\><br\>
'''刪除陣列特定元素'''<br\>
當要刪除陣列特定的元素時,可以使用delete方法來刪除該元素。除了指定元素來刪除外,也可以使用delete_at方法透過索引來進行刪除。
*delete方法:
<code lang="ruby">
arr=[1, "one",1, "two"]
arr.delete(1)
p arr				#=>[ "one","two"]
</code>
*delete_at方法:
<code lang="ruby">
arr=[1, "one",1, "two"]
arr.delete_at(2)
p arr				#=> [1, "one", "two"] 
</code>
'''換掉陣列的元素'''<br\>
透過collect(或map)方法,能夠將陣列中的元素轉換成其他元素的方法。如果在方法後加上「!」時,會直接改變原本陣列的元素。如果沒加的話,會產生新的陣列。Collect方法後會緊跟著「{ }」區塊,而區塊的前方有個「|變數|」,Collect方法會將每一圈逐項取得的陣列元素指派給這個變數,以供區塊內程式的使用。
<code lang="ruby">
arr=[1,2,3]
arr2=arr.collect { | var |
        var * 2
     }
p arr				#=> [1, 2, 3]
</code>
<code lang="ruby">
arr.collect! { | var |
   var * 2
}
p arr				#=> [2, 4, 6]
</code>
下面我們就搭配著判斷敘述來進行取代的動作。
<code lang="ruby">
arr=[1,2,3]
arr.collect! { | var |
 if var>2
   var * 2
 end
}
p arr     #=> [nil, nil, 6]
</code>
這個範例我們發現我們只是希望將大於2的數值乘上2,但其他數值保留不變,但卻出現了nil的情形。那是因為collect方法是將在區塊內的最後一行的程式敘述的值,當作回傳值。所以當判斷小於2時,並沒指派值來進行回傳,所以理所當然是nil。<br\>
<code lang="ruby">
arr=[1,2,3]
arr.collect! { | var |
  if var>2
     var * 2
  else
     var
  end
}
p arr       #=> [1, 2, 6]
</code>
'''陣列其他方法'''<br\>
*size方法(length方法):能夠取得陣列的大小
<code lang="ruby">
arr=[1, "one",2, "two"]
print arr.size		#=>4
print arr.length		#=>4
</code>
*unshift方法:在陣列的前端插入新的元素
*shift方法:能夠取出陣列前端的元素
<code lang="ruby">
arr=[1, "one",2, "tw	o"]
arr.unshift(0)
p arr				#=> [0, 1, "one", 2, "two"]
arr.shift
p arr				#=> [1, "one", 2, "two"]
</code>
*push(<<)方法:在陣列後端插入新的元素
*pop方法:能夠取出陣列後端的元素
<code lang="ruby">
arr=[1, "one",2, "two"]
arr.push(0)
p arr				#=> [1, "one", 2, "two", 0] arr.pop
p arr				#=> [1, "one", 2, "two"]
</code>
<br\><br\>'''請開始練習第4、5題'''<br\><br\>
 
===Hash===
雜湊類似陣列,與陣列功能相同,陣列是以索引去取得元素,而雜湊則是透過”鍵 ”取得物件,能夠使用任何Ruby物件來當作鍵,例如可以使用整數、字串,甚至是陣列來當鍵。雜湊所存入的鍵值並非依照存入順序,換句話說就是沒有順序可言,可能第一筆存入的資料會是最後輸出。<br\><br\>
'''建立雜湊''':<br\>
建立雜湊也有幾種建立方式,這邊我們介紹以new方法以及{ }來建立雜湊,建立方式如下:	
<code lang="ruby">
雜湊名稱 = Hash.new
</code>
<code lang="ruby">
hname= Hash.new
p hname			#=>{}
</code>
使用new方法也可以指定預設值,當沒有定義鍵時,其傳回值會是該預設值。
<code lang="ruby">
hname= Hash.new(5)
puts hname["one"]		#=>即使沒有定義鍵one還是顯示5
puts hname[1]			#=>即使沒有定義鍵1還是顯示5
-----result-----
5
5
</code>
如同陣列,雜湊也有簡便的建立方式,透過{ }來建立雜湊。鍵值之間以符號 => 來產生關聯,而每組的值之間則是以逗號來進行區隔。建立方式如下:
<code lang="ruby">
雜湊名稱 = { 鍵 => 值 , 鍵 => 值}
</code>
<code lang="ruby">
hname= {"one"=>1,"two"=>2}
p hname			#=>{"two"=>2, "one"=>1}
</code>
'''存取雜湊:'''<br\>
存取雜湊的值方式與陣列很類似,差別在於陣列是使用索引來存取元素,而雜湊是使用鍵來存取得值。語法如下:
<code lang="ruby">
雜湊名稱["鍵"]= 值
</code>
<code lang="ruby">
hname= {"one"=>1,"two"=>2}
print hname["one"]		#取得鍵為one的值
hname["three"] = 3		#加入新的值3,鍵為three
p hname
-----result-----
1
{"one"=>1,"two"=>2,"three"=>3}
</code>
'''逐項處理雜湊內容'''<br\>
除了陣列類別提供了each方法外,雜湊類別也提供了each方法。透過each方法就能夠逐項的處理雜湊的內容。而雜湊所提供的each方法與陣列的each方法不同處在於多了一個變數,來儲存鍵。表示方法如下:
<code lang="ruby">
雜湊名稱.each{ | 鍵的變數 , 值的變數|
	想要反覆執行的程式
}
</code>
<code lang="ruby">
hname= {"one"=>1,"two"=>2}
hname.each{| key , value|
    print key,"值為",value,"\n"
}		
-----result-----
two值為2
one值為1 
</code>
接下來我們使用在陣列說明過的姓名、生日的範例,以雜湊來紀錄資料,我們以姓名來當做鍵值,其餘當作值。這樣我們就能透過姓名來找尋到我們所需要的值,無需要一筆一筆進行比對。
<code lang="ruby">
hash = {}
puts "name:"
name = gets.chomp!
while(name != "q")
  puts "birth:"
  birth = gets.chomp!  
  hash[name]= birth
  puts "name:"
  name = gets.chomp!
end
p hash
puts hash["merry"]     #=>直接以merry當鍵值,搜尋merry的生日
-----result-----
7/9
name:
merry
birth:
9/5
name:
q
{"john"=>"7/9", "merry"=>"9/5"}
9/5
</code>
<br\>
'''請開始練習第6題'''<br\><br\>
 
==檔案處理==
Ruby主要是利用Dir和File的類別方法與實體方法,處理檔案目錄(資料夾)和檔案的操作。我們就先開始由目錄開始介紹。<br\><br\>
'''目錄'''<br\>
這麼我們介紹IO類別的子類別Dir所提供的四個對目錄操作的類別方法。分別是Dir.pwd、Dir.chdir、Dir.mkdir以及Dir.entries這幾種類別方法。Dir.pwd主要是顯示目前的所在目錄(資料夾)。Dir.chdir則是將目前從所在的目錄更換到所指定的目錄。Dir.mkdir則是用來建立新的目錄(資料夾)。Dir.entries能夠顯示出目前目錄中有哪些其他的子目錄、檔案(包含隱藏檔)。以下我們就來實際進行的操作:
<code lang="ruby">
p Dir.pwd             #=>顯示出當前目錄, "C:/RubyApplication1/lib"
Dir.chdir("C:/")         #=>切換到C:/下
p Dir.pwd        
Dir.mkdir("test")        #=>在C:/底下使用mkdir建立名為test的目錄
Dir.mkdir("c:/test/test2")  #=>使用絕對路徑的方式來建立test2目錄
p Dir.entries("c:/test")    #=>顯示test目錄底下有哪些子目錄
-----result-----
"C:/RubyApplication1/lib"
"C:/"
[".", "..", "test2"]
</code>
上面這個例子,我們先使用pwd先顯示出目前的所在目錄,再來使用chdir將所在目錄更改到C:/。接下來使用mkdir在「C:/」建立一個名為test的資料夾,以及使用mkdir在test資料夾內建立一個名為test2的資料夾。最後在以entries來顯示路徑test中有哪些子目錄、檔案,這邊要注意的是entries是以一個陣列型態來當做回傳值。<br\><br\>
'''請開始練習第1題'''<br\><br\>
''' 檔案'''<br\>
Ruby處理檔案,主要是由File類別來進行處理。接下來我們將介紹一些對檔案的基本操作,使用File類別的方法,來建立新的檔案、開啟舊檔及檔案的搜尋、檔案的寫入與讀取。
*檔案的建立、開啟與寫入
要建立新檔案或開啟舊檔時,可以使用File類別所提供的new或open方法,表示方法如下:
<code lang="ruby">
物件名 = File.open ("檔案名稱" , "檔案模式")
</code>
第一個引數是新檔案的名稱,第二個引數則是指定檔案的模式。檔案的模式說明如下:<br\>
{| border="1" style="border-collapse;"
|-
||模式
||說明
|-
||"r"
||只限讀取。從檔案的起始處開始讀取(預設模式)
|-
||"r+"
||可讀可寫。從檔案的起始處開始讀取
|-
||"w"
||只限寫入。截斷現有檔案的長度至零,或建立一新檔案,以備寫入。
|-
||"w+"
||可讀可寫。截斷現有檔案的長度至零,或建立一新檔案,以備寫入。
|-
||"a"
||只限寫入。若檔案存在,則由檔案的結尾處開始寫入;否則建立新檔案以備寫入。
|-
||"a+"
||可讀可寫。若檔案存在,則由檔案的結尾處開始寫入;否則建立新檔案以備寫入。
|}
這邊要注意的是當開啟檔案進行處理時,處理完畢必須要將檔案使用close方法進行關閉的動作。一支程式能夠開啟的檔案是有限的,所以如果不進行關閉時,可能會產生例外。而要進行寫入時,我們可以使用write方法,來將資料進行寫入。
<code lang="ruby">
io = File.open("C://test.txt","a+" )
io.write("這是\r\n寫入測試")
io.close
</code>	
OutPut:<br\>
[[image:第五張圖.jpg]]	
<br\>
底下我們先在C底下建立個名為test的目錄來存放所新增txt檔案,而這個txt檔案存放的是由使用者自行輸入的內容。
<code lang="ruby">
puts "請輸入目錄名稱:"
dirname = gets.chomp!
if not(Dir.entries("c:/").index(dirname))
  Dir.mkdir("c:/test")
end
io = File.open("c:/test/text.txt","a+")
puts "請輸入檔案內容:"
str = gets.chomp!
while(str!="q")
  io.write(str+"\r\n")
  str=gets.chomp!
end
io.close
-----result-----
請輸入目錄名稱:
test
請輸入檔案內容:
http://www.yuntech.edu.tw
http://webmail.yuntech.edu.tw
q
</code>
'''請開始練習第2題'''<br\><br\>		
'''檔案的查詢'''<br\>
Ruby的File類別提供file?方法來搜尋檔案,回傳結果為true或false。
<code lang="ruby">
File.file?( "C://test.txt")
-----result----
true
</code>
'''檔案讀取'''<br\>
讀取檔案時,我們可以使用使用IO類別所提供的gets、readlines、read方法。彼此的差異是gets方法是指一次讀取一行;而readlines則是每次讀取一行,直到結尾;read方法則是一次讀取全部,如果加上引數的話則是指定取幾個位元。除了這三種方法,IO類別還提供許多其他種類的輸出方法,這邊我們主要介紹這三種的使用方式。
*gets
<code lang="ruby">
io = File.open("C://test.txt" )
print io.gets
io.close
-----result-----
這是
</code>
接下來我們使用readlines方法來達成與gets方法相同的結果。下面這個範例我們將每一行事先存入陣列,再透過陣列來顯示出結果。
*readlines
<code lang="ruby">
io = File.open("C://test.txt" )
arr=io.readlines
print arr[1]
io.close
-----result-----
這是 
</code>
*read
下面這範例是使用read方法來達成相同的結果,記住當沒指定引數時,會一次載入全部。當指定時,引數代表顯示的位元數,這邊由於是中文所以6bit代表兩個中文字。
<code lang="ruby">
io = File.open("C://test.txt" )
print io.read(6)
io.close
-----result-----
這是
</code>
*pos
當要合併使用這些方法,要注意目前的檔案指標。查詢或指定檔案指標可以使用IO類別所提供的pos方法。我們就直接以範例來進行說明:
<code lang="ruby">
io = File.open("C://test.txt" )
print io.gets
puts "pos=#{io.pos}"
print io.read
#這邊發現使用read方法進行輸出只顯示了寫入測試,那是因為
#目前檔案指標在8。要解決這問題,必須先把檔案指標回歸到0。
io.pos=0
puts
puts "pos=#{io.pos}"
print io.read
io.close 
-----result-----
這是
pos=8
寫入測試
pos=0
這是
寫入測試
</code>
接下來我們設計一個能夠透過使用者輸入來讀取檔案內容的小程式:
<code lang="ruby">
puts "請輸入檔案名稱:"
filename=gets.chomp!
while(not(File.file?("c:/test/#{filename}"))&& filename!="q")
  puts "沒有該筆檔案,請輸入檔案名稱:" 
  filename=gets.chomp!
end
io=File.open("c:/test/#{filename}")
puts "檔案內容:"
print io.read
io.close
-----result-----
請輸入檔案名稱:
text.txt
檔案內容: 
http://yuntech.edu.tw 
http://webmail.yuntech.edu.tw 
</code>
'''請開始練習剩下的題目'''<br\>
==常規表示式(Regexp)類別==
常規表示式主要是用來判斷「字串」是否符合某個「樣式」,當符合時,在對字串進行操作。<br\><br\>
'''常規表示式建立'''<br\>
我們現在就先介紹該怎樣建立常規表示式物件。建立方式與其他類別一樣可以使用new方法來建立,也有較簡單的方式,使用「/ /」來建立,表示如下:
<code lang="ruby">
/樣式/ =~ 字串
</code>
<code lang="ruby">
p /programming/ =~ "Ruby programming" 
p /programming/ =~ "Ruby Programming"
-----result-----
5
nil 
</code>
當比對成功時,會傳回比對成功的起始位置。當比對失敗時,則會傳回nil,所以我們也可以使用在條件判斷式中,當條件使用。要注意的是起始位置是由0開始算起,所以上例的的起始位置p為5。<br\><br\>
'''多字選一的比對方式'''<br\>
當我們要比對的字數是多個選一個字的話,我們可以使用「[ ]」來將複選的字括弧起來。而在「[ ]」裡我們可以使用「-」來表示一個範圍,例如:[A-Z]是指大寫A到Z其中一個都可以;[a-z]指小寫a到z其中一個都可以;[1-9]指數字1-9其中一個都可以。這邊還有一個重要的符號「^」,當「^」在「[ ]」裡時,代表相反的意思。
<code lang="ruby">
p /[arg]/=~"Ruby programming" 
#比對到a或r或g都算比對成功
p /r[oa]/=~"Ruby programming"  
#比對到ro或ra都算比對成功
p /[a1-9]/=~"9Ruby programming" 
#比對到a或數字1到9都算成功
p /[^abc]/=~"bcd"
#除了字元abc以外的字元都算比對成功
-----result-----
6
6
0
2
</code>
'''多組選一的比對方式'''<br\>
我們剛剛說明多個字選擇一個字進行比對,現在我們來說明多個字組進行比對。要達成多個字組比對時,可以使用「|」符號來達成。
<code lang="ruby">
p / ra | ro/ =~ "Ruby programming"
#代表比對到ra或ro時,都算比對成功
</code>
'''scan方法'''<br\>
scan方法是由String類別所提供的方法,它主要是掃描所有字串,並且將與樣式比對成功的字串指派給區塊裡頭的變數進行處理。表示方式如下:
<code lang="ruby">
字串.scan(樣式) { |變數|
    區塊敘述
}
</code>
<code lang="ruby">
str="Ruby programming"
str.scan(/[aeiou]/){|matched|
    print matched
}
-----result-----
uoai 			
</code>
這個練習是針對字串str從頭到尾進行掃描,如有字母則依序印出
<code lang="ruby">
str="Ruby programming"
str.scan(/ro|ra|ru/){|matched|
    puts matched
}
-----result-----
ro
ra 
</code>
這練習是將字串str從頭到尾進行掃描,如有字組是ro、ra或ru則顯示出,上面結果只顯示出ro與ra但沒顯示ru,是因為字串當中的Ruby子字串中的單字R是大寫,所以比對失敗。後面我們會介紹如何不區分大小寫來進行比對。
<br\><br\>
'''請進行練習1、2'''
<br\><br\>
'''比對任意字元、行首與行尾的比對'''<br\>
在樣式裡「.」點,可以用來比對任意的字元。如下面範例所示:
<code lang="ruby">
p /R . by/ =~ "Ruby programming" 
#只要是大寫R且by結尾,R與by中夾任意一字元都成立
p /R . . y/ =~ "Ruby programming" 
#只要是大寫R且y結尾,大寫R與y中夾二任意字元都成立
-----result-----
0
0
</code>
如果只是想比對一「行」的頭或是尾,我們就可以使用轉譯字元(meta-character),「^」代表只比對行首,這與前面的多字選一的相反符號是有差異的,相反符號必須是在「[ ]」才會成立;使用「$」來表示指比對行尾。這樣子就不會去比對行裡的內容,只會單純去比對行首或行尾。
<code lang="ruby">
p  /^ABC/  =~  /aABC/
p  /^ABC/  =~  /ABC/
p  /ABC$/  =~  /ABCa/
p  /ABC$/  =~  /ABC/
-----result-----
nil
0
nil
0
</code>		
'''倒斜線樣式'''<br\>
與字串的換行符號「/n」一樣,樣式也有許多倒斜線來幫助樣式的建立。我們下面列出了幾樣:
{| border="1" style="border-collapse;"
|-
||倒斜線符號
||說明
|-
||\s
||表示空白,這邊會與空白字元、定位字元、換行字元、換頁字元比對成功
|-
||\d
||與0到9之間的數字比對成功
|-
||\w
||與英文及數字比對成功
|-
||\A
||與字串前端比對成功(與「^」是有差異的,「^」是針對”行”首。
|-
||\Z
||與字串尾端比對成功(與「$」是有差異的,「$」是針對”行”尾。
|-
||\特殊字元
||如果要比對有特殊含意的字元,如:^、$、[等,都可以在這些字元前加上\進行比對。
|}
<code lang="ruby">
p  /AB\sCD/=~"aAB CD"				#=>1
p  /AB\sCD/=~"aAB\nCD"				#=>1
p  /\d\d-\d\d\d\d\d\d\d/=~"05-1234567"	#=>0
</code>
'''split方法'''<br\>
split方法是由String類別所提供的方法。split方法可以將字串依照指定的「樣式」或「字串」進行切割,並將切割後的字串存入陣列,並回傳陣列。其表示格式如下:
<code lang="ruby">
字串.split(樣式或字串) 
</code>
<code lang="ruby">
str="a;b,c!d,e"
p str.split(/[!,;]/)  #=>["a", "b", "c", "d", "e"]
</code>
這個範例的字串是以空白符號以及痘號進行區隔。所以我們在樣式裡,使用了多選一的方式來進行切割。使用split方法切割會將結果儲存到陣列並回傳。
<br\><br\>
'''請進行練習3、4、5'''
<br\><br\>
'''重覆出現的字元、不區分大小寫的比對'''<br\>
如果要比對的類似像ABBBBC、ABBC這種只要是A開頭C結尾的字,而B可以無限多個。Ruby提供了「*」、「+」、「?」字元來代表反覆出現的樣式。如下表所示:
{| border="1" style="border-collapse;"
|-
||字元
||說明
|-
||*
||表示出現0次以上
|-
||+
||表示出現1次以上
|-
||?
||表示出現0次或1次以上
|}
<code lang="ruby">
p  /A*BC/ =~ "AAABC"
p  /A*BC/ =~ "BC"
-----result-----
0
0
</code>
上面所說的是針對一個重覆出現的字元進行比對,如果比對的是" 一組字元 "的話,我們可以透過( )」來達成。
<code lang="ruby">
p  /(ABC)+/ =~ "AAABCABCCC"
p  /(ABC)+/ =~ "AAABBBCCC"
-----result-----
2
nil
</code>
如果想要在比對的時候,不區分大小寫的話,我們可以使用「i」這個常規表示式選項。常規表示式選項是放置於最後斜線「/」的後面,如下面範例所示:
<code lang="ruby">	
p /programming / i =~ "Ruby programming"   #=>0
#加上i以後,比對時就不會區分大小寫
</code>
常規表示式選項除了i以外,還有其他像是s用來將字串視為Shift_JIS進行比對、u用來將字串視為UTF-8進行比對等常規表示式選項。<br\><br\>
'''最短距離比對「+?」、「*?」'''<br\>
<code lang="ruby">	
str="Ruby programming,java programming,c programming"
str.scan(/ruby.*,/i){|matched|
  p matched  
}
-----result-----
"Ruby programming,java programming,"
</code>
上面這個例子,我們只是想要取出Ruby programming而已但卻連java都取出了,那是因為Ruby的預設是取出比對中最長距離的結果。所以我們必須在*後方加上「?」,才能正確比對出最短距離。
<code lang="ruby">
str="Ruby programming,java programming,c programming"
str.scan(/ruby.*?,/i){|matched|
p matched  
}
-----result-----
"Ruby programming,"
</code>
'''sub方法與gsub方法'''<br\>
sub方法與gsub方法是String類別所提供的方法,主要是用在字串的替代上,透過樣式比對到的字串,使用一個新字串來加以取代成為一個新字組。而sub與gsub的差別在於sub只會取代第一個比對到的字串,而gsub則會取代所有比對到的字串。這邊要注意的事,使用sub與gsub方法後,會產生新的字串,並不會去修改到原始字串。如果要直接修改原始字串的話,必須加上「!」在sub或gsub的後方。表示如下:
<code lang="ruby">
字串.sub! (樣式 , 新字串)
字串.gsub! (樣式 , 新字串)
</code>
<code lang="ruby">
str="a b c d e"
p str.sub(/\s/,"_")
p str.gsub(/\s/,"_")
p str
-----result-----
"a_b c d e"
"a_b_c_d_e"
"a b c d e"
</code>
'''請完成剩下的練習'''
==方法、Proc、lambda==
前面章節我們都在Object類別內,使用Ruby內建類別所提供的方法進行程式的處理。現在我們就來介紹方法以及方法的建立、操作等,使用我們自己所建立的方法,來幫助程式撰寫上更加順暢。
===方法===
*方法的分類
類別內定義了兩種方法,分別為實體(物件)方法以及類別方法。實體(物件)方法就像前面我們介紹Array類別時,所使用的size、sort等實體方法,它們的呼叫格式都是「物件.方法名(引數)」,ex:「arr.size、str.sub(“a”,”b”)」。而類別方法則是不用建立物件,就可以直接使用該方法,只要透過「類別名.方法名(引數)」。類別方法主要是搭配著類別變數來進行操作,當介紹類別章節時,我們再進一步的介紹。現在我們就開始來介紹實體方法的定義等操作。
*方法的呼叫方式:
<code lang="ruby">
物件.方法名(引數1,引數2…..引數n)
</code>
這邊的物件就是一個類別所建立的物件,例如使用Array類別所建立的arr物件。而方法名則是在Array類別內所建立的方法,該方法需要傳遞引數,則必須指派引數給該方法,如果不需要則可以省略。這邊我們稱物件為接收者,因為是將方法名以及引數傳遞給這個物件。使用它所擁有的方法,進行程式的操作。
[[Image:方法1.jpg]]
*定義方法
方法定義的語法格式如下:
<code lang="ruby">
def 方法名 ( 參數1 , 參數2….參數n)
    想要執行的程式碼
end
</code>
<code lang="ruby">
def printer( name )
    print "Hello,"+name
end
printer("Ruby")
-----result-----
Hello,Ruby
</code>
上面這個例子我們建立一個名為printer的方法,它有一個參數叫name。當呼叫printer方法時,會將Ruby字串指派給name這變數,進行操作。這邊我們發現到呼叫的格式並不是「物件.方法名(參數)」,而是直接使用「方法名(參數)」,那是因為我們在Object類別裡定義方法,並且在Object類別裡進行呼叫。所以不用指定物件,它會在Object類別裡搜尋是否有個名叫printer的方法。
*參數預設值
參數可以指定預設值。當沒有傳遞引數給予參數時,則參數就使用該預設值。指定方式是透過「=」符號來進行指定。
<code lang="ruby">
def printer( name="Ruby" )
  puts "Hello,"+name
end
printer
printer("John")
-----result-----
Hello,Ruby
Hello,John
</code>
這樣要注意的是指定預設值必須從參數最右邊開始指定。如下
<code lang="ruby">
正確:
def printer( name1,name2="Ruby",name3="Java" )
 
end
錯誤:
def printer( name1,name2="Ruby",name3 )
 
end
def printer( name1="Ruby",name2,name3 )
 
end
</code>
*可改變數量的參數
有時候,我們不曉得方法會有多少個引數。Ruby在這方面的處理很有彈性,因為只要在參數前面加上*,傳送給方法的引數數量,就可以彈性的改變。
<code lang="ruby">
def printer( *name )
   p name
end
printer("John","Merry","Bill")
-----result-----
["John", "Merry", "Bill"]
</code>
從上面這個範例,我們得知name是一個陣列物件。我們就可以由name陣列取出所傳遞的引數,進行操作。
*方法的回傳值
在方法中可以使用return敘述,來回傳值。當沒有使用return來指定回傳值時,會以方法中最後一敘述當作回傳值。表示範例如下:
<code lang="ruby">
def volume(x,y,z)
    return x*y*z
end
print volume(1,2,3)			
-----result-----
6
</code>
<code lang="ruby">
def compare(a)
   if a>10
     "a > 10"
   elsif a<10
     "a < 10"
   else
     "a = 10"
   end
end
print compare(5)
-----result-----
a < 10
</code>
*方法的別名
我們可以使用Ruby的關鍵字alias來為方法建立別名。語法表示如下:
<code lang="ruby">
alias 別名 原名 
</code>
下面這個範例是透過alias為compare建立另一個別名為area。所以當呼叫area時,等同於呼叫compare。
<code lang="ruby">
def volume(x,y,z)
    return x*y*z
end
alias  area volume
print  area(5, 4, 2)		
-----result-----
40
</code>
 
===區塊===
區塊是由左大括弧「{」(或do)以及右大括弧「}」 (或end)所組成的,它不能單獨存在,只能夠伴隨著方法調用,例如我們前面章節所提到的迭代器each就有使用到區塊來幫助方法的運作。區塊內能夠指定參數,而參數會事先定義界定符「| |」內,參數之間以逗號進行區隔。當在方法內呼叫區塊時,使用yield來進行呼叫,當區塊需要參數時,則yield後方需加上引數,例如「yield 1,2」。
<code lang="ruby">
def math(i,j)
   yield(i+j)
end
math(1,2){ |x| 
puts x
}
</code>
這個範例我們建立了一個math方法,主要是計算兩個參數的加總。再呼叫math方法時,我們伴隨著一個區塊,這區塊主要是印出所傳入區塊的參數。再math方法主體裡我們要呼叫區塊時,透過yield來呼叫伴隨的區塊,並且傳遞一個參數給區塊以顯示結果。
 
===Proc物件===
我們前面範例從math方法主體並沒有辦法去判斷它是否有判隨著區塊。如果想要以較明確的方式控制一個區塊,我們可以再方法主體的引數裡最後方加上一個以「&」符號為起始的引數。這個引數會參照到要被傳遞給方法的區塊。該引數的值將是一個Proc物件,因此可以調用Proc的call方法,而不必使用yield。
<code lang="ruby">
def math(i,j,&k)
   k.call(i+j)
end
math(1,2){|x| 
   puts x
}
</code>			 
Proc物件是用來代表區塊的物件。主要分為兩種,分別為proc物件與lambda物件,其各自的建立方式如下:
[[Image:方法2.jpg]]
<br\>
不管建立出來的lambda物件或是proc物件,它們都是代表區塊的Proc物件。都可以使用call方法來進行調用。proc與lambda物件之間的差異,proc物件行為如同區塊,而lambda物件如同方法。我們以return來進行說明:
<code lang="ruby">
def test
   Proc.new{|x|puts x ; return}.call(1)
   puts "區塊調用後"
end
test
-----result-----
1
</code>
區塊的調用是指將控制權轉交給區塊,而proc調用就是類似區塊,所以當執行區塊遇到return時,它會跳脫該方法。所以在這個範例裡並沒有執行區塊以後的程式碼。
<code lang="ruby">
def test2
  lambda{|x| puts x;return}.call(1)
  puts "區塊調用後"
end
test2
-----result-----
1
區塊調用後
</code>
方法呼叫是在語意上指去調用該方法,並非讓出控制權。所以使用lambda所建立的Proc物件進行調用時,如同方法呼叫一般,遇到return時它只會跳出區塊繼續執行方法後的程式碼。任何方法都可以伴隨著區塊,下面的例子是透過區塊進一步的處理資料,包含了顯示結果或者是進一步的運算。
<code lang="ruby">
def math(x,y,z)
   arr=[]
   arr.push(x,y,z)
   yield(arr)
end
math(1,3,2){|i|
   sum=0
   i.each{ |num|  sum=sum+num }
   print "全部加總為",sum,"\n"
}
math(1,3,2){|i|
   i.sort!
   print "排序後為"
   i.each{|num| print num}
}
-----result-----
全部加總為6
排序後為123
</code>
 
==類別==
前面我們都在介紹Ruby的內建類別,在Object類別下使用內建類別建立物件、方法等,現在我們就要開始正式介紹類別。Ruby是個物件導向程式語言,而物件導向程式語言的中心概念就是類別。類別像是個容器,裝載的內容包含方法,以及變數與常數等特性。而類別的重點在於能透過繼承(inheritance)循環利用。類別可以透過繼承來取得其他類別的方法和資料,將這些方法重覆再利用,有效的縮短開發時間,以及程式在的再利用。就像我們重覆的再利用Ruby內建類別所提供的物件、方法等。
*定義類別
類別是以關鍵字class定義,是以一個end做為界定符。類別名稱如同常數名稱,因此所有類別名稱必須以大寫字母開頭。建立語法格式如下:
<code lang="ruby">
class 類別名稱
   類別定義
end
</code>
<code lang="ruby">
class Hello
 
end
</code>
*initialize實體方法
initialize方法是Ruby類別的特殊實體方法,當呼叫new方法建立物件時,這個initailize方法會自動被呼叫。同時,傳遞給new方法的所有引數都會傳給initialize方法。initailize方法主要是做變數的初始化動作。
<code lang="ruby">
class Hello
  def initialize(name)
     @name=name
  end
  def printer
     puts "Hello,"+@name
  end
end
obj=Hello.new("Ruby")
obj.printer
-----result-----
Hello,Ruby
</code>
這邊我們建立了Hello類別,類別裡有特殊的initialize實體方法以及自訂的printer實體方法。當呼叫new方法時,initialize會自動被呼叫,並且將傳給new方法的引數傳給initialize方法。Initialize方法在這邊主要的動作就是將參數值指派給實體變數@name。而printer方法則是將該實體變數@name給列印出來。
*實體變數
實體變數是以@為開頭的變數。與區域變數的不同差別在於,實體變數的值在離開方法後依然存在,但每個使用new方法所建立出來的物件彼此之間實體變數值會各自不同。
<code lang="ruby">
class Hello
   def initialize(name)
      @name=name
   end
   def printer
      puts "Hello,"+@name
   end
end
obj1=Hello.new("Ruby")
obj2=Hello.new("Java")
obj1.printer
obj2.printer
-----result-----
Hello,Ruby
Hello,Java
</code>
由上面範例可以得知,obj1與obj2這兩個物件彼此對應的實體變數值是不相同的。類別如同一個模板一樣,會將所有的方法與實體變數複製一份給各個物件,所以實體變數值會互不相同。
*實體變數的存取方法
Ruby不允許直接從物件的外部直接存取實體變數,所以前面我們必須使用initialize方法來設定實體變數的值,又或者是建立實體方法來存入實體變數的值。而要取得實體變數的值,又必須透過方法的return來將實體變數的值回傳才能夠取得。如下所示:
<code lang="ruby">  
class Hello
    def initialize(name)
	@name=name
    end
    def ch_name=(new_name)
	@name=new_name
    end
    def re_name
	@name
    end
end
</code>
如同上例,我們要修改實體變數@name值時,建立ch_name=的方法來修改實體變數@name。而要取得@name值時,又必須建立另一個方法來將@name進行回傳。而當實體變數很多時,不就要建立許多的方法來進行存取動作。所以Ruby的內建Module類別提供了attr_reader、attr_writer、attr_accessor方法(又稱為存取器)來幫助實體變數的存取動作。
方法	說明
attr_reader	定義用來讀取實體變數的方法
attr_writer	定義用來寫入實體變數的方法
attr_accessor	定義用來讀取、寫入實體變數的方法
語法格式表示如下:
<code lang="ruby">
attr_accessor :變數名
	或
attr_accessor "變數名"
</code>
「:」稱為符號,主要是用來區別字串與特殊表示名稱的差異。所有的符號都可以轉換為字串,但為了容易去區分特殊表示的名稱與字串彼此的差異,建議還是使用符號會是比較洽當。我們現在就來將上面的例子使用存取器來進行修改:
<code lang="ruby">
class Hello
    attr_accessor :name
    def initialize(name)
 	@name=name
    end 
end
obj=Hello.new("Ruby")
puts obj.name
obj.name="Java"
puts obj.name
-----result-----
Ruby
Java
</code>
這邊我們使用了存取器來幫助實體變數的存取,現在只需要使用「物件.變數名」就可以取得實體變數。使用「物件.變數名=」就可以修改實體變數。
*類別方法與類別變數
我們前面有說過類別方法是多個物件所共用的方法,現在我們就來介紹該如何建立類別方法。建立類別方法有許多種方式,現在我們介紹普遍大家所使用的方式來建立,其語法格式如下:
<code lang="ruby">
def 類別名.方法名
   類別方法定義
end
</code>
類別方法的呼叫語法,則是直接使用「類別名.方法名」進行呼叫。接下來我們介	紹類別變數,類別變數開頭必須為@@,且必須要有初值。類別變數是各個物件所共用的變數。與實體方變數不同,實體變數是各個物件擁有各自不同的實體變數。
<code lang="ruby">
class Hello
   @@count=0
   def initialize
      @@count=@@count+1
   end
   def Hello.count
      @@count
   end   
end
obj1=Hello.new
obj2=Hello.new
print Hello.count
-----result-----
2
</code>
上面這個範例,主要是計算目前有幾個物件被建立。透過類別變數@@count來累加目前的物件數,由於類別變數是由各個實體所共用的所以離開方法時,並不會消失。這邊使用類別方法來回傳類別變數值而類別方法不需建立物件,就可以直接呼叫。
*常數
在類別內定義常數,常數一旦被定義完後就不能進行修改。常數名稱開頭必須為大寫。最常使用在版本的說明,因為版本是固定不變的,所以最適合使用常數來表示。而再類別外部想要取得常數時,可以使用「類別名::常數名」來進行取得。
<code lang="ruby">
class Hello
   Version = "2.0" 							 
end
print Hello::Version
-----result-----
2.0
</code>
'''繼承'''<br\>
繼承是指能夠擴充目前的類別,定義出新的類別。使用繼承方式定義的新類別叫做子類別,它被繼承的類別則稱為父類別。繼承主要功能有三種,第一、它能夠保存原本所有功能,並且追加新的功能。第二、它能夠重新定義原有的功能,使相同名稱的方法有不同的行為。第三、將原本的功能定義,擴充更多動作。
*使用繼承
使用繼承的方法是透過「<」符號來達成,表示方式如下:
<code lang="ruby">
class 類別名稱 < 父類別
    類別定義
end
</code>
<code lang="ruby">
class Name
   @@arr=[]
   def initialize(i)
      @@arr<<i
   end  
   def data
      @@arr
   end
end
class Show_name < Name
   def initailize(i)
      super(i)
   end
   def print_name
      @@arr.each{|name|
         name
      }
   end
   def data_size
     @@arr.size
   end
end
obj1=Show_name.new("John")
obj2=Show_name.new("Merry")
puts obj1.print_name
puts obj1.data_size
-----result-----
John
Merry
2
</code>
上面這個例子,Name類別它主要功能是能夠將名字存入陣列裡頭,並無其他的方法來操作這些資料。我們建立一個Show_name類別繼承Name類別,並進行擴充。透過繼承後Show_name類別就可以使用Name類別的所有功能,例如可以使用Name類別所提供的data方法以及arr陣列所儲存的資料。這邊還有一個新名詞,就是super這個識別字,它的意思是「呼叫父類別的同名方法」,這邊使用在initialize這個實體方法,所以它會去呼叫父類別的同名方法initialize,進行父類別的初始化動作。由於父類別的initialize需要一個引數,所以super也需要傳遞一個引數給父類別。
==What's inside O.O==
<code lang="ruby">
def makeabscounter
   {"addn"=>Proc.new{|this,n|
      if n!=0 
       call(this,"add1")
       call(this,"addn",(n-1))
      end},"subn"=>Proc.new{|this,n|
 
      if n!=0
        call(this,"sub1")
        call(this,"subn",(n-1))
      end
      }    
   }  
end
def makecounter
  ssuper=makeabscounter
  count=0
  {"add1"=>Proc.new{|this| count=count+1},
    "get-count"=>Proc.new{|this|count},
    "set-count"=>Proc.new{|this,n|count=n}
  }.merge(ssuper)
end
def makesubcounter
  ssuper=makecounter
  {"sub1"=>Proc.new{|this|
      call(ssuper,"set-count",call(ssuper,"get-count")-1)}
  }.merge(ssuper)
end
def call(obj,method,*arg)
  def findmethod(obj,method)
    if obj==nil 
      puts "Method ~a not found.#{method}"
    elsif obj[method]
       obj[method]
    end
 
  end
  findmethod(obj,method).call(obj,*arg)
end
c1=makesubcounter.dup
p c1
call(c1,"add1")
p call(c1,"get-count")
call(c1,"subn",3)
p call(c1,"get-count")
call(c1,"set-count",9)
p call(c1,"get-count")
c2=makesubcounter.dup
call(c2,"add1")
p call(c2,"get-count")
call(c2,"subn",3)
p call(c2,"get-count")
call(c2,"set-count",9)
p call(c2,"get-count")
-----result-----
{"subn"=>#<Proc:0x42552c>, "addn"=>#<Proc:0xe5bbd6>, 
 "get-count"=>#<Proc:0x8ee016>, "sub1"=>#<Proc:0x19e15c>, 
 "set-count"=>#<Proc:0x11a75a2>, "add1"=>#<Proc:0x210b5b>}
1
-2
9
1
-2
9
 
</code>
==Ruby on Rails==
Ruby on Rails章節,只是簡單的介紹一些Ruby on Rails的應用,目的是讓同學了解Rails的一些基本應用。
 
'''安裝ROR'''<br\>
<code>
可在命令提示字元輸入:
      gem install rails --include-dependencies
</code>
 
'''安裝sqlite數據庫'''<br\>
<code>
命令提示字元輸入:
     gem install sqlite3-ruby --version 1.2.3
</code>
 
'''下載sqlite'''<br\>
到 http://www.sqlite.org/download.html ,將sqlite-3_6_14_2.zip和sqlitedll-3_6_14_2.zip解壓到ruby下的bin資料夾裡(C:\ruby\bin)<br\>
[[Image:sqlite.JPG]]<br\>
 
'''MVC'''<br\>
ROR是採用MVC(Model、View、Controller)框架,Model是負責與資料庫進行互動(新增、刪除、修改、讀取等動作),View是提供使用者網頁瀏覽,Controller則是接收使用者請求,能夠與模組進行互動(例如使用者要求顯示一筆資料),並將結果透過View展示給使用者。 <br\>
[[Image:MVC.jpg]]<br\>
當使用者輸入一個網址時,會透過路由將網址進行解析,將參數傳遞給適當的Controller(Controller可能不止一個)進行處理。上圖是將參數傳遞給test_controller並且呼叫它的test_action方法。
===練習1===
[[Image:exercise1.JPG]]<br\>
Exercise1是設計兩個頁面分別是new以及show,new頁面可以讓使用者填寫表單,透過show頁面將使用者所填寫的表單顯示出來。
*step1:建立一個名為exercise1的應用程式。
<code lang="rails">
rails exercise1
cd exercise1
 
說明:
   1.使用rails 命令建立一個名為exercise1的應用程式 
   2.切換到exercise1目錄
</code>
[[Image:exercise1_step1.JPG]]<br\>
*step2:為exercise1應用程式建立控制器。
<code lang="rails">
ruby script/generate controller animals new show
 
說明: 
    執行script子目錄下的generate腳本來產生animals控制器。
     animals控制器下有兩個方法(action),分別為new與show。
 
格式:
    ruby script/generate controller控制器名稱 方法 方法 …..
 
app\controllers\animals_controller.rb內容:
     class AnimalsController < ApplicationController
      def new
      end
 
      def show    
      end
    end
</code>
[[Image:exercise1_step2.JPG]]<br\>
*step3:啟動伺服器
<code lang="rails">
ruby script/server
說明:
     目前我們的控制器的new方法並沒有添加任何東西,我們現在
     就來啟動Ruby內建的WEBrick伺服器。如下所示
</code>
[[Image:exercise1_step3_1.JPG]]<br\>
<code lang="rails">
開啟瀏覽器輸入:
http://localhost:3000/animals/new
 
執行過程:
   會根據網址列,找到animals控制器,在來執行new方法。當執行完時
   ,沒有特地指定轉換的網頁時,rails會自動執行與方法同名的view
   (產生控制器時,一併產生的檔案),即new.html.erb這個檔案。右邊
   的所顯示的網頁,就是new.html.erb這個檔案裡頭的內容。
</code>
[[Image:exercise1_step3_2.JPG]]<br\>
*step4:修改new頁面,檔案位於\app\views\animals\new.html.erb
<code lang="rails">
<h1>add_animal:</h1>
<% form_tag :action=>:create do%>
  <table>
    <tr>
      <td> name:</td>
      <td><%=text_field :animal_ob,:name%></td>
    </tr>
    <tr>
        <td>description:</td>       
        <td><%=text_area :animal_ob,:description%></td>
    </tr>
  </table>
  <%=submit_tag "submit"%>
<% end %>
 
說明:
   1.要在view裡頭加入ruby的程式碼必須使用<% 程式碼 %>或<%= 程式碼 %>
    ,差別在於<%= 程式碼 %>會顯示出執行結果。
   2.form_tag 是「ActionView::Helpers::FormTagHelper」的實體方法。
     相關參數資訊可查閱「http://api.rubyonrails.org/ 」,當參數沒有
     指定:controller時,則會套用當前的控制器,即animals。
     EX:「form_tag :controller=>:animals,:action=> create」。
   3.text_field格式:「 text_field :物件名稱 , :參數值名稱」,結果會產生如下:
        “animal_ob” => {“name”=>”cat” , “description”=>”this is a cat”}
   4.當使用sumit_tag送出時,會將表單以及其他資訊儲存在params這個hash裡
      頭一同送出。讓控制器可以透過params  [:animal_ob]來取得表單
</code>
[[Image:exercise1_step4.JPG]]<br\>
*step5:建立模組animal
<code lang="rails">
ruby script/generate model animal
 
說明:
   我們產生一個名為animal的模組,模組名稱務必使用單數,在資料庫的練習會進一步講解。
</code>
[[Image:exercise1_step5.JPG]]<br\>
*step6:修改animals控制器,檔案為於\app\controllers\animals_controller.rb
<code lang="rails">
class AnimalsController < ApplicationController
  def new
  end
  def create
    @animal=Animal.new(params[:animal_ob])
    session[:name]=@animal.name
    session[:description]=@animal.description
    redirect_to :action=>:show
  end
  def show   
  end
end
 
說明:
  1.new view將參數傳遞給create方法,所以我們建立了一個create方法
    來處理傳遞過來的參數。我們建立一個模組Animal物件@animal並將參
    數傳遞給它來設定初值。並且我們希望透過「@animal.name」(物件.方法名)
   可以取得name的值。
  2.session是一個rails內建的雜湊物件,讓controller內的方法共享元素。
     我們將「:name=>值」加入session為了是即將建立的show方法來使用。
  3.redirect_to這個方法,將動作導向show這個方法。所以create就不會
    自動呼叫create. .html.erb這個view。(redirect_to 如果沒指定
    controller,則使用當前controller)
</code>
*step7:修改animal模組,檔案為於\app\models\animal.rb
<code lang="ruby">
class Animal < ActiveRecord::Base
  attr_accessor :name,:description
  def initialize(hash)
     @name=hash[:name]
     @description=hash[:description]
  end    
end
 
說明:
    由於建立時會傳送一個Hash物件,所以我們在initialize方法裡,使用一
    個名為hash的變數來進行接收。將實體變數name以及description給定初值
   ,其值就是我們所輸入name以及description。我們希望能夠使用「物件.方法名」
    來取得name以及description,所以我們將實體變數name以及description
   加入存取器accessor中。 
</code>
*修改animals控制器,檔案為於\app\controllers\animals_controller.rb
<code lang="ruby">
class AnimalsController < ApplicationController
  def new
  end
  def create
    @animal=Animal.new(params[:animal_ob])
    session[:name]=@animal.name
    session[:description]=@animal.description
    redirect_to :action=>:show
  end
  def show
    @name=session[:name]
    @description=session[:description]
  end
end
 
說明:
   create方法將動作導向show方法。在show方法中,我們建立二個
   實體變數name以及description並將值指派給它們。Rails中方法
   與同名方法的view能夠共用實體變數,所以在view時,我們可以透
   過@name來取得name值。
</code>
step8:修改show頁面,檔案位於\app\views\show.html.erb
<code lang="ruby">
<h1>animal_name : <%=@name%> </h1>
<h1>description : <%=@description%></h1>
<h1>
<%=link_to"add" ,
       :controller=>:animals,
       :action=>:new%>
</h1>
</code>
[[Image:exercise1_step8.JPG]]<br\>
*step9:增加layout,建立檔案app\views\layouts\ animals.html.erb
<code lang="ruby">
<html>
  <head>
    <h1>Exercise</h1>
<hr>    
  </head>
  <body>
   <%=yield%>   
  </body>
</html>
 
說明:
   1.當rails接收到來自控制器的動態產生樣版的請求時,會自動套用
     app/views/layouts目錄中與控制器同名的版面設計樣版檔案。
     套用了樣板後,在去開啟app/views中的view。
   2.yield方法:當動態產生樣版時,會先將view的內容儲存起來,在樣版
     內部呼叫yield方法時,就會獲得這些被儲存的內容,最後顯示到頁面上。
</code>
[[Image:exercise1_step9_1.JPG]]<br\>
*step10:認識route,檔案位於\config\routes.rb
<code lang="ruby">
我們是如何將一個URL進行拆解得知控制器與動作。
現在開啟C:\ror_exercise\exercise1\config\routes.rb
 
ActionController::Routing::Routes.draw do |map|
   map.connect ':controller/:action/:id'
   map.connect ':controller/:action/:id.:format'
end
 
說明:
    routes.rb是一個路由定義檔。Routing元件建立一個映射關係,將外部
    的URL與內部的應用程式連結起來。每個map.connect宣告都指定一到聯
    繫URL與程式碼的路徑。Rails會用它匹配請求的URL路徑部分
    EX: animals/new  會與map.connect ':controller/:action/:id'
       匹配。第一個部分animals會設定給:controller,第二個部分new會
        設定給:action。由於我們沒有id所以則省略。會產生如下:
           @params = { :controller =>  'animals '
                      :action =>  'new '}
   Rails會依據這份參數清單,呼叫animals控制器中的new方法。
</code>
*step11: 變更首頁,開啟檔案\config\routes.rb
修改routes.rb這個檔,將首頁變成show view。
<code lang="ruby">
ActionController::Routing::Routes.draw do |map|
   map.connect ':controller/:action/:id'
   map.connect ':controller/:action/:id.:format'
   map.connect '',:controller=>'animals',:action=>"show"
end
</code>
接下來刪除C:\ror_exercise\exercise1\public\ index.html 。假如沒刪除rails會自動執行該檔<br\>
[[Image:exercise1_step11.JPG]]<br\>
 
===練習2、資料庫應用===
migrate檔就像是我們資料庫的DDL(Data Definition Language),我們可以透過這個檔進行建立資料表以及其屬性等。我們使用generate產生animal模組,會建立一個migration檔,我們可以修改migration增加表單項目。現在我們就來看一下修改前的migration檔,該檔位於db\migrate 20090426130251_create_animals.rb。
<code lang="ruby">
class CreateAnimals < ActiveRecord::Migration
  def self.up
    create_table :animals do |t|
      t.timestamps
    end
  end
  def self.down
    drop_table :animals
  end
end
 
說明:
   該檔定義了self.up以及self.down兩個方法,其中up會在套用該版本
   時呼叫,down則會取消該版本時呼叫。而creat_table與drop_tabe是
   分別建立與刪除資料表。timestamps是系統預設,能夠產生created_at
   與updated_at欄位,當新增或修改一筆紀錄時,該欄位的值會自動產生。
   另外一點是資料表的主鍵如果沒有特別指名的話,Rails會自動建立欄位名
   稱為「id」的欄位並指定為主鍵,其值為rails自動產生。
</code>
*step1: 修改migration檔,檔案位於db\migrate 20090426130251_create_animals.rb
<code lang="ruby">
class CreateAnimals < ActiveRecord::Migration
  def self.up
    create_table :animals do |t|
      t.string:name
      t.text:description
      t.timestamps
    end
  end
  def self.down
    drop_table :animals
  end
end
 
說明:
   1.建立欄位name、description,並將name屬性設為string,description
     屬性設為text。(rails 預設資料庫型別::primary_key, :string, 
     :text, :integer, :float, :decimal, :datetime, :timestamp,
     :time, :date, :binary, :boolean.) 。
    2.我們也可以在產生animal模組時,順便建立起欄位名稱,如下表示:
      ruby script/generate model animal name:string description:text
</code>
*step2:套用migration(遷移)檔
<code lang="ruby">
cmd:
   rake db:migrate
 
說明:
   Rake命令,可以指定完成哪些任務,在這邊我們告訴rails對資料庫套用所有未套用的遷移。
</code>
[[Image:exercise2_step2.JPG]]<br\>
由上圖得知我們建立一個資料表animals。如果想執行drop:「rake db:drop」,animals資料表就會不見。或遷移特定的migration檔:「rake db:migrate VERSION=版本」。版本為檔名前的那串數字。<br\><br\>
 
ORM(Object-Relational Mapping)物件-關聯資料庫映射:資料庫的操作及對應都是透過模型來運作實現的,Active Record模型提供模型(Model類別)預設操作資料庫的方法,EX:「資料表映射到Model類別」、欄位映射到Model類別的物件屬性、紀錄映射到Model類別的物件。而要達成這樣的映射,必須將Model名稱設定為單數,將資料表名稱設定為複數。
*step3:修改animal模組,檔案位於\app\models\ animal.rb
<code lang="ruby">
class Animal < ActiveRecord::Base
end
 
說明:
   現在我們開啟模型Animal,並且把原本的程式碼定義全部刪除。執行
    ruby script/console指令搞來測試模型以及新增一筆紀錄(參閱圖):
</code>
[[Image:exercise2_step3.JPG]]<br\>
>> animal_ob=Animal.new(自行輸入)<br\>
=> #<Animal id: nil, name: nil, description: nil, created_at: nil, updated_at: n
il><br\>
>> animal_ob.name="cat"(自行輸入)<br\>
=> "cat"<br\>
>> animal_ob.description="this is a cat"(自行輸入)<br\>
=> "this is a cat"<br\>
>> animal_ob.save(自行輸入)<br\>
=> true<br\>
>> animal_ob.id(自行輸入)<br\>
=> 1<br\>
 
*step4:修改animals控制器,檔案位於\app\controllers\ animals_controller.rb
<code lang="ruby">
class AnimalsController < ApplicationController
  def new
  end
  def create
    @animal=Animal.new(params[:animal_ob])
    @animal.save
     redirect_to :action=>:show, :id=>@animal
  end
  def show
    @animal=Animal.find(params[:id])
  end
end
 
說明:
   creat method:
   這裡介紹另外一種新增資料庫紀錄的方法,修改Animals控制器,我們
   建立了一個Animal物件,並且將animal_ob這個雜湊傳遞給Animal,
   當我們沒有在Animal中建立initialize這個方法時,會使用rails預設的
   initialize,自動建立該筆紀錄,建立完畢後記得要加上save這個方法,
   來儲存紀錄。
   最後我們透過redirect_to 這方法來將動作導向show,設定id值為
   @animal.id,@animal等同於@animal.id。產生url如下:
       http://localhost:3000/animals/show/1 
   如果增加其他參數時,
    EX:  redirect_to :action=>:show, :id=>@animal , :test => “test”
   就會成為 http://localhost:3000/animals/show/5?iid=2
   show method:
   這邊我們只要將由路由器產生的params雜湊裡的名為id的值抓取出
    來,透過find方法就能找尋到該筆紀錄。透過@animal.name就能取出
    name值。
</code>
*step5:修改show頁面,檔案位於\app\views\animals\ show.html.erb
<code lang="ruby">
<h1>
animal_name : <%=@animal.name%> 
</h1>
<h1>
description : <%=@animal.description%>
</h1>
<h1>
<%=link_to "add" ,
:controller=>:animals ,
:action=>:new %>
</h1>
 
現在啟動伺服器,看一下是否能夠正常的輸入。
接下來我們製作一個index view,來列出所有存在於資料庫的紀錄。
</code>
*step6:修改animals控制器,檔案位於\app\controllers\ animals_controller.rb
為了列出所有存在於資料庫的紀錄,我們要建立index視圖來顯示所有紀錄,首先先要在控制器裡新增一個index方法。
<code lang="ruby">
def  index
    @animal_ob=Animal.find(:all)
end
 
說明:
   使用find這個類別方法,回傳了一個陣列,陣列是包含所有資料庫紀錄的物件。
</code>
*step7:建立index視圖,檔案位於\app\views\animals\index.html.erb
<code lang="ruby">
 <table border="">
       <tr>
         <td><h2>name</h2></td>
         <td><h2>description</h2></td>
       </tr>
     <%for animal in @animal_ob%>
       <tr>
         <td><%=animal.name%></td>
         <td><%=animal.description%></td>
       </tr>
     <%end%>
 </table>
</code>
[[Image:exercise2_step7.JPG]]<br\>
*step8:開啟routes檔更改首頁為index,檔案位於\config\routes.rb
<code lang="ruby">
ActionController::Routing::Routes.draw do |map|
  map.connect ':controller/:action/:id'
  map.connect ':controller/:action/:id.:format'
  map.connect '',:controller=>'animals',
:action=>"index"
end
</code>
*step9:修改show頁面,增加一個能夠返回index的連結,檔案位於\app\views\animals\show.html.erb
<code lang="ruby">
<h1>animal_name : <%=@animal.name%> </h1>
<h1>description : <%=@animal.description%></h1>
<h1>
<%=link_to "add" ,
:controller=>:animals,
:action=>:new%>
<%=link_to "index" ,:action=>:index%>
</h1>
</code>
[[Image:exercise2_step9.JPG]]<br\>
 
===練習3===
step1:修改new頁面增加上傳功能,檔案位於\app\views\animals\new.html.erb
<code lang="ruby">
<h1>add_animal:</h1>
<% form_tag({:action=>:create},
:multipart => true) do%>
  <table>
    <tr>
      <td> name:</td>
      <td><%=text_field :animal_ob,:name%></td>
    </tr>
    <tr>
        <td>description:</td>        
<td>
<%=text_area :animal_ob,:description%>
</td>
    </tr>
    <tr>
        <td> file:</td>
        <td><%=file_field :animal_ob, :filename %></td>
    </tr>
 
  </table>
  <%=submit_tag "submit"%>
<% end %>
 
說明:
   我們想要讓使用者能夠上傳照片檔案,首先我們來修改new的頁面,
   透過file_field這個方法,讓使用者能夠指定所要上傳的照片。
   這裡要特別說明的是form_tag方法,參閱API說明form_tag的參數
   主要有(url_for_options = {}, options = {}, *parameters_for_url, &block)
  ,在之前我們傳遞的都是一個hash,並沒有指定其他的雜湊物件,所
   以我們沒特地使用{}。由於我們必須指定:multipart=>true,所以
   必須把前面的參數加上{}。我們列出兩者差異: 
   錯誤寫法:  
       <%= form_tag( :action=>'create',
                   :multipart=> true ) %>
   output:
       <form action="/animals/create?multipart=true"
        method="post">
   正確寫法:
        <%= form_tag( { :action=>'create' },
                        :multipart=> true ) %>
   output:
        <form action="/animals/create"
         enctype="multipart/form-data"
         method="post">
 
補充:
   enctype="data type",data type為資料處理格式,用於送出資料
   的格式是否需要經過編碼等功能處理,一般可不用加此標籤。用於寄信
   (ACTION="Mailto:")時,屬性值應設為 enctype="text/plain"。
    用於上傳檔案(Type="file")時,屬性值應設為 enctype="multipart/form-data"。
</code>
[[Image:exercise3_step1.JPG]]<br\>
*step2:修改animals控制器的create方法,檔案位於\app\controllers\ animals_controller.rb
修改create方法,讓使用者所上傳的檔案能夠儲存在/public/images的目錄下。
<code lang="ruby">
class AnimalsController < ApplicationController
  ………….
 def create 
   filename = params[:animal_ob]["filename"].original_filename
   filepath = "./public/images/#{filename}"
   File.open("#{filepath}", "wb") do |f|
      f.write(params[:animal_ob]["filename"].read)
   end
   @animal=Animal.new(params[:animal_ob])
   @animal.save
   redirect_to :action=>:show,:id=>@animal,:iid=>@animal
 end 
end
 
說明:
   1.original_filename是能夠取得原始的檔案名稱
    2.使用File類別方法open來建立檔案,模式wb之中的b是代表以二進制模
      式打開。接下來是使用read讀取位於我們電腦的暫存區的檔案並且將他
      寫入到我們所建立的新檔中。(傳送的檔案會位於電腦裡的暫存區,執行
      時查閱命令提示字元)
   這時候我們開啟網頁進行新增時,會發現錯誤,原因是我們新增資料表紀錄時,
   是將整個animal_ob雜湊傳送給Animal 類別,我們的欄位名稱並沒有filename
   所以在新增紀錄時會出現錯誤。我們為了要顯示圖片,必須增加新的欄位來儲存路徑。
</code>
*step3:產生新的migration檔
為了在資料庫中增加用來儲存圖片路徑的欄位,所以我們建立一個新的migration檔
<code lang="ruby">
cmd:
   ruby script/generate migration add_image_item
 
說明:
   執行script子目錄下的generate腳本來產生migration檔,
    其檔案名稱為add_image_item
</code>
[[Image:exercise3_step3.JPG]]<br\>
*step4:修改migration檔,新增圖片路徑欄位,檔案位於\db\migrate\20090505170039_add_image_item.rb
<code lang="ruby">
class AddImageItem < ActiveRecord::Migration
  def self.up
    add_column :animals,:filename,:string
  end
  def self.down
    remove_column :animals,:filename
  end
end
 
說明:
   1.使用add_column方法來新增資料表欄位,其格式如下
    「add_column 資料表名稱 , 資料表欄位 , 屬性」
   2.使用remove_column方法來移除資料表欄位,其格式如下
    「remove_column 資料表名稱 , 資料表欄位 」
</code>
*step5:透用migration檔
<code lang="ruby">
cmd:
   rake db:migrate 
</code>
現在我們的new view 已經可以上傳檔案了。但我們資料表filename欄位所儲存的路徑是類似下面這樣:<br\>
[[Image:exercise3_step5.JPG]]<br\>
我們所希望的是「images/#{filename}」,因為使用者只能存取/public底下的檔案,修改create方法來達成。
*step6:修改animals控制器的create方法,檔案位於\app\controllers\animals_controller.rb
<code lang="ruby">
class AnimalsController < ApplicationController
  ………….
 def create 
   filename = params[:animal_ob]["filename"].original_filename
   filepath = "/public/images/#{filename}"
   File.open(".#{filepath}", "wb") do |f|
      f.write(params[:animal_ob]["filename"].read)
   end
   params[:animal_ob]["filename"]= "/images/#{filename}"
   @animal=Animal.new(params[:animal_ob])
   @animal.save
   redirect_to :action=>:show,:id=>@animal,:iid=>@animal
 end 
end
 
說明:
   File.open("#{filepath}", "wb") do |f|
     f.write(params[:animal_ob]["filename"].read)
   end
   底下新增params[:animal_ob]["filename"]= "/images/#{filename}"
   將路徑設為"/images/#{filename}"這樣的格式,接下來我們修改index view
   讓它能夠顯示filename欄位,看格式是否如我們預期。 
</code>
step7:顯示照片,修改index視圖,檔案位於\app\views\animals\index.html.erb
<code lang="ruby">
<table border="">
       <tr>
         <td><h2>picture</h2></td>
         .......
         <td><h2>image_url</h2></td>
       </tr>
     <%for animal in @animal_ob%>
         <tr>
         <td><img src="<%= animal.filename %>"></td>
         <td><%=animal.name%></td>
         <td><%=animal.description%></td>
         <td><%= animal.filename %></td>
         </tr>
     .......
</code>
[[Image:exercise3_step7.JPG]]<br\>
*step8:修改animal模組加入驗證,檔案位於\app\models\animal.rb 
這個步驟是要驗證欄位name、description、filename是否為空,如果為空則返回new視圖。
<code lang="ruby">
class Animal < ActiveRecord::Base
  validates_presence_of :name ,:description ,:filename
end
 
說明:
   使用validate_presence_of方法,驗證欄位是否為空。
   模型中一些常用的方法:
   • validates_presence_of方法: 資料要寫入資料庫時,要確定是否輸入值
   • validates_presence_of是Rails驗證器,會檢查指定的欄位確實存在且質不為空。
     EX:  validates_presence_of :name,:link
   • validates_numericality_of方法:欄位值必須要數值。
   • validates_uniqueness_of方法:確保名稱是唯一的。
   • errors.add方法會在form(表單)上顯示錯誤資訊給使用者,
     第一個參數是欄位名稱,第二個參數是訊息文字。
     EX:  errors.add(price,”should be at least 0.01))
   • validates_format_of : 驗證欄位格式是否符合,第一個參數是欄位,
      第二個參數是給定的規則運算是否匹配,第三個欄位是訊息。
      EX:validates_format_of  :image_url , 
               :with => %r {\.(gif|jpg|png)$}i , 
        :message=>”must be a url for a gif,jpg,or png image”
</code>
*step9:修改animals控制器,檔案位於\app\controllers\animals_controller.rb
如果檔案無法存入時,返回new視圖。
<code lang="ruby">
class AnimalsController < ApplicationController 
 def new  
 end  
 
 def create 
   if  params[:animal_ob]["filename"]!=nil
     filename = params[:animal_ob]["filename"].original_filename
   filepath = "./public/images/#{filename}"
   File.open("#{filepath}", "wb") do |f|
    f.write(params[:animal_ob]["filename"].read)
  end
  params[:animal_ob]["filename"]= "/images/#{filename}"
  end
   @animal=Animal.new(params[:animal_ob]) 
   unless     @animal.save
   redirect_to :action=>:"new"
   else
     redirect_to :action=>:show, :id=>@animal, :iid=>@animal
   end
   end
 ……
end
 
說明:
   1.當file欄位是空時,我們就不執行上傳動作。
    2.當@animal無法存入資料庫時,返回new view。
 
現在可以開啟網頁測試一下是否能夠運行。
</code>
*step10:為index視圖增加刪除功能,檔案位於\app\views\animals\index.html.erb
<code lang="ruby">
<table border="">
     ………
        <%for animal in @animal_ob%>
       <tr>
         .......
         <td><%=animal.filename%></td>
         <td>
             <%=link_to "delete",
                  :action=>:delete,
                  :id=>animal%>
         </td>
       </tr>
     <%end%>
</table>
…
<%end%>
</code>
[[Image:exercise3_step10.JPG]]<br\>
*step11:修改animals控制器,檔案位於\app\controllers\animals_controller.rb
新增delete方法,來處理刪除的動作。
<code lang="ruby">
class AnimalsController < ApplicationController
  ……
  def delete
    @animal = Animal.find(params[:id])
    @animal.destroy
    redirect_to "http://localhost:3000/"
  end
end
 
說明:
   destroy方法能夠刪除該筆資料庫
</code>
 
===練習4、添加CSS===
*step1:建立style.css,存放於\public\stylesheets\ style.css
<code lang="css">
body{
	background-color: #666;
	margin: 15px 25px;
	font-family: Arial, Helvetica, sans-serif;
	}
#content{
	background-color:#fff;
	border:10px solid #ccc;
	padding: 10px 10px 20px 10px;
	}
 
說明:
   我們在\public\stylesheets\目錄下建立一個style.css樣式表。
   設計一個body標籤選擇器(Tag Selector)以及#content識別碼選
   擇器(ID selector)
</code>
*step2:修改layout,檔案位於\app\views\layouts\animals.html.erb
載入style.css
<code lang="ruby">
<html>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
 <head>
    <%=stylesheet_link_tag "style"%>
    <h1>practice</h1><hr>    
  </head>
  <body>
    <div id="content">
   <%=yield%>   
   </div>  
  </body>
</html>
 
說明:
   stylesheet_link_tag方法會載入位於public\stylesheets裡的css檔。
    Ex: stylesheet_link_tag “style1”,”style2”。就可以載入style1.css
    、style2.css。載入後就可以使用CSS。這邊我們除了原本body標籤已經套用
    外,我們將所有的頁面都套用content。
</code>
[[image:Exercise4_step2.jpg]]<br\>
*step3:修改style.css,檔案位於\public\stylesheets\ style.css
開啟後我們發現圖片有大有小,我們想要讓圖片顯示固定長寬時,可以使用CSS來調整長寬。
<code lang="ruby">
.......
#image{
	  width:        60px;
          height:       70px;            
      }
 
說明:
   我們加入了一個style.css裡加入了# image識別碼選擇器(ID selector)。
</code>
*step4:修改index視圖,檔案位於\app\views\animals\index.html.erb
修改顯示圖片的標籤,讓它套用css
<code lang="ruby">
<table border="">
     …………
  <%for animal in @animal_ob%>
  <tr>
    <td>
<img id="image" src="<%= animal.filename %>">
</td>
    <td><%=animal.name%></td>
    <td><%=animal.description%></td>
    <td><%=animal.filename%></td>
   …………….
<%end%>
</code>
[[image:Exercise4_step4.jpg]]<br\>
 
 
===練習5、資料庫關聯===
另外建立一個vote資料表,來紀錄animal資料庫中,每筆紀錄的得票數。vote資料表內容如下:
<code>
    animal_id   id 
        1        1
        1        2
        2        3
        3        4
 
說明:
   我們就可以得知animal資料表id為1的共有2票,
   id為2的共有1票,id為3的共有1票。
</code>
*step1:產生vote模組
<code lang="ruby">
ruby script/generate model vote animal_id:integer
rake db:migrate
 
說明:
   vote模組是用來處理紀錄使用者推薦animal照片的次數。
   每個animal中有多張選票,而每個選票則是屬於1張animal
   照片。屬於一對多的關聯
</code>
[[image:Exercise5_step1.jpg]]<br\>
*step2:修改vote模組,檔案位於\app\models\vote.rb
<code lang="ruby">
class Vote < ActiveRecord::Base
  belongs_to :animal
end 
 
說明:
   在Vote模組中,我們使用belongs_to來進行定義。連結外來鍵
   必須以目標表單的單數名稱,所以這邊使用animal。外來鍵的命
   名也是以單數名稱_id來表示,即animal_id。
</code>
*step3:修改animal模組,檔案位於\app\models\animal.rb
<code lang="ruby">
class Animal < ActiveRecord::Base
   validates_presence_of :name ,:description ,:filename
   has_many :votes
end
 
說明:
   在animal模組中,我們使用has_many 來進行定義。這邊則是直
   接使用資料表名稱。
</code>
*step4:產生votes控制器
<code lang="ruby">
cmd:
   ruby script/generate controller votes create
 
說明:
   產生votes控制器,有一個create方法
</code>
*step5:修改index視圖,檔案位於\app\views\animals\ index.html.erb
<code lang="ruby">
<table border="">
          ……
         <td><h2>image_url</h2></td>
         <td><h2>votes</h2></td>         
       </tr>
          ………
         <td><%=animal.filename%></td>
         <td>Score: <%=animal.votes.size%></td>
         <td>
             <%form_tag :controller=>:votes ,
                        :action=>:create,
                        :id=>animal do%>
                 <%=submit_tag "shove it"%>
             <%end%>         
         </td>
   …………..
 
說明:
   1.animal.votes 會回傳在votes資料表中animal這物件的所有紀
      錄,而尋找紀錄就是透過我們在votes所建立的animal_id,回傳
      是以陣列的型態,所以我們可以透過size方法取得animal物件總
      共有幾筆。
    2.由於我們呼叫的是votes控制器的create方法,所以要特別指名控
      制器名稱,否則rails會使用當前的animal控制器。
</code>
[[image:Exercise5_step5.jpg]]<br\>
*step6:修改votes控制器,檔案位於app\controllers\votes_controller.rb
<code lang="ruby">
class VotesController < ApplicationController
  def create
    @animal=Animal.find(params[:id])
    @animal.votes.create    redirect_to :controller=>:animals,:action=>"index"
  end
end
 
說明:
   此時我們使用@animal.votes.create就能夠在votes資料表建立一筆資料。
   透過這樣的方法可以省去撰寫複雜sql。現在可以開啟網頁測試一下推薦按
   鈕是否如我們所預期。
</code>
 
===練習6、為網頁增加管理===
*step1:建立user資料表,紀錄使用者帳號與密碼。
<code>
cmd:
   ruby script/generate model user login:string password:string
</code>
[[image:Exercise6_step1.jpg]]<br\>
*step2:修改migration檔,檔案位於\db\migrate\ 20090511155417_create_users.rb
<code lang="ruby">
class CreateUsers < ActiveRecord::Migration
  def self.up
    create_table :users do |t|
      t.string :login
      t.string :password
      t.timestamps
    end
    add_column :animals,:user_id,:integer
    add_column :votes, :user_id,:integer
  end
  def self.down
    drop_table :users
    remove_column :animals, :user_id
    remove_column :votes,:user_id
  end
end
 
說明:
  1.使用add_column在animals和votes資料表新增欄位user_id。
  2.使用remove_column來刪除animals與votes資料表欄位user_id。    
</code>
[[image:Exercise6_step2.jpg]]<br\>
*step3:套用migration檔
<code>
cmd:
   rake db:migrate
</code>
*step4:產生sessions控制器
<code>
ruby script/generate controller sessions new login logout
</code>
[[image:Exercise6_step4.jpg]]
*step5:修改new視圖,檔案位於\app\views\sessions\new.html.erb
<code lang="ruby">
<h1>add_new_user:</h1>
<% form_tag :controller=>"sessions",:action=>"create" do%>
  <table>
    <tr>
      <td> name:</td>
      <td><%=text_field :user_ob,:login%></td>
    </tr>
    <tr>
        <td>password:</td>
        <td><%=password_field :user_ob,:password%><td>
    </tr>
  </table>
  <%=submit_tag "submit"%>
<% end %>
</code>
[[image:Exercise6_step5.jpg]]
*step6:修改sessions控制器,檔案位於\app\controllers\sessions_controller.rb
建立create方法,來處理由new視圖所傳遞過來的參數。
<code lang="ruby">
class SessionsController < ApplicationController
  def new
  end
  def create
     @user=User.new(params[:user_ob]) 
     @user.save
     redirect_to :action=>:login     
  end
  def login
  end
  def logout
  end
end
</code>
*step7:修改layout,檔案位於\app\views\layouts\animals.html.erb
在step5中的new視圖並沒有顯示css,那是因為animals.html.erb只提供給animals控制器裡的所有視圖使用,而要把它與其他控制器共享時,將animals.html.erb更名為application.html.erb
<br\>
*step8:修改login視圖,檔案位於\app\views\sessions\login.html.erb
login視圖是可以上使用者能夠進行登入動作
<code lang="ruby">
<% form_tag :action=>:login_create do%>
  <p>Please log in </p>
  <p>
    <label>Username:</label>
    <%=text_field_tag :login%>
  </p>
  <p>
    <label>password:</label>
    <%=password_field_tag :password%>
  </p>
  <p><%= submit_tag "login"%></p>
<%end%>
</code>
[[image:Exercise6_step8.jpg]]
*step9:修改sessions控制器,檔案位於\app\controllers\sessions_controller.rb
建立login_create方法,處理從login視圖所傳遞過來的參數。
<code lang="ruby">
class SessionsController < ApplicationController
 ………….
  def login
  end
  def login_create    
    @current_user=User.find_by_login_and_password(params[:login],params[:password])
    if @current_user
       session[:user_id]=@current_user.id   
       redirect_to :controller=>"animals",
                   :action=>"index"
    else
       render :action=>:login
    end
  end
  def logout
  end
end
 
說明:
    1.使用「find_by_」時就能夠動態產生方法(dynamic finder),
       EX:
        User.find_by_login("John")
        #找尋User資料表裡login為John的紀錄
         User.find_by_login_and_password("John","1234")
        #找尋User資料表裡login為John 且 密碼為1234的紀錄
     2.由於其他地方需要用到目前的使用者的資料,我們這邊將目前的使用者id存入session裡。
</code>
*step10:修改application控制器,檔案位於app\controllers\application_controller.rb
使用者檢視網頁時,會先檢查使用者是否已經登入,由於其他控制器可能會用到,定義在application控制器的方法、變數,可供繼承application的類別使用。
<code lang="ruby">
class ApplicationController < ActionController::Base
  helper :all 
  protect_from_forgery 
  before_filter :fetch_logged_in_user
    protected
    def fetch_logged_in_user
      return unless session[:user_id]      
      @current_user=User.find_by_id(session[:user_id])
    end    
end
</code>
*step11:修改layout,檔案位於\app\views\layouts\application.html.erb
顯示目前登入的使用者,或尚未登入
<code lang="ruby">
……….
 <body>
  <div id="content">
    <% if @current_user%>
         Logged in as: <%=@current_user.login%>
         <em> <%=link_to "(Logout)",
                :controller=>:sessions,
                :action=>:logout%>
         </em>
   <%else%>
         <em>Not logged in.</em>
         <%=link_to "Login",
              :controller=>:sessions,
              :action=>:login%>
           /<%=link_to " create account ",
              :controller=>:sessions,
              :action=>:new%>
       <%end%>
     <%=yield%>
   …….
</code>
[[image:Exercise6_step11_1.jpg]][[image:Exercise6_step11_2.jpg]]<br\>
*step12:修改sessions控制器,檔案位於\app\controllers\sessions_controller.rb
修改logout,進行登出時的處理。
<code lang="ruby">
class SessionsController < ApplicationController
  ......  
  def logout
     session[:user_id]=nil
     render :action=>:login
  end
end
</code>
*step13:修改application控制器,檔案位於app\controllers\application_controller.rb:
建立login_required方法判斷使用者是否已經登入,如果登入則return true,否則紀錄使用者登入前的所在網頁,並且return false
<code lang="ruby">
class ApplicationController < ActionController::Base
  .......
  def fetch_logged_in_user
      return unless session[:user_id]      
      @current_user=User.find_by_id(session[:user_id])
  end    
  def logged_in?
      !@current_user.nil?
  end
  def login_required
      return true if logged_in?
      session[:return_to]=request.request_uri
      redirect_to :controller=>:sessions,
                  :action=>:login and return false
   end
   helper_method :logged_in?
end
 
說明:
   logged_in?方法沒宣告為helper_method的話,就只能在控制器中
    使用。如果想要在視圖中使用此方法可以將它宣告為helper_method,
    這樣所有的視圖就都可以直接使用這個方法。
    EX: 
      <h1><%=logged_in?%></h1>
</code>
*step14:修改animals控制器,檔案位於\app\controllers\ animals_controller.rb
使用控制器,先呼叫login_required這方法。來確定使用者是否已經登入,並且紀錄一些資訊。
<code lang="ruby">
class AnimalsController < ApplicationController  
  before_filter :login_required,:only=>[:new,:create,:show,:delete]
  def index
    @animal_ob=Animal.find(:all)
  end
  ......
end
 
說明:
   在執行animals控制器時,使用,會先呼叫login_required這個
    方法,判斷使用者是否已經登入。可以指定only或者是except
   格式:
         :only=>[:action_name,:action_name]
           只對列出的action做檢驗
         :except=>[:action_name,:action_name]
           除了列出的action外,都要做檢驗
 
現在可以測試一下,如果尚未登入是否能夠新增或刪除。
</code>
*step15:修改sessions控制器,檔案位於\app\controllers\sessions_controller.rb
使用者如果因原本已登入造成param[return_to]為nil時,則回到index視圖,如果param[return_to]為使用者原來頁面的網址時,則返回到原本的頁面。
<code lang="ruby">
class SessionsController < ApplicationController
  ......
  def login_create
    @current_user=User.find_by_login_and_password(params[:login],params[:password])
    if @current_user
      session[:user_id]=@current_user.id
      if session[:return_to]
      redirect_to session[:return_to]
      session[:return_to]=nil
      else
        redirect_to :controller=>:animals,
                     :action=>:index
      end
     else
      render :action=>:login
    end
  end
  def logout
    session[:user_id]=@current_user=nil
  end
end
</code>
 
===scaffold===
scaffold是用來處理模型的基本框架。當我們啟動應用程式時,模型檢查了資料表以及資料表有哪些欄位,並在資料庫的資料與模型物件建立了映射關係。在透過scaffold建立的表單產生器會像模型詢問這些欄位的資訊,再建立合適的HTML。 
<code lang=rails>
格式:
   ruby script/generate scaffold 模型名稱 欄位名稱1:欄位屬性1 欄位名稱2:欄位屬性2 .....
 
說明:
   scaffold會自動建立資料庫以及產生控制器、頁面(包含基本功能)。
</code>
*step1:建立testscaffold專案
<code lang=rails>
命令提示字元:
   rails testscaffold
</code>
*step2:產生基本框架
<code lang=rails>
命令提示字元:
   ruby script/generate scaffold animal name:string description:text
 
說明:
   根據模型名稱animal產生animals資料表、animals控制器與views
</code>
*step3:套用遷移檔
<code lang=rails>
命令提示字元:
   rake db:migrate
</code>
*step4:開啟server
<code lang=rails>
命令提示字元:
   ruby script/server
</code>
[[image:scaffold.jpg]]<br\>
 
透過scaffold來產生基本的顯示、新增、刪除、修改等基本功能,網頁開發人員能夠直接修改該網頁,能夠有效減少網頁開發的時間。
 
==Homework==
請寫一個題庫管理系統,同學只需讓網頁能夠完整呈現下面的功能,資料庫以及版面等由同學自行設計。
<code>
1.首頁
(1).帳號、密碼登入
(2).新增使用者
 
2.題庫管理
(1).新增、刪除、修改、顯示題庫的名稱、建立及修改時間
    (可在同一頁面或轉另一頁面)
(2).點選題庫名稱能夠進入該題庫進行題目管理
 
3.題目管理
(1).列出目前目前題庫裡的題目
(2).設計一個按鈕能夠新增題目(可在同一頁面或轉另一頁面)
(3).可以刪除或修改題目
(4).可以勾選題目,並且按下"產生測驗"按鈕時,會將勾選的題目於頁面
    顯示出來。顯示的頁面,會依據題目數量來進行自動配分並顯示於每
    筆題目的後方。(允許小數點)
</code>
Personal tools