第三章 了解 Ruby 前提下的 Rails 開發
一、YAML 和實際為程式設計的配置
YAML (按 UNIX 平台的傳統,最初代表 Yet Another Markup Language,現在卻代表 YAML Ain’t Markup Language)可以說是一個标記語言,也可以說是一個串行化格式,取決于你看問題的角度。下面是一個簡單的例子:一個嵌套的數組結構轉變成一個 YAML 表示,然後再由它恢複成一個數組:
require 'yaml'
array = [1, 2, 3, [4, "five", :six]]
puts "Original array:"
puts array.inspect
yarray = array.to_yaml
puts "YAML representation of array: "
puts yarray
thawed = YAML.load(yarray)
puts "Array re-loaded from YAML string: "
p thawed
(此例中,inspect 方法為對象産生一個詳細的字元串表示;還有 p 方法,等價于 puts)。
運作這段腳本的輸出如下:
Original array:
[1, 2, 3, [4, "five", :six]]
YAML representation of array:
---
- 1
- 2
- 3
- - 4
- five
- :six
Array re-loaded from YAML string:
[1, 2, 3, [4, "five", :six]]
Rails 在幾種上下文中使用 YAML,在 database.yml 中會看到如下的塊:
development:
adapter: mysql
database: r4rmusic1_development
username: r4r
password: railzrulez
socket: /tmp/mysql.sock
将這些行放到一個檔案(如sample.yml)中,然後運作下面指令:
F:\ruby_project>ruby -ryaml -e 'p YAML.load(File.read("sample.rb"))'
{"development" => {"socket"=>"/tmp/mysql.sock",
"username"=>"r4r",
"adapter"=>"mysql",
"password"=>"railzrulez",
"database"=>"r4rmusic1_development"
}
}
二、給控制器增加功能
通過點選合适的列标題,可以讓清單以不同的方式排序(按标題,按作者,按狀态):
def all
@order = params[:order] || "number"
sort_proc = case @order
when "author" then lambda {|r| [r.user.name.downcase, r.number] }
when "status",
"title" then lambda {|r| [r.send(@order).downcase, r.number]}
when "number" then lambda {|r| -r.number }
end
@rcrs = Rcr.find(:all).sort_by &sort_proc
end
變量@order(一個執行個體變量)設定為CGI變量order的值;如果該CGI變量的值沒有設定,則它的預設值是字元串"number"。然後,變量sort_proc(排序過程)被設定為三個可能的lambda表達式(匿名函數)中的一個。選擇哪一個lambda表達式取決于@order的值,這個選擇是通過一個case語句來執行的。
一旦標明了正确的lambda表達式,所有現存的RCR将根據該lambda表達式的邏輯排序,這個排序需使用ActiveRecord的find方法來擷取所有的RCR,以及根據sort_proc中儲存的lambda表達式來使用Ruby的sort_by方法過濾這個清單。
如果了解Ruby,則該方法很容易編寫。但是一定要了解Ruby!确切地說,需要了解下面這些内容:
case語句;
lambda關鍵字,用它生成一個匿名函數;
send方法(請注意status和title是怎樣一起處理的);
sort_by方法,需要給它傳遞一個lambda表達式。
三、部署 Rails 的輔助檔案
Rails 對每一個控制器都會自動産生一個輔助檔案,可以編寫任意多的 Ruby 方法。位于 app\helpers 下。
下面是一個從RCRchive清單排序代碼中提取的例子。在RCR的每一個視圖中的每一個列标題,都超連結到rcr/all動作。這些連結隻在一個方面有差別:order參數的值(“author”、“title”、“number”或“Status”)。這意味着這四個連結使用幾乎完全相同的代碼。為減少重複,可用一個輔助方法自動産生合适的連結。你所需要做的僅僅是傳遞給它一個order參數。
該輔助方法定義在檔案rcr_helper.rb中,如下所示:
def link_to_order(order)
link_to(order.capitalize,
:controller => "rcr",
:action => "all",
:params => { "order" => order })
end
在視圖(app/views/rcr/all.rhtml)中,下面四行産生表的列标題:
<th class="rcr"><%= link_to_order("number") %></th>
<th class="rcr"><%= link_to_order("title") %></th>
<th class="rcr"><%= link_to_order("status") %></th>
<th class="rcr"><%= link_to_order("author") %></th>
四、給模型增加功能
1. 實作預定義的回調方法
如果在一個ActiveRecord模型檔案中編寫一個名為before_create的方法,該方法會在該模型的執行個體的資料庫記錄産生之前被執行。
如果description字段有預設的字元串值,那應該更好。如果在資料庫記錄首次産生的時候,該edition不存在描述資訊,讓我們給它一個預設值"standard"。
def before_create
self.description = "standard" unless description
end
2. 自由形式的程式設計的模型改進
假設在一個視圖模闆中,變量@customer包含一個客戶對象,你可以像下面這樣顯示這個人的名字:
<p>Hello, <%= @customer.title + " " + @customer.first_name + " " +
@customer.middle_initial + ". " +
@customer.last_name" %></p>
下面讓 customer 對象自己産生該名字的友好闆本:
class Customer < ActiveRecord::Base
def nice_name
title + " " + first_name + " " +
(if middle_initial then middle_initial + ". " else "" end) +
last_name
end
end
下面可以在視圖中直接調用 @customer 中該執行個體的方法:
<p>Good morning, <%= @customer.nice_name %>.</p>