edge
更多信息请访问 rubyonrails.org: 更多 Ruby on Rails

升级Ruby on Rails

本指南提供了升级应用程序到较新版本的Ruby on Rails时应遵循的步骤。这些步骤也可以在各个发布指南中找到。

Chapters

  1. 一般建议
  2. 从Rails 7.0升级到Rails 7.1
  3. 从Rails 6.1升级到Rails 7.0
  4. 从Rails 6.0升级到Rails 6.1
  5. 从Rails 5.2升级到Rails 6.0
  6. 从Rails 5.1升级到Rails 5.2
  7. 从Rails 5.0升级到Rails 5.1
  8. 从Rails 4.2升级到Rails 5.0
  9. 从 Rails 4.1 升级到 Rails 4.2
  10. 从 Rails 4.0 升级到 Rails 4.1
  11. 从Rails 3.2升级到Rails 4.0
  12. 从Rails 3.1升级到Rails 3.2
  13. 从Rails 3.0升级到Rails 3.1

1 一般建议

在尝试升级现有应用程序之前,您应该确保有充分的理由进行升级。您需要平衡几个因素:对新功能的需求、对旧代码支持的难度增加以及可用的时间和技能等。

1.1 测试覆盖率

确保在开始升级之前,您的应用程序仍然正常工作的最佳方法是在开始过程之前具备良好的测试覆盖率。如果您没有自动化测试来覆盖应用程序的大部分功能,您将需要花时间手动测试所有已更改的部分。在Rails升级的情况下,这将意味着应用程序中的每个功能都需要测试。在开始升级之前,请确保您的测试覆盖率良好。

1.2 Ruby版本

Rails通常在发布时与最新发布的Ruby版本保持接近:

  • Rails 7 需要 Ruby 2.7.0 或更高版本。
  • Rails 6 需要 Ruby 2.5.0 或更高版本。
  • Rails 5 需要 Ruby 2.2.2 或更高版本。

最好分别升级Ruby和Rails。首先升级到最新的Ruby版本,然后再升级Rails。

1.3 升级过程

更改Rails版本时,最好慢慢进行,一次只升级一个次要版本,以充分利用弃用警告。Rails版本号的格式为主版本.次要版本.修订版本。主版本和次要版本允许对公共API进行更改,因此这可能会导致应用程序出错。修订版本仅包括错误修复,不会更改任何公共API。

该过程应按照以下步骤进行:

  1. 编写测试并确保测试通过。
  2. 在当前版本之后移动到最新的修订版本。
  3. 修复测试和弃用的功能。
  4. 移动到下一个次要版本的最新修订版本。

重复此过程,直到达到目标Rails版本。

1.3.1 在版本之间移动

要在版本之间移动:

  1. Gemfile中更改Rails版本号并运行bundle update
  2. package.json中更改Rails JavaScript包的版本并运行yarn install(如果使用Webpacker)。
  3. 运行更新任务
  4. 运行您的测试。

您可以在此处找到所有已发布的Rails gem的列表。

1.4 更新任务

Rails提供了rails app:update命令。在Gemfile中更新Rails版本后,运行此命令。这将在交互会话中帮助您创建新文件并更改旧文件。

$ bin/rails app:update
       exist  config
    conflict  config/application.rb
Overwrite /myapp/config/application.rb? (enter "h" for help) [Ynaqdh]
       force  config/application.rb
      create  config/initializers/new_framework_defaults_7_0.rb
...

不要忘记查看差异,以查看是否有任何意外更改。

1.5 配置框架默认值

新的Rails版本可能与先前版本具有不同的配置默认值。但是,在按照上述步骤进行操作后,您的应用程序仍将使用*先前*Rails版本的配置默认值运行。这是因为config/application.rb中的config.load_defaults的值尚未更改。

为了让您逐个升级到新的默认值,更新任务已经创建了一个名为config/initializers/new_framework_defaults_X.Y.rb的文件(其中X.Y是所需的Rails版本)。您应该取消注释文件中的新配置默认值,这可以逐步在多个部署中完成。一旦您的应用程序准备好使用新的默认值运行,您可以删除此文件并切换config.load_defaults的值。

2 从Rails 7.0升级到Rails 7.1

有关Rails 7.1所做更改的更多信息,请参阅发布说明

2.1 自动加载路径不再在加载路径中

从Rails 7.1开始,由自动加载程序管理的所有路径将不再添加到$LOAD_PATH中。这意味着无法使用手动的require调用加载它们,而是可以直接引用类或模块。

减少$LOAD_PATH的大小可以加快未使用bootsnap的应用程序的require调用,并减小其他应用程序的bootsnap缓存的大小。

2.2 ActiveStorage::BaseController不再包含流处理关注点

继承自ActiveStorage::BaseController的应用控制器,如果使用流处理来实现自定义文件服务逻辑,现在必须显式地包含ActiveStorage::Streaming模块。

2.3 MemCacheStoreRedisCacheStore现在默认使用连接池

connection_pool gem已作为activesupport gem的依赖项添加, MemCacheStoreRedisCacheStore现在默认使用连接池。

如果您不想使用连接池,请在配置缓存存储时将:pool选项设置为false

config.cache_store = :mem_cache_store, "cache.example.com", pool: false

有关更多信息,请参阅Rails缓存指南

2.4 SQLite3Adapter现在配置为在严格字符串模式下使用

使用严格字符串模式会禁用双引号字符串文字。

SQLite在处理双引号字符串文字时有一些怪异之处。 它首先尝试将双引号字符串视为标识符名称,但如果它们不存在,则将其视为字符串文字。因此,拼写错误可能会悄悄地被忽略。 例如,可以为不存在的列创建索引。 有关详细信息,请参阅SQLite文档

如果您不想在严格模式下使用SQLite3Adapter,可以禁用此行为:

# config/application.rb
config.active_record.sqlite3_adapter_strict_strings_by_default = false

2.5 ActionMailer::Preview支持多个预览路径

选项config.action_mailer.preview_path已弃用,改为使用config.action_mailer.preview_paths。将路径附加到此配置选项将导致在搜索邮件预览时使用这些路径。

config.action_mailer.preview_paths << "#{Rails.root}/lib/mailer_previews"

2.6 config.i18n.raise_on_missing_translations = true现在在任何缺失的翻译上都会引发异常。

以前,它只会在视图或控制器中调用时引发异常。现在,只要I18n.t提供了一个无法识别的键,它就会引发异常。

# with config.i18n.raise_on_missing_translations = true

# in a view or controller:
t("missing.key") # 在7.0中引发异常,在7.1中引发异常
I18n.t("missing.key") # 在7.0中不引发异常,在7.1中引发异常

# anywhere:
I18n.t("missing.key") # 在7.0中不引发异常,在7.1中引发异常

如果您不希望出现这种行为,可以将config.i18n.raise_on_missing_translations设置为false

# with config.i18n.raise_on_missing_translations = false

# in a view or controller:
t("missing.key") # 在7.0中不引发异常,在7.1中不引发异常
I18n.t("missing.key") # 在7.0中不引发异常,在7.1中不引发异常

# anywhere:
I18n.t("missing.key") # 在7.0中不引发异常,在7.1中不引发异常

或者,您可以自定义I18n.exception_handler。 有关更多信息,请参阅i18n指南

3 从Rails 6.1升级到Rails 7.0

有关升级到Rails 7.0的更多信息,请参阅发布说明

3.1 ActionView::Helpers::UrlHelper#button_to的行为已更改

从Rails 7.0开始,如果使用持久化的Active Record对象来构建按钮URL,button_to将呈现带有patch HTTP动词的form标签。 要保持当前行为,请考虑显式传递method:选项:

-button_to("Do a POST", [:my_custom_post_action_on_workshop, Workshop.find(1)])
+button_to("Do a POST", [:my_custom_post_action_on_workshop, Workshop.find(1)], method: :post)

或者使用助手构建URL:

-button_to("Do a POST", [:my_custom_post_action_on_workshop, Workshop.find(1)])
+button_to("Do a POST", my_custom_post_action_on_workshop_workshop_path(Workshop.find(1)))

3.2 Spring

如果您的应用程序使用Spring,需要升级到至少3.0.0版本。否则,您将会得到以下错误:

undefined method `mechanism=' for ActiveSupport::Dependencies:Module

此外,请确保在config/environments/test.rb中将config.cache_classes设置为false

3.3 Sprockets现在是可选的依赖项

rails gem不再依赖于sprockets-rails。如果您的应用程序仍然需要使用Sprockets,请确保将sprockets-rails添加到Gemfile中。

gem "sprockets-rails"

3.4 应用程序需要在zeitwerk模式下运行

仍在运行classic模式的应用程序必须切换到zeitwerk模式。请查看Classic to Zeitwerk HOWTO指南以获取详细信息。

3.5 删除了设置器config.autoloader=

在Rails 7中,没有配置点来设置自动加载模式,已删除config.autoloader=。如果您将其设置为:zeitwerk,只需将其删除。

3.6 删除了ActiveSupport::Dependencies私有API

已删除ActiveSupport::Dependencies的私有API。其中包括hook!unhook!depend_onrequire_or_loadmechanism等方法。

以下是一些亮点:

  • 如果您使用了ActiveSupport::Dependencies.constantizeActiveSupport::Dependencies.safe_constantize,只需将它们更改为String#constantizeString#safe_constantize
  ActiveSupport::Dependencies.constantize("User") # 不再可行
  "User".constantize # 👍
  • 任何使用ActiveSupport::Dependencies.mechanism的地方,无论是读取器还是写入器,都必须根据需要访问config.cache_classes进行替换。

  • 如果要跟踪自动加载器的活动,不再提供ActiveSupport::Dependencies.verbose=,只需在config/application.rb中添加Rails.autoloaders.log!

辅助的内部类或模块也被删除了,例如 ActiveSupport::Dependencies::ReferenceActiveSupport::Dependencies::Blamable 等等。

3.7 初始化期间的自动加载

在初始化期间自动加载可重载的常量的应用程序(不在 to_prepare 块中)会导致这些常量被卸载,并在 Rails 6.0 中发出此警告:

DEPRECATION WARNING: Initialization autoloaded the constant ....

Being able to do this is deprecated. Autoloading during initialization is going
to be an error condition in future versions of Rails.

...

如果您仍然在日志中收到此警告,请在 自动加载指南 中检查应用程序启动时的自动加载部分。否则,在 Rails 7 中会出现 NameError

3.8 配置 config.autoload_once_paths 的能力

config.autoload_once_paths 可以在 config/application.rb 中定义的应用程序类的主体中设置,也可以在 config/environments/* 中的环境配置中设置。

同样,引擎可以在引擎类的类主体中或在环境的配置中配置该集合。

之后,该集合将被冻结,并且您可以从这些路径进行自动加载。特别是在初始化期间,您可以从这里进行自动加载。它们由 Rails.autoloaders.once 自动加载器管理,它不重新加载,只进行自动加载/急切加载。

如果您在环境配置已处理之后配置了此设置并且收到 FrozenError,请将代码移动到其他位置。

3.9 ActionDispatch::Request#content_type 现在返回原样的 Content-Type 标头。

以前,ActionDispatch::Request#content_type 返回的值不包含字符集部分。 这个行为已更改为返回原样包含字符集部分的 Content-Type 标头。

如果您只想获取 MIME 类型,请改用 ActionDispatch::Request#media_type

之前:

request = ActionDispatch::Request.new("CONTENT_TYPE" => "text/csv; header=present; charset=utf-16", "REQUEST_METHOD" => "GET")
request.content_type #=> "text/csv"

之后:

request = ActionDispatch::Request.new("Content-Type" => "text/csv; header=present; charset=utf-16", "REQUEST_METHOD" => "GET")
request.content_type #=> "text/csv; header=present; charset=utf-16"
request.media_type   #=> "text/csv"

密钥生成器的默认摘要类从 SHA1 更改为 SHA256。 这会影响到 Rails 生成的任何加密消息,包括加密的 cookie。

为了能够使用旧的摘要类读取消息,需要注册一个旋转器。如果不这样做,升级过程中可能会导致用户的会话失效。

以下是用于加密和签名 cookie 的旋转器示例。

# config/initializers/cookie_rotator.rb
Rails.application.config.after_initialize do
  Rails.application.config.action_dispatch.cookies_rotations.tap do |cookies|
    authenticated_encrypted_cookie_salt = Rails.application.config.action_dispatch.authenticated_encrypted_cookie_salt
    signed_cookie_salt = Rails.application.config.action_dispatch.signed_cookie_salt

    secret_key_base = Rails.application.secret_key_base

    key_generator = ActiveSupport::KeyGenerator.new(
      secret_key_base, iterations: 1000, hash_digest_class: OpenSSL::Digest::SHA1
    )
    key_len = ActiveSupport::MessageEncryptor.key_len

    old_encrypted_secret = key_generator.generate_key(authenticated_encrypted_cookie_salt, key_len)
    old_signed_secret = key_generator.generate_key(signed_cookie_salt)

    cookies.rotate :encrypted, old_encrypted_secret
    cookies.rotate :signed, old_signed_secret
  end
end

3.11 ActiveSupport::Digest 的摘要类更改为 SHA256

ActiveSupport::Digest 的默认摘要类从 SHA1 更改为 SHA256。 这会对诸如 Etag 的内容产生影响,并且还会更改缓存键。 更改这些键可能会对缓存命中率产生影响,因此在升级到新的哈希算法时要小心并注意这一点。

3.12 新的 ActiveSupport::Cache 序列化格式

引入了一种更快、更紧凑的序列化格式。

要启用它,您必须设置 config.active_support.cache_format_version = 7.0

# config/application.rb

config.load_defaults 6.1
config.active_support.cache_format_version = 7.0

或者简单地:

# config/application.rb

config.load_defaults 7.0

但是,Rails 6.1 应用程序无法读取这种新的序列化格式, 因此为了确保无缝升级,您必须首先使用 config.active_support.cache_format_version = 6.1 部署您的 Rails 7.0 升级, 然后只有在所有 Rails 进程都已更新之后,您才可以设置 config.active_support.cache_format_version = 7.0

Rails 7.0 能够读取这两种格式,因此在升级过程中缓存不会失效。

3.13 Active Storage 视频预览图生成

视频预览图生成现在使用 FFmpeg 的场景变化检测来生成更有意义的预览图像。以前会使用视频的第一帧,如果视频从黑色淡入,则会出现问题。此更改需要 FFmpeg v3.4+。

3.14 Active Storage 默认的变体处理器更改为 :vips

对于新的应用程序,图像转换将使用 libvips 而不是 ImageMagick。这将减少生成变体所需的时间,以及 CPU 和内存的使用量,提高依赖 Active Storage 为其图像提供服务的应用程序的响应时间。

:mini_magick 选项不会被弃用,因此继续使用它是可以的。

要将现有应用程序迁移到 libvips,请设置: ruby Rails.application.config.active_storage.variant_processor = :vips

然后,您需要将现有的图像转换代码更改为image_processing宏,并使用libvips的选项替换ImageMagick的选项。

3.14.1 使用resize_to_limit替换resize

- variant(resize: "100x")
+ variant(resize_to_limit: [100, nil])

如果您不这样做,当您切换到vips时,您将看到此错误:no implicit conversion to float from string

3.14.2 裁剪时使用数组

- variant(crop: "1920x1080+0+0")
+ variant(crop: [0, 0, 1920, 1080])

如果您在迁移到vips时不这样做,您将看到以下错误:unable to call crop: you supplied 2 arguments, but operation needs 5

3.14.3 限制裁剪值:

与ImageMagick相比,Vips在裁剪时更严格:

  1. 如果x和/或y是负值,它将不会进行裁剪。例如:[-10, -10, 100, 100]
  2. 如果位置(xy)加上裁剪尺寸(widthheight)大于图像,它将不会进行裁剪。例如:一个125x125的图像和一个裁剪区域为[50, 50, 100, 100]

如果您在迁移到vips时不这样做,您将看到以下错误:extract_area: bad extract area

3.14.4 调整resize_and_pad使用的背景颜色

Vips使用黑色作为resize_and_pad的默认背景颜色,而不是像ImageMagick一样使用白色。通过使用background选项来修复:

- variant(resize_and_pad: [300, 300])
+ variant(resize_and_pad: [300, 300, background: [255]])

3.14.5 移除基于EXIF的旋转

Vips在处理变体时会使用EXIF值自动旋转图像。如果您以前使用ImageMagick存储用户上传照片的旋转值以应用旋转,则必须停止这样做:

- variant(format: :jpg, rotate: rotation_value)
+ variant(format: :jpg)

3.14.6 使用colourspace替换monochrome

Vips使用不同的选项来生成单色图像:

- variant(monochrome: true)
+ variant(colourspace: "b-w")

3.14.7 切换到libvips选项以压缩图像

JPEG

- variant(strip: true, quality: 80, interlace: "JPEG", sampling_factor: "4:2:0", colorspace: "sRGB")
+ variant(saver: { strip: true, quality: 80, interlace: true })

PNG

- variant(strip: true, quality: 75)
+ variant(saver: { strip: true, compression: 9 })

WEBP

- variant(strip: true, quality: 75, define: { webp: { lossless: false, alpha_quality: 85, thread_level: 1 } })
+ variant(saver: { strip: true, quality: 75, lossless: false, alpha_q: 85, reduction_effort: 6, smart_subsample: true })

GIF

- variant(layers: "Optimize")
+ variant(saver: { optimize_gif_frames: true, optimize_gif_transparency: true })

3.14.8 部署到生产环境

Active Storage将需要执行的转换列表编码到图像的URL中。如果您的应用程序缓存这些URL,当您将新代码部署到生产环境后,图像将无法显示。因此,您必须手动使受影响的缓存键无效。

例如,如果您在视图中有以下内容:

<% @products.each do |product| %>
  <% cache product do %>
    <%= image_tag product.cover_photo.variant(resize: "200x") %>
  <% end %>
<% end %>

您可以通过触发产品或更改缓存键来使缓存无效:

<% @products.each do |product| %>
  <% cache ["v2", product] do %>
    <%= image_tag product.cover_photo.variant(resize_to_limit: [200, nil]) %>
  <% end %>
<% end %>

3.15 Rails版本现在包含在Active Record模式转储中

Rails 7.0更改了某些列类型的默认值。为了避免从6.1升级到7.0的应用程序使用新的7.0默认值加载当前模式,Rails现在在模式转储中包含框架的版本。

在首次在Rails 7.0中加载模式之前,请确保运行rails app:update以确保模式的版本包含在模式转储中。

模式文件将如下所示:

# This file is auto-generated from the current state of the database. Instead
# of editing this file, please use the migrations feature of Active Record to
# incrementally modify your database, and then regenerate this schema definition.
#
# This file is the source Rails uses to define your schema when running `bin/rails
# db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to
# be faster and is potentially less error prone than running all of your
# migrations from scratch. Old migrations may fail to apply correctly if those
# migrations use external dependencies or application code.
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema[6.1].define(version: 2022_01_28_123512) do

注意:在Rails 7.0中首次转储模式时,您将看到该文件的许多更改,包括一些列信息。请确保查看新的模式文件内容并将其提交到您的存储库中。

4 从Rails 6.0升级到Rails 6.1

有关Rails 6.1所做更改的更多信息,请参阅发布说明

4.1 Rails.application.config_for返回值不再支持使用字符串键访问。

给定以下配置文件:

# config/example.yml
development:
  options:
    key: value
Rails.application.config_for(:example).options

以前,这将返回一个哈希,您可以使用字符串键访问其中的值。这在6.0中已被弃用,现在不再起作用。

如果您仍然希望使用字符串键访问值,可以在config_for的返回值上调用with_indifferent_access,例如:

Rails.application.config_for(:example).with_indifferent_access.dig('options', 'key')

4.2 使用respond_to#any时响应的Content-Type

响应中返回的Content-Type标头可能与Rails 6.0返回的不同,特别是如果您的应用程序使用respond_to { |format| format.any }。现在,Content-Type将基于给定的块而不是请求的格式。

示例:

def my_action
  respond_to do |format|
    format.any { render(json: { foo: 'bar' }) }
  end
end
get('my_action.csv')

以前的行为是返回一个text/csv响应的Content-Type,这是不准确的,因为正在呈现一个JSON响应。当前行为正确返回一个application/json响应的Content-Type。

如果您的应用程序依赖于先前的错误行为,建议您指定您的操作接受的格式,例如:

format.any(:xml, :json) { render request.format.to_sym => @people }

4.3 ActiveSupport::Callbacks#halted_callback_hook现在接收第二个参数

Active Support允许您在回调停止链时重写halted_callback_hook。此方法现在接收第二个参数,即被停止的回调的名称。如果您有覆盖此方法的类,请确保它接受两个参数。请注意,这是一个没有先前弃用周期的破坏性更改(出于性能原因)。

示例:

class Book < ApplicationRecord
  before_save { throw(:abort) }
  before_create { throw(:abort) }

  def halted_callback_hook(filter, callback_name) # => 此方法现在接受2个参数而不是1个
    Rails.logger.info("无法#{callback_name}图书")
  end
end

4.4 控制器中的helper类方法使用String#constantize

在Rails 6.1之前的概念上,

