天天看點

Ruby on Rails 2.0的新特性介紹

萬衆矚目的Ruby on Rails 2.0已經釋出了,Rails架構在2004年誕生以來,一直保持着相當快的版本更新速度:2005年釋出了Rails1.0版本,2006年初釋出Rails1.1版本,2007年初釋出Rails1.2版本,而還沒有等到2008年,在2007年聖誕前夕的12月6日,Rails2.0已經釋出。

Rails架構每個大的版本更新都給我們帶來了相當多的新功能,新驚喜。Rails1.0帶給我們完善的單元測試和內建測試;Rails1.1帶給我們DataBase Migration和RJS;Rails1.2讓我們看到了REST的光明前景,那麼Rails2.0又将帶給我們什麼呢? 我粗略的翻譯了一下Rails架構作者DHH寫的Rails2.0架構介紹文章,以下翻譯自DHH的文章,原文在:

http://weblog.rubyonrails.org/2007/12/7/rails-2-0-it-s-done

在經過差不多一年的開發之後,Rails2.0終于釋出了。這是一個棒極了的Rails版本,包括了大量堪稱偉大的新特性,無數的bugfix和大量功能的打磨。我們花了無數的精力去打造這樣一個非常完美合理的軟體包。

這也是Rails架構發展曆史上的一個裡程碑。我個人在Rails架構上面的開發工作已經有四年半的時間了,并且我們現在的貢獻者也越來越多。我對這幾年我們所做出的工作和我們一直堅持的信念感到非常的滿意。我們一直在堅持這一點并且不斷推動它。

在詳細的介紹Rails2.0之前,我要向那些為Rails架構做出過貢獻的每一個人緻以深深的謝意,不論是像一個家庭那樣其樂融融的Rails核心開發團隊,還是成千上萬的、而且年複一年為Rails送出更新檔,積極參與Rails社群人們。Rails2.0也是大規模開源軟體開發社群的一個重大勝利,而你完全可以自豪于你在Rails社群當中扮演的角色和做出的貢獻。幹杯!

現在讓我們簡單的窺一斑見全豹的看看Rails的那些閃閃發光的特性:

[color=red][b]Action Pack: Resources[/b][/color]

Controller裡面充斥着大量action方法的時代已經過去了,我們對REST架構的應用進行了大量的改進和提高。首先,我們不再使用分号來隔離自定義的方法,而是采用斜線,例如原來是 /people/1;edit的URL,現在改成了 /people/1/edit.另外我們還對URL路由資源添加了命名空間的支援,是以像背景管理的接口你可以像這樣非常簡單的定義:

map.namespace(:admin) do |admin|
  admin.resources :products,
    :collection => { :inventory => :get },
    :member     => { :duplicate => :post },
    :has_many   => [ :tags, :images, :variants ]
end
           

這種方式可以讓你按照如下的方式定義命名路由,例如:inventory_admin_products_url和 admin_product_tags_url等等。為了友善的記錄所有的路由規則,我們添加了一個rake任務叫做“rake routes”,它能夠列舉出來routes.rb定義的所有命名路由規則。

此外我們還引入了一個新的約定,即所有基于資源的controller預設都是複數形式的。這樣即便單個資源在不同的路由規則中被多次引用,仍然可以指向同一個controller來處理,例如:

[color=red][b]Action Pack: Multiview[/b][/color]

與資源映射一起進行功能增強的還有MultiView。我們已經有了respond_to方法,但我們可以更進一步,把MultiView控制延伸到模闆裡面去。我們現在可以根據模闆檔案的字尾格式來決定使用什麼render機制。是以,show.rhtml你可以寫成show.rhtml.erb,這就表明是一個預設的rhtml模闆,和你過去在Action裡面使用respond_to聲明的format.html是一個意思。此外你還可以使用諸如show.csv.erb,它表明顯示為csv格式的資料,并且使用預設的erb去render它。

是以,新的模闆格式是: action.format.renderer。例如:

* show.erb: 不管什麼格式的顯示方式都使用預設的erb顯示show模闆

* index.atom.builder: 用Builder庫來render XML檔案,輸出的檔案類型為RSS的AOTM類型

* edit.iphone.haml: 使用使用者自己定義的HAML render機制來輸出模闆内容到iPhone手機上面

