1. 程式人生 > >Ruby on Rails 開發 web

Ruby on Rails 開發 web


Ruby on Rails 正在令整個 Web 開發領域受到震憾。讓我們首先了解底層的技術:

  • Ruby 是一門免費的、簡單的、直觀的、可擴充套件的、可移植的、解釋的指令碼語言,用於快速而簡單的面向物件程式設計。類似於 Perl,它支援 處理文字檔案和執行系統管理任務的很多特性。
  • Rails 是用 Ruby 編寫的一款完整的、開放原始碼的 Web 框架,目的是使用更簡單而且更少的程式碼編寫實際使用的應用程式

作為一個完整的框架,這意味著 Rails 中的所有的層都是為協同工作而構造的,所以您不必自己再重複,可以完全只使用一門單一的語言。 在 Rails 中,所有內容(從模板到控制流再到業務邏輯)都是用 Ruby 編寫的。Rails 支援基於配置檔案和註釋的反射(reflection)和執行時擴充套件。

本文詳細介紹了 Rails 的組成部分,並介紹了它的工作原理。

Rails 介紹

關於 Rails,首先需要理解的是它的模型/檢視/控制器(model/view/controller,MVC)架構。雖然這種技術不是 Rails 所特有的 —— 甚至不是 Web 應用程式所特有的(相對於其他程式),但是 Rails 具有非常清晰而專一的 MVC 思維方式。如果您並不使用 MVC 方法,那麼 Rails 的用處將大為降低(與遵循其模式的情況相比)。

模型

Rails 應用程式的模型部分主要是它所使用的底層資料庫。實際上,在很多情形中 Rails 應用程式正是以一種受管理的方式對關係型資料庫管理 系統(RDBMS)中的資料執行操作的一個途徑。

ActiveRecord 類是 Rails 的一個核心組成部分,它將關係型表對映為 Ruby 物件,使其成為控制器可以操作並能在檢視 中顯示的資料。Rails 應用程式特別傾向於使用廣為應用的 MySQL 資料庫,不過也有與很多其他 RDBMS 的繫結,比如 IBM? DB2?。

如果您願意,您可以新增 Ruby 程式碼來在應用程式模型中執行額外的驗證,加強資料關聯,或者觸發其他操作。應用程式的 app/models/ 目錄中的 Ruby 檔案能夠呼叫 ActiveRecord 的多種驗證方法。不過,您也可以將模型程式碼留作一個存根,而只是依賴儲存資料 的 RDBMS 的約束。例如,我在這個示例中所開發的應用程式只包含這個骨架模型程式碼(至少在開始時是):

清單 1. 骨架模型 app/models/contact.rb
class Contact < ActiveRecord::Base
end

控制器

控制器以其抽象形式執行應用程式的邏輯。也就是說,應用程式的 app/controllers/ 目錄中的 Ruby 指令碼能把模型資料匯入為變數,儲存回去,或對其進行修改 和處理。不過,控制器不關心使用者如何適當地顯示或者輸入資料。在通常的 MVC 模型中,這可以讓使用者能夠以多種方式與同一控制器進行互動:本地 GUI, Web 介面,以及視力較弱的人使用的語音介面都可以與相同的控制器進行互動。

不過,Rails 不像那樣非常通用;相反,它僅侷限於在 Web 頁中提供和收集資料。雖然如此,但是您可以修改那些 Web 頁的佈局 —— 顏色、字型、表格、 樣式表單,等等 —— 與控制器程式碼無關。

檢視

Rails 檢視是我們編寫 Ruby 程式碼的地方。Rails 包含有一門用於 .rhtml 的非常好的模板語言,它將純粹的 HTML 與嵌入的 Ruby 程式碼組合起來。 Rails 應用程式介面的最表層外觀通常是由 CSS 樣式表單控制的。.rhtml 格式是一種增強的 HTML。實際上,一個簡單的 HTML 檔案本身也是一個 合法的 RHTML 模板,不過,不應該忽略 RHTML 為您提供的指令碼控制。

RHTML 是真正的模板格式 —— 不僅是在 HTML 中嵌入程式碼的方式 —— 這是一種更為有效的方法。如果您熟悉 PHP,那麼可以考慮 PHP 本身與 Smarty 模板之間的對照。也就是說,嵌入的指令碼只是將程式碼與未被解釋的 HTML 混合在一起;當需要向客戶機輸出某些內容時,程式碼部分 仍要負責執行 print 語句。

與之不同的是,模板引擎向 HTML 添加了一組定製的標籤,讓您能夠將條件、迴圈以及其他邏輯作為增強的 HTML 標記的一部分來表示。

生成程式碼