helper "foo/bar"

结果是

require_dependency "foo/bar_helper"
module_name = "foo/bar_helper".camelize
module_name.constantize

现在它改为:

prefix = "foo/bar".camelize
"#{prefix}Helper".constantize

对于大多数应用程序来说,此更改是向后兼容的,您不需要做任何操作。

但从技术上讲,控制器可以配置helpers_path以指向$LOAD_PATH中不在自动加载路径中的目录。这种用法不再默认支持。如果助手模块无法自动加载,应用程序需要在调用helper之前加载它。

4.5 从HTTP重定向到HTTPS现在使用308 HTTP状态码

在将非GET/HEAD请求从HTTP重定向到HTTPS时,ActionDispatch::SSL中使用的默认HTTP状态码已更改为308,如https://tools.ietf.org/html/rfc7538中定义。

4.6 Active Storage现在需要图像处理

在Active Storage中处理变体时,现在需要捆绑image_processing gem而不是直接使用mini_magick。 Image Processing默认配置为在幕后使用mini_magick,因此升级的最简单方法是将mini_magick gem替换为image_processing gem,并确保删除对combine_options的显式使用,因为它不再需要。

为了提高可读性,您可能希望将原始的resize调用更改为image_processing宏。例如,不再使用:

video.preview(resize: "100x100")
video.preview(resize: "100x100>")
video.preview(resize: "100x100^")

而是分别使用:

video.preview(resize_to_fit: [100, 100])
video.preview(resize_to_limit: [100, 100])
video.preview(resize_to_fill: [100, 100])

4.7 新的ActiveModel::Error

错误现在是新的ActiveModel::Error类的实例,API有所更改。根据您如何操作错误,其中一些更改可能会引发错误,而其他更改将打印弃用警告以在Rails 7.0中修复。

有关此更改的更多信息以及有关API更改的详细信息,请参阅此PR

5 从Rails 5.2升级到Rails 6.0

有关Rails 6.0所做更改的更多信息,请参阅发布说明

5.1 使用Webpacker

Webpacker 是Rails 6的默认JavaScript编译器。但是,如果您正在升级应用程序,则默认情况下不会激活它。 如果您想使用Webpacker,请在Gemfile中包含它并安装它:

gem "webpacker"
$ bin/rails webpacker:install

5.2 强制SSL

控制器上的force_ssl方法已被弃用,并将在Rails 6.1中删除。建议您启用config.force_ssl以在整个应用程序中强制使用HTTPS连接。如果您需要豁免某些端点的重定向,可以使用config.ssl_options来配置该行为。

5.3 目的和过期元数据现在嵌入在签名和加密的cookie中,以增加安全性

为了提高安全性,Rails将目的和过期元数据嵌入到加密或签名cookie的值中。

这样,Rails可以防止攻击者尝试复制cookie的签名/加密值并将其用作另一个cookie的值。

这些新的嵌入元数据使这些cookie与早于6.0版本的Rails不兼容。

如果您需要Rails 5.2及更早版本读取您的cookie,或者您仍在验证您的6.0部署并希望能够回滚,请将Rails.application.config.action_dispatch.use_cookies_with_metadata设置为false

5.4 所有npm包已移至@rails范围

如果您以前通过npm/yarn加载actioncableactivestoragerails-ujs包,您必须在将它们升级到6.0.0之前更新这些依赖项的名称:

actioncable   → @rails/actioncable
activestorage → @rails/activestorage
rails-ujs     → @rails/ujs

5.5 Action Cable JavaScript API更改

Action Cable JavaScript包已从CoffeeScript转换为ES2015,并且我们现在在npm分发中发布源代码。

此版本对Action Cable JavaScript API的可选部分进行了一些重大更改:

  • WebSocket适配器和日志记录器适配器的配置已从ActionCable的属性移动到ActionCable.adapters的属性。如果您正在配置这些适配器,您需要进行以下更改:

    -    ActionCable.WebSocket = MyWebSocket
    +    ActionCable.adapters.WebSocket = MyWebSocket
    
    -    ActionCable.logger = myLogger
    +    ActionCable.adapters.logger = myLogger
    
  • ActionCable.startDebugging()ActionCable.stopDebugging()方法已被移除,并用属性ActionCable.logger.enabled替换。如果您正在使用这些方法,您需要进行以下更改:

    -    ActionCable.startDebugging()
    +    ActionCable.logger.enabled = true
    
    -    ActionCable.stopDebugging()
    +    ActionCable.logger.enabled = false
    

5.6 ActionDispatch::Response#content_type现在返回不经修改的Content-Type头

以前,ActionDispatch::Response#content_type的返回值不包含字符集部分。 这个行为已经改变,现在包括之前省略的字符集部分。

如果您只想要MIME类型,请改用ActionDispatch::Response#media_type

之前:

resp = ActionDispatch::Response.new(200, "Content-Type" => "text/csv; header=present; charset=utf-16")
resp.content_type #=> "text/csv; header=present"

之后:

resp = ActionDispatch::Response.new(200, "Content-Type" => "text/csv; header=present; charset=utf-16")
resp.content_type #=> "text/csv; header=present; charset=utf-16"
resp.media_type   #=> "text/csv"

5.7 新的config.hosts设置

Rails现在有一个新的config.hosts设置,用于安全目的。该设置在开发环境中默认为localhost。如果您在开发中使用其他域名,您需要像这样允许它们:

# config/environments/development.rb

config.hosts << 'dev.myapp.com'
config.hosts << /[a-z0-9-]+\.myapp\.com/ # 可选地,也可以使用正则表达式

对于其他环境,默认情况下config.hosts为空,这意味着Rails不会验证主机。如果您想在生产环境中验证它,可以选择添加它们。

5.8 自动加载

Rails 6的默认配置

# config/application.rb

config.load_defaults 6.0

在CRuby上启用了zeitwerk自动加载模式。在这种模式下,自动加载、重新加载和急切加载由Zeitwerk管理。

如果您使用的是以前版本的Rails的默认值,您可以这样启用zeitwerk:

# config/application.rb

config.autoloader = :zeitwerk

5.8.1 公共API

一般来说,应用程序不需要直接使用Zeitwerk的API。Rails根据现有的约定设置事物:config.autoload_pathsconfig.cache_classes等。

虽然应用程序应该遵守该接口,但实际的Zeitwerk加载器对象可以通过以下方式访问:

Rails.autoloaders.main

如果您需要预加载单表继承(STI)类或配置自定义的inflector,这可能会很方便。

5.8.2 项目结构

如果正在升级的应用程序正确地自动加载,项目结构应该已经基本兼容。

然而,classic模式从缺失的常量名(underscore)推断文件名,而zeitwerk模式从文件名推断常量名(camelize)。这些辅助函数并不总是彼此的逆操作,特别是如果涉及首字母缩略词。例如,"FOO".underscore"foo",但"foo".camelize"Foo",而不是"FOO"。 可以使用zeitwerk:check任务来检查兼容性:

$ bin/rails zeitwerk:check
请稍等,我正在加载应用程序。
一切正常!

5.8.3 require_dependency

已经消除了require_dependency的所有已知用例,您应该在项目中使用grep命令并删除它们。

如果您的应用程序使用单表继承,请参阅自动加载和重新加载常量(Zeitwerk模式)指南中的单表继承部分

5.8.4 类和模块定义中的限定名称

现在您可以在类和模块定义中稳健地使用常量路径:

# 此类主体中的自动加载与Ruby语义现在匹配。
class Admin::UsersController < ApplicationController
  # ...
end

需要注意的是,根据执行顺序,经典的自动加载程序有时可以自动加载Foo::Wadus

class Foo::Bar
  Wadus
end

这不符合Ruby语义,因为Foo不在嵌套中,并且在zeitwerk模式下根本不起作用。如果您发现这种特殊情况,可以使用限定名称Foo::Wadus

class Foo::Bar
  Foo::Wadus
end

或者将Foo添加到嵌套中:

module Foo
  class Bar
    Wadus
  end
end

5.8.5 Concerns

您可以从标准结构中自动加载和急切加载,例如:

app/models
app/models/concerns

在这种情况下,app/models/concerns被假定为根目录(因为它属于自动加载路径),并且被忽略为命名空间。因此,app/models/concerns/foo.rb应该定义Foo,而不是Concerns::Foo

Concerns::命名空间在经典的自动加载程序中作为实现的副作用工作,但这实际上并不是预期的行为。使用Concerns::的应用程序需要将这些类和模块重命名,以便能够在zeitwerk模式下运行。

5.8.6 在自动加载路径中添加app

某些项目希望像app/api/base.rb这样定义API::Base,并将app添加到自动加载路径以在classic模式下实现。由于Rails自动将app的所有子目录添加到自动加载路径中,我们有了另一种情况,其中存在嵌套的根目录,因此该设置不再起作用。与上面解释的concerns类似的原则。

如果要保留该结构,您需要在初始化程序中从自动加载路径中删除子目录:

ActiveSupport::Dependencies.autoload_paths.delete("#{Rails.root}/app/api")

5.8.7 自动加载的常量和显式命名空间

如果在文件中定义了命名空间,例如这里的Hotel

app/models/hotel.rb         # 定义了Hotel。
app/models/hotel/pricing.rb # 定义了Hotel::Pricing。

则必须使用classmodule关键字设置Hotel常量。例如:

class Hotel
end

是正确的。

以下替代方法不起作用,例如:

Hotel = Class.new

或者

Hotel = Struct.new

这样的子对象,例如Hotel::Pricing将无法找到。

此限制仅适用于显式命名空间。不定义命名空间的类和模块可以使用这些习惯用法进行定义。

5.8.8 一个文件,一个常量(在同一顶级)

classic模式下,您可以在同一顶级定义多个常量并重新加载它们。例如,给定以下代码:

# app/models/foo.rb

class Foo
end

class Bar
end

虽然无法自动加载Bar,但自动加载Foo将标记Bar为已自动加载。但在zeitwerk模式下不是这样的,您需要将Bar移动到它自己的文件bar.rb中。一个文件,一个常量。

这仅适用于与上面示例中的相同顶级的常量。内部类和模块是可以的。例如,请考虑以下代码:

# app/models/foo.rb

class Foo
  class InnerClass
  end
end

如果应用程序重新加载Foo,它也将重新加载Foo::InnerClass

5.8.9 Spring和test环境

如果有更改,Spring会重新加载应用程序代码。在test环境中,您需要启用重新加载才能使其工作:

# config/environments/test.rb

config.cache_classes = false

否则,您将收到以下错误:

reloading is disabled because config.cache_classes is true

5.8.10 Bootsnap

Bootsnap的版本应至少为1.4.2。