說到iPhone手機,我們可以自己造一個專用的類型來實作内部路由。當你需要類似iPhone這樣的特殊的HTML接口的時候,我們所要做的就是:

# should go in config/initializers/mime_types.rb
  Mime.register_alias "text/html", :iphone

  class ApplicationController < ActionController::Base
    before_filter :adjust_format_for_iphone

    private
      def adjust_format_for_iphone
        if request.env["HTTP_USER_AGENT"] && request.env["HTTP_USER_AGENT"][/(iPhone|iPod)/]
          request.format = :iphone
        end
      end
  end

  class PostsController < ApplicationController
    def index
      respond_to do |format|
        format.html   # renders index.html.erb
        format.iphone # renders index.iphone.erb
      end
    end
  end
           

你完全可以在config/initializers/mime_types.rb裡面注冊自己的mime type類型的映射,這個檔案預設已經提供了。

[color=red][b]Action Pack: Record identification[/b][/color]

為了驅使你使用基于資源的映射,我們對資源映射的controller和view的URL處理進行了大量的簡化。我們添加了大量的命名約定,讓你可以直接把model對象轉化為資源映射的路由,例如:

# person is a Person object, which by convention will 
  # be mapped to person_url for lookup
  redirect_to(person)
  link_to(person.name, person)
  form_for(person)
           

[color=red][b]Action Pack: HTTP Loving[/b][/color]

如你所期望的那樣,Rails2.0的Action Pack更加貼近HTTP,并且充分利用HTTP協定,例如資源、多種視圖,還有更多的呢。我們添加了一個子產品來處理HTTP的Basic驗證,它能夠讓授權的API輕松跨越SSL協定,而且他是如此的簡單易用。下面是一個例子(更多的例子請參考ActionController::HttpAuthentication):

class PostsController < ApplicationController
    USER_NAME, PASSWORD = "dhh", "secret" 

    before_filter :authenticate, :except => [ :index ]

    def index
      render :text => "Everyone can see me!" 
    end

    def edit
      render :text => "I'm only accessible if you know the password" 
    end

    private
      def authenticate
        authenticate_or_request_with_http_basic do |user_name, password| 
          user_name == USER_NAME && password == PASSWORD
        end
      end
  end
           

此外,我們也做了很多工作讓你把JavaScript和CSS檔案組織到一個邏輯單元裡面去,而不需要讓浏覽器發起多次HTTP請求,分别擷取每個JavaScript和CSS檔案,以便減少HTTP請求次數。使用javascript_include_tag(:all, :cache => true) 這個helper在生産環境下自動把public/javascripts/目錄下面的所有js檔案打包到單個public/javascripts/all.js檔案裡面,但在開發環境下,仍然保持每個檔案獨立的修改。

我們還添加了一些選項讓,僅僅幾行代碼,就能夠讓浏覽器去通路多台伺服器上面的資源。如果你添加如下設定:ActionController::Base.asset_host = “assets%d.example.com”,那麼Rails架構就會自動的把靜态資源的請求分發到多台實體伺服器上面去,例如分發到assets1.example, assets2.example.com, assets3.example.com等等。這樣浏覽器可以同時向多台伺服器下載下傳資源,增加你的應用的通路速度。

[color=red][b]Action Pack: Security[/b][/color]

能夠很簡單的建立出來安全的應用總是一件令人愉快的事情,而Rails2.0提供了大量先進的功能來達到這一點。非常重要的是我們現在提供了一種内建的機制來處理CRSF攻擊。我們在所有的HTML表單和AJAX請求當中包含了一個特殊的token,而請求來自于其他的應用的時候,你就可以檢測到。所有的這些選項在新建立的Rails2.0項目當中預設就是打開的狀态,對于你更新到Rails2.0的項目要打開這一個選項也很容易,使用ActionController::Base.protect_from_forgery就可以了,詳細的說明請看:ActionController::RequestForgeryProtection。

對于允許使用者在應用當中送出HTML代碼的情況,防止XSS攻擊現在也變得更加簡單了。TextHelper#sanitize方法從過濾黑名單變成了驗證白名單。如果你已經使用了sanitize方法,你就會自動獲得更好的保護。當然你也可以自行調整預設允許的HTML tag,請看TextHelper#sanitize擷取詳情。

