Rails Ruby on Rails 升级指南 (3.2 => 4.0)

juanito · 2013年11月15日 · 最后由 tianshuai 回复于 2013年12月04日 · 13943 次阅读
本帖已被管理员设置为精华贴

也可以到 GitHub 浏览:http://git.io/5V3OPg

Ruby on Rails 升级指南

特别要强调的翻译名词

application 应用程序 deprecated 弃用的、不宜使用的、过时的:即将在下一版移除的功能 middleware 中间件 route 路由 raise 抛出 exception 异常 associations 关联

本篇讲解升级至新版 Rails 所需的步骤。同时也提供各版本的升级指导。

1. 一般建议

升级前先想好为何要升级:需要新功能?旧代码越来越难维护?有多少时间?有能力解决升级的兼容问题吗?等等。

1.1 测试覆盖度

最好的方式来确保应用程序升级后仍然正常工作,便是有全面的测试覆盖度。若没有撰写测试,将会花上许多时间,来处理升级带来的新变化。在升级前,先确保测试覆盖得够广吧!

1.2 Ruby 版本

Rails 通常与最新的 Ruby 一起前进:

  • Rails 3 以上需要高于 1.8.7 版本的 Ruby。
  • Rails 3.2.x 是最后支持 Ruby 1.8.7 的版本。
  • Rails 4 推荐使用 Ruby 2.0。

小贴士:Ruby 1.8.7 p248 与 p249 有 marshaling bugs,会让 Rails 无预警的 crash。REE 从 1.8.7-2010.02 之后的版本已经修正了这个问题。关于 Ruby 1.9,不要使用 1.9.1,有 segfaults 的问题,1.9 就用 1.9.3 吧。

定案:

  • 强烈推荐使用 Ruby 2.0.0-p353
  • Ruby 1.9.3-p484

Ruby 1.8.7 退出历史舞台

2. 从 Rails 4.0 升级到 Rails 4.1

施工中,请上 GitHub Repo 关注最新动态。

3. 从 Rails 3.2 升级到 Rails 4.0

若你是 3.2 以前的版本,先升到 3.2 再试著升到 Rails 4.0。

以下是针对从 Rails 3.2 升级至 Rails 4.0 的说明。

3.1 HTTP PATCH

这里的路由作动词解。

Rails 4 更新操作的主要 HTTP 动词换成了 PATCH。当你在 config/routes.rbRESTful 形式宣告某个 resource 时,PUT 仍会路由到 update action,只是多了个 PATCH,同样路由到 update action。

resources :users
<%= form_for @user do |f| %>
class UsersController < ApplicationController
  def update
    # 代码不用改;偏好使用 PATCH,PUT 仍然可用。
  end
end

但是,当使用 form_for 来更新自定路由(使用 PUT HTTP 动词)的 resource 时,

resources :users, do
  put :update_name, on: :member
end
<%= form_for [ :update_name, @user ] do |f| %>
class UsersController < ApplicationController
  def update_name
    # 要修改代码;form_for 会试著使用不存在的 PATCH 路由。
  end
end

若不是公有的 API,并且你有决定权换 HTTP 动词,那就把它从 PUT 改成 PATCH 吧。

在 Rails 4 对 /users/:idPUT 请求,会被导向 update。所以要是 API 接受 PUT 请求,那没问题。Router 同时也会将来自 /users/:idPATCH 请求导向 update action。

resources :users do
  patch :update_name, on: :member
end

若此 action 正被公有的 API 使用,且你无权更改 HTTP 动词时,可更新 form,使用 PUT 动词:

<%= form_for [ :update_name, @user ], method: :put do |f| %>

至于为什么要改成 PATCH,参考这篇文章

3.1.1 关于 media types 的说明

RFC 5789 的勘误表示某些 media type 要用 PATCH 动词才正确,比如 JSON Patch。

Rails 没有原生支持 JSON Patch,但添加 JSON Patch 的支持非常简单:

# 在 controller
def update
  respond_to do |format|
    format.json do
      # perform a partial update
      @post.update params[:post]
    end

    format.json_patch do
      # perform sophisticated change
    end
  end
end

# 在 config/initializers/json_patch.rb:
Mime::Type.register 'application/json-patch+json', :json_patch