除此之外,由于解释器中的一个错误,Bootsnap需要禁用iseq缓存,如果运行的是Ruby 2.5,请确保至少依赖于Bootsnap 1.4.4。

5.8.11 config.add_autoload_paths_to_load_path

新的配置点config.add_autoload_paths_to_load_path默认为true,以保持向后兼容性,但允许您选择不将自动加载路径添加到$LOAD_PATH中。

这在大多数应用程序中是有意义的,因为您永远不应该在app/models中要求文件,例如,Zeitwerk只在内部使用绝对文件名。 通过选择退出,您可以优化$LOAD_PATH的查找(减少目录检查),并节省Bootsnap的工作和内存消耗,因为它不需要为这些目录构建索引。

5.8.12 线程安全

在经典模式下,常量自动加载不是线程安全的,尽管Rails已经放置了锁定机制,例如在启用自动加载时使Web请求线程安全,这在开发环境中很常见。

zeitwerk模式下,常量自动加载是线程安全的。例如,您现在可以在由runner命令执行的多线程脚本中自动加载。

5.8.13 config.autoload_paths中的通配符

注意配置如下的情况:

config.autoload_paths += Dir["#{config.root}/lib/**/"]

config.autoload_paths的每个元素都应该代表顶级命名空间(Object),并且它们不能嵌套(除了上面解释的concerns目录)。

要修复这个问题,只需移除通配符:

config.autoload_paths << "#{config.root}/lib"

5.8.14 预加载和自动加载的一致性

classic模式下,如果app/models/foo.rb定义了Bar,您将无法自动加载该文件,但是预加载将工作,因为它会盲目递归加载文件。如果您首先进行预加载测试,然后执行自动加载,可能会导致错误。

zeitwerk模式下,这两种加载模式是一致的,它们在相同的文件中失败和出错。

5.8.15 如何在Rails 6中使用经典自动加载器

应用程序可以加载Rails 6的默认设置,并通过设置config.autoloader来使用经典自动加载器,如下所示:

# config/application.rb

config.load_defaults 6.0
config.autoloader = :classic

在Rails 6应用程序中使用经典自动加载器时,建议在开发环境中将并发级别设置为1,用于Web服务器和后台处理器,以解决线程安全问题。

5.9 Active Storage分配行为更改

使用Rails 5.2的配置默认值,对于使用has_many_attached声明的附件集合进行分配会追加新文件:

class User < ApplicationRecord
  has_many_attached :highlights
end

user.highlights.attach(filename: "funky.jpg", ...)
user.highlights.count # => 1

blob = ActiveStorage::Blob.create_after_upload!(filename: "town.jpg", ...)
user.update!(highlights: [ blob ])

user.highlights.count # => 2
user.highlights.first.filename # => "funky.jpg"
user.highlights.second.filename # => "town.jpg"

使用Rails 6.0的配置默认值,对于使用has_many_attached声明的附件集合进行分配会替换现有文件,而不是追加到它们后面。这与将值分配给集合关联时的Active Record行为相匹配:

user.highlights.attach(filename: "funky.jpg", ...)
user.highlights.count # => 1

blob = ActiveStorage::Blob.create_after_upload!(filename: "town.jpg", ...)
user.update!(highlights: [ blob ])

user.highlights.count # => 1
user.highlights.first.filename # => "town.jpg"

#attach可以用于添加新的附件而不删除现有的附件:

blob = ActiveStorage::Blob.create_after_upload!(filename: "town.jpg", ...)
user.highlights.attach(blob)

user.highlights.count # => 2
user.highlights.first.filename # => "funky.jpg"
user.highlights.second.filename # => "town.jpg"

现有的应用程序可以通过将config.active_storage.replace_on_assign_to_many设置为true来选择使用这种新行为。旧行为将在Rails 7.0中弃用,并在Rails 7.1中删除。

5.10 自定义异常处理应用程序

无效的AcceptContent-Type请求头现在会引发异常。默认的config.exceptions_app专门处理该错误并进行补偿。自定义异常应用程序也需要处理该错误,否则这样的请求将导致Rails使用回退的异常应用程序,返回500 Internal Server Error

6 从Rails 5.1升级到Rails 5.2

有关Rails 5.2所做更改的更多信息,请参阅发布说明

6.1 Bootsnap

Rails 5.2在新生成的应用程序的Gemfile中添加了bootsnap gem。app:update命令在boot.rb中设置了它。如果您想使用它,请将其添加到Gemfile中:

# 通过缓存减少启动时间;在config/boot.rb中需要
gem 'bootsnap', require: false

否则,请更改boot.rb以不使用bootsnap。

6.2 签名或加密cookie中的过期时间现在嵌入在cookie值中

为了提高安全性,Rails现在还将过期信息嵌入在加密或签名cookie的值中。

这个新的嵌入信息使得这些cookie与早于5.2版本的Rails不兼容。

如果您需要让您的cookie被5.1和更早版本读取,或者您仍在验证您的5.2部署并希望允许回滚,请将Rails.application.config.action_dispatch.use_authenticated_cookie_encryption设置为false

7 从Rails 5.0升级到Rails 5.1

有关Rails 5.1所做更改的更多信息,请参阅发布说明

7.1 顶级HashWithIndifferentAccess已被软弃用

如果您的应用程序使用顶级HashWithIndifferentAccess类,您应该逐步将您的代码改为使用ActiveSupport::HashWithIndifferentAccess。 它只是软弃用,这意味着您的代码目前不会出错,也不会显示任何弃用警告,但是这个常量将来会被删除。

此外,如果您有非常旧的YAML文档,其中包含这些对象的转储,您可能需要重新加载和转储它们,以确保它们引用正确的常量,并且加载它们不会在将来出错。

7.2 application.secrets现在加载所有键为符号

如果您的应用程序将嵌套配置存储在config/secrets.yml中,现在所有键都将作为符号加载,因此应更改使用字符串的访问方式。

从:

Rails.application.secrets[:smtp_settings]["address"]

到:

Rails.application.secrets[:smtp_settings][:address]

7.3 删除了render中对:text:nothing的弃用支持

如果您的控制器使用render :text,它们将不再起作用。使用MIME类型为text/plain的新方法来呈现文本是使用render :plain

类似地,已删除render :nothing,您应该使用head方法发送仅包含头部的响应。例如,head :ok发送一个没有正文的200响应。

7.4 删除了对redirect_to :back的弃用支持

在Rails 5.0中,redirect_to :back已被弃用。在Rails 5.1中,它被完全删除。

作为替代,使用redirect_back。重要的是要注意,redirect_back还接受一个fallback_location选项,该选项将在HTTP_REFERER丢失的情况下使用。

redirect_back(fallback_location: root_path)

8 从Rails 4.2升级到Rails 5.0

有关Rails 5.0所做更改的更多信息,请参阅发布说明

8.1 需要Ruby 2.2.2+

从Ruby on Rails 5.0开始,只支持Ruby 2.2.2+版本。在继续之前,请确保您使用的是Ruby 2.2.2版本或更高版本。

8.2 Active Record模型现在默认继承自ApplicationRecord

在Rails 4.2中,Active Record模型继承自ActiveRecord::Base。在Rails 5.0中,所有模型都继承自ApplicationRecord

ApplicationRecord是所有应用程序模型的新超类,类似于应用程序控制器继承ApplicationController而不是ActionController::Base。这为应用程序提供了一个单一的位置来配置应用程序范围的模型行为。

从Rails 4.2升级到Rails 5.0时,您需要在app/models/中创建一个application_record.rb文件,并添加以下内容:

class ApplicationRecord < ActiveRecord::Base
  self.abstract_class = true
end

然后确保所有模型都继承自它。

8.3 通过throw(:abort)停止回调链

在Rails 4.2中,当Active Record和Active Model中的'before'回调返回false时,整个回调链将停止。换句话说,连续的'before'回调不会执行,也不会执行包装在回调中的操作。

在Rails 5.0中,在Active Record或Active Model回调中返回false将不会导致回调链停止的副作用。相反,必须通过调用throw(:abort)来显式停止回调链。

当您从Rails 4.2升级到Rails 5.0时,返回false在这些类型的回调中仍然会停止回调链,但是您将收到有关此即将发生的更改的弃用警告。

当您准备好时,可以选择使用新的行为,并通过将以下配置添加到您的config/application.rb中来删除弃用警告:

ActiveSupport.halt_callback_chains_on_return_false = false

请注意,此选项不会影响Active Support回调,因为它们在返回任何值时从不停止链。

有关更多详细信息,请参见#17227

8.4 ActiveJob现在默认继承自ApplicationJob

在Rails 4.2中,Active Job继承自ActiveJob::Base。在Rails 5.0中,此行为已更改为继承自ApplicationJob

从Rails 4.2升级到Rails 5.0时,您需要在app/jobs/中创建一个application_job.rb文件,并添加以下内容:

class ApplicationJob < ActiveJob::Base
end

然后确保所有作业类都继承自它。

有关更多详细信息,请参见#19034

8.5 Rails控制器测试

8.5.1 将一些辅助方法提取到rails-controller-testing

assignsassert_template已提取到rails-controller-testing gem中。要在控制器测试中继续使用这些方法,请将gem 'rails-controller-testing'添加到您的Gemfile中。

如果您在使用RSpec进行测试,请参阅该gem文档中所需的额外配置。

8.5.2 上传文件时的新行为

如果您在测试中使用ActionDispatch::Http::UploadedFile来上传文件,则需要更改为使用类似的Rack::Test::UploadedFile类。 有关更多详细信息,请参见#26404

8.6 在生产环境中启动后禁用自动加载

默认情况下,在生产环境中启动后禁用自动加载。

应用程序的预加载是启动过程的一部分,因此顶级常量是可以的,仍然会自动加载,无需要求它们的文件。

深层次的常量只有在运行时才会执行,例如常规方法体,也是可以的,因为在启动时已经预加载了定义它们的文件。

对于绝大多数应用程序,此更改无需采取任何操作。但在极少数情况下,如果您的应用程序在生产环境中需要自动加载,请将Rails.application.config.enable_dependency_loading设置为true

8.7 XML序列化

ActiveModel::Serializers::Xml已从Rails中提取到activemodel-serializers-xml gem中。要继续在应用程序中使用XML序列化,请将gem 'activemodel-serializers-xml'添加到您的Gemfile中。

8.8 移除对传统mysql数据库适配器的支持

Rails 5移除了对传统mysql数据库适配器的支持。大多数用户应该可以使用mysql2代替。当我们找到维护者时,它将转换为一个单独的gem。

8.9 移除对Debugger的支持

Ruby 2.2不支持debugger,而Rails 5需要使用Ruby 2.2。请改用byebug

8.10 使用bin/rails运行任务和测试