最後,我們還添加了“[url=http://msdn2.microsoft.com/en-us/library/ms533046.aspx]HTTP only cookies[/url]”支援,這一特性并不是所有的浏覽器都支援,但是對于支援的浏覽器你就可以派上用場了。

[color=red][b]Action Pack: Exception handling[/b][/color]

大多數常見的異常都可以統一處理,而不是每個需要單獨的處理。通常情況下,你隻需要覆寫rescue_action_in_public方法,來進行統一的異常處理即可。但是你也有可能需要使用自己的case語句來處理特定場合的異常。是以我們現在提供了一個類級别的宏叫做rescue_from,你可以使用它來聲明針對某個特定的Action來捕獲異常,例如:

class PostsController < ApplicationController
    rescue_from User::NotAuthorized, :with => :deny_access

    protected
      def deny_access
        ...
      end
  end
           

[color=red][b]Action Pack: Cookie store sessions[/b][/color]

Rails2.0預設的Session存儲機制現在是基于Cookie的方案。Session也可以不必存儲在伺服器的檔案系統或者資料庫系統裡面,而是以ruby hash的格式每次作為cookie發送到用戶端浏覽器來保持。這樣做不單單會比傳統的伺服器端儲存Session的方式要快一些,而且完全不需要維護。你不需要在伺服器上面運作cron job任務來清理session檔案,也不必擔心因為你忘記清理session檔案導緻你的伺服器/tmp分區下面因為塞滿了50萬的session檔案,進而讓你的伺服器crash掉。

如果你能夠遵循一些最佳實踐,保持session最小化,例如隻在session裡面存放user_id和flash資訊,那麼這種session機制就會很棒。但是,如果你打算在session裡面儲存核彈發射代碼的話,這一存儲機制就不是一個好主意了。因為它們無法被加密(例如使用者僞造is_admin=true),它們很容易被使用者看到。如果對于你的應用程式來說,這是一個必須注意的問題,那麼你就應該使用傳統的session存儲機制(但你首先應該先做一下調查)。

[color=red][b]Action Pack: New request profiler[/b][/color]

在一個真實的應用當中找出性能瓶頸是一個艱難的活,但有了我們新的request profiler,工作會變得簡單很多。request profiler跟蹤一個完整的執行腳本,報告執行結果,你可以像這樣來使用它:

$ cat login_session.rb
  get_with_redirect '/'
  say "GET / => #{path}" 
  post_with_redirect '/sessions', :username => 'john', :password => 'doe'
  say "POST /sessions => #{path}" 
  $ ./script/performance/request -n 10 login_session.rb
           

這樣你就可以得到一份非常詳盡的HTML和text格式的運作報告,每個步驟執行了多少時間,有了這個東西,你就可以很清楚的知道怎樣優化你的應用程式了。

[color=red][b]Action Pack: Miscellaneous[/b][/color]

還有一個值得一提的是AtomFeedHelper。它可以讓你更容易的使用增強的builder格式來建立RSS輸出,例如:

# index.atom.builder:
  atom_feed do |feed|
    feed.title("My great blog!")
    feed.updated((@posts.first.created_at))

    for post in @posts
      feed.entry(post) do |entry|
        entry.title(post.title)
        entry.content(post.body, :type => 'html')

        entry.author do |author|
          author.name("DHH")
        end
      end
    end
  end
           

在Rails2.0裡面,我們已經進行了大量的性能優化,是以對于helper的調用開銷已經變得很小了,而且對于簡單的命名路由,我們還使用了cache,讓它們能夠執行的更快。

最後我們把in_place_editor和autocomplete_for這兩個helper從Rails架構當中挪出去,放到了Rails官方SVN的插件目錄下面了。

[color=red][b]Active Record: Performance[/b][/color]

ActiveRecord進行了無數的bug修複和少量的調整,但是仍然有一些值得一提的亮點。我們添加了一個非常簡單的查詢緩存,它能夠在同一個請求的過程當中記錄相似的SQL調用,并且緩存查詢結果。查詢緩存對于那些很難用:include來解決的N+1次查詢問題會非常有幫助。另外我們也徹底提高了fixtures的性能,對于大多數正常的測試套件,性能提高了50-100%。

[color=red][b]Active Record: Sexy migrations[/b][/color]

現在我們有一種新的migration檔案中聲明的格式。以前我們是這樣寫的:

create_table :people do |t|
  t.column, "account_id",  :integer
  t.column, "first_name",  :string, :null => false
  t.column, "last_name",   :string, :null => false
  t.column, "description", :text
  t.column, "created_at",  :datetime
  t.column, "updated_at",  :datetime
end
           

而現在,我們可以這樣寫:

create_table :people do |t|
  t.integer :account_id
  t.string  :first_name, :last_name, :null => false
  t.text    :description
  t.timestamps
end
           

[color=red][b]Active Record: Foxy fixtures[/b][/color]

近來fixtures功能受到了很多抨擊,對于fixtures的批評主要集中在fixtures之間聲明的依賴關系上。在fixtures裡面通過聲明id屬性來作為主鍵,進而構造fixtures之間的關聯關系是個郁悶的活。現在你可以通過這種方式聲明和編寫fixtures:

如上面所示,不再需要定義id屬性,通過id來關聯fixtures了,你現在可以直接使用fixtures的名字來建立關聯關系。

[color=red][b]Active Record: XML in, JSON out[/b][/color]

ActiveRecord支援XML的序列化已經有一段時間了。在Rails2.0當中,我們還添加了XML的反序列化功能,是以你現在可以這樣用Person.new.from_xml(“David“) 來擷取person對象。當然我們也添加了序列化到JSON格式的功能,它和XML序列化的支援是一樣的,還可以支援關聯關系的抓取,隻需要寫person.to_json就可以了。

[color=red][b]Active Record: Shedding some weight[/b][/color]

為了讓ActiveRecord更加簡潔和通用,我們把acts_as_list,acts_as_tree等acts_as_xxx功能挪出了Rails,放在了Rails官方的SVN插件裡面。如果你需要用到諸如acts_as_list的話,那麼你需要安裝這個插件,你可以./script/plugin install acts_as_list 來安裝,安裝好以後,acts_as_list功能又回來了,用法沒有任何差別。

更加激進一點的改動是我們把所有的商業資料庫驅動全部挪到了外部的gem包裡面。是以Rails2.0僅僅自帶MySQL、SQLite和PostgreSQL資料庫驅動。這三個資料庫是我們更加積極測試和支援的資料庫。當然,這并非意味着我們排斥商業資料庫,我們隻是希望它們能夠在Rails發行版本之外保持自己獨立的開發和釋出計劃。對于商業資料庫來說,這其實是一件好事情,讓廠商可以在基礎版本上面添加更多的異常和處理機制,使它們工作的更好。

所有的商業資料庫驅動現在都放在gems包裡面,符合如下命名規則:activerecord-XYZ-adapter。是以如果你安裝了activerecord-oracle-adapter包,那麼這台機器上面所有的各種版本的Rails應用程式都可以通路Oracle資料庫了,你卻無需修改任何一行應用的代碼。

這種方式對于新的資料庫驅動來說在Rails社群也可以更加友善的獲得支援。隻要你把資料庫驅動安裝命名規範打包成為gem,使用者就可以安裝這個gem,立刻在Rails程式當中使用到它們了。

[color=red][b]Active Record: with_scope with a dash of syntactic vinegar[/b][/color]

ActiveRecord::Base.with_scope被勸阻使用以避免使用者在controoler,特别是filter裡面誤用。現在我們鼓勵使用者僅僅在model裡面使用這種格式,這也是當初我們設計這項功能的初衷和保持一個良好實踐的需要。當然,這僅僅隻是鼓勵和勸阻,如果你在衡量得失之後,非要堅持在model之外使用with_scope的話,你盡管可以用這種方式來調用:.send(:with_scope)。

[color=red][b]ActionWebService out, ActiveResource in[/b][/color]

在SOAP和REST的争論當中,Rails選擇堅定的站在REST這一邊似乎不出人意料。如果你并沒有內建其他異構系統需求的話,那麼我們強烈勸阻你使用SOAP。作為很自然的選擇,ActionWebService現在并不在Rails架構的依賴當中,gem包仍然保留,但是這是一個重要的資訊,建議你盡量不用它。

與此同時,我們把新的ActiveResource包從beta版本更新挪入Rails架構之内。ActiveResource很像ActiveRecord,隻不過面向的不是model,而是資源。它有和ActiveRecord非常相似的API,并且可以和基于資源的Rails應用良好的整合。例如,ActiveResource提供了一個vanilla scaffold,你可以參考。

[color=red][b]ActiveSupport[/b][/color]

ActiveSupport沒有多少新東西,我們隻是添加了大量新的方法,例如Array#rand可以随機取得集合的元素,Hash#except可以過濾掉不想要的key和其他大量的Date類型的擴充。另外單元測試增加了一個assert_difference的便利方法。簡而言之,僅僅是bugfix和調整。

[color=red][b]Action Mailer[/b][/color]

Action Mailer有不少更新,除了一大堆bugfix之外,我們添加了一個選項可以注冊可選的模闆渲染機制,此外還給email的單元測試添加一套assert_emails,例如驗證郵件投遞的數量:

assert_emails 1 do post :signup, :name => ‘Jonathan’ end
           

[color=red][b]Rails: The debugger is back[/b][/color]

為了更好的整合調試器,我們對Rails架構整體進行了一系列的改進。我最得意之作就是調試器的斷點功能回來了,這不僅僅隻是一個類似irb dump那樣的斷點資料觀測,而是一個真正的調試器。你可以單步前進、單步後退、列舉目前位置等等。這完全得益于ruby-debug這個gem包,是以我們推薦你安裝這個gem包,然後Rails新的調試器就可以工作了。

如果你想使用調試器,那麼首先安裝ruby-debug這個gem包,然後把“debugger”指令寫在你的應用程式當中,接着用-debugger或者-u參數啟動伺服器,當代碼執行到debugger指令的地方,你就可以在運作伺服器的終端上面直接操縱應用了,完全不需要使用script/breakpointer或者其他的什麼東西。當然你還可以在單元測試當中使用調試器。

[color=red][b]Rails: Clean up your environment[/b][/color]

在Rails2.0之前的版本,config/environment.rb當中塞滿了各種各樣的啟動配置資訊和代碼。現在你可以把這些東西分門别類的放在獨立的檔案當中,然後把檔案放在config/initializers目錄下面,當Rails應用啟動的時候,它們就會被自動的加載。新的Rails2.0應用自帶了兩個這樣的例子,分别是inflections.rb(定義你自己的單複數規則)和mime_types.rb(定義你自己的擴充類型)。我們鼓勵你把啟動配置資訊放在獨立的檔案裡面,而不要去改動environment.rb檔案。

[color=red][b]Rails: Easier plugin order[/b][/color]

現在我們開始把很多功能從Rails剝離出來放到插件裡面去了,你也許有可能有一些其他依賴這些功能的插件。例如在你自己的acts_as_extra_cool_list插件被加載之前,需要首先加載acts_as_list插件,因為acts_as_extra_cool_list擴充了acts_as_list。

在Rails2.0之前,設定插件的加載次序需要你在config.plugins裡面列舉所有的插件。這種做法主要的問題是當你僅僅需要acts_as_list插件加載次序在前,而不關心其他插件加載次序的時候,未免需要多寫太多東西。而現在你僅僅這樣寫就可以了:config.plugins = [ :acts_as_list, :all ]。

[color=red][b]And hundreds upon hundreds of other improvements[/b][/color]

上面我提到的這麼多特性也僅僅隻是Rails2.0的冰山一角。我們可以列舉出來成千上萬的bug修複、功能調優、新功能的添加。許許多多的熱情的貢獻者不知疲倦的在各個細節上面改進Rails架構,但是這些工作都是非常重要的。

我希望你能夠不介意麻煩去閱讀Rails的CHANGELOG,了解更多的Rails2.0的改進。

[color=red][b]So how do I upgrade?[/b][/color]

如果你希望更新到Rails2.0,那麼你應該首先更新到Rails 1.2.6版本。它對于所有在Rails2.0當中被挪出去的功能給出了警告資訊。如果你的應用程式在Rails 1.2.6上面良好的運作,并且沒有任何警告資訊,那麼你就可以更新到Rails 2.0了。當然如果你用到了Rails的分頁的話,你需要安裝classic_pagination這個插件。如果你需要使用Oracle資料庫,那麼你需要安裝activerecord-oracle-adapter這個gem包,諸如此類等等等等。

繼續閱讀