大改動小變化 Ruby On Rails 3蓄勢待發(fā)
在過去的兩年中,Ruby on Rails應用程序框架已經(jīng)具備了一個由托管和服務提供商組成的一體式行業(yè)、一套功能廣泛且令人印象深刻的開發(fā)工具,還有各種在Ruby 用語里稱為gems和plug-ins的補充庫 — 可增強軟件的功能。
自 2007年以來,Rails 社區(qū)也已不斷壯大。全球有眾多的Rails開發(fā)人員充滿活力、團結互助,并且渴望不斷地改進該軟件。可以毫不夸張地說,Rails 的改進應歸功于社區(qū),因為是程序員之間不斷的相互超越使得構建出的軟件越來越完善。如此不斷地循環(huán),各個功能才得以從初期階段快速發(fā)展,逐漸變的實用、功能不斷強大并完善,最終成為不可或缺的工具。
在很多情況下,社區(qū)認為最基本的 gems 和 plug-ins 都會保留到 Rails 核心中。Rails 的 named scopes(一個查詢快捷方式),可實現(xiàn)與 nested forms 相同的功能。這是一個新增的功能,可取代以前的嘗試,并在相同的 HTML 表單中創(chuàng)建和編輯多個模型。實際上,對于 Rails 開發(fā)人員來說最困難的任務或許就是跟上變化的節(jié)奏。
Rails下一個主要的發(fā)布是Rails版本3,該版本繼續(xù)保持工具包的快速改進。與以往相同,該軟件仍然非常 “固執(zhí)”,即仍然偏向于約定優(yōu)于配置。Rails 的核心組件始終存在,即 RESTful 路徑、關系、驗證、模板和數(shù)據(jù)庫抽象化。不過,這些組件的許多內(nèi)部內(nèi)容已經(jīng)被重寫或者進行了改進。最明顯的并且很大程度上借用了 Merb 的理念的就是很多 Rails 的基本功能不再密切結合。
例如,以前只對 Rails 應用程序可用的數(shù)據(jù)驗證便捷功能現(xiàn)在成為獨立的組件,并且可以包含在 Vanilla Ruby 代碼中。如呈現(xiàn)部件和模板等控制器功能,現(xiàn)在也是相互獨立的,并且可以嵌入到任一庫中。
大改動、小變化
Rails 3中變動的數(shù)量太多,無法在此全部列出。此處,僅介紹一些很可能會影響到開發(fā)人員的一些變動:
Ruby On Rails 3發(fā)布說明:http://edgeguides.rubyonrails.org/3_0_release_notes.html
◆一條命令可以控制所有事件。有了 Rails 3,無需在每一個應用程序中使用整套腳本(script/server、script/generate 以及其他),使用一條命令即可取代其功能,該命令恰如其分地被命名為 rails。例如,在以前需要鍵入 ./script/console 的地方,您現(xiàn)在只需鍵入 rails console。rails 命令還可以像以前一樣生成新的應用程序。其運行方式依據(jù)其是否在現(xiàn)有的 Rails 應用程序中啟動而有所不同。
◆可為依賴性提供具體的解決方案。協(xié)調和解決 gem 依賴性是個棘手的問題。與可用 gems 的集合一樣,gem 的修訂也是隨系統(tǒng)的不同而有所變化。因為具有這樣的多樣化,所以很難廣泛地部署或共享一個 Rails 應用程序。Rails 3 引入了 Bundler,這是一個專門用于管理依賴性的實用程序(因此無需再使用 config.gem)。您可以在應用程序根目錄內(nèi)一個名為 Gemfile 的目錄中聲明依賴性。Bundler 將下載和存儲所有指定的 gem。您甚至可以在應用程序中 “打包” gem ,以便阻止從外部存儲庫下載。
◆不帶查詢語句的查詢功能。一直以來,Rails 已經(jīng)可以充分運用特定于領域的語言 (DSL) — 考慮一下 has_one 或 validates_numericality_of — 有一個明顯的例外:數(shù)據(jù)庫查詢??梢源_定地說,Rails 的動態(tài)搜尋器便捷、易用,但混合使用選項哈希值如 :conditions、:order 和 :limit 非常常見,因為都是 find_by_sql 語句。Rails 3 合并了 relational algebra,這是專門設計用于表示查詢的 DSL。基本命令包括 project(用于選擇列)、where(用于表示條件)、join(用于指定關系)、take 和 skip(分別用于限定和抵消),以及 group(用于聚集)等其他屬性。
用于模糊樣板代碼的控制器。Rails控制器的核心操作—new、create、edit和update—通常不變,尤其是當控制器大部分用于CRUD操作時。事實上,控制器生成器的輸出 ./script/generate controller一般不需要進一步的修改就可以滿足需要??紤]到這些相似性,Rails 3引入了Responder來進一步簡化代碼。例如下面是create操作全部所需的幾行代碼:
- class PostsController
- respond_to :html, :xml
- def create
- @post = Post.create(params[:post])
- respond_with(@post)
- end
- end
在該代碼片段中,如果@post保存成功,respond_with(@post) 將發(fā)送到show以顯示新的記錄,而假設對象的驗證失敗,則發(fā)送到new。這僅僅是一個小樣本。您可以在下一章節(jié)找到這些新功能的示例以及更多內(nèi)容,例如從頭構建Rails 3應用程序。
#p#
***構建 Rails 3應用程序
要運行 Rails 3,您的系統(tǒng)必須安裝有Ruby 1.8.7版或 Ruby 1.9.2 版,或者該編程語言的較新版本及其附加庫和解釋程序。您的機器上***同時安裝有 Git 軟件版本控制系統(tǒng),因為 Rails 3 和許多其他重要的 Rails 項目都是在 Git 中進行維護的。您的系統(tǒng)還需要數(shù)據(jù)庫引擎,例如 SQLite(版本 3)、MySQL 或者 PostgreSQL。開發(fā) Rails 應用程序時,Web 服務器不是必須的,但它通常是生產(chǎn)部署的一部分。
要創(chuàng)建 Rails 3 應用程序,您必須擁有Rails 3預發(fā)布gem和所有其相關產(chǎn)品。這時,您只需通過運行幾條命令(請參閱 清單 1)即可安裝所需的組件。(在您繼續(xù)進行之前請查看 Rails 3 文檔,因為根據(jù)版本的不同具體的操作會有所不同。)
- 清單 1. Rails 3 預發(fā)布 gem 和相關產(chǎn)品
- $ gem install rails3b
- Due to a rubygems bug, you must uninstall all older versions of bundler for 0.9 to work
- Successfully installed mime-types-1.16
- Successfully installed mail-2.1.2
- Successfully installed text-hyphen-1.0.0
- Successfully installed text-format-1.0.0
- Successfully installed memcache-client-1.7.8
- Successfully installed rack-1.1.0
- Successfully installed rack-mount-0.4.7
- Successfully installed abstract-1.0.0
- Successfully installed erubis-2.6.5
- Successfully installed i18n-0.3.3
- Successfully installed tzinfo-0.3.16
- Successfully installed bundler-0.9.5
- Successfully installed thor-0.13.1
- Successfully installed rails3b-3.0.1
- 14 gems installed
- $ gem install arel --pre
- Successfully installed activesupport-3.0.0.beta
- Successfully installed arel-0.2.pre
- 2 gems installed
- $ gem install rails --pre
- Successfully installed activemodel-3.0.0.beta
- Successfully installed actionpack-3.0.0.beta
- Successfully installed activerecord-3.0.0.beta
- Successfully installed activeresource-3.0.0.beta
- Successfully installed actionmailer-3.0.0.beta
- Successfully installed railties-3.0.0.beta
- Successfully installed rails-3.0.0.beta
- 7 gems installed
下一步是生成應用程序 — 在 清單 2 中顯示了一個小 wiki。該應用程序創(chuàng)建并管理文章。每一篇文章都有一個標題和一些散文,通過從現(xiàn)有頁面的正文創(chuàng)建一個指向新文章的引用,您即可創(chuàng)建一篇新的文章。引用可以是任一駝峰式大小寫單詞,例如 TheSolarSystem 或者 TheOscars。
注意:可通過下面的 下載 表格獲取該 wiki 應用程序的源代碼。
- 清單 2. Wiki Rails 應用程序
- $ rails wiki
如果您運行了 ls -lR 來查看應用程序的內(nèi)容,將會顯示一些新文件:
◆Gemfile,即前面曾提到的 gem 清單。該文件必須至少包含兩行:一行指向 Rails 3 beta gem 的源,另一行則綁定 Rails 3 beta gem 本身。您或許還需要第三行(至少)以連接數(shù)據(jù)庫:
- source 'http://gemcutter.org'
- gem "rails", "3.0.0.beta"
- gem "sqlite3-ruby", :require => "sqlite3"
◆config/application.rb,它包含 config/environment.rb 中以前提供的很多選項。雖然后者仍然保留,但很大程度上已不再使用該文件。config/application.rb 的一個顯著的附加功能是 generators block:
- config.generators do |g|
- g.orm :active_record
- g.template_engine :erb
- g.test_framework :test_unit, :fixture => true
- end
您的 Rails 3 應用程序可以使用一些兼容的對象關系映射器 (ORM)、模板引擎和測試框架。生成器塊會指定應用程序的***項,并根據(jù)您的模型、視圖等調用適當?shù)纳善鳌?/p>
◆db/seeds.rb,該文件對于 Rails 3 來說并不是新增的,但卻有必要著重介紹一下,因為它是最近不久剛增加的功能(在 Rails 2.3.4 版引入的)。如果您的應用程序需要初始數(shù)據(jù)以正常運行,例如一個管理用戶、價格代碼或靜態(tài)頁面,那么您可以在 db/seeds.rb 中創(chuàng)建這些數(shù)據(jù)并運行任務 rake db:seed。在 Seed 文件之前,不存在初始化的慣例,許多開發(fā)人員把代碼放入遷移中,這樣容易混淆創(chuàng)建數(shù)據(jù)庫和填充數(shù)據(jù)庫之間的不同之處。
◆config.ru,存在于每個 Rails 3 應用程序的根目錄下,即所謂的 rackup 文件,也就是基于 Rack 的應用程序的配置文件。Rails 3 是一個 Rack 應用程序,并且與任一支持 Rack 的 Web 服務器相兼容??偟膩碚f,除非您想要添加其他 Rack 組件,否則請不要更改 config.ru 文件。
還有一些其他新文件;不過大多數(shù)看上去與 Rails 版本 2.3 相似。config/routes.rb 文件的功能與以往相同,只不過更加簡化、更具有 Ruby 的特色。您將很快會看到一個示例。
生成應用程序并編輯 Gemfile 以聲明依賴性之后,下一步就是收集應用程序所需的 gem。這是由新的實用程序 bundle(請參閱 清單 3)來完成的工作。
- 清單 3. 收集所需的gem
- $ bundle
- installFetching source index from http://gemcutter.org
- Resolving dependencies
- Installing abstract (1.0.0) from system gems
- Installing actionmailer (3.0.0.beta) from system gems
- Installing actionpack (3.0.0.beta) from system gems
- Installing activemodel (3.0.0.beta) from system gems
- Installing activerecord (3.0.0.beta) from system gems
- Installing activeresource (3.0.0.beta) from system gems
- Installing activesupport (3.0.0.beta) from system gems
- Installing arel (0.2.1) from rubygems repository at http://gemcutter.org
- Installing builder (2.1.2) from system gems
- Installing bundler (0.9.7) from rubygems repository at http://gemcutter.org
- Installing erubis (2.6.5) from system gems
- Installing i18n (0.3.3) from system gems
- Installing mail (2.1.2) from system gems
- Installing memcache-client (1.7.8) from system gems
- Installing mime-types (1.16) from system gems
- Installing rack (1.1.0) from system gems
- Installing rack-mount (0.4.7) from system gems
- Installing rack-test (0.5.3) from system gems
- Installing rails (3.0.0.beta) from system gems
- Installing railties (3.0.0.beta) from system gems
- Installing rake (0.8.7) from system gems
- Installing sqlite3-ruby (1.2.5) from rubygems repository at
- http://gemcutter.org with native extensions
- Installing text-format (1.0.0) from system gems
- Installing text-hyphen (1.0.0) from system gems
- Installing thor (0.13.3) from rubygems repository at http://gemcutter.org
- Installing tzinfo (0.3.16) from system gems
- Your bundle is complete!
該bundle實用程序,簡稱 Bundler,可用于下載和安裝所有在 Gemfile 中指定的 gem 以及任何這些 gems 的依賴項(請參閱 清單 4)。該 bundle 實用程序還可以將所有依賴項復制到您的應用程序中,使得您的代碼庫自給自足。具體來說,如果您運行 bundle pack,Bundler 會將所有 gem 的資料復制到 vendor/cache。
- 清單 4. 運行 bundle 實用程序
- $ bundle pack
- Copying .gem files into vendor/cache
- * bundler-0.9.7.gem
- * thor-0.13.3.gem
- * abstract-1.0.0.gem
- * mime-types-1.16.gem
- * text-hyphen-1.0.0.gem
- * rack-mount-0.4.7.gem
- * rake-0.8.7.gem
- * text-format-1.0.0.gem
- * tzinfo-0.3.16.gem
- * rack-test-0.5.3.gem
- * builder-2.1.2.gem
- * erubis-2.6.5.gem
- * memcache-client-1.7.8.gem
- * rack-1.1.0.gem
- * sqlite3-ruby-1.2.5.gem
- * i18n-0.3.3.gem
- * activesupport-3.0.0.beta.gem
- * arel-0.2.1.gem
- * mail-2.1.2.gem
- * activemodel-3.0.0.beta.gem
- * activerecord-3.0.0.beta.gem
- * actionpack-3.0.0.beta.gem
- * railties-3.0.0.beta.gem
- * actionmailer-3.0.0.beta.gem
- * activeresource-3.0.0.beta.gem
- * rails-3.0.0.beta.gem
- $ ls vendor/cache
- abstract-1.0.0.gem memcache-client-1.7.8.gem
- actionmailer-3.0.0.beta.gem mime-types-1.16.gem
- actionpack-3.0.0.beta.gem rack-1.1.0.gem
- activemodel-3.0.0.beta.gem rack-mount-0.4.7.gem
- activerecord-3.0.0.beta.gem rack-test-0.5.3.gem
- activeresource-3.0.0.beta.gem rails-3.0.0.beta.gem
- activesupport-3.0.0.beta.gem railties-3.0.0.beta.gem
- arel-0.2.1.gem rake-0.8.7.gem
- builder-2.1.2.gem sqlite3-ruby-1.2.5.gem
- bundler-0.9.7.gem text-format-1.0.0.gem
- erubis-2.6.5.gem text-hyphen-1.0.0.gem
- i18n-0.3.3.gem thor-0.13.3.gem
- mail-2.1.2.gem tzinfo-0.3.16.gem
將vendor/cache視為應用程序自己的gem存儲庫。您可以將代碼庫移動到任何地方,并可以獲得您所需的gem軟件和版本 — 無需遠程存儲器即可實現(xiàn)。例如,如果您在 bundle pack之后運行bundle install,gem會從您的應用程序存儲庫安裝到您的系統(tǒng)中(請參閱 清單 5)。
- 清單 5. 安裝 gem
- Fetching source index from http://gemcutter.org
- Resolving dependencies
- Installing abstract (1.0.0) from .gem files at
- /Users/strike/projects/rails3/wiki/vendor/cache
- Installing actionmailer (3.0.0.beta) from .gem files at
- /Users/strike/projects/rails3/wiki/vendor/cache
- Installing actionpack (3.0.0.beta) from .gem files at
- /Users/strike/projects/rails3/wiki/vendor/cache
- ...
- Installing thor (0.13.3) from .gem files at
- /Users/strike/projects/rails3/wiki/vendor/cache
- Installing tzinfo (0.3.16) from .gem files at
- /Users/strike/projects/rails3/wiki/vendor/cache
- Your bundle is complete!
#p#
使用wiki
要創(chuàng)建應用程序,則需要為頁面生成一個工作框架(scaffold)、創(chuàng)建數(shù)據(jù)庫、將初始頁面放到數(shù)據(jù)庫并且設定所需的路徑(請參閱 清單 6)。為了簡單化,僅限在某些字段使用 wiki 頁面記錄:標題、標頭(標題的縮略語)、正文和時間截(以用于記錄頁面的創(chuàng)建時間和***修改時間)。標題和標頭是字符串字段;散文是文本字段;時間截是日期和時間字段。(當然,一個真正的 wiki 還會有其他字段,如最近的作者以及頁面的修訂歷史記錄。為了盡量簡潔,該例子還省略了用戶和會話、格式以及各種身份驗證和授權。)您可以使用 rails generate scaffold 命令生成一個初始模型、一系列視圖以及一個控制器。
- 清單 6. 完整的 wiki 應用程序
- $ rails generate scaffold page title:string slug:string body:text --timestamps
- invoke active_record
- create db/migrate/20100221115613_create_pages.rb
- create app/models/page.rb
- invoke test_unit
- create test/unit/page_test.rb
- create test/fixtures/pages.yml
- route resources :pages
- invoke scaffold_controller
- create app/controllers/pages_controller.rb
- invoke erb
- create app/views/pages
- create app/views/pages/index.html.erb
- create app/views/pages/edit.html.erb
- create app/views/pages/show.html.erb
- create app/views/pages/new.html.erb
- create app/views/pages/_form.html.erb
- create app/views/layouts/pages.html.erb
- invoke test_unit
- create test/functional/pages_controller_test.rb
- invoke helper
- create app/helpers/pages_helper.rb
- invoke test_unit
- create test/unit/helpers/pages_helper_test.rb
- invoke stylesheets
- create public/stylesheets/scaffold.css
如果您想知道 ./script/generate 命令有何變化,回憶一下,該命令已經(jīng)被全能的 rails 命令包含了。運行rake db:create db:migrate 以創(chuàng)建數(shù)據(jù)庫:
- $ rake db:create db:migrate
- == CreatePages: migrating ====================================================
- -- create_table(:pages)
- -> 0.0010s
- == CreatePages: migrated (0.0011s) ===========================================
該Wiki現(xiàn)已存在,但卻是空的。添加一個初始頁面作為所有其他頁面的基準。編輯文件 db/seeds.rb,并編寫代碼以創(chuàng)建一個新的頁面,如 清單 7 中所示:
- 清單 7. wiki 基準頁面
- Page.create(
- :title => 'The Marx Brothers Wiki',
- :slug => 'Home',
- :body => 'An encyclopedic guide to the Marx Brothers.')
運行 rake db:seed 以執(zhí)行代碼。您可以通過使用 rails console 快速瀏覽以驗證頁面,如 清單 8 中所示。
- 清單 8. 驗證基準頁面
- $ rake db:seed
- (in /Users/strike/projects/rails3/wiki)
- $ rails console
- Loading development environment (Rails 3.0.0.beta)
- irb(main):001:0> Page.all
- => [#<Page id: 1, title: "The Marx Brothers Wiki", slug: "Home",
- body: "An encyclopedic guide to the Marx Brothers.",
- created_at: "2010-02-21 12:24:43", updated_at: "2010-02-21 12:24:43">]
在繼續(xù)運行編碼之前,請先設定路徑。需要兩條路徑:一條默認的路徑用來查找主頁面,而另外一條路徑則通過標頭來查找頁面。清單 9 顯示了最終版的 config/routes.rb 文件。
- 清單 9. config/routes.rb(最終版)
- Wiki::Application.routes.draw do |map|
- resources :pages
- root :to => "pages#show"
- end
在清單 6中,rails generate scaffold page 這一行命令可自動在第二行創(chuàng)建路徑,這是 REST 式的。您必須在第三行手動添加路徑。用于指定站點路徑的默認 “根目錄” 的語法是 Rails 3 中的新增功能。第三行定義的是,“將路徑 ‘/’ 映射到頁面控制器的 ‘show’ 方法”。show 方法的代碼將在數(shù)據(jù)庫中查找主頁面并顯示出來。添加新的根目錄路徑后,需要刪除 public/index.html 文件以避免產(chǎn)生沖突:
- $ rm public/index.html
現(xiàn)在,讓我們來關注頁面控制器。Rails 3 中的控制器代碼可以極其簡單。清單 10 通過單一的 show 方法,顯示了控制器的初始實現(xiàn)。
- 清單 10. Rails 3 控制器
- class PagesController < ApplicationController
- respond_to :html
- def show
- @page = Page.where( :slug => ( params[:id] || 'Home' ) ).first
- respond_with( @page )
- end
- end
正如您所看到的,通常在 Rails 2 控制器中提供的所有模板都不見了。respond_to 列出了控制器所支持的格式;此處,它僅會對 HTML 的請求做出反應。respond_with 是邏輯快捷方式,用于決定控制器應如何繼續(xù)處理。
查詢的語法也是大有不同。查詢是 Rails 3 關系代數(shù)的一個示例。您可能會想知道為什么需要有 first 后綴。where 和其他表達查詢的操作數(shù)并不會真正引起查詢語句被執(zhí)行。相反地,查詢站點一直閑置,直到真正需要數(shù)據(jù)時才啟動。這就是延遲加載,即盡可能長的延遲查詢語句的執(zhí)行。first 命令將觸發(fā)數(shù)據(jù)庫中的實際查詢。
如果您現(xiàn)在運行應用程序,您會看到與 圖 1 相似的情況,現(xiàn)在,您可以向控制器中添加更多的代碼。清單 11 顯示了完整的控制器。
- 清單 11. 完整的 Rails 3 控制器
- class PagesController < ApplicationController
- respond_to :html
- before_filter :get_page, :except => [ :create ]
- def create
- respond_with( @page = Page.create( params[ :page ] ) )
- end
- def edit
- end
- def index
- render :action => :show
- end
- def show
- @page ||= Page.new( :slug => params[ :id ] )
- if @page.new_record?
- render :action => :new
- else
- respond_with( @page )
- end
- end
- def update
- @page.update_attributes( params[ :page ] )
- respond_with( @page )
- end
- private
- def get_page
- @page = Page.where( :slug => ( params[:id] || 'Home' ) ).first ||
- Page.where( :id => params[:id] ).first
- end
- end
在該控制器中,index 方法僅僅反映沒有頁面標示符的 show 操作,從而呈現(xiàn)主頁面。show 會顯示一個頁面,并提供一個 ID 或標頭(所有操作的查詢都集中在 get_page 中,從而進一步減少了代碼的數(shù)量);如果某個頁面不存在,則會準備一個新的頁面以供進行編輯。Page模型僅僅可以驗證所有顯示的字段:
- class Page > ActiveRecord::Base
- validates_presence_of :body, :slug, :title
- end
將駝峰式大小寫引用轉換為指向其他頁面的鏈接,這一工作是在 Page 模型的視圖中進行的。由 app/helpers/pages_helper.rb 中的 helper 函數(shù)來完成這一工作,從而保持視圖的最小化(請參閱 清單 12)。
- 清單 12. 駝峰式大小寫轉換 helper 函數(shù)
- module PagesHelper
- def wikify( page )
- return '' if page.body.blank?
- page.body.gsub( /^([A-Z][[:alnum:]]*([A-Z][[:alnum:]]*)+)/ ) do |match|
- link_to( $1, :action => :show, :id => $1 )
- end
- end
- end
該視圖是典型的視圖,如清單 13中所示。
- 清單 13. 典型視圖
- <p>
- <b>Title:b>
- <%= @page.title %>
- p>
- <p>
- <b>Body:b>
- <%= raw wikify( @page ) %>
- p>
- <%= link_to 'Edit', edit_page_path(@page) %> |
- <%= link_to 'Back', pages_path %>
raw操作數(shù)是Rails 3中新增的功能。與以前版本的Rails不同,默認情況下所有的字符串都可以(去掉了HTML)安全發(fā)送。如果要通過HTML發(fā)送一個字符串,則必須使用 raw。
切換Rails
除了此處所介紹的功能改進和便捷性,Rails 3還提供了比以前版本更佳的性能,尤其是在呈現(xiàn)部件方面。您還可以創(chuàng)建您專有的驗證器類,并充分利用更為流暢的標準驗證。例如,由 Jeremy McAnally 編寫以下驗證,一次需要四行單獨的代碼:
- validates :login, :presence => true, :length => {:minimum => 4},
- :uniqueness => true, :format => { :with => /[A-Za-z0-9]+/ }
【編輯推薦】