Rails 5添加了通过bin/rails而不是rake运行任务和测试的功能。通常这些更改与rake并行进行,但有些是完全移植过来的。

要使用新的测试运行器,只需键入bin/rails test

rake dev:cache现在是bin/rails dev:cache

在应用程序的根目录中运行bin/rails以查看可用的命令列表。

8.11 ActionController::Parameters不再继承自HashWithIndifferentAccess

在应用程序中调用params现在将返回一个对象而不是哈希。如果您的参数已经被允许,则不需要进行任何更改。如果您正在使用map和其他依赖于无论permitted?如何都能读取哈希的方法,则需要升级您的应用程序,先进行许可,然后转换为哈希。

params.permit([:proceed_to, :return_to]).to_h

8.12 protect_from_forgery现在默认为prepend: false

protect_from_forgery默认为prepend: false,这意味着它将在您在应用程序中调用它的位置插入到回调链中。如果您希望protect_from_forgery始终首先运行,则应更改应用程序以使用protect_from_forgery prepend: true

8.13 默认模板处理程序现在是RAW

没有模板处理程序的文件将使用原始处理程序进行渲染。以前,Rails会使用ERB模板处理程序来渲染文件。

如果您不希望通过原始处理程序处理文件,则应为文件添加一个可以由适当的模板处理程序解析的扩展名。

8.14 添加了模板依赖项的通配符匹配

现在可以使用通配符匹配来匹配模板依赖项。例如,如果您定义模板如下:

<% # Template Dependency: recordings/threads/events/subscribers_changed %>
<% # Template Dependency: recordings/threads/events/completed %>
<% # Template Dependency: recordings/threads/events/uncompleted %>

现在您只需使用通配符一次调用依赖项。

<% # Template Dependency: recordings/threads/events/* %>

8.15 ActionView::Helpers::RecordTagHelper移至外部gem(record_tag_helper)

content_tag_fordiv_for已被移除,建议只使用content_tag。要继续使用旧方法,请将record_tag_helper gem添加到您的Gemfile中:

gem 'record_tag_helper', '~> 1.0'

有关更多详细信息,请参见#18411

8.16 移除对protected_attributes gem的支持

Rails 5不再支持protected_attributes gem。

8.17 移除对activerecord-deprecated_finders gem的支持

Rails 5不再支持activerecord-deprecated_finders gem。

8.18 ActiveSupport::TestCase默认测试顺序现在是随机的

当在应用程序中运行测试时,默认顺序现在是:random,而不是:sorted。使用以下配置选项将其设置回:sorted

# config/environments/test.rb
Rails.application.configure do
  config.active_support.test_order = :sorted
end

8.19 ActionController::Live变为Concern

如果您在另一个模块中包含ActionController::Live,而该模块又包含在您的控制器中,则还应该使用ActiveSupport::Concern扩展该模块。或者,您可以使用self.included钩子,在包含StreamingSupport后直接将ActionController::Live包含到控制器中。

这意味着如果您的应用程序以前有自己的流模块,则以下代码将在生产中中断: ```ruby

这是一个用于在流式控制器中使用Warden/Devise进行身份验证的解决方法。

参见 https://github.com/plataformatec/devise/issues/2332

在路由器中进行身份验证是该问题中提出的另一种解决方案

class StreamingSupport include ActionController::Live # 这在Rails 5的生产环境中无法工作 # extend ActiveSupport::Concern # 除非你取消注释此行。

def process(name) super(name) rescue ArgumentError => e if e.message == 'uncaught throw :warden' throw :warden else raise e end end end ```

8.20 新的框架默认设置

8.20.1 Active Record belongs_to 默认要求

如果关联不存在,belongs_to 现在默认会触发验证错误。

可以通过 optional: true 关闭每个关联的默认要求。

这个默认设置将自动配置在新的应用程序中。如果现有应用程序想要添加此功能,需要在初始化程序中打开它:

config.active_record.belongs_to_required_by_default = true

这个配置默认是全局的,适用于所有模型,但你可以在每个模型上覆盖它。这应该帮助你将所有模型迁移到默认要求关联的状态。

class Book < ApplicationRecord
  # 模型还没有准备好默认要求关联

  self.belongs_to_required_by_default = false
  belongs_to(:author)
end

class Car < ApplicationRecord
  # 模型已准备好默认要求关联

  self.belongs_to_required_by_default = true
  belongs_to(:pilot)
end

8.20.2 每个表单的 CSRF 令牌

Rails 5 现在支持每个表单的 CSRF 令牌,以防止 JavaScript 创建的表单的代码注入攻击。打开此选项后,应用程序中的每个表单都会有自己的 CSRF 令牌,该令牌特定于该表单的动作和方法。

config.action_controller.per_form_csrf_tokens = true

8.20.3 带来源检查的伪造保护

现在,你可以配置应用程序检查 HTTP Origin 标头是否应与站点的来源进行检查,作为额外的 CSRF 防御。在配置中设置以下内容为 true:

config.action_controller.forgery_protection_origin_check = true

8.20.4 允许配置 Action Mailer 队列名称

默认的邮件队列名称是 mailers。这个配置选项允许你全局更改队列名称。在配置中设置以下内容:

config.action_mailer.deliver_later_queue_name = :new_queue_name

8.20.5 在 Action Mailer 视图中支持片段缓存

在配置中设置 config.action_mailer.perform_caching,以确定你的 Action Mailer 视图是否支持缓存。

config.action_mailer.perform_caching = true

8.20.6 配置 db:structure:dump 的输出

如果你正在使用 schema_search_path 或其他 PostgreSQL 扩展,你可以控制如何转储模式。设置为 :all 以生成所有转储,或设置为 :schema_search_path 以从模式搜索路径生成。

config.active_record.dump_schemas = :all

8.20.7 配置 SSL 选项以启用带子域名的 HSTS

在配置中设置以下内容以在使用子域名时启用 HSTS:

config.ssl_options = { hsts: { subdomains: true } }

8.20.8 保留接收者的时区

在使用 Ruby 2.4 时,当调用 to_time 时,你可以保留接收者的时区。

ActiveSupport.to_time_preserves_timezone = false

8.21 JSON/JSONB 序列化的变化

在 Rails 5.0 中,JSON/JSONB 属性的序列化和反序列化方式发生了变化。现在,如果你将一个列设置为 String,Active Record 将不再将该字符串转换为 Hash,而只会返回字符串。这不仅限于与模型交互的代码,还影响 db/schema.rb 中的 :default 列设置。建议不要将列设置为 String,而是传递一个 Hash,它将自动转换为 JSON 字符串。

9 从 Rails 4.1 升级到 Rails 4.2

9.1 Web Console

首先,在你的 Gemfile 中的 :development 组中添加 gem 'web-console', '~> 2.0',然后运行 bundle install(在升级 Rails 时它不会被包含)。安装完成后,你可以简单地在任何你想启用它的视图中添加对控制台助手的引用(例如,<%= console %>)。在开发环境中查看任何错误页面时,也会提供一个控制台。

9.2 Responders