Rails 所提供的工具主要是一組程式碼生成器。相對於那些強迫我使用嚴格的工作空間和 IDE 的開發環境,我更喜歡這種方法。 Rails 不會妨礙您,但是卻會為您省去大部分手工程式設計的工作 —— 或者,通過提供“可自由獲得的”初步(first-pass)支架(scaffolding), 至少幫助您輕鬆將需要手工編碼的工作分為多個部分。

支架 概念是 Rails 中的核心概念。非常簡單的應用程式可能完全不用編碼,讓 Rails 在執行時動態地生成客戶機 HTML 頁面。 第一遍生成程式碼時建立的只是粗略的支架;接下來您可以生成更詳細的能夠定製的控制器、檢視和模型。不過在開始時不需要生成太多。

Rails 對其檔案的組織是固定的而且非常普通的,不過這種組織相對嚴格。如果您試圖強行使用其他檔案和程式碼組織方式,那麼您可能得 付出努力去修改 Rails 環境。再者說,我找不到不使用 Rails 所提供的組織方式的理由;在大部分情況下,它“fits your brain”(Ruby 的 支持者喜歡這樣講)。例如,如果您從頭開始設計一個框架(至少如果您以“Ruby 方式”思考),那麼這些目錄名稱及其組織可能與您的選擇非常接近。

構建一個簡單的應用程式

在 Ruby on Rails Web 站點上有一些教程,可以完整地引導您建立一個簡單的 Rails 應用程式(見 參考資料)。 這裡的示例程式與之類似,因為正確開始構建 Rails 應用程式的方式是確定的。由於此介紹的長度相對較短,所以我 極力 推薦 那些較長的教程中的一篇,以使得您能夠打好更為全面的基礎。

示例應用程式是一個基本的通訊錄。它演示了建立應用程式的一般步驟:

  1. 生成模型(在此步驟中建立 MySQL 資料庫和表)。
  2. 生成應用程式(包括生成基本程式碼和目錄)。
  3. 啟動 Rails(並配置資料庫的訪問)。
  4. 建立一些內容(包括生成支架模型和控制器,並告知控制器去使用那個支架)。

我們將詳細研究每一個步驟。

生成 AddressBook 模型

對於任何應用程式,您需要做的第一件事情是為它建立一個存放資料的資料庫。技術上這個步驟不必最先進行,不過需要在早期完成;應該在編寫任何 應用程式程式碼(甚至是自動生成的程式碼)之前建立資料庫,這應該是顯然的。所以,讓我們在 MySQL 資料庫中建立一個數據庫,並在此資料庫中建立第一張表。 (閱讀其他文件以瞭解如何安裝執行 MySQL 或其他 RDBMS。)

我們假定 MySQL 已經安裝並且可用。

清單 2. 建立 MySQL 資料庫和表
[~/Sites]$ cat AddressBook.sql
CREATE DATABASE IF NOT EXISTS AddressBook;
USE AddressBook;
CREATE TABLE IF NOT EXISTS contacts (
  id smallint(5) unsigned NOT NULL auto_increment,
  name varchar(30) NOT NULL default '',
  created_on timestamp(14) NOT NULL,
  updated_on timestamp(14) NOT NULL,
  PRIMARY KEY (id),
  UNIQUE KEY name_key (name)
) TYPE=MyISAM COMMENT='List of Contacts';
[~/Sites]$ cat AddressBook.sql | mysql

在這第一張表中有些地方需要注意。最重要的是每一張表都必須擁有一個 id 列,列名稱就是 id。Rails 使用 主鍵列 id 來完成各種記錄保持和引用任務。域 created_on 和 updated_on 是不需要的,不過,如果您使用了它們,那麼 Rails 會自動地“在後臺”維護它們;在大部分情況下使用這些時間戳沒有什麼不好。所以,您還要新增的“真正” 資料就只是通訊錄內容的名稱。

另一個稍微古怪的方面是,Rails 為不同的內容使用單數和複數的名稱。根據上下文,各種條目會被重新命名為單數或複數形式。表的名稱應該使用複數格式。 我沒有使用不規則複數單詞的經驗;datum 和 data 等單詞可能會令 Rails 出現問題。

生成 AddressBook 應用程式

既然已經擁有了一個能夠互動的資料庫,就可以建立 AddressBook 應用程式了。第一個步驟是簡單地執行 rails 來生成基本目錄和支架程式碼:

清單 3. 生成基本程式碼和目錄
[~/Sites]$ rails AddressBook
create
create  app/apis
create  app/controllers
create  app/helpers
create  app/models
create  app/views/layouts
create  config/environments
create  components
[...]
create  public/images
create  public/javascripts
create  public/stylesheets
create  script
[...]
create  README
create  script/generate
create  script/server
[...]

