1 升級至 Rails 4.2
如果您正試著升級現有的應用程式,應用程式最好要有足夠的測試。第一步先升級至 4.1,確保應用程式仍正常工作,接著再升上 4.2。升級需要注意的事項在 Ruby on Rails 升級指南可以找到。
2 主要的新功能
2.1 Active Job
Active Job 是 Rails 4.2 新搭載的框架。是佇列系統(Queuing systems)的統一接口,用來連接像是 Resque、Delayed Job、Sidekiq 等佇列系統。
採用 Active Job API 撰寫的背景任務程式(Background jobs),便可在任何支持的佇列系統上運行而無需對程式碼進行任何修改。Active Job 預設會即時執行任務。
任務通常需要傳入 Active Record 物件作為參數。Active Job 將傳入的物件作為 URI(統一資源標識符),而不是直接對物件進行 marshal。新增的 GlobalID 函式庫,給物件生成統一資源標識符,並使用該標識符來查找物件。現在因為內部使用了 Global ID,任務只要傳入 Active Record 物件即可。
譬如,trashable
是一個 Active Record 物件,則下面這個任務無需做任何序列化,便可正常完成任務:
class TrashableCleanupJob < ActiveJob::Base def perform(trashable, depth) trashable.cleanup(depth) end end
參考 Active Job 基礎指南來進一步瞭解。
2.2 異步郵件
基於 Active Job 之上,Action Mailer 新增了 #deliver_later
方法,通過佇列來發送郵件,若開啓了佇列的異步特性,便不會拖慢控制器或模型的運行(預設佇列是即時執行任務)。
想直接發送信件仍可以使用 deliver_now
。
2.3 Adequate Record
Adequate Record 是對 Active Record find
和 find_by
方法以及其它的關聯查詢方法所進行的一系列重構,查詢速度最高提升到了兩倍之多。
工作原理是在執行 Active Record 調用時,把 SQL 查詢語句快取起來。有了查詢語句的快取之後,同樣的 SQL 查詢就無需再次把調用轉換成 SQL 語句。更多細節請參考 Aaron Patterson 的博文。
Adequate Record 已經合併到 Rails 里,所以不需要特別啓用這個特性。多數的 find
和 find_by
調用和關聯查詢會自動使用 Adequate Record,比如:
Post.find(1) # First call generates and cache the prepared statement Post.find(2) # Subsequent calls reuse the cached prepared statement Post.find_by_title('first post') Post.find_by_title('second post') post.comments post.comments(true)
有一點特別要說明的是,如上例所示,快取的語句不會快取傳入的數值,只是快取查詢語句的模版而已。
下列場景則不會使用緩存:
- 當 model 有預設作用域時
- 當 model 使用了單表繼承時
- 當
find
查詢一組 ID 時:
# not cached Post.find(1, 2, 3) Post.find([1,2])
- 以 SQL 片段執行
find_by
:
Post.find_by('published_at < ?', 2.weeks.ago)
2.4 Web 終端
用 Rails 4.2 新產生的應用程式,預設搭載了 Web 終端。Web 終端給錯誤頁面添加了一個互動式 Ruby 終端,並提供視圖幫助方法 console
,以及一些控制器幫助方法。
錯誤頁面的互動式的終端,讓你可以在異常發生的地方執行程式碼。插入 console
視圖幫助方法到任何頁面,便可以在頁面的上下文里,在頁面算繪(render)結束後啓動一個互動式的終端。
最後,可以執行 rails console
來啓動一個 VT100 終端。若需要建立或修改測試資料,可以直接從瀏覽器里執行。
2.5 外鍵支持
遷移 DSL 現在支持新增、移除外鍵,外鍵也會導出到 schema.rb
。目前只有 mysql
、mysql2
以及 postgresql
的連接器(adapter)支持外鍵。
# add a foreign key to `articles.author_id` referencing `authors.id` add_foreign_key :articles, :authors # add a foreign key to `articles.author_id` referencing `users.lng_id` add_foreign_key :articles, :users, column: :author_id, primary_key: "lng_id" # remove the foreign key on `accounts.branch_id` remove_foreign_key :accounts, :branches # remove the foreign key on `accounts.owner_id` remove_foreign_key :accounts, column: :owner_id
完整說明請參考 API 文檔:add_foreign_key 和 remove_foreign_key。
3 Rails 4.2 向下不相容的部份
前版棄用的功能已全部移除。請參考文後下列各 Rails 元件來了解 Rails 4.2 新棄用的功能有那些。
以下是升級至 Rails 4.2 所需要立即採取的行動。
3.1 render
字串參數
4.2 以前在 Controller 動作呼叫 render "foo/bar"
時,效果等同於:render file: "foo/bar"
;Rails 4.2 被改為 render template: "foo/bar"
。如需 render
檔案,請將程式碼改為 render file: "foo/bar"
。
3.2 respond_with
/ class-level respond_to
respond_with
以及對應的類別層級 respond_to
被移到了 responders
gem。要使用這個功能,把 gem 'responders', '~> 2.0'
加入到 Gemfile:
# app/controllers/users_controller.rb class UsersController < ApplicationController respond_to :html, :json def show @user = User.find(params[:id]) respond_with @user end end
而實體層級的 respond_to
則不受影響:
# app/controllers/users_controller.rb class UsersController < ApplicationController def show @user = User.find(params[:id]) respond_to do |format| format.html format.json { render json: @user } end end end
3.3 rails server
的預設主機(host)變更
由於 Rack 的一項修正,rails server
現在預設會監聽 localhost
而不是 0.0.0.0
。http://127.0.0.1:3000 和 http://localhost:3000 仍可以像以前一樣使用。
但這項變更禁止了從其它機器訪問 Rails 伺服器(譬如開發環境位於虛擬環境裡,而想要從宿主機器上訪問),會需要用 rails server -b 0.0.0.0
來啟動才能像之前一樣使用。
若是使用了 0.0.0.0
,記得要把防火牆設定好,改成只有信任的機器才可以存取你的開發伺服器。
3.4 HTML Sanitizer
HTML sanitizer 換成一個新的、更加安全的實作,基於 Loofah 和 Nokogiri。新的 Sanitizer 更安全,而 sanitization 更加完善與靈活。
有了新的 sanitization 演算法之後,某些 pathological 的輸入的輸出會和之前不一樣。
若真的需要使用舊的 sanitizer,可以把 rails-deprecated_sanitizer
加到 Gemfile,便會用舊的 sanitizer 取代掉新的。因為這是自己選擇性加入的 gem,所以並不會拋出棄用警告。
Rails 4.2 仍會維護 rails-deprecated_sanitizer
,但 Rails 5.0 之後便不會再進行維護。
參考這篇文章來了解更多關於新的 sanitizer 的變更內容細節。
3.5 assert_select
assert_select
測試方法現在用 Nokogiri 改寫。
不再支援某些先前可用的選擇器。若應用程式使用了以下的選擇器,則會需要進行更新:
-
屬性選擇器的數值需要用雙引號包起來。
a[href=/] => a[href="/"] a[href$=/] => a[href$="/"]
-
含有錯誤嵌套的 HTML 所建出來的 DOM 可能會不一樣
譬如:
# content: <div><i><p></i></div> # before: assert_select('div > i') # => true assert_select('div > p') # => false assert_select('i > p') # => true # now: assert_select('div > i') # => true assert_select('div > p') # => true assert_select('i > p') # => false
-
之前要比較含有 HTML entities 的元素要寫未經轉譯的 HTML,現在寫轉譯後的即可
# content: <p>AT&T</p> # before: assert_select('p', 'AT&T') # => true assert_select('p', 'AT&T') # => false # now: assert_select('p', 'AT&T') # => true assert_select('p', 'AT&T') # => false
4 Railties
請參考 CHANGELOG 來了解更多細節。
4.1 移除
--skip-action-view
選項從 app generator 移除了。(Pull Request)移除
rails application
命令。(Pull Request)
4.2 棄用
Production 環境新增
config.log_level
設定。 (Pull Request)棄用
rake test:all
,請改用rake test
來執行test
目錄下的所有測試。 (Pull Request)棄用
rake test:all:db
,請改用rake test:db
。 (Pull Request)棄用
Rails::Rack::LogTailer
,沒有替代方案。(Commit)
4.3 值得一提的變化
web-console
導入為內建的 Gem。 (Pull Request)Model 用來產生關聯的產生器新增
required
選項。 (Pull Request)導入
after_bundle
回呼到 Rails 模版。 (Pull Request)-
導入
x
命名空間,用來自訂設定選項:# config/environments/production.rb config.x.payment_processing.schedule = :daily config.x.payment_processing.retries = 3 config.x.super_debugger = true
這些選項都可以從設定物件裡獲取:
Rails.configuration.x.payment_processing.schedule # => :daily Rails.configuration.x.payment_processing.retries # => 3 Rails.configuration.x.super_debugger # => true
(Commit)
-
導入
Rails::Application.config_for
,用來給當前的環境載入設定# config/exception_notification.yml: production: url: http://127.0.0.1:8080 namespace: my_app_production development: url: http://localhost:3001 namespace: my_app_development # config/production.rb Rails.application.configure do config.middleware.use ExceptionNotifier, config_for(:exception_notification) end
產生器新增
--skip-turbolinks
選項,新建應用程式便不會內建 turbolink。 (Commit)導入
bin/setup
腳本來啟動應用程式。 (Pull Request)config.assets.digest
在開發模式的預設值改為true
。 (Pull Request)導入給
rake notes
註冊新擴充功能的 API。 (Pull Request)導入
Rails.gem_version
作為回傳Gem::Version.new(Rails.version)
的便捷方法。 (Pull Request)
5 Action Pack
請參考 CHANGELOG 來了解更多細節。
5.1 移除
將
respond_with
以及類別層級的respond_to
從 Rails 移除,移到responders
gem(版本 2.0)。要繼續使用這個功能,請在 Gemfile 加入:gem 'responders', '~> 2.0'
。(Pull Request)移除棄用的
AbstractController::Helpers::ClassMethods::MissingHelperError
, 改用AbstractController::Helpers::MissingHelperError
取代。 (Commit)
5.2 棄用
棄用
*_path
輔助方法的only_path
選項。 (Commit)棄用
assert_tag
、assert_no_tag
、find_tag
以及find_all_tag
,請改用assert_select
。 (Commit)-
棄用路由的
:to
選項裡,:to
可以指向符號或不含井號的字串這兩個功能。get '/posts', to: MyRackApp => (No change necessary) get '/posts', to: 'post#index' => (No change necessary) get '/posts', to: 'posts' => get '/posts', controller: :posts get '/posts', to: :index => get '/posts', action: :index
(Commit)
-
棄用 URL 輔助方法不再支持使用字串作為鍵:
# bad root_path('controller' => 'posts', 'action' => 'index') # good root_path(controller: 'posts', action: 'index')
5.3 值得一提的變化
-
*_filter
方法已經從文件中移除,已經不鼓勵使用。偏好使用*_action
方法:after_filter => after_action append_after_filter => append_after_action append_around_filter => append_around_action append_before_filter => append_before_action around_filter => around_action before_filter => before_action prepend_after_filter => prepend_after_action prepend_around_filter => prepend_around_action prepend_before_filter => prepend_before_action skip_after_filter => skip_after_action skip_around_filter => skip_around_action skip_before_filter => skip_before_action skip_filter => skip_action_callback
若應用程式依賴這些
*_filter
方法,應該使用*_action
方法替換。 因為*_filter
方法最終會從 Rails 裡拿掉。 (Commit 1, 2) render nothing: true
或算繪nil
不再加入一個空白到響應主體。 (Pull Request)Rails 現在會自動把模版的 digest 加入到 ETag。 (Pull Request)
傳入 URL 輔助方法的片段現在會自動 Escaped。 (Commit)
導入
always_permitted_parameters
選項,用來設定全局允許賦值的參數。 預設值是['controller', 'action']
。 (Pull Request)從 RFC 4791 新增 HTTP 方法
MKCALENDAR
。 (Pull Request)*_fragment.action_controller
通知訊息的 Payload 現在包含 Controller 與動作名稱。 (Pull Request)改善路由錯誤頁面,搜索路由支持模糊搜尋。 (Pull Request)
新增關掉記錄 CSRF 失敗的選項。 (Pull Request)
當 Rails 伺服器設為提供靜態資源時,若客戶端支援 gzip,則會自動傳送預先產生好的 gzip 靜態資源。Asset Pipeline 預設會給所有可壓縮的靜態資源產生
.gz
檔。傳送 gzip 可將所需傳輸的資料量降到最小,並加速靜態資源請求的存取。當然若要在 Rails 線上環境提供靜態資源,最好還是使用 CDN。(Pull Request)-
在整合測試里呼叫
process
輔助方法時,路徑開頭需要有/
。以前可以忽略開頭的/
,但這是實作所產生的副產品,而不是有意新增的特性,譬如:test "list all posts" do get "/posts" assert_response :success end
6 Action View
請參考 CHANGELOG 來了解更多細節。
6.1 棄用
棄用
AbstractController::Base.parent_prefixes
。想修改尋找 View 的位置, 請覆寫AbstractController::Base.local_prefixes
。 (Pull Request)棄用
ActionView::Digestor#digest(name, format, finder, options = {})
, 現在參數改用 Hash 傳入。 (Pull Request)
6.2 值得一提的變化
render "foo/bar"
現在展開為render template: "foo/bar"
而不是render file: "foo/bar"
。(Pull Request)導入一個特別的
#{partial_name}_iteration
區域變數,給在 collection 裡算繪的局部頁面(Partial)使用。這個變數可以透過#index
、#size
、first?
以及last?
等方法來存取目前迭代的狀態。(Pull Request)隱藏欄位的表單輔助方法不再產生含有行內樣式表的
<div>
元素。 (Pull Request)Placeholder I18n follows the same convention as
label
I18n. (Pull Request)
7 Action Mailer
請參考 CHANGELOG 來了解更多細節。
7.1 棄用
Mailer 全部棄用
*_path
輔助方法。請全面改用*_url
。 (Pull Request)棄用
deliver
與deliver!
,請改用deliver_now
或deliver_now!
。 (Pull Request)
7.2 值得一提的變化
link_to
和url_for
在模版裡預設會產生絕對路徑,不再需要傳入only_path: false
。 (Commit導入
deliver_later
方法,將要寄的信加到應用程式的佇列裡,用來異步發送信件。 (Pull Request)新增
show_previews
選項,用來在開發環境之外啟用郵件預覽功能。 (Pull Request)
8 Active Record
請參考 CHANGELOG 來了解更多細節。
8.1 移除
移除
cache_attributes
以及其它相關的方法。現在所有屬性都有快取。 (Pull Request)移除已棄用的方法
ActiveRecord::Base.quoted_locking_column
. (Pull Request)移除已棄用的方法
ActiveRecord::Migrator.proper_table_name
。 請改用ActiveRecord::Migration
的實體方法:proper_table_name
。 (Pull Request)移除了未使用的
:timestamp
類型。把所有timestamp
類型都改為:datetime
的別名。 修正在ActiveRecord
之外,欄位類型不一致的問題,譬如 XML 序列化。 (Pull Request)
8.2 棄用
棄用
after_commit
和after_rollback
會吃掉錯誤的行為。 (Pull Request)棄用對
has_many :through
自動偵測 counter cache 的支持。要自己對has_many
與belongs_to
關聯,給through
的紀錄手動設定。 (Pull Request)棄用
sanitize_sql_hash_for_conditions
,沒有替代方案。使用Relation
物件來進行查詢與更新是偏好的使用方式。 (Commit)棄用未連接資料庫便呼叫
DatabaseTasks.load_schema
,請改用DatabaseTasks.load_schema_current
。 (Commit)棄用
Reflection#source_macro
,沒有替代方案。因為 Active Record 不再需要這個方法。 (Pull Request)棄用
.find
或.exists?
可傳入 Active Record 物件。請先對物件呼叫#id
。 (Commit 1, 2)-
棄用僅支持一半的 PostgreSQL 範圍數值(不包含起始值)。目前我們把 PostgreSQL 的範圍對應到 Ruby 的範圍。但由於 Ruby 的範圍不支援不包含起始值,所以無法完全轉換。
目前的解決方法是將起始數遞增,這是不對的,已經棄用了。關於不知如何遞增的子類型(比如沒有定義
#succ
)會對不包含起始值的拋出ArgumentError
。(Commit)
棄用沒有連上資料庫,缺可以呼叫
DatabaseTasks.load_schema
的行為。請改用DatabaseTasks.load_schema_current
來取代。 (Commit)棄用
sanitize_sql_hash_for_conditions
,沒有替代方案。使用Relation
來進行查詢或更新是推薦的做法。 (Commit)棄用
add_timestamps
和t.timestamps
可不用傳入:null
選項的行為。Rails 5 將把預設值null: true
改為null: false
。 (Pull Request)棄用
Reflection#source_macro
,沒有替代方案。Active Record 不再需要這個方法了。 (Pull Request)棄用了
serialized_attributes
,沒有替代方案。 (Pull Request)棄用了當欄位不存在時,還會從
column_for_attribute
回傳nil
的情況。 Rails 5.0 將會回傳 Null 物件。 (Pull Request)依賴實體狀態(有定義接受參數的作用域)的關聯現在不能使用
.joins
、.preload
以及.eager_load
了。 (Commit)
8.3 值得一提的變化
SchemaDumper
對create_table
使用force: :cascade
。這樣就可以重載加入外鍵的綱要文件。單數關聯增加
:required
選項,用來定義關聯的存在性驗證。 (Pull Request)PostgreSQL 連接器現在支持 PostgreSQL 9.4 的
JSONB
資料類型。 (Pull Request)遷移的
#references
方法現在可以指定類型,type
選項,可用來指定外鍵的類型(比如:uuid
)。 (Pull Request)ActiveRecord::Dirty
現在會偵測可變數值的改變。序列化過的屬性有變更才會儲存。 修復了像是 PostgreSQL 不會偵測到變更的字串欄位、JSON 欄位。 (Pull Requests 1, 2, 3)導入
bin/rake db:purge
任務,用來清空當前環境的資料庫。 (Commit)導入
ActiveRecord::Base#validate!
,會在記錄不合法時拋出RecordInvalid
異常。 (Pull Request)引入
#validate
作為#valid?
的別名。 (Pull Request)#touch
現在可一次對多屬性操作。 (Pull Request)PostgreSQL 連接器現在支持 PostgreSQL 9.4+ 的
jsonb
資料類型。 (Pull Request)新增 PostgreSQL 連接器的
citext
支持。 (Pull Request)PostgreSQL 與 SQLite 適配器不再默認限制字串只能 255 字元。 (Pull Request)
新增 PostgreSQL 連接器的使用自訂的範圍類型支持。 (Commit)
sqlite3:///some/path
現在可以解析系統的絕對路徑/some/path
。 相對路徑請使用sqlite3:some/path
。(先前是sqlite3:///some/path
會解析成some/path
。這個行為已在 Rails 4.1 被棄用了。 Rails 4.1.) (Pull Request)新增 MySQL 5.6 以上版本的 fractional seconds 支持。 (Pull Request 1, 2)
新增
ActiveRecord::Base
物件的#pretty_print
方法。 (Pull Request)PostgreSQL 與 SQLite 連接器不再預設限制字串只能 255 字元。 (Pull Request)
ActiveRecord::Base#reload
行為同m = Model.find(m.id)
,代表自訂的select
不再保有額外的屬性。 meaning that it no longer retains the extra attributes from customselect
s. (Pull Request)ActiveRecord::Base#reflections
現在返回的 hash 的鍵是字串類型,而不是符號。 (Pull Request)遷移的
references
方法支持type
選項,用來指定外鍵的類型,比如:uuid
。 (Pull Request)
9 Active Model
請參考 CHANGELOG 來了解更多細節。
9.1 移除
- 移除了
Validator#setup
,沒有替代方案。 (Pull Request)
9.2 棄用
棄用
reset_#{attribute}
,請改用restore_#{attribute}
。 (Pull Request)棄用
ActiveModel::Dirty#reset_changes
,請改用#clear_changes_information
。 (Pull Request)
9.3 值得一提的變化
引入
#validate
作為#valid?
的別名。 (Pull Request)ActiveModel::Dirty
導入restore_attributes
方法,用來恢復已修改的屬性到先前的數值。 (Pull Request 1, 2)has_secure_password
現在預設允許空密碼(只含空白的密碼也算空密碼)。 (Pull Request)驗證啟用時,
has_secure_password
現在會檢查密碼是否少於 72 個字元。 (Pull Request)
10 Active Support
請參考 CHANGELOG 來了解更多細節。
10.1 移除
移除棄用的
Numeric#ago
、Numeric#until
、Numeric#since
以及Numeric#from_now
. (Commit)移除棄用
ActiveSupport::Callbacks
基於字串的終止符。 (Pull Request)
10.2 棄用
棄用
Kernel#silence_stderr
、Kernel#capture
以及Kernel#quietly
方法,沒有替代方案。(Pull Request)棄用
Class#superclass_delegating_accessor
,請改用Class#class_attribute
。 (Pull Request)棄用
ActiveSupport::SafeBuffer#prepend!
請改用ActiveSupport::SafeBuffer#prepend
(兩者功能相同)。 (Pull Request)
10.3 值得一提的變化
導入新的設定選項:
active_support.test_order
,用來指定測試執行的順序,預設是:sorted
,在 Rails 5.0 將會改成:random
。(Commit)Object#try
和Object#try!
方法現在無需有訊息接收者也可以使用。 (Commit, Pull Request)travel_to
測試輔助方法現在會把usec
部分截斷為 0。 (Commit)Object#with_options
方法現在無需有訊息接收者也可以使用。 (Pull Request)導入
String#truncate_words
方法,可指定要單字截斷至幾個單字。 (Pull Request)新增
Hash#transform_values
與Hash#transform_values!
方法,來簡化 Hash 值需要更新、但鍵保留不變這樣的常見模式。 (Pull Request)humanize
現在會去掉前面的底線。 (Commit)導入
Concern#class_methods
來取代module ClassMethods
以及Kernel#concern
, 來避免使用module Foo; extend ActiveSupport::Concern; end
這樣的樣板。 (Commit)新增一篇指南,關於常數的載入與重載。
11 致謝
許多人花費寶貴的時間貢獻至 Rails 專案,使 Rails 成為更穩定、更強韌的網路框架,參考完整的 Rails 貢獻者清單,感謝所有的貢獻者!
反饋
歡迎幫忙改善指南的品質。
如發現任何錯誤之處,歡迎修正。開始貢獻前,可以先閱讀貢獻指南:文件。
翻譯如有錯誤,深感抱歉,歡迎 Fork 修正,或至此處回報。
文章可能有未完成或過時的內容。請先檢查 Edge Guides 來確定問題在 master 是否已經修掉了。再上 master 補上缺少的文件。內容參考 Ruby on Rails 指南準則來了解行文風格。
最後,任何關於 Ruby on Rails 文件的討論,歡迎至 rubyonrails-docs 郵件論壇。