respond_with 和类级别的 respond_to 方法已经提取到 responders gem 中。要使用它们,只需在你的 Gemfile 中添加 gem 'responders', '~> 2.0'。在你的依赖项中没有包含 responders gem 的情况下,调用 respond_with 和类级别的 respond_to 将不再起作用: ```ruby

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不受影响,不需要额外的gem:

# 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

更多详细信息请参见#16526

9.3 事务回调中的错误处理

当前,Active Record会抑制在after_rollbackafter_commit回调中引发的错误,并仅将其打印到日志中。在下一个版本中,这些错误将不再被抑制。相反,错误将像其他Active Record回调一样正常传播。

当您定义一个after_rollbackafter_commit回调时,您将收到有关即将发生的更改的弃用警告。当您准备好时,您可以选择新的行为,并通过将以下配置添加到您的config/application.rb中来删除弃用警告:

config.active_record.raise_in_transactional_callbacks = true

更多详细信息请参见#14488#16537

9.4 测试用例的排序

在Rails 5.0中,默认情况下,测试用例将以随机顺序执行。为了预期这个变化,Rails 4.2引入了一个新的配置选项active_support.test_order,用于显式指定测试的顺序。这允许您通过将选项设置为:sorted来锁定当前行为,或者通过将选项设置为:random来选择未来的行为。

如果您没有为此选项指定值,将发出弃用警告。为了避免这种情况,请将以下行添加到您的测试环境中:

# config/environments/test.rb
Rails.application.configure do
  config.active_support.test_order = :sorted # 或者 `:random`,如果您喜欢
end

9.5 序列化属性

当使用自定义编码器(例如serialize :metadata, JSON)时,将nil赋值给序列化属性将将其保存到数据库中作为NULL,而不是通过编码器传递nil值(例如,使用JSON编码器时为"null")。

9.6 生产日志级别

在Rails 5中,生产环境的默认日志级别将从:info更改为:debug。为了保留当前的默认设置,请将以下行添加到您的production.rb中:

# 设置为`:info`以匹配当前的默认设置,或者设置为`:debug`以选择未来的默认设置。
config.log_level = :info

9.7 Rails模板中的after_bundle

如果您有一个将所有文件添加到版本控制的Rails模板,它在生成binstubs之前执行,因此无法添加生成的binstubs:

# template.rb
generate(:scaffold, "person name:string")
route "root to: 'people#index'"
rake("db:migrate")

git :init
git add: "."
git commit: %Q{ -m 'Initial commit' }

现在,您可以将git调用包装在after_bundle块中。它将在生成binstubs之后运行。

# template.rb
generate(:scaffold, "person name:string")
route "root to: 'people#index'"
rake("db:migrate")

after_bundle do
  git :init
  git add: "."
  git commit: %Q{ -m 'Initial commit' }
end

9.8 Rails HTML Sanitizer

在应用程序中,对HTML片段进行消毒的新选择已经出现。古老的html-scanner方法现已正式弃用,取而代之的是Rails HTML Sanitizer

这意味着sanitizesanitize_cssstrip_tagsstrip_links方法都有了新的实现。

这个新的消毒器在内部使用Loofah。而Loofah又使用了Nokogiri,它包装了用C和Java编写的XML解析器,所以无论您运行哪个Ruby版本,消毒速度都应该更快。

新版本更新了sanitize,因此它可以接受Loofah::Scrubber进行强大的消毒。 在这里可以看到一些Scrubber的示例

还添加了两个新的Scrubber:PermitScrubberTargetScrubber。 阅读gem的自述文件获取更多信息。

PermitScrubberTargetScrubber的文档解释了如何完全控制何时以及如何剥离元素。

如果您的应用程序需要使用旧的消毒器实现,请在您的Gemfile中包含rails-deprecated_sanitizer

gem 'rails-deprecated_sanitizer'

9.9 Rails DOM测试

TagAssertions模块(包含assert_tag等方法)已被弃用,取而代之的是从SelectorAssertions模块中提取出来的assert_select方法,该模块已被提取到rails-dom-testing gem中。

9.10 掩码认证令牌

为了减轻SSL攻击,form_authenticity_token现在被掩码,以便每个请求都有所不同。因此,令牌通过解码和解密进行验证。因此,验证来自非Rails表单的请求的策略必须考虑到这一点。

9.11 Action Mailer

之前,在邮件类上调用邮件方法会直接执行相应的实例方法。随着 Active Job 和 #deliver_later 的引入,这种情况不再成立。在 Rails 4.2 中,实例方法的调用被推迟到调用 deliver_nowdeliver_later 时才执行。例如:

class Notifier < ActionMailer::Base
  def notify(user, ...)
    puts "Called"
    mail(to: user.email, ...)
  end
end
mail = Notifier.notify(user, ...) # 此时 Notifier#notify 还未被调用
mail = mail.deliver_now           # 输出 "Called"

对于大多数应用程序来说,这不会导致任何明显的差异。然而,如果您需要同步执行一些非邮件方法,并且之前依赖于同步代理行为,您应该直接在邮件类上定义它们作为类方法:

class Notifier < ActionMailer::Base
  def self.broadcast_notifications(users, ...)
    users.each { |user| Notifier.notify(user, ...) }
  end
end

9.12 外键支持

迁移 DSL 已扩展以支持外键定义。如果您一直在使用 Foreigner gem,您可能想考虑将其移除。请注意,Rails 的外键支持是 Foreigner 的一个子集。这意味着并非每个 Foreigner 定义都可以完全由其 Rails 迁移 DSL 对应物替代。

迁移过程如下:

  1. Gemfile 中删除 gem "foreigner"
  2. 运行 bundle install
  3. 运行 bin/rake db:schema:dump
  4. 确保 db/schema.rb 包含了每个外键定义及其必要的选项。

10 从 Rails 4.0 升级到 Rails 4.1

10.1 防止来自远程 <script> 标签的 CSRF 攻击

或者说,"我的测试失败了!!!" 或者 "我的 <script> 小部件坏了!!"

跨站请求伪造 (CSRF) 保护现在也覆盖了带有 JavaScript 响应的 GET 请求。这可以防止第三方站点通过 <script> 标签远程引用您的 JavaScript 以提取敏感数据。

这意味着使用以下代码的功能测试和集成测试

get :index, format: :js

现在将触发 CSRF 保护。改为使用

xhr :get, :index, format: :js

来显式地测试 XmlHttpRequest

注意:您自己的 <script> 标签也被视为跨域并默认被阻止。如果您确实需要从 <script> 标签加载 JavaScript,您现在必须显式跳过这些操作的 CSRF 保护。

10.2 Spring

如果您想使用 Spring 作为应用程序的预加载器,您需要:

  1. Gemfile 中添加 gem 'spring', group: :development
  2. 使用 bundle install 安装 spring。
  3. 使用 bundle exec spring binstub 生成 Spring binstub。

注意:用户定义的 rake 任务默认在 development 环境中运行。如果您希望它们在其他环境中运行,请参阅 Spring README

10.3 config/secrets.yml

如果您想使用新的 secrets.yml 约定来存储应用程序的密钥,您需要:

  1. config 文件夹中创建一个名为 secrets.yml 的文件,内容如下:

    development:
      secret_key_base:
    
    test:
      secret_key_base:
    
    production:
      secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
    
  2. 使用现有的 secret_token.rb 初始化文件中的 secret_key_base 来为在生产环境中运行 Rails 应用程序的用户设置 SECRET_KEY_BASE 环境变量。或者,您可以直接将现有的 secret_key_basesecret_token.rb 初始化文件复制到 secrets.ymlproduction 部分,替换 <%= ENV["SECRET_KEY_BASE"] %>

  3. 删除 secret_token.rb 初始化文件。

  4. 使用 rake secretdevelopmenttest 部分生成新的密钥。

  5. 重新启动服务器。

10.4 测试助手的更改

如果您的测试助手包含对 ActiveRecord::Migration.check_pending! 的调用,可以将其删除。现在在 require "rails/test_help" 时会自动进行检查,尽管在助手中保留此行不会有任何危害。

10.5 Cookies 序列化器

在 Rails 4.1 之前创建的应用程序使用 Marshal 将 cookie 值序列化为签名和加密的 cookie 存储。如果您想在应用程序中使用新的基于 JSON 的格式,可以添加一个初始化文件,内容如下:

Rails.application.config.action_dispatch.cookies_serializer = :hybrid

这将自动将现有的使用 Marshal 序列化的 cookie 迁移到新的基于 JSON 的格式。

当使用 :json:hybrid 序列化器时,您应该注意并非所有的 Ruby 对象都可以序列化为 JSON。例如,DateTime 对象将被序列化为字符串,而 Hash 的键将被转换为字符串。

class CookiesController < ApplicationController
  def set_cookie
    cookies.encrypted[:expiration_date] = Date.tomorrow # => Thu, 20 Mar 2014
    redirect_to action: 'read_cookie'
  end

  def read_cookie
    cookies.encrypted[:expiration_date] # => "2014-03-20"
  end
end

建议只在cookie中存储简单的数据(字符串和数字)。如果必须存储复杂的对象,在后续请求中读取值时需要手动处理转换。

如果使用cookie会话存储,这也适用于sessionflash哈希。

10.6 Flash结构变化

Flash消息键已经normalized to strings。仍然可以使用符号或字符串访问它们。循环遍历flash将始终产生字符串键:

flash["string"] = "a string"
flash[:symbol] = "a symbol"

# Rails < 4.1
flash.keys # => ["string", :symbol]

# Rails >= 4.1
flash.keys # => ["string", "symbol"]

确保将Flash消息键与字符串进行比较。

10.7 JSON处理的变化

Rails 4.1中与JSON处理相关的几个重大变化。

10.7.1 移除MultiJSON

MultiJSON已经达到了end-of-life,并已从Rails中移除。

如果您的应用程序当前直接依赖于MultiJSON,您有几个选择:

  1. 将'multi_json'添加到您的Gemfile。请注意,这可能在将来停止工作。

  2. 通过使用obj.to_jsonJSON.parse(str)来迁移到MultiJSON。

警告:不要简单地用JSON.dumpJSON.load替换MultiJson.dumpMultiJson.load。这些JSON gem API用于序列化和反序列化任意Ruby对象,通常是不安全的。

10.7.2 JSON gem兼容性

在历史上,Rails与JSON gem存在一些兼容性问题。在Rails应用程序中使用JSON.generateJSON.dump可能会产生意外的错误。

Rails 4.1通过将其自己的编码器与JSON gem隔离来解决了这些问题。JSON gem API将正常工作,但它们将无法访问任何Rails特定的功能。例如:

class FooBar
  def as_json(options = nil)
    { foo: 'bar' }
  end
end
irb> FooBar.new.to_json
=> "{\"foo\":\"bar\"}"
irb> JSON.generate(FooBar.new, quirks_mode: true)
=> "\"#<FooBar:0x007fa80a481610>\""

10.7.3 新的JSON编码器

Rails 4.1中的JSON编码器已经重写,以利用JSON gem的优势。对于大多数应用程序,这应该是一个透明的更改。然而,作为重写的一部分,编码器删除了以下功能:

  1. 循环数据结构检测
  2. 支持encode_json钩子
  3. BigDecimal对象编码为数字而不是字符串的选项

如果您的应用程序依赖于这些功能之一,可以通过将activesupport-json_encoder gem添加到您的Gemfile中来恢复它们。

10.7.4 Time对象的JSON表示

具有时间组件(TimeDateTimeActiveSupport::TimeWithZone)的对象的#as_json现在默认返回毫秒精度。如果需要保留没有毫秒精度的旧行为,请在初始化程序中设置以下内容:

ActiveSupport::JSON::Encoding.time_precision = 0

10.8 内联回调块中的return的使用

以前,Rails允许内联回调块使用return,如下所示:

class ReadOnlyModel < ActiveRecord::Base
  before_save { return false } # BAD
end

这种行为从未被有意支持过。由于ActiveSupport::Callbacks内部的更改,这在Rails 4.1中不再允许。在内联回调块中使用return语句会在执行回调时引发LocalJumpError

可以将使用return的内联回调块重构为评估返回的值:

class ReadOnlyModel < ActiveRecord::Base
  before_save { false } # GOOD
end

或者,如果更喜欢使用return,建议显式定义一个方法:

class ReadOnlyModel < ActiveRecord::Base
  before_save :before_save_callback # GOOD

  private
    def before_save_callback
      false
    end
end

此更改适用于Rails中使用回调的大多数地方,包括Active Record和Active Model回调,以及Action Controller中的过滤器(例如before_action)。

有关更多详细信息,请参阅此pull request

10.9 在Active Record fixtures中定义的方法

Rails 4.1在单独的上下文中评估每个fixture的ERB,因此在fixture中定义的辅助方法将不会在其他fixture中可用。

在多个fixture中使用的辅助方法应该在新引入的ActiveRecord::FixtureSet.context_class中包含的模块中定义,在test_helper.rb中。

module FixtureFileHelpers
  def file_sha(path)
    OpenSSL::Digest::SHA256.hexdigest(File.read(Rails.root.join('test/fixtures', path)))
  end
end

ActiveRecord::FixtureSet.context_class.include FixtureFileHelpers

10.10 I18n强制可用的区域设置

Rails 4.1现在将I18n选项enforce_available_locales默认设置为true。这意味着它将确保传递给它的所有区域设置必须在available_locales列表中声明。 要禁用它(并允许I18n接受任何区域设置选项),请将以下配置添加到您的应用程序中:

config.i18n.enforce_available_locales = false

请注意,此选项是作为安全措施添加的,以确保用户输入不能用作区域设置信息,除非事先已知。因此,除非您有充分的理由这样做,否则建议不要禁用此选项。

10.11 在关系上调用的Mutator方法

Relation不再具有像#map!#delete_if这样的Mutator方法。在使用这些方法之前,请调用#to_a将其转换为Array

这旨在防止在代码中直接调用Mutator方法时出现奇怪的错误和混淆。

# 不再这样写
Author.where(name: 'Hank Moody').compact!

# 现在需要这样写
authors = Author.where(name: 'Hank Moody').to_a
authors.compact!

10.12 默认作用域的更改

默认作用域不再被链式条件覆盖。

在之前的版本中,当您在模型中定义default_scope时,它会被相同字段的链式条件覆盖。现在它像任何其他作用域一样合并。

之前:

class User < ActiveRecord::Base
  default_scope { where state: 'pending' }
  scope :active, -> { where state: 'active' }
  scope :inactive, -> { where state: 'inactive' }
end

User.all
# SELECT "users".* FROM "users" WHERE "users"."state" = 'pending'

User.active
# SELECT "users".* FROM "users" WHERE "users"."state" = 'active'

User.where(state: 'inactive')
# SELECT "users".* FROM "users" WHERE "users"."state" = 'inactive'

之后:

class User < ActiveRecord::Base
  default_scope { where state: 'pending' }
  scope :active, -> { where state: 'active' }
  scope :inactive, -> { where state: 'inactive' }
end

User.all
# SELECT "users".* FROM "users" WHERE "users"."state" = 'pending'

User.active
# SELECT "users".* FROM "users" WHERE "users"."state" = 'pending' AND "users"."state" = 'active'

User.where(state: 'inactive')
# SELECT "users".* FROM "users" WHERE "users"."state" = 'pending' AND "users"."state" = 'inactive'

要恢复以前的行为,需要使用unscopedunscoperewhereexcept显式删除default_scope条件。

class User < ActiveRecord::Base
  default_scope { where state: 'pending' }
  scope :active, -> { unscope(where: :state).where(state: 'active') }
  scope :inactive, -> { rewhere state: 'inactive' }
end

User.all
# SELECT "users".* FROM "users" WHERE "users"."state" = 'pending'

User.active
# SELECT "users".* FROM "users" WHERE "users"."state" = 'active'

User.inactive
# SELECT "users".* FROM "users" WHERE "users"."state" = 'inactive'

10.13 从字符串渲染内容

Rails 4.1引入了render:plain:html:body选项。这些选项现在是渲染基于字符串的内容的首选方式,因为它允许您指定要将响应发送为的内容类型。

  • render :plain将内容类型设置为text/plain
  • render :html将内容类型设置为text/html
  • render :body不会设置内容类型头。

从安全角度来看,如果您不希望在响应正文中有任何标记,则应使用render :plain,因为大多数浏览器会为您转义响应中的不安全内容。

我们将在将来的版本中弃用使用render :text。因此,请开始使用更精确的:plain:html:body选项。使用render :text可能会带来安全风险,因为内容将作为text/html发送。

10.14 PostgreSQL JSON和hstore数据类型

Rails 4.1将jsonhstore列映射为以字符串为键的Ruby Hash。在早期版本中,使用的是HashWithIndifferentAccess。这意味着不再支持符号访问。对于基于jsonhstore列的store_accessors也是如此。请确保始终使用字符串键。

10.15 ActiveSupport::Callbacks的显式块用法

Rails 4.1现在在调用ActiveSupport::Callbacks.set_callback时期望传递一个显式块。这个变化源于ActiveSupport::Callbacks在4.1版本中被大部分重写。

# 在Rails 4.0中以前
set_callback :save, :around, ->(r, &block) { stuff; result = block.call; stuff }

# 在Rails 4.1中现在
set_callback :save, :around, ->(r, block) { stuff; result = block.call; stuff }

11 从Rails 3.2升级到Rails 4.0

如果您的应用程序当前使用的是早于3.2.x的任何版本的Rails,请在尝试升级到Rails 4.0之前先升级到Rails 3.2。

以下更改适用于将应用程序升级到Rails 4.0。

11.1 HTTP PATCH

Rails 4现在在config/routes.rb中声明RESTful资源时,使用PATCH作为更新的主要HTTP动词。update动作仍然被使用,PUT请求也将继续路由到update动作。因此,如果您只使用标准的RESTful路由,无需进行任何更改:

resources :users
<%= form_for @user do |f| %>
class UsersController < ApplicationController
  def update
    # 无需更改;PATCH将被优先使用,PUT仍然有效。
  end
end

然而,如果您正在使用form_for来更新资源,并且与使用PUT HTTP方法的自定义路由结合使用,则需要进行更改:

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方法,则可以更新路由以使用patch而不是put

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

在Rails 4中,PUT请求到/users/:id将路由到update,与现在的情况相同。因此,如果您有一个接收真实PUT请求的API,它将正常工作。路由器还将PATCH请求路由到/users/:idupdate动作。

如果该动作在公共API中使用,并且您无法更改正在使用的HTTP方法,则可以更新表单以使用PUT方法:

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

有关PATCH以及为什么进行此更改的更多信息,请参阅Rails博客上的此文章

11.1.1 关于媒体类型的说明

PATCH动词的勘误指定应使用'diff'媒体类型。其中一种格式是JSON Patch。虽然Rails不原生支持JSON Patch,但很容易添加支持:

# 在您的控制器中:
def update
  respond_to do |format|
    format.json do
      # 执行部分更新
      @article.update params[:article]
    end

    format.json_patch do
      # 执行复杂的更改
    end
  end
end
# config/initializers/json_patch.rb
Mime::Type.register 'application/json-patch+json', :json_patch

由于JSON Patch最近才成为RFC,因此还没有很多出色的Ruby库。Aaron Patterson的hana是一个这样的宝石,但对规范的最后几个更改没有完全支持。

11.2 Gemfile

Rails 4.0从Gemfile中删除了assets组。在升级时,您需要从Gemfile中删除该行。您还应该更新应用程序文件(位于config/application.rb中):

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

11.3 vendor/plugins

Rails 4.0不再支持从vendor/plugins加载插件。您必须将任何插件提取为宝石并将它们添加到您的Gemfile中。如果您选择不将它们制作成宝石,您可以将它们移动到lib/my_plugin/*,并在config/initializers/my_plugin.rb中添加适当的初始化程序。

11.4 Active Record

  • Rails 4.0从Active Record中删除了标识映射,原因是与关联存在一些不一致性。如果您在应用程序中手动启用了它,您将不得不删除以下没有效果的配置:config.active_record.identity_map

  • 集合关联中的delete方法现在可以接收IntegerString类型的记录ID作为参数,与destroy方法几乎相同。以前,对于这样的参数,它会引发ActiveRecord::AssociationTypeMismatch错误。从Rails 4.0开始,在删除之前,delete会自动尝试找到与给定ID匹配的记录。

  • 在Rails 4.0中,当重命名列或表时,相关的索引也会被重命名。如果您有重命名索引的迁移,它们不再需要。

  • Rails 4.0已将serialized_attributesattr_readonly更改为仅类方法。您不应该使用实例方法,因为它已被弃用。您应该将它们更改为使用类方法,例如self.serialized_attributes改为self.class.serialized_attributes

  • 使用默认编码器时,将nil分配给序列化属性将将其保存到数据库中作为NULL,而不是通过YAML传递nil值("--- \n...\n")。

  • Rails 4.0在Strong Parameters的支持下移除了attr_accessibleattr_protected功能。您可以使用Protected Attributes gem进行平滑升级。

  • 如果您没有使用Protected Attributes,可以删除与该gem相关的任何选项,例如whitelist_attributesmass_assignment_sanitizer选项。

  • Rails 4.0要求作用域使用可调用对象,例如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已弃用旧式基于哈希的查找器API。这意味着以前接受“查找器选项”的方法不再接受。例如,Book.find(:all, conditions: { name: '1984' })已被弃用,改用Book.where(name: '1984')

  • 除了find_by_...find_by_...!之外,所有动态方法都已弃用。以下是如何处理这些更改:

    • 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),而不是旧的查找器中的数组。如果需要一个Array,请使用where(...).to_a

  • 这些等效方法可能不会执行与先前实现相同的SQL。

  • 要重新启用旧的查找器,可以使用activerecord-deprecated_finders gem

  • Rails 4.0已更改了has_and_belongs_to_many关系的默认连接表,以去除第二个表名的公共前缀。任何具有公共前缀的现有has_and_belongs_to_many模型之间的关系都必须使用join_table选项指定。例如:

    CatalogCategory < ActiveRecord::Base
      has_and_belongs_to_many :catalog_products, join_table: 'catalog_categories_catalog_products'
    end
    
    CatalogProduct < ActiveRecord::Base
      has_and_belongs_to_many :catalog_categories, join_table: 'catalog_categories_catalog_products'
    end
    
  • 请注意,前缀也考虑了作用域,因此Catalog::CategoryCatalog::ProductCatalog::CategoryCatalogProduct之间的关系需要进行类似的更新。

11.5 Active Resource

Rails 4.0将Active Resource提取为独立的gem。如果您仍然需要该功能,可以在Gemfile中添加Active Resource gem

11.6 Active Model

  • Rails 4.0已更改了ActiveModel::Validations::ConfirmationValidator附加错误的方式。现在,当确认验证失败时,错误将附加到:#{attribute}_confirmation而不是attribute

  • 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
    

11.7 Action Pack

  • Rails 4.0引入了ActiveSupport::KeyGenerator,并将其用作生成和验证签名cookie(等等)的基础。如果您保留现有的secret_token并添加新的secret_key_base,则会自动升级现有的Rails 3.x签名cookie。

      # config/initializers/secret_token.rb
      Myapp::Application.config.secret_token = 'existing secret token'
      Myapp::Application.config.secret_key_base = 'new secret key base'
    

    请注意,应在将100%的用户迁移到Rails 4.x并且合理确定不需要回滚到Rails 3.x之后再设置secret_key_base。这是因为基于新的secret_key_base在Rails 4.x中生成的cookie与Rails 3.x不兼容。您可以保留现有的secret_token,不设置新的secret_key_base,并忽略弃用警告,直到您合理确定升级已经完成。

    如果您依赖于外部应用程序或JavaScript能够读取您的Rails应用程序的签名会话cookie(或签名cookie),则在解耦这些问题之前不应设置secret_key_base

  • Rails 4.0如果设置了secret_key_base,会对基于cookie的会话内容进行加密。Rails 3.x对基于cookie的会话内容进行了签名,但没有加密。签名cookie是“安全”的,因为它们经过验证是由您的应用程序生成的,并且是防篡改的。但是,内容可以被最终用户查看,加密内容可以消除这个注意事项/问题,而不会有显著的性能损失。

    有关移至加密会话cookie的详细信息,请阅读Pull Request #9978

  • Rails 4.0已删除了ActionController::Base.asset_path选项。请使用资产管道功能。

  • Rails 4.0已弃用ActionController::Base.page_cache_extension选项。请改用ActionController::Base.default_static_extension

  • Rails 4.0已从Action Pack中移除了Action和Page缓存。您需要在控制器中添加actionpack-action_caching gem以使用caches_action,并添加actionpack-page_caching gem以使用caches_page

  • Rails 4.0已移除了XML参数解析器。如果需要此功能,您需要添加actionpack-xml_parser gem。

  • Rails 4.0更改了使用符号或返回nil的procs进行默认layout查找设置。要获得“无布局”行为,应返回false而不是nil。

  • Rails 4.0将默认的memcached客户端从memcache-client更改为dalli。要升级,只需将gem 'dalli'添加到您的Gemfile中。

  • Rails 4.0在控制器中弃用了dom_iddom_class方法(在视图中使用它们是可以的)。如果需要此功能,您需要在需要的控制器中包含ActionView::RecordIdentifier模块。

  • Rails 4.0在link_to助手中弃用了:confirm选项。您应该使用数据属性(例如data: { confirm: 'Are you sure?' })来替代。此弃用还涉及基于此助手的其他助手(如link_to_iflink_to_unless)。

  • Rails 4.0更改了assert_generatesassert_recognizesassert_routing的工作方式。现在,所有这些断言都会引发Assertion而不是ActionController::RoutingError

  • Rails 4.0如果定义了冲突的命名路由,则会引发ArgumentError。这可以通过显式定义的命名路由或resources方法触发。以下是与名为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还更改了绘制Unicode字符路由的方式。现在可以直接绘制Unicode字符路由。如果您已经绘制了此类路由,则必须更改它们,例如:

    get Rack::Utils.escape('こんにちは'), controller: 'welcome', action: 'index'
    

    变为

    get 'こんにちは', controller: 'welcome', action: 'index'
    
  • Rails 4.0要求使用match的路由必须指定请求方法。例如:

      # Rails 3.x
      match '/' => 'root#index'
    
      # 变为
      match '/' => 'root#index', via: :get
    
      # 或者
      get '/' => 'root#index'
    
  • Rails 4.0已移除ActionDispatch::BestStandardsSupport中间件,<!DOCTYPE html>已经根据 https://msdn.microsoft.com/en-us/library/jj676915(v=vs.85).aspx 触发了标准模式,并且ChromeFrame头已移至config.action_dispatch.default_headers

    请记住,您还必须从应用程序代码中删除对中间件的任何引用,例如:

    # 引发异常
    config.middleware.insert_before(Rack::Lock, ActionDispatch::BestStandardsSupport)
    

    还要检查环境设置中的config.action_dispatch.best_standards_support并在存在时删除它。

  • Rails 4.0允许通过设置config.action_dispatch.default_headers来配置HTTP头。默认值如下:

      config.action_dispatch.default_headers = {
        'X-Frame-Options' => 'SAMEORIGIN',
        'X-XSS-Protection' => '1; mode=block'
      }
    

    请注意,如果您的应用程序依赖于在<frame><iframe>中加载某些页面,则可能需要显式设置X-Frame-OptionsALLOW-FROM ...ALLOWALL

  • 在Rails 4.0中,预编译资产不再自动从vendor/assetslib/assets复制非JS/CSS资产。Rails应用程序和引擎开发人员应将这些资产放在app/assets中或配置config.assets.precompile

  • 在Rails 4.0中,当操作不处理请求格式时,将引发ActionController::UnknownFormat异常。默认情况下,该异常通过响应406 Not Acceptable来处理,但现在可以覆盖它。在Rails 3中,始终返回406 Not Acceptable。无法覆盖。

  • 在Rails 4.0中,当ParamsParser无法解析请求参数时,会引发通用的ActionDispatch::ParamsParser::ParseError异常。您应该捕获此异常,而不是低级别的MultiJson::DecodeError,例如。

  • 在Rails 4.0中,当引擎挂载在从URL前缀提供的应用程序上时,SCRIPT_NAME会正确嵌套。您不再需要设置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

    Active Support

Rails 4.0移除了ERB::Util#json_escapej别名,因为j已经用于ActionView::Helpers::JavaScriptHelper#escape_javascript

11.7.1 缓存

Rails 3.x和4.0之间的缓存方法发生了变化。您应该更改缓存命名空间并使用冷缓存进行部署。

11.8 Helpers加载顺序

在Rails 4.0中,从多个目录加载的helpers的顺序发生了变化。以前,它们被收集然后按字母顺序排序。升级到Rails 4.0后,helpers将保留加载目录的顺序,并且只在每个目录内按字母顺序排序。除非您明确使用helpers_path参数,否则此更改只会影响从引擎加载helpers的方式。如果您依赖于顺序,请在升级后检查正确的方法是否可用。如果您想更改引擎加载的顺序,可以使用config.railties_order=方法。

11.9 Active Record Observer和Action Controller Sweeper

ActiveRecord::ObserverActionController::Caching::Sweeper已经提取到rails-observers gem中。如果您需要这些功能,您需要添加rails-observers gem。

11.10 sprockets-rails

  • assets:precompile:primaryassets:precompile:all已被删除。请改用assets:precompile
  • config.assets.compress选项应更改为config.assets.js_compressor,例如:

    config.assets.js_compressor = :uglifier
    

11.11 sass-rails

  • asset-url带有两个参数的用法已被弃用。例如:asset-url("rails.png", image)变为asset-url("rails.png")

12 从Rails 3.1升级到Rails 3.2

如果您的应用程序当前处于3.1.x之前的任何版本的Rails上,您应该在尝试升级到Rails 3.2之前先升级到Rails 3.1。

以下更改适用于将应用程序升级到最新的Rails 3.2.x版本。

12.1 Gemfile

对您的Gemfile进行以下更改。

gem 'rails', '3.2.21'

group :assets do
  gem 'sass-rails',   '~> 3.2.6'
  gem 'coffee-rails', '~> 3.2.2'
  gem 'uglifier',     '>= 1.0.3'
end

12.2 config/environments/development.rb

您应该在开发环境中添加一些新的配置设置:

# 对Active Record模型的批量赋值保护引发异常
config.active_record.mass_assignment_sanitizer = :strict

# 记录查询计划,对于执行时间超过此阈值的查询(适用于SQLite、MySQL和PostgreSQL)
config.active_record.auto_explain_threshold_in_seconds = 0.5

12.3 config/environments/test.rb

mass_assignment_sanitizer配置设置也应添加到config/environments/test.rb中:

# 对Active Record模型的批量赋值保护引发异常
config.active_record.mass_assignment_sanitizer = :strict

12.4 vendor/plugins

Rails 3.2弃用了vendor/plugins,而Rails 4.0将完全删除它们。虽然作为Rails 3.2升级的一部分并不是严格必需的,但您可以通过将它们提取为gems并将它们添加到您的Gemfile中来开始替换任何插件。如果您选择不将它们制作为gems,您可以将它们移动到lib/my_plugin/*,并在config/initializers/my_plugin.rb中添加适当的初始化程序。

12.5 Active Record

belongs_to中删除了dependent => :restrict选项。如果您想要防止删除对象,如果存在任何关联对象,您可以设置dependent => :destroy,并在检查任何关联对象的destroy回调中返回false

13 从Rails 3.0升级到Rails 3.1

如果您的应用程序当前处于3.0.x之前的任何版本的Rails上,您应该在尝试升级到Rails 3.1之前先升级到Rails 3.0。

以下更改适用于将应用程序升级到Rails 3.1.12,最后一个3.1.x版本的Rails。

13.1 Gemfile

对您的Gemfile进行以下更改。

gem 'rails', '3.1.12'
gem 'mysql2'

# 为新的asset pipeline所需
group :assets do
  gem 'sass-rails',   '~> 3.1.7'
  gem 'coffee-rails', '~> 3.1.1'
  gem 'uglifier',     '>= 1.0.3'
end

# jQuery是Rails 3.1的默认JavaScript库
gem 'jquery-rails'

13.2 config/application.rb

asset pipeline需要以下添加:

config.assets.enabled = true
config.assets.version = '1.0'

如果您的应用程序为资源使用"/assets"路由,您可能希望更改用于资源的前缀以避免冲突:

# 默认为'/assets'
config.assets.prefix = '/asset-files'

13.3 config/environments/development.rb

删除RJS设置config.action_view.debug_rjs = true

如果启用asset pipeline,请添加以下设置:

# 不压缩assets
config.assets.compress = false

# 展开加载assets的行
config.assets.debug = true

13.4 config/environments/production.rb

同样,下面的大部分更改都是为了asset pipeline。您可以在Asset Pipeline指南中了解更多信息。 ```ruby