我刪減了執行 rails 的輸出;所忽略了那些行只是提醒您已經建立的各種檔案和目錄。在您的系統上試執行它,瀏覽生成的所有檔案。 我已經在程式碼中顯示了一些最重要的檔案和目錄。

執行 Rails

建立了 AddressBook/ 目錄和所需要的子目錄後,您需要執行一次惟一的初始配置。首先,通過修改 YAML 配置檔案來設定資料庫,如下:

清單 4. 配置資料庫訪問
[~/Sites]$ cd AddressBook
[~/Sites/AddressBook]$ head -6 config/database.yml # after editing
development:
  adapter: mysql
  database: AddressBook
  host: localhost
  username: some_user
  password: password_if_needed

最後,您需要提供資料。Rails 附帶了它自己的單一功能的 Web 伺服器,即 WEBrick,非常適用於我們的試驗。您可能也會遵循 Ruby on Rails Web 站點上的說明來配置 Apache 或者其他伺服器,以通過 FCGI(或者普通的 CGI,但是普通的 CGI 將會較慢)向 Rails 應用程式提供服務。

清單 5. 啟動 WEBrick 伺服器
[~/Sites/AddressBook]$ ruby script/server -d
=> Rails application started on http://0.0.0.0:3000
[2005-03-21 17:57:38] INFO  WEBrick 1.3.1
[2005-03-21 17:57:38] INFO  ruby 1.8.2 (2004-12-25) [powerpc-darwin7.8.0]

建立一些內容

要在 WEBrick 埠上看到一個歡迎頁面,先前的步驟就足夠了。例如,在我的本地系統中,現在可以訪問 http://gnosis-powerbook.local:3000/。不過,為了操作定製資料庫,需要 生成稍微多一些程式碼。可以使用指令碼 generate 來完成此任務, 這個指令碼建立在 AddressBook/ 應用程式目錄中:

清單 6. 支架模型和控制器程式碼的生成
[~/Sites/AddressBook]$ ruby script/generate model contact
      exists  app/models/
      exists  test/unit/
      exists  test/fixtures/
      create  app/models/contact.rb
      create  test/unit/contact_test.rb
      create  test/fixtures/contacts.yml
[~/Sites/AddressBook]$ ruby script/generate controller contact
      exists  app/controllers/
      exists  app/helpers/
      create  app/views/contact
      exists  test/functional/
      create  app/controllers/contact_controller.rb
      create  test/functional/contact_controller_test.rb
      create  app/helpers/contact_helper.rb

注意,在相應的表名中,這裡應該使用單數的 contact,而不是複數的 contacts

現在需要編輯一個或多個生成的檔案(只需稍加編輯)來讓控制器去使用支架:

清單 7. 告知控制器去使用支架
[~/Sites/AddressBook]$ cat app/controllers/contact_controller.rb
class ContactController < ApplicationController
  model :contact
  scaffold :contact
end

現在可以通過類似於 http://rails.server/contact/ 的 URL (在我的測試用例中是 http://gnosis-powerbook.local:3000/contact/)來檢視和修改資料庫的內容。 輸入一些資料後,它看起來如圖 1 和圖 2 所示:

圖 1. 列出聯絡人
列出聯絡人
圖 2. 編輯聯絡人
編輯聯絡人

建立可定製的內容

前面的程式碼建立了一個檢視和修改資料庫的功能完全的介面,不過,所有格式化、顯示以及業務邏輯(比如本來就有的)都由 Rails 動態完成, 沒有任何重大修改。為了建立一些更為定製的內容,需要生成更多一些程式碼。現在我們所需要的是讓 Rails 顯式地寫出它在執行時隱式地生成的 所有支架,以使得我們能夠修改它。

圖 8. 顯式控制器和檢視程式碼生成
[~/Sites/AddressBook]$ ruby script/generate scaffold Contact
  dependency  model
      [...]
      create  app/views/contacts
      exists  test/functional/
      create  app/controllers/contacts_controller.rb
      create  test/functional/contacts_controller_test.rb
      create  app/helpers/contacts_helper.rb
      create  app/views/layouts/contacts.rhtml
      create  public/stylesheets/scaffold.css
      create  app/views/contacts/list.rhtml
      create  app/views/contacts/show.rhtml
      create  app/views/contacts/new.rhtml
      create  app/views/contacts/edit.rhtml

現在有了更多一些要做的,所以嘗試去修改一些內容。(注意此程式碼已經重新使用了複數格式 contacts, 我不清楚其原因;現在我們需要接受它。)嘗試在 CSS 中修改一些顏色和字型:

清單 9. 配置層疊樣式表單
[~/Sites/AddressBook]$ head -8 public/stylesheets/scaffold.css
body { background-color: #ffe; color: #338; }
body, p, ol, ul, td {
  font-family: verdana, arial, helvetica, sans-serif;
  font-size:   13px;
}
td { border: 1px solid; }
a { color: #eef; background-color: #446; }
a:hover { color: #fff; background-color:#000; }

您已經擁有了這段程式碼,那麼 contacts_controller.rb 做什麼?就其操作而言,它比前面的程式碼中所出現的 contact_controller.rb 更為顯式且可配置。控制器類似如下:

清單 10. 控制器 app/controllers/contacts_controller.rb
class ContactsController < ApplicationController
  def list
    @contacts = Contact.find_all
  end
  def show
    @contact = Contact.find(@params['id'])
  end
  def create
    @contact = Contact.new(@params['contact'])
    if @contact.save
      flash['notice'] = 'Contact was successfully created.'
      redirect_to :action => 'list'
    else
      render_action 'new'
    end
  end

如前所述,控制器的主要任務是將資料匯入到變數之中。物件 Contact 是模型所提供的 ActiveRecord 物件-關係對映。變數 @contacts 或者@contact 是它們的適當方法中所給出的資料。 通過 URL 可以訪問那些方法本身,比如 http://rails.server/contacts/show/2 (這一個方法顯示出 id 為“2”的聯絡人)。

此示例中的控制器最終連線到了檢視,即 RHTML 檔案,它們使用的是控制器匯入到變數中的資料值。例如,這裡是 list 檢視的一部分:

清單 11. 列出檢視 app/views/contacts/list.rhtml
[...]
<% for contact in @contacts %>
  <tr>
  <% for column in Contact.content_columns %>
    <td><%=h contact.send(column.name) %></td>
  <% end %>
    <td><%= link_to 'Show', :action => 'show', :id => contact.id %></td>
    <td><%= link_to 'Edit', :action => 'edit', :id => contact.id %></td>
    <td><%= link_to 'Destroy', :action => 'destroy', :id => contact.id %></td>
  </tr>
<% end %>
[...]

方法 ContactsController.list 匯入變數 @contacts,RHTML 中的流控制標籤從陣列中 取出單個的記錄。

修改模型

初始的模型只包含聯絡人的名字。不幸的是,本文中我已經沒有餘地擴充套件這個模型以使其包含實際的聯絡人資料,比如電話號碼、地址、電子郵件等等。通常,那些資料 應該存放在一張子表中,子表的外部關鍵字關聯到表 contacts。Rails 模型會使用類似這樣的定製程式碼來指明關聯:

清單 12. 定製程式碼 app\models\phone.rb
class Phone < ActiveRecord::Base
  belongs_to :contact
end

在結束之前,讓我們來對資料模型稍加修改,以檢視它如何影響應用程式。首先,新增一列:

清單 13. 向模型新增 first_met 資料
$ cat add-contact-date.sql
USE AddressBook;
ALTER TABLE contacts ADD first_met date;
$ cat add-contact-date.sql | mysql

既然已經修改了底層的模型,http://rails.server/contact/ —— 支架的後臺版本 —— 就會直接調整過來,不需要您做什麼。 控制器和檢視是完全自動基於模型的。不過,在 http://rails.server/contacts/ 上應用程式版本使用了我們手工編寫的檔案, 並不是那樣自動化的。

list 檢視將 Contact.content_columns 作為模板迴圈的一部分,能夠 自動查詢 所有 的列,不管它們是什麼。不過,edit 等其他檢視已經被生成了,需要新增新的 資料域。例如:

清單 14. 編輯檢視 app/views/contacts/edit.rhtml
<h1>Editing contact</h1>
<%= error_messages_for 'contact' %>
<%= start_form_tag :action => 'update' %>
<%= hidden_field 'contact', 'id' %>
<p><label for="contact_name">Name</label><br/>
  <%= text_field 'contact', 'name'  %></p>
<p><label for="first_met">Known Since</label><br/>
  <%= date_select "contact", "first_met", :use_month_numbers => false %></p>
<input type="submit" value="Update" />
<%= end_form_tag %>
<%= link_to 'Show', :action => 'show', :id => @contact.id %> |
<%= link_to 'Back', :action => 'list' %>

那麼您手工修改的應用程式看起來如何了呢?與預設的區別不太大,不過在圖 3 和 4 中可以看到修改已經生效了:

圖 3. 列出聯絡人,修改後
列出聯絡人,修改後
圖 4. 編輯聯絡人,修改後
編輯聯絡人,修改後