由于 JSON Patch 最近才有 RFC,仍未有好的 Ruby 函式库出现。Aaron Patterson 的 hana 是一个实作 JSON Patch 的 gem,但仍未完整支援 Spec 里所有最近更新的内容。

3.2 Gemfile

Rails 4.0 移除了 Gemfile 里的 assets group。升级至 4.0 时要移除这个 group,同时需要更新 config/application.rb

# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(:default, Rails.env)

3.3 vendor/plugins

Rails 4.0 不再支援从 vendor/plugins 载入 plugins。必须将任何 plugins 包成 Gems,再加入至 Gemfile。若你不想包成 Gem,则可将 plugin 移到 lib/my_plugin/*,并使用适当的 initializer:config/initializer/my_plugin.rb

3.4 Active Record

  • Rails 4.0 移除了 Active Record 的 identity map,因为这会导致某些关联的不一致性。也就是说 config.active_record.identity_map ,这个设置不再有作用。

  • Collection association 里的 delete 方法现在可接受 FixnumString 参数作为 record id,跟 destroy 类似。先前会抛出 ActiveRecord::AssociationTypeMismatch。从 Rails 4.0 起,delete 会自动在删除前,找到匹配的 id

  • Rails 4.0 当 column 或 table 重命名时,相关的 index 也会重新命名。也就是不用写 rename index 的 migration 了。

  • Rails 4.0 将 serialized_attributesattr_readonly 改为类别方法。即先前 self.serialized_attributes 改为 self.class.serialized_attributes

  • Rails 4.0 引入 Strong Parameters 机制,故移除了 attr_accessibleattr_protected (抽离成 Protected Attributes gem)。

  • 若你没有使用 Protected Attributes,可以把任何与 whitelist_attributesmass_assignment_sanitizer 有关的选项移除。

  • Rails 4.0 要求 scope 必须是可调用的对象(Proc 或 lambda):

scope :active, where(active: true)

# 变成
scope :active, -> { where active: true }
  • Rails 4.0 弃用了 ActiveRecord::Fixtures,请使用 ActiveRecord::FixtureSet

  • Rails 4.0 弃用了 ActiveRecord::TestCase,请使用 ActiveSupport::TestCase

  • Rails 4.0 弃用了旧式,以 hash 为基础的 Finder API。这表示新的 Finder API 不再接受“finder options”。

  • 弃用了除了 find_by_...find_by_...! 这两个以外的动态 Finder 方法,以下是如何修正:

| Rails 3 | Rails 4 | |-----|-----| | find_all_by_... | 改成 where(...) | | find_last_by_... | 改成 where(...).last | | scoped_by_... | 改成 where(...) | | find_or_initialize_by_... | 改成 find_or_initialize_by(...) | | find_or_create_by_... | 改成 find_or_create_by(...) |

  • 注意! where(...) 返回 relation,而不像旧式 Finder 方法会返回阵列,需要返回阵列请用 to_a

  • 注意!这些对应的方法,不会与先前的 Finder 方法,生成出相同的 SQL 语句。

  • 要重新启用旧式的 Finder 方法,可以使用 activerecord-deprecated_finders gem

3.5 Active Resource

Rails 4.0 将 Active Resource 抽成独立的 Gem。若你仍需要此功能,将 Active Resource gem 加到 Gemfile。

3.6 Active Model

  • Rails 4.0 更改了 ActiveModel::Validations::ConfirmationValidator 错误附加的方式。以前 confirmation 验证错误发生时,错误会加到 attribute 上,现在则会附加到 :#{attribute}_confirmation

  • Rails 4.0 将 ActiveModel::Serializers::JSON.include_root_in_json 的缺省值改为 false,现在 Active Model Serializers 与 Active Record 对象缺省有著相同的行为。这表示你可以移除或注解掉这一行:

config/initializers/wrap_parameters.rb:

# Disable root element in JSON by default.
# ActiveSupport.on_load(:active_record) do
#   self.include_root_in_json = false
# end

3.7 Action Pack

  • Rails 4.0 引入了 ActiveSupport::KeyGenerator,用来生成及检查已签署的 cookie。请在 config/initializers/secret_token.rb 加入新的 secret_key_base
# config/initializers/secret_token.rb
Myapp::Application.config.secret_token = 'existing secret token'
Myapp::Application.config.secret_key_base = 'new secret key base'

请注意!要等到使用者都使用你的 Rails 4.x app,并确保你不会降级到 Rails 3.x,才设置 secret_key_base。因为 cookie 签署的算法并不向下相容。忽略 deprecation warning 使用 secret_token 也是没问题的,只要你知道你自己在做什么就好。

如果有外部应用程序或是 JavaScript,需要能够读 Rails app 签署的 session cookies,在你还没有解决这些问题之前,不要设置 secret_key_base

  • 有设置 secret_key_base 的话,Rails 4.0 会加密以 cookie-based session 的内容。Rails 3.x 有签署,但未加密。签署的 cookie 是安全的,因为他们是经由你的 app 生成与签署。然而 cookie 的内容仍可被使用者看到,因此加密内容排除了此风险,且没有降低多少的效能。

请阅读 Pull Request #9978 来了解更多有关 session cookie 加密的细节。

  • Rails 4.0 移除了 ActionController::Base.asset_path 选项。请使用 Assets Pipeline。

  • Rails 4.0 弃用了 ActionController::Base.page_cache_extension 选项。请使用 ActionController::Base.default_static_extension 来取代。

  • Rails 4.0 从 Action Pack 移除了 Action 与 Page 的 Cache。若要使用 caches_actioncaches_pages 请加入 actionpack-action_caching gem。

  • Rails 4.0 移除了 XML 参数解析器。若要使用请加入 actionpack-xml_parser

  • Rails 4.0 更改了缺省的 memcached 用户端,从 memcache-client 换成了 dalli,升级只需加入 gem 'dalli'Gemfile

  • Rails 4.0 弃用了 Controller 的 dom_iddom_class 方法(View 依然能用)。要用的话请在 Controller include ActionView::RecordIdentifier

  • Rails 4.0 弃用了 link_to:confirm 选项,应改写为 data: { confirm: 'Are you sure?' }link_to_iflink_to_unless 同样受影响。

  • Rails 4.0 修改了 assert_generatesassert_recognizes 以及 assert_routing 的工作方式。这些 assertions 会抛出Assertion而不是ActionController::RoutingError` 错误。

  • 在 Rails 4.0,如果定义了重复名称的路由时,会抛出 ArgumentError。请见下面两例(重复的 example_path):

get 'one' => 'test#example', as: :example
get 'two' => 'test#example', as: :example
resources :examples
get 'clashing/:id' => 'test#example', as: :example

第一个例子可直接换名字来解决。第二个例子可使用 resources 方法提供的 onlyexcept 选项来限制生成出的路由,详见 Routing Guide

  • Rails 4.0 更改了 route 有 unicode 字符的生成方式。现在 route 里可直接使用 unicode 字符,先前需要 escape 的作法不再需要了:
get Rack::Utils.escape('こんにちは'), controller: 'welcome', action: 'index'

改为

get 'こんにちは', controller: 'welcome', action: 'index'
  • Rails 4.0 要求使用 match 的 route 必须指定 HTTP 动词:
# Rails 3.x
match "/" => "root#index"

# 改成
match "/" => "root#index", via: :get

# 或
get "/" => "root#index"
  • Rails 4.0 移除了 ActionDispatch::BestStandardsSupport 中间件。因为 <!DOCTYPE html>此文所述,已触发了标准模式。而 ChromeFrame header 被移到 config.action_dispatch.default_headers 了。

记得移除所有使用到 ActionDispatch::BestStandardsSupport middleware 的参照:

# 会抛出异常
config.middleware.insert_before(Rack::Lock, ActionDispatch::BestStandardsSupport)

并移除环境设置中的 config.action_dispatch.best_standards_support

  • Rails 4.0 预编译不再自动从 vendor/assetslib/assets 拷贝非 JS 或 CSS 的 assets。Rails 应用程序与 Engine 的开发者应将这些 assets 移到 app/assets 或设置 config.assets.precompile

  • Rails 4.0 当 action 不知道如何处理 request 格式时会抛出 ActionController::UnknownFormat 异常。缺省是 406 Not Acceptable,但你可以改成别的 status code,在 Rails 3 只能是 406。

  • Rails 4.0 当 ParamsParser 无法解析 request params 时,会抛出通用的 ActionDispatch::ParamsParser::ParseError 异常。你可以 rescue 这个异常,而不是较为底层的 MultiJson::DecodeError

  • Rails 4.0,当 Engine 安装到有 URL 前缀的宿主(hosting application)时,SCRIPT_NAME 已经将 URL 前缀适当地设置好了。不再需要设置 default_url_options[:script_name] 来覆写 URL 前缀。

  • Rails 4.0 弃用了 ActionController::Integration 请使用 ActionDispatch::Integration

  • Rails 4.0 弃用了 ActionController::IntegrationTest 请使用 ActionDispatch::IntegrationTest

  • Rails 4.0 弃用了 ActionController::PerformanceTest 请使用 ActionDispatch::PerformanceTest

  • Rails 4.0 弃用了 ActionController::AbstractRequest 请使用 ActionDispatch::Request

  • Rails 4.0 弃用了 ActionController::Request 请使用 ActionDispatch::Request

  • Rails 4.0 弃用了 ActionController::AbstractResponse 请使用 ActionDispatch::Response

  • Rails 4.0 弃用了 ActionController::Response 请使用 ActionDispatch::Response

  • Rails 4.0 弃用了 ActionController::Routing 请使用 ActionDispatch::Routing

3.8 Active Support

Rails 4.0 移除了 ERB::Util#json_escapej 别名。因为 j 已经被 ActionView::Helpers::JavaScriptHelper#escape_javascript 所使用。

3.9 Helpers 加载顺序

Rails 4.0 更改了 Helpers 的加载顺序。之前是将各目录的 Helpers 集合起来,并按字母排序加载。Rails 4.0 之后,Helpers 会按照目录原本加载的顺序,并在各自的目录里按字母依序加载。除非你特别使用了 helpers_path 参数,否则这个改动只会影响到从 Engine 加载 Helpers 的顺序。如果你正依赖加载的顺序,可以检查升级后这些 Helper 是否正常工作。如果想更改 Engine 加载的顺序,可以使用 config.railties_order= 方法。

3.10 Active Record Observer 与 Action Controller Sweeper

Active Record Observer 与 Action Controller Sweeper 被抽成独立的 Gem:rails-observers

3.11 sprockets-rails

  • assets:precompile:primary 被移除了。请改用 assets:precompile
  • config.assets.compress 选项应改成 config.assets.js_compressor
config.assets.js_compressor = :uglifier

3.12 sass-rails

  • asset-url("rails.png", image) 改成 asset-url("rails.png")

强大,支持!

全体起立为 lz 鼓掌

主要的坑在周边 gem,不知道有没有可搜索哪些 gem 已完美支持 Rails4 的网站,就像之前 Rails3 出现时一样。

lz 在哪个城市?如果在上海的话可以来 ruby tuesday 给大家详细讲讲!

整理的很好

#13 楼 @zgm 臺北,其實就是翻譯。我 Rails 水平很差。

匿名 #17 2013年11月18日

rails4 的 bug 真不少,定义: students_teaching_exam_adds GET /teaching/exams/:exam_id/adds/students/:type/:type_id(.:format) 测试: students_teaching_exam_adds_path(@entity,"klass",1) 你期望的结果是: /teaching/exams/4/adds/students/klass/1 实际得到的结果会是: /teaching/exams/4/adds/students/klass/klass_id

你好!我刚加入这个社区,请教个问题,在 linux 下运行$GEM_HOME/bin/bundle install 反馈的结果为什么是:bash:bin/bundle:no such file or directory,这个问题我整了两天了也没解决,能不能指导一下

#18 楼 @guojia

試試:

Rails < 4.0

$ bundle exec bundle install

Rails 4.0+

$ bin/bundle install

给 32 个赞

赞一个,正打算升级呢

整理得很全面,以后可以参考这个了!

warmwind Rails 3.2 升级到 Rails 4 中遇到的问题 提及了此话题。 04月03日 10:57
需要 登录 后方可回复, 如果你还没有账号请 注册新账号