压缩JavaScript和CSS

config.assets.compress = true

如果预编译的资源丢失,则不回退到资源管道

config.assets.compile = false

为资源URL生成摘要

config.assets.digest = true

默认为Rails.root.join("public/assets")

config.assets.manifest = YOUR_PATH

预编译其他资源(application.js、application.css以及所有非JS/CSS文件已添加)

config.assets.precompile += %w( admin.js admin.css )

强制通过SSL访问应用程序,使用Strict-Transport-Security,并使用安全的cookie。

config.force_ssl = true


### config/environments/test.rb

您可以通过以下方式在测试环境中测试性能:

```ruby
# 为测试配置静态资源服务器,使用Cache-Control提高性能
config.public_file_server.enabled = true
config.public_file_server.headers = {
  'Cache-Control' => 'public, max-age=3600'
}

13.5 config/initializers/wrap_parameters.rb

如果您希望将参数包装成嵌套哈希,请添加此文件并包含以下内容。这在新应用程序中默认启用。

# 修改此文件后请务必重新启动服务器。
# 此文件包含ActionController::ParamsWrapper的设置,默认情况下启用。

# 启用JSON的参数包装。您可以通过将:format设置为空数组来禁用此功能。
ActiveSupport.on_load(:action_controller) do
  wrap_parameters format: [:json]
end

# 默认情况下在JSON中禁用根元素。
ActiveSupport.on_load(:active_record) do
  self.include_root_in_json = false
end

13.6 config/initializers/session_store.rb

您需要将会话密钥更改为新的值,或者删除所有会话:

# 在config/initializers/session_store.rb中
AppName::Application.config.session_store :cookie_store, key: 'SOMETHINGNEW'

或者

$ bin/rake db:sessions:clear

13.7 从视图中的资源助手引用中删除:cache和:concat选项

  • 使用资源管道,不再使用:cache和:concat选项,从视图中删除这些选项。

反馈

欢迎您帮助改进本指南的质量。

如果您发现任何拼写错误或事实错误,请贡献您的意见。 要开始,请阅读我们的 文档贡献 部分。

您还可能会发现不完整的内容或过时的内容。 请为主要内容添加任何缺失的文档。请先检查 Edge 指南,以验证问题是否已经修复或尚未修复。 请参阅 Ruby on Rails 指南准则 以了解样式和规范。

如果您发现需要修复但无法自行修复的问题,请 提交问题

最后但同样重要的是,欢迎您在 官方 Ruby on Rails 论坛 上讨论有关 Ruby on Rails 文档的任何问题。