1. 一般建议
在尝试升级现有应用程序之前,您应该确保有充分的理由进行升级。您需要权衡几个因素:对新功能的需求、查找旧代码支持的难度越来越大,以及您可用的时间和技能等。
1.1. 测试覆盖率
确保您的应用程序在升级后仍然正常工作的最佳方法是在开始此过程之前具有良好的测试覆盖率。如果您没有自动化测试来覆盖您应用程序的大部分功能,您将需要花费时间手动测试所有已更改的部分。在 Rails 升级的情况下,这意味着应用程序中的每一个功能。请帮自己一个忙,在开始升级之前确保您的测试覆盖率良好。
1.2. Ruby 版本
Rails 通常在发布时与最新发布的 Ruby 版本保持紧密。
- Rails 8.0 需要 Ruby 3.2.0 或更高版本。
- Rails 7.2 需要 Ruby 3.1.0 或更高版本。
- Rails 7.0 和 7.1 需要 Ruby 2.7.0 或更高版本。
- Rails 6 需要 Ruby 2.5.0 或更高版本。
- Rails 5 需要 Ruby 2.2.2 或更高版本。
最好将 Ruby 和 Rails 分开升级。首先升级到您能达到的最新 Ruby 版本,然后升级 Rails。
1.3. 升级过程
更改 Rails 版本时,最好慢慢地,一次一个次要版本地进行,以便充分利用弃用警告。Rails 版本号的形式为 Major.Minor.Patch。主要版本和次要版本允许更改公共 API,因此这可能会导致您的应用程序中出现错误。补丁版本仅包含错误修复,不更改任何公共 API。
过程应如下进行:
- 编写测试并确保它们通过。
- 升级到当前版本之后的最新补丁版本。
- 修复测试和弃用功能。
- 升级到下一个次要版本的最新补丁版本。
重复此过程,直到达到目标 Rails 版本。
1.3.1. 版本之间移动
要在版本之间移动:
- 更改
Gemfile中的 Rails 版本号并运行bundle update rails。 - 如果运行 jsbundling-rails,请更改
package.json中 Rails JavaScript 包的版本并运行bin/rails javascript:install。 - 运行更新任务。
- 运行您的测试。
您可以在此处找到所有已发布的 Rails gem 列表。
1.4. 更新任务
Rails 提供了 bin/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_8_0.rb
...
不要忘记查看差异,以查看是否有任何意外的更改,并注意在此过程中使用的差异和合并工具可以使用 THOR_DIFF 和 THOR_MERGE 环境变量定义。
1.5. 配置框架默认值
新的 Rails 版本可能与之前的版本具有不同的配置默认值。但是,在遵循上述步骤后,您的应用程序仍将使用之前 Rails 版本的配置默认值运行。这是因为 config/application.rb 中的 config.load_defaults 值尚未更改。
为了让您逐一升级到新的默认值,更新任务已创建一个文件 config/initializers/new_framework_defaults_X_Y.rb(文件名中带有所需的 Rails 版本)。您应该通过取消注释文件中的新配置默认值来启用它们;这可以分多次部署逐渐完成。一旦您的应用程序准备好使用新的默认值运行,您可以删除此文件并翻转 config.load_defaults 值。
2. 从 Rails 8.0 升级到 Rails 8.1
有关 Rails 8.1 所做更改的更多信息,请参阅发布说明。
2.1. schema.rb 中的表列现在按字母顺序排序。
Active Record 现在默认按字母顺序对 schema.rb 中的表列进行排序,因此在不同机器上的转储是一致的,并且不会因迁移顺序而翻转——这意味着更少的嘈杂差异。有关字母化模式更改的更多详细信息,请参阅 #53281。 structure.sql 仍然可以用于保留精确的列顺序。
3. 从 Rails 7.2 升级到 Rails 8.0
有关 Rails 8.0 所做更改的更多信息,请参阅发布说明。
4. 从 Rails 7.1 升级到 Rails 7.2
有关 Rails 7.2 所做更改的更多信息,请参阅发布说明。
4.1. 所有测试现在都遵循 active_job.queue_adapter 配置
如果您在 config/application.rb 或 config/environments/test.rb 文件中设置了 config.active_job.queue_adapter,那么您选择的适配器以前并未在所有测试中一致使用。在某些测试中会使用您的适配器,但在其他测试中会使用 TestAdapter。
在 Rails 7.2 中,如果提供了 queue_adapter 配置,所有测试都将遵循它。如果您将 queue_adapter 配置设置为 :test 以外的值,但以依赖 TestAdapter 的方式编写了测试,这可能会导致测试错误。
如果没有提供配置,将继续使用 TestAdapter。
4.2. alias_attribute 现在绕过原始属性上的自定义方法
在 Rails 7.2 中,alias_attribute 现在绕过原始属性上定义的自定义方法,直接访问底层数据库值。此更改已在 Rails 7.1 中通过弃用警告宣布。
之前 (Rails 7.1)
class User < ActiveRecord::Base
def email
"custom_#{super}"
end
alias_attribute :username, :email
end
user = User.create!(email: "test@example.com")
user.username
# => "custom_test@example.com"
之后 (Rails 7.2)
user = User.create!(email: "test@example.com")
user.username
# => "test@example.com" # Raw database value
如果您收到弃用警告 "Since Rails 7.2 #{method_name} will not be calling #{target_name} anymore",您应该手动定义别名方法:
class User < ActiveRecord::Base
def email
"custom_#{super}"
end
def username
email # This will call the custom email method
end
end
或者,您可以使用 alias_method:
class User < ActiveRecord::Base
def email
"custom_#{super}"
end
alias_method :username, :email
end
5. 从 Rails 7.0 升级到 Rails 7.1
有关 Rails 7.1 所做更改的更多信息,请参阅发布说明。
5.1. 开发和测试环境 secret_key_base 文件已更改
在开发和测试环境中,Rails 读取 secret_key_base 的文件已从 tmp/development_secret.txt 重命名为 tmp/local_secret.txt。
您可以简单地将以前的文件重命名为 local_secret.txt 以继续使用相同的密钥,或将密钥从以前的文件复制到新文件中。
如果不这样做,Rails 将在应用程序加载时在新文件 tmp/local_secret.txt 中生成一个新的密钥。
这将使开发和测试环境中所有现有会话/cookie 失效,并导致从 secret_key_base 派生的其他签名(例如 Active Storage/Action Text 附件)损坏。
生产和其他环境不受影响。
5.2. 自动加载路径不再位于 $LOAD_PATH 中
从 Rails 7.1 开始,由自动加载器管理的目录不再添加到 $LOAD_PATH。这意味着无法通过手动 require 调用加载其文件,这无论如何都不应该这样做。
减小 $LOAD_PATH 的大小可以加快不使用 bootsnap 的应用程序的 require 调用速度,并减小其他应用程序的 bootsnap 缓存大小。
如果您希望这些路径仍然在 $LOAD_PATH 中,您可以选择加入:
config.add_autoload_paths_to_load_path = true
但我们不鼓励这样做,自动加载路径中的类和模块旨在自动加载。也就是说,只需引用它们。
lib 目录不受此标志的影响,它始终添加到 $LOAD_PATH 中。
5.3. config.autoload_lib 和 config.autoload_lib_once
如果您的应用程序在自动加载或自动加载一次路径中没有 lib,请跳过此部分。您可以通过检查以下输出了解:
# Print autoload paths.
$ bin/rails runner 'pp Rails.autoloaders.main.dirs'
# Print autoload once paths.
$ bin/rails runner 'pp Rails.autoloaders.once.dirs'
如果您的应用程序的自动加载路径中已经有 lib,通常 config/application.rb 中会有类似以下的配置:
# Autoload lib, but do not eager load it (maybe overlooked).
config.autoload_paths << config.root.join("lib")
或
# Autoload and also eager load lib.
config.autoload_paths << config.root.join("lib")
config.eager_load_paths << config.root.join("lib")
或
# Same, because all eager load paths become autoload paths too.
config.eager_load_paths << config.root.join("lib")
这仍然有效,但建议用更简洁的以下内容替换这些行:
config.autoload_lib(ignore: %w(assets tasks))
请将任何不包含 .rb 文件或不应重新加载或预加载的其他 lib 子目录添加到 ignore 列表中。例如,如果您的应用程序有 lib/templates、lib/generators 或 lib/middleware,您将添加它们相对于 lib 的名称:
config.autoload_lib(ignore: %w(assets tasks templates generators middleware))
有了这一行,如果 config.eager_load 为 true(production 模式下的默认值),lib 中的(未忽略的)代码也将被预加载。这通常是您想要的,但如果之前 lib 未添加到预加载路径中,并且您仍然希望保持这种方式,请选择退出:
Rails.autoloaders.main.do_not_eager_load(config.root.join("lib"))
如果应用程序的 config.autoload_once_paths 中有 lib,则 config.autoload_lib_once 方法是类似的。
5.4. ActiveStorage::BaseController 不再包含流媒体关注点
继承自 ActiveStorage::BaseController 并使用流媒体来实现自定义文件服务逻辑的应用程序控制器现在必须显式包含 ActiveStorage::Streaming 模块。
5.5. MemCacheStore 和 RedisCacheStore 现在默认使用连接池
connection_pool gem 已作为 activesupport gem 的依赖项添加,MemCacheStore 和 RedisCacheStore 现在默认使用连接池。
如果您不想使用连接池,请在配置缓存存储时将 :pool 选项设置为 false:
config.cache_store = :mem_cache_store, "cache.example.com", { pool: false }
有关更多信息,请参阅使用 Rails 进行缓存指南。
5.6. SQLite3Adapter 现在配置为在严格字符串模式下使用
使用严格字符串模式会禁用双引号字符串字面量。
SQLite 在双引号字符串字面量方面有一些奇怪之处。它首先尝试将双引号字符串视为标识符名称,但如果它们不存在,则将其视为字符串字面量。因此,拼写错误可能会悄无声息地被忽略。例如,可以为不存在的列创建索引。有关更多详细信息,请参阅SQLite 文档。
如果您不想在严格模式下使用 SQLite3Adapter,您可以禁用此行为:
# config/application.rb
config.active_record.sqlite3_adapter_strict_strings_by_default = false
5.7. 支持 ActionMailer::Preview 的多个预览路径
选项 config.action_mailer.preview_path 已弃用,取而代之的是 config.action_mailer.preview_paths。将路径附加到此配置选项将导致这些路径用于搜索邮件预览。
config.action_mailer.preview_paths << "#{Rails.root}/lib/mailer_previews"
5.8. 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") # raises in 7.0, raises in 7.1
I18n.t("missing.key") # didn't raise in 7.0, raises in 7.1
# anywhere:
I18n.t("missing.key") # didn't raise in 7.0, raises in 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") # didn't raise in 7.0, doesn't raise in 7.1
I18n.t("missing.key") # didn't raise in 7.0, doesn't raise in 7.1
# anywhere:
I18n.t("missing.key") # didn't raise in 7.0, doesn't raise in 7.1
或者,您可以自定义 I18n.exception_handler。有关更多信息,请参阅i18n 指南。
AbstractController::Translation.raise_on_missing_translations 已被删除。这是一个私有 API,如果您依赖它,您应该迁移到 config.i18n.raise_on_missing_translations 或自定义异常处理程序。
5.9. bin/rails test 现在运行 test:prepare 任务
当通过 bin/rails test 运行测试时,rake test:prepare 任务将在测试运行之前运行。如果您增强了 test:prepare 任务,您的增强将在您的测试之前运行。tailwindcss-rails、jsbundling-rails 和 cssbundling-rails 以及其他第三方 gem 都增强了此任务。
有关更多信息,请参阅测试 Rails 应用程序指南。
如果您运行单个文件的测试 (bin/rails test test/models/user_test.rb),test:prepare 不会在其之前运行。
5.10. @rails/ujs 的导入语法已修改
从 Rails 7.1 开始,@rails/ujs 模块的导入语法已修改。Rails 不再支持直接从 @rails/ujs 导入模块。
例如,尝试从库中导入函数将会失败:
import { fileInputSelector } from "@rails/ujs"
// ERROR: export 'fileInputSelector' (imported as 'fileInputSelector') was not found in '@rails/ujs' (possible exports: default)
在 Rails 7.1 中,用户应该首先直接从 @rails/ujs 导入 Rails 对象。然后,用户可以从 Rails 对象导入特定的模块。
以下是 Rails 7.1 中的导入示例:
import Rails from "@rails/ujs"
// Alias the method
const fileInputSelector = Rails.fileInputSelector
// Alternatively, reference it from the Rails object where it is used
Rails.fileInputSelector(...)
5.11. Rails.logger 现在返回一个 ActiveSupport::BroadcastLogger 实例
ActiveSupport::BroadcastLogger 类是一个新的日志记录器,它允许以简单的方式将日志广播到不同的接收器(STDOUT、日志文件...)。
用于广播日志的 API(使用 ActiveSupport::Logger.broadcast 方法)已被删除,并且以前是私有的。如果您的应用程序或库依赖于此 API,您需要进行以下更改:
logger = Logger.new("some_file.log")
# Before
Rails.logger.extend(ActiveSupport::Logger.broadcast(logger))
# After
Rails.logger.broadcast_to(logger)
如果您的应用程序配置了自定义日志记录器,Rails.logger 将包装并代理所有方法。您无需进行任何更改即可使其工作。
如果您需要访问自定义日志记录器实例,可以使用 broadcasts 方法:
# config/application.rb
config.logger = MyLogger.new
# Anywhere in your application
puts Rails.logger.class #=> BroadcastLogger
puts Rails.logger.broadcasts #=> [MyLogger]
5.12. Active Record 加密算法更改
Active Record 加密现在使用 SHA-256 作为其哈希摘要算法。如果您有使用以前 Rails 版本加密的数据,则需要考虑两种情况:
如果您将
config.active_support.key_generator_hash_digest_class配置为 SHA-1(Rails 7.0 之前的默认值),则您还需要为 Active Record 加密配置 SHA-1:config.active_record.encryption.hash_digest_class = OpenSSL::Digest::SHA1如果您所有数据都已非确定性加密(除非
encrypts传递deterministic: true,否则为默认值),您可以改为为 Active Record 加密配置 SHA-256(如以下场景 2 所示),并通过设置以下内容允许解密以前使用 SHA-1 加密的列:config.active_record.encryption.support_sha1_for_non_deterministic_encryption = true如果您将
config.active_support.key_generator_hash_digest_class配置为 SHA-256(7.0 中的新默认值),则您需要为 Active Record 加密配置 SHA-256:config.active_record.encryption.hash_digest_class = OpenSSL::Digest::SHA256
有关 config.active_record.encryption.hash_digest_class 的更多信息,请参阅配置 Rails 应用程序指南。
此外,引入了一个新配置config.active_record.encryption.support_sha1_for_non_deterministic_encryption,以解决一个错误,该错误导致某些属性即使通过上述 hash_digest_class 配置为 SHA-256,也使用 SHA-1 加密。
默认情况下,config.active_record.encryption.support_sha1_for_non_deterministic_encryption 在 Rails 7.1 中是禁用的。如果您在 Rails < 7.1 版本中加密的数据可能受到上述错误的影响,则应启用此配置:
config.active_record.encryption.support_sha1_for_non_deterministic_encryption = true
如果您正在处理加密数据,请仔细查看以上内容。
5.13. 在控制器测试、集成测试和系统测试中处理异常的新方法
config.action_dispatch.show_exceptions 配置控制 Action Pack 如何处理响应请求时引发的异常。
在 Rails 7.1 之前,设置 config.action_dispatch.show_exceptions = true 配置 Action Pack 捕获异常并渲染适当的 HTML 错误页面,例如渲染 public/404.html 并带有 404 Not found 状态码,而不是引发 ActiveRecord::RecordNotFound 异常。设置 config.action_dispatch.show_exceptions = false 配置 Action Pack 不捕获异常。在 Rails 7.1 之前,新应用程序会在 config/environments/test.rb 中生成一行代码,将 config.action_dispatch.show_exceptions = false。
Rails 7.1 将可接受的值从 true 和 false 更改为 :all、:rescuable 和 :none。
:all- 为所有异常渲染 HTML 错误页面(等同于true):rescuable- 为config.action_dispatch.rescue_responses声明的异常渲染 HTML 错误页面:none(等同于false) - 不捕获任何异常
由 Rails 7.1 或更高版本生成的应用程序在 config/environments/test.rb 中设置 config.action_dispatch.show_exceptions = :rescuable。升级时,现有应用程序可以更改 config.action_dispatch.show_exceptions = :rescuable 以利用新行为,或用相应的旧值替换旧值(:all 替换 true,:none 替换 false)。
6. 从 Rails 6.1 升级到 Rails 7.0
有关 Rails 7.0 所做更改的更多信息,请参阅发布说明。
6.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)))
6.2. Spring
如果您的应用程序使用 Spring,它需要升级到至少 3.0.0 版本。否则您会得到:
undefined method `mechanism=' for ActiveSupport::Dependencies:Module
此外,请确保在 config/environments/test.rb 中将config.cache_classes设置为 false。
6.3. Sprockets 现在是可选依赖项
rails gem 不再依赖于 sprockets-rails。如果您的应用程序仍然需要使用 Sprockets,请确保将 sprockets-rails 添加到您的 Gemfile 中。
gem "sprockets-rails"
6.4. 应用程序需要以 zeitwerk 模式运行
仍以 classic 模式运行的应用程序必须切换到 zeitwerk 模式。请查看经典到 Zeitwerk 操作指南以获取详细信息。
6.5. setter config.autoloader= 已被删除
在 Rails 7 中没有配置点来设置自动加载模式,config.autoloader= 已被删除。如果您出于某种原因将其设置为 :zeitwerk,只需将其删除。
6.6. ActiveSupport::Dependencies 私有 API 已被删除
ActiveSupport::Dependencies 的私有 API 已被删除。这包括 hook!、unhook!、depend_on、require_or_load、mechanism 等许多方法。
几个亮点:
- 如果您使用
ActiveSupport::Dependencies.constantize或ActiveSupport::Dependencies.safe_constantize,只需将它们更改为String#constantize或String#safe_constantize。
ActiveSupport::Dependencies.constantize("User") # NO LONGER POSSIBLE
"User".constantize # 👍
任何
ActiveSupport::Dependencies.mechanism的使用(无论是读取器还是写入器)都必须替换为相应地访问config.cache_classes。如果您想跟踪自动加载器的活动,
ActiveSupport::Dependencies.verbose=不再可用,只需在config/application.rb中放置Rails.autoloaders.log!。
辅助内部类或模块也已消失,例如 ActiveSupport::Dependencies::Reference、ActiveSupport::Dependencies::Blamable 等。
6.7. 初始化期间的自动加载
在 Rails 6.0 之后,在 to_prepare 块之外初始化期间自动加载可重载常量并得到这些常量被卸载的应用程序会收到此警告:
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。
由 once 自动加载器管理的常量可以在初始化期间自动加载,并且可以正常使用,无需 to_prepare 块。然而,once 自动加载器现在已提前设置以支持这一点。如果应用程序有自定义变形,并且 once 自动加载器应该知道它们,您需要将 config/initializers/inflections.rb 中的代码移动到 config/application.rb 中的应用程序类定义主体中:
module MyApp
class Application < Rails::Application
# ...
ActiveSupport::Inflector.inflections(:en) do |inflect|
inflect.acronym "HTML"
end
end
end
6.8. 配置 config.autoload_once_paths 的能力
config.autoload_once_paths 可以在 config/application.rb 中定义的应用程序类主体中设置,也可以在 config/environments/* 中的环境配置中设置。
类似地,引擎可以在引擎类的类主体中或在环境的配置中配置该集合。
之后,集合被冻结,您可以从这些路径自动加载。特别是,您可以在初始化期间从那里自动加载。它们由 Rails.autoloaders.once 自动加载器管理,该加载器不重新加载,只自动加载/预加载。
如果您在环境配置处理后配置此设置并收到 FrozenError,请直接移动代码。
6.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"
6.10. 密钥生成器摘要类更改需要 cookie 旋转器
密钥生成器的默认摘要类正在从 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
6.11. ActiveSupport::Digest 的摘要类更改为 SHA256
ActiveSupport::Digest 的默认摘要类正在从 SHA1 更改为 SHA256。这会对 Etag 等更改和缓存键产生影响。更改这些键可能会影响缓存命中率,因此在升级到新哈希时要小心并注意这一点。
6.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 能够读取两种格式,因此在升级过程中缓存不会失效。
6.13. Active Storage 视频预览图像生成
视频预览图像生成现在使用 FFmpeg 的场景变化检测来生成更有意义的预览图像。以前会使用视频的第一帧,如果视频从黑色淡入,这会导致问题。此更改需要 FFmpeg v3.4+。
6.14. Active Storage 默认变体处理器更改为 :vips
对于新应用程序,图像转换将使用 libvips 而不是 ImageMagick。这将减少生成变体所需的时间以及 CPU 和内存使用量,从而提高依赖 Active Storage 服务图像的应用程序的响应时间。
:mini_magick 选项并未弃用,因此继续使用它没有问题。
要将现有应用程序迁移到 libvips,请设置:
Rails.application.config.active_storage.variant_processor = :vips
然后您需要将现有图像转换代码更改为 image_processing 宏,并用 libvips 的选项替换 ImageMagick 的选项。
6.14.1. 将 resize 替换为 resize_to_limit
- variant(resize: "100x")
+ variant(resize_to_limit: [100, nil])
如果您不这样做,当您切换到 vips 时,您将看到此错误:no implicit conversion to float from string。
6.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。
6.14.3. 限制您的裁剪值
Vips 在裁剪方面比 ImageMagick 更严格:
- 如果
x和/或y是负值,它将不会裁剪。例如:[-10, -10, 100, 100] - 如果位置(
x或y)加上裁剪尺寸(width,height)大于图像,它将不会裁剪。例如:一张 125x125 的图像和裁剪[50, 50, 100, 100]
如果您在迁移到 vips 时不这样做,您将看到以下错误:extract_area: bad extract area
6.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]])
6.14.5. 移除任何基于 EXIF 的旋转
Vips 在处理变体时会自动使用 EXIF 值旋转图像。如果您之前存储了用户上传照片的旋转值以使用 ImageMagick 应用旋转,您必须停止这样做:
- variant(format: :jpg, rotate: rotation_value)
+ variant(format: :jpg)
6.14.6. 将 monochrome 替换为 colourspace
Vips 使用不同的选项来制作单色图像:
- variant(monochrome: true)
+ variant(colourspace: "b-w")
6.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 })
6.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 %>
6.15. Rails 版本现在包含在 Active Record schema dump 中
Rails 7.0 更改了一些列类型的默认值。为了避免从 6.1 升级到 7.0 的应用程序使用新的 7.0 默认值加载当前模式,Rails 现在在模式转储中包含了框架的版本。
在 Rails 7.0 中首次加载模式之前,请务必运行 bin/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
# ...
end
首次使用 Rails 7.0 转储模式时,您会看到该文件发生了许多更改,包括一些列信息。请务必查看新的模式文件内容并将其提交到您的存储库。
7. 从 Rails 6.0 升级到 Rails 6.1
有关 Rails 6.1 所做更改的更多信息,请参阅发布说明。
7.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")
7.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 }
7.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) # => This method now accepts 2 arguments instead of 1
Rails.logger.info("Book couldn't be #{callback_name}d")
end
end
7.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 之前加载它。
7.5. 从 HTTP 重定向到 HTTPS 现在将使用 308 HTTP 状态码
ActionDispatch::SSL 在将非 GET/HEAD 请求从 HTTP 重定向到 HTTPS 时使用的默认 HTTP 状态码已更改为 https://tools.ietf.org/html/rfc7538 中定义的 308。
7.6. Active Storage 现在需要 Image Processing
在 Active Storage 中处理变体时,现在需要捆绑 image_processing gem,而不是直接使用 mini_magick。Image Processing 默认配置为在后台使用 mini_magick,因此最简单的升级方法是用 image_processing gem 替换 mini_magick 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])
7.7. 新的 ActiveModel::Error 类
错误现在是新的 ActiveModel::Error 类的实例,API 发生了变化。其中一些更改可能会根据您处理错误的方式引发错误,而另一些则会打印弃用警告,需要为 Rails 7.0 修复。
有关此更改和 API 更改的更多详细信息,请参阅此 PR。
8. 从 Rails 5.2 升级到 Rails 6.0
有关 Rails 6.0 所做更改的更多信息,请参阅发布说明。
8.1. 使用 Webpacker
Webpacker 是 Rails 6 的默认 JavaScript 编译器。但如果您正在升级应用程序,它默认不会激活。如果您想使用 Webpacker,请将其包含在 Gemfile 中并安装:
gem "webpacker"
$ bin/rails webpacker:install
8.2. 强制 SSL
控制器上的 force_ssl 方法已弃用,并将在 Rails 6.1 中移除。建议您启用config.force_ssl以在整个应用程序中强制执行 HTTPS 连接。如果您需要从重定向中排除某些端点,可以使用config.ssl_options来配置该行为。
8.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。
8.4. 所有 npm 包都已移至 @rails 范围
如果您之前通过 npm/yarn 加载了任何 actioncable、activestorage 或 rails-ujs 包,您必须在将它们升级到 6.0.0 之前更新这些依赖项的名称:
actioncable → @rails/actioncable
activestorage → @rails/activestorage
rails-ujs → @rails/ujs
8.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 = myLoggerActionCable.startDebugging()和ActionCable.stopDebugging()方法已被移除,并由属性ActionCable.logger.enabled取代。如果您正在使用这些方法,您将需要进行这些更改:- ActionCable.startDebugging() + ActionCable.logger.enabled = true- ActionCable.stopDebugging() + ActionCable.logger.enabled = false
8.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"
8.7. 新的 config.hosts 设置
Rails 现在出于安全目的有一个新的 config.hosts 设置。此设置在开发环境中默认为 localhost。如果您在开发环境中使用其他域,您需要像这样允许它们:
# config/environments/development.rb
config.hosts << "dev.myapp.com"
config.hosts << /[a-z0-9-]+\.myapp\.com/ # Optionally, regexp is allowed as well
对于其他环境,config.hosts 默认为空,这意味着 Rails 根本不会验证主机。如果您想在生产环境中验证它,可以选择添加它们。
8.8. 自动加载
Rails 6 的默认配置:
# config/application.rb
config.load_defaults 6.0
在 CRuby 上启用 zeitwerk 自动加载模式。在这种模式下,自动加载、重新加载和预加载由Zeitwerk管理。
如果您使用的是以前 Rails 版本的默认值,您可以像这样启用 zeitwerk:
# config/application.rb
config.autoloader = :zeitwerk
8.8.1. 公共 API
通常,应用程序不需要直接使用 Zeitwerk 的 API。Rails 根据现有约定进行设置:config.autoload_paths、config.cache_classes 等。
虽然应用程序应该坚持使用该接口,但实际的 Zeitwerk 加载器对象可以通过以下方式访问:
Rails.autoloaders.main
如果您需要预加载单表继承 (STI) 类或配置自定义变形器(例如),这可能会很有用。
8.8.2. 项目结构
如果被升级的应用程序正确自动加载,则项目结构应该已经大部分兼容。
然而,classic 模式从缺失的常量名称推断文件名(underscore),而 zeitwerk 模式从文件名推断常量名称(camelize)。这些辅助函数并不总是相互反转的,特别是在涉及首字母缩写词时。例如,"FOO".underscore 是 "foo",但 "foo".camelize 是 "Foo",而不是 "FOO"。
兼容性可以使用 zeitwerk:check 任务进行检查:
$ bin/rails zeitwerk:check
Hold on, I am eager loading the application.
All is good!
8.8.3. require_dependency
所有已知的 require_dependency 用例都已消除,您应该 grep 项目并将其删除。
如果您的应用程序使用单表继承,请参阅自动加载和重新加载常量(Zeitwerk 模式)指南的单表继承部分。
8.8.4. 类和模块定义中的限定名称
您现在可以在类和模块定义中可靠地使用常量路径:
# Autoloading in this class' body matches Ruby semantics now.
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
8.8.5. Concerns
您可以从标准结构中自动加载和预加载,例如:
app/models
app/models/concerns
在这种情况下,app/models/concerns 被假定为一个根目录(因为它属于自动加载路径),并且它被忽略为命名空间。因此,app/models/concerns/foo.rb 应该定义 Foo,而不是 Concerns::Foo。
Concerns:: 命名空间在经典自动加载器中作为实现的一个副作用而起作用,但这并不是一个真正预期的行为。使用 Concerns:: 的应用程序需要重命名这些类和模块才能在 zeitwerk 模式下运行。
8.8.6. 将 app 放在自动加载路径中
有些项目希望 app/api/base.rb 之类的文件定义 API::Base,并在 classic 模式下将 app 添加到自动加载路径中以实现此目的。由于 Rails 会自动将 app 的所有子目录添加到自动加载路径中,我们又遇到了嵌套根目录的情况,因此该设置不再有效。这与我们上面解释的 concerns 类似。
如果您想保留该结构,您需要在一个初始化程序中从自动加载路径中删除该子目录:
ActiveSupport::Dependencies.autoload_paths.delete("#{Rails.root}/app/api")
8.8.7. 自动加载常量和显式命名空间
如果命名空间在文件中定义,如下面的 Hotel:
app/models/hotel.rb # Defines Hotel.
app/models/hotel/pricing.rb # Defines Hotel::Pricing.
Hotel 常量必须使用 class 或 module 关键字设置。例如:
class Hotel
end
是好的。
替代方法,例如:
Hotel = Class.new
或
Hotel = Struct.new
将不起作用,找不到像 Hotel::Pricing 这样的子对象。
此限制仅适用于显式命名空间。不定义命名空间的类和模块可以使用这些习语定义。
8.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。
8.8.9. Spring 和 test 环境
如果代码发生变化,Spring 会重新加载应用程序代码。在 test 环境中,您需要启用重新加载才能使其工作:
# config/environments/test.rb
config.cache_classes = false
否则你会得到这个错误:
reloading is disabled because config.cache_classes is true
8.8.10. Bootsnap
Bootsnap 应至少为 1.4.2 版本。
此外,如果运行 Ruby 2.5,由于解释器中的错误,Bootsnap 需要禁用 iseq 缓存。在这种情况下,请确保至少依赖 Bootsnap 1.4.4。
8.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 的工作和内存消耗,因为它不需要为这些目录构建索引。
8.8.12. 线程安全
在经典模式下,常量自动加载不是线程安全的,尽管 Rails 有锁定机制,例如在开发环境中启用自动加载时使 Web 请求线程安全。
在 zeitwerk 模式下,常量自动加载是线程安全的。例如,您现在可以在由 runner 命令执行的多线程脚本中自动加载。
8.8.13. config.autoload_paths 中的通配符
请注意以下配置:
config.autoload_paths += Dir["#{config.root}/lib/**/"]
config.autoload_paths 的每个元素都应表示顶级命名空间 (Object),因此它们不能嵌套(除了上面解释的 concerns 目录)。
要解决此问题,只需删除通配符:
config.autoload_paths << "#{config.root}/lib"
8.8.14. 预加载和自动加载是一致的
在 classic 模式下,如果 app/models/foo.rb 定义了 Bar,您将无法自动加载该文件,但预加载将有效,因为它会盲目地递归加载文件。如果您首先测试预加载,则执行可能会在稍后自动加载时失败,这可能是错误源。
在 zeitwerk 模式下,两种加载模式都是一致的,它们会在相同的文件中失败并出错。
8.8.15. 如何在 Rails 6 中使用经典自动加载器
应用程序可以加载 Rails 6 默认值,但仍可以通过以下方式设置 config.autoloader 来使用经典自动加载器:
# config/application.rb
config.load_defaults 6.0
config.autoloader = :classic
在 Rails 6 应用程序中使用经典自动加载器时,建议将开发环境中的并发级别设置为 1,对于 Web 服务器和后台处理器,这是由于线程安全问题。
8.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 的默认配置,将文件分配给附件集合会替换现有文件,而不是追加。这与 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 中移除。
8.10. 自定义异常处理应用程序
无效的 Accept 或 Content-Type 请求头现在将引发异常。默认的config.exceptions_app专门处理此错误并进行补偿。自定义异常应用程序也需要处理此错误,否则此类请求将导致 Rails 使用回退异常应用程序,该应用程序返回 500 Internal Server Error。
9. 从 Rails 5.1 升级到 Rails 5.2
有关 Rails 5.2 所做更改的更多信息,请参阅发布说明。
9.1. Bootsnap
Rails 5.2 在新生成的应用程序的 Gemfile中添加了 bootsnap gem。app:update 命令在 boot.rb 中设置它。如果您想使用它,请将其添加到 Gemfile 中:
# Reduces boot times through caching; required in config/boot.rb
gem "bootsnap", require: false
否则,请更改 boot.rb 以不使用 bootsnap。
9.2. 签名或加密 cookie 中的有效期现在嵌入在 cookie 值中
为了提高安全性,Rails 现在还将有效期信息嵌入到加密或签名 cookie 值中。
此新的嵌入信息使这些 cookie 与早于 5.2 的 Rails 版本不兼容。
如果您需要 5.1 及更早版本读取您的 cookie,或者您仍在验证您的 5.2 部署并希望能够回滚,请将 Rails.application.config.action_dispatch.use_authenticated_cookie_encryption 设置为 false。
10. 从 Rails 5.0 升级到 Rails 5.1
有关 Rails 5.1 所做更改的更多信息,请参阅发布说明。
10.1. 顶级 HashWithIndifferentAccess 已软弃用
如果您的应用程序使用顶级 HashWithIndifferentAccess 类,您应该慢慢将代码更改为使用 ActiveSupport::HashWithIndifferentAccess。
它只是软弃用,这意味着您的代码目前不会损坏,也不会显示弃用警告,但此常量将在未来移除。
此外,如果您有包含此类对象转储的旧 YAML 文档,您可能需要重新加载并重新转储它们,以确保它们引用正确的常量,并且将来加载它们不会损坏。
10.2. application.secrets 现在加载所有键作为符号
如果您的应用程序在 config/secrets.yml 中存储嵌套配置,所有键现在都作为符号加载,因此应更改使用字符串的访问方式。
从
Rails.application.secrets[:smtp_settings]["address"]
到
Rails.application.secrets[:smtp_settings][:address]
10.3. 删除了 render 中对 :text 和 :nothing 的弃用支持
如果您的控制器使用 render :text,它们将不再起作用。使用 MIME 类型为 text/plain 渲染文本的新方法是使用 render :plain。
同样,render :nothing 也被移除,您应该使用 head 方法发送仅包含标头的响应。例如,head :ok 发送一个 200 响应,没有正文可渲染。
10.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)
11. 从 Rails 4.2 升级到 Rails 5.0
有关 Rails 5.0 所做更改的更多信息,请参阅发布说明。
11.1. 需要 Ruby 2.2.2+
从 Ruby on Rails 5.0 开始,Ruby 2.2.2+ 是唯一受支持的 Ruby 版本。在继续之前,请确保您使用的是 Ruby 2.2.2 或更高版本。
11.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
然后确保您的所有模型都继承自它。
11.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。
11.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。
11.5. Rails 控制器测试
11.5.1. 将一些辅助方法提取到 rails-controller-testing
assigns 和 assert_template 已提取到 rails-controller-testing gem。要在控制器测试中继续使用这些方法,请将 gem "rails-controller-testing" 添加到您的 Gemfile。
如果您使用 RSpec 进行测试,请参阅 gem 文档中所需的额外配置。
11.5.2. 上传文件时的新行为
如果您在测试中使用 ActionDispatch::Http::UploadedFile 上传文件,您需要更改为使用类似的 Rack::Test::UploadedFile 类。
有关更多详细信息,请参阅 #26404。
11.6. 生产环境启动后禁用自动加载
默认情况下,生产环境启动后将禁用自动加载。
预加载应用程序是启动过程的一部分,因此顶级常量很好,并且仍然会自动加载,无需 require 它们的文件。
在更深层(仅在运行时执行,如常规方法体)的常量也很好,因为定义它们的文件将在启动时已预加载。
对于绝大多数应用程序,此更改无需任何操作。但如果您的应用程序在生产环境中运行时需要自动加载(这种情况非常罕见),请将 Rails.application.config.enable_dependency_loading 设置为 true。
11.7. XML 序列化
ActiveModel::Serializers::Xml 已从 Rails 提取到 activemodel-serializers-xml gem。要在您的应用程序中继续使用 XML 序列化,请将 gem "activemodel-serializers-xml" 添加到您的 Gemfile。
11.8. 移除对旧版 mysql 数据库适配器的支持
Rails 5 移除了对旧版 mysql 数据库适配器的支持。大多数用户应该能够使用 mysql2 代替。当我们找到维护者时,它将转换为一个单独的 gem。
11.9. 移除对 Debugger 的支持
debugger 不受 Rails 5 所需的 Ruby 2.2 支持。请改用 byebug。
11.10. 使用 bin/rails 运行任务和测试
Rails 5 增加了通过 bin/rails 而不是 rake 运行任务和测试的功能。通常这些更改与 rake 并行,但有些已完全移植过来。
要使用新的测试运行器,只需键入 bin/rails test。
rake dev:cache 现在是 bin/rails dev:cache。
在您的应用程序根目录中运行 bin/rails 以查看可用命令列表。
11.11. ActionController::Parameters 不再继承自 HashWithIndifferentAccess
现在,在您的应用程序中调用 params 将返回一个对象而不是一个哈希。如果您的参数已允许,则无需进行任何更改。如果您使用 map 和其他依赖于无论 permitted? 都能读取哈希的方法,则需要升级您的应用程序,首先允许,然后转换为哈希。
params.permit([:proceed_to, :return_to]).to_h
11.12. protect_from_forgery 现在默认设置为 prepend: false
protect_from_forgery 默认为 prepend: false,这意味着它将在您在应用程序中调用它的位置插入到回调链中。如果您希望 protect_from_forgery 始终首先运行,那么您应该将应用程序更改为使用 protect_from_forgery prepend: true。
11.13. 默认模板处理程序现在是 RAW
没有模板处理程序扩展名的文件将使用原始处理程序呈现。以前 Rails 会使用 ERB 模板处理程序呈现文件。
如果您不希望您的文件通过原始处理程序处理,您应该为您的文件添加一个扩展名,以便适当的模板处理程序可以解析它。
11.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/* %>
11.15. ActionView::Helpers::RecordTagHelper 移至外部 gem (record_tag_helper)
content_tag_for 和 div_for 已被移除,转而只使用 content_tag。要继续使用旧方法,请将 record_tag_helper gem 添加到您的 Gemfile
gem "record_tag_helper", "~> 1.0"
有关更多详细信息,请参阅 #18411。
11.16. 移除了对 protected_attributes Gem 的支持
Rails 5 不再支持 protected_attributes gem。
11.17. 移除了对 activerecord-deprecated_finders gem 的支持
Rails 5 不再支持 activerecord-deprecated_finders gem。
11.18. ActiveSupport::TestCase 默认测试顺序现在是随机的
当您的应用程序中运行测试时,默认顺序现在是 :random 而不是 :sorted。使用以下配置选项将其设置回 :sorted。
# config/environments/test.rb
Rails.application.configure do
config.active_support.test_order = :sorted
end
11.19. ActionController::Live 变为 Concern
如果您在控制器中包含的另一个模块中包含 ActionController::Live,那么您也应该使用 ActiveSupport::Concern 扩展该模块。或者,您可以使用 self.included 钩子在包含 StreamingSupport 后将 ActionController::Live 直接包含到控制器中。
这意味着如果您的应用程序以前有自己的流媒体模块,以下代码将在生产环境中中断
# This is a work-around for streamed controllers performing authentication with Warden/Devise.
# See https://github.com/plataformatec/devise/issues/2332
# Authenticating in the router is another solution as suggested in that issue
class StreamingSupport
include ActionController::Live # this won't work in production for Rails 5
# extend ActiveSupport::Concern # unless you uncomment this line.
def process(name)
super(name)
rescue ArgumentError => e
if e.message == "uncaught throw :warden"
throw :warden
else
raise e
end
end
end
11.20. 新的框架默认值
11.20.1. Active Record belongs_to 默认需要选项
如果关联不存在,belongs_to 现在默认会触发验证错误。
这可以通过 optional: true 为每个关联关闭。
此默认值将在新应用程序中自动配置。如果现有应用程序想要添加此功能,则需要在初始化器中打开它
config.active_record.belongs_to_required_by_default = true
此配置默认对您的所有模型全局有效,但您可以根据每个模型进行覆盖。这应该有助于您迁移所有模型,使其关联默认必填。
class Book < ApplicationRecord
# model is not yet ready to have its association required by default
self.belongs_to_required_by_default = false
belongs_to(:author)
end
class Car < ApplicationRecord
# model is ready to have its association required by default
self.belongs_to_required_by_default = true
belongs_to(:pilot)
end
11.20.2. 每个表单的 CSRF 令牌
Rails 5 现在支持每个表单的 CSRF 令牌,以缓解针对使用 JavaScript 创建的表单的代码注入攻击。启用此选项后,您的应用程序中的每个表单都将拥有自己的 CSRF 令牌,该令牌特定于该表单的操作和方法。
config.action_controller.per_form_csrf_tokens = true
11.20.3. 带有来源检查的伪造保护
您现在可以将应用程序配置为检查 HTTP Origin 标头是否应与站点的来源进行检查,作为额外的 CSRF 防御。在您的配置中将以下内容设置为 true
config.action_controller.forgery_protection_origin_check = true
11.20.4. 允许配置 Action Mailer 队列名称
默认的 mailer 队列名称是 mailers。此配置选项允许您全局更改队列名称。在您的配置中设置以下内容
config.action_mailer.deliver_later_queue_name = :new_queue_name
11.20.5. 支持 Action Mailer 视图中的片段缓存
在您的配置中设置 config.action_mailer.perform_caching 以确定您的 Action Mailer 视图是否应支持缓存。
config.action_mailer.perform_caching = true
11.20.6. 配置 db:structure:dump 的输出
如果您使用 schema_search_path 或其他 PostgreSQL 扩展,您可以控制模式的转储方式。设置为 :all 以生成所有转储,或设置为 :schema_search_path 以从模式搜索路径生成。
config.active_record.dump_schemas = :all
11.20.7. 配置 SSL 选项以启用带子域的 HSTS
在您的配置中设置以下内容以在使用子域时启用 HSTS
config.ssl_options = { hsts: { subdomains: true } }
11.20.8. 保留接收者时区
当使用 Ruby 2.4 时,在调用 to_time 时可以保留接收者的时区。
ActiveSupport.to_time_preserves_timezone = false
11.21. JSON/JSONB 序列化更改
在 Rails 5.0 中,JSON/JSONB 属性的序列化和反序列化方式发生了变化。现在,如果您将列设置为 String,Active Record 将不再将该字符串转换为 Hash,而只会返回字符串。这不仅限于与模型交互的代码,还影响 db/schema.rb 中的 :default 列设置。建议您不要将列设置为 String,而是传递一个 Hash,它将自动转换为 JSON 字符串。
12. 从 Rails 4.1 升级到 Rails 4.2
12.1. Web 控制台
首先,将 gem "web-console", "~> 2.0" 添加到您的 Gemfile 中的 :development 组,然后运行 bundle install(升级 Rails 时不会包含它)。安装后,您只需将控制台辅助方法(即 <%= console %>)放入任何您想要启用它的视图中。在开发环境中查看的任何错误页面也将提供控制台。
12.2. Responders
respond_with 和类级别的 respond_to 方法已提取到 responders gem。要使用它们,只需将 gem "responders", "~> 2.0" 添加到您的 Gemfile。如果没有在您的依赖项中包含 responders gem,对 respond_with 和 respond_to(再次,在类级别)的调用将不再起作用。
# 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。
12.3. 事务回调中的错误处理
目前,Active Record 会抑制 after_rollback 或 after_commit 回调中引发的错误,只将它们打印到日志中。在下一个版本中,这些错误将不再被抑制。相反,错误将像其他 Active Record 回调中一样正常传播。
当您定义 after_rollback 或 after_commit 回调时,您将收到有关此即将发生的更改的弃用警告。当您准备好后,可以通过将以下配置添加到 config/application.rb 来选择新行为并移除弃用警告
config.active_record.raise_in_transactional_callbacks = true
12.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 # or `:random` if you prefer
end
12.5. 序列化属性
当使用自定义编码器(例如 serialize :metadata, JSON)时,将 nil 分配给序列化属性会将其作为 NULL 保存到数据库中,而不是通过编码器传递 nil 值(例如,当使用 JSON 编码器时为 "null")。
12.6. 生产日志级别
在 Rails 5 中,生产环境的默认日志级别将更改为 :debug(从 :info 更改)。为了保留当前默认值,请将以下行添加到您的 production.rb 中
# Set to `:info` to match the current default, or set to `:debug` to opt-into
# the future default.
config.log_level = :info
12.7. Rails 模板中的 after_bundle
如果您有一个将所有文件添加到版本控制的 Rails 模板,它将无法添加生成的 binstubs,因为它在 Bundler 之前执行
# 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
12.8. Rails HTML Sanitizer
您的应用程序中用于清理 HTML 片段现在有了新选择。古老的 html-scanner 方法现在已正式弃用,转而使用 Rails HTML Sanitizer。
这意味着 sanitize、sanitize_css、strip_tags 和 strip_links 方法由新实现支持。
这个新的清理器内部使用 Loofah。Loofah 又使用 Nokogiri,它封装了用 C 和 Java 编写的 XML 解析器,因此无论您运行哪个 Ruby 版本,清理都应该更快。
新版本更新了 sanitize,因此它可以接受 Loofah::Scrubber 以进行强大的清理。 此处查看一些擦洗器的示例。
还添加了两个新的擦洗器:PermitScrubber 和 TargetScrubber。阅读 gem 的自述文件以获取更多信息。
PermitScrubber 和 TargetScrubber 的文档解释了如何完全控制何时以及如何剥离元素。
如果您的应用程序需要使用旧的清理器实现,请在您的 Gemfile 中包含 rails-deprecated_sanitizer
gem "rails-deprecated_sanitizer"
12.9. Rails DOM 测试
TagAssertions 模块(包含 assert_tag 等方法)已被弃用,转而使用 SelectorAssertions 模块中的 assert_select 方法,该模块已提取到 rails-dom-testing gem 中。
12.10. 遮蔽的真实性令牌
为了缓解 SSL 攻击,form_authenticity_token 现在被遮蔽,使其随每个请求而变化。因此,通过解密和解密来验证令牌。因此,任何依赖静态会话 CSRF 令牌来验证非 Rails 表单请求的策略都必须考虑到这一点。
12.11. Action Mailer
以前,在邮件程序类上调用邮件程序方法将导致直接执行相应的实例方法。随着 Active Job 和 #deliver_later 的引入,情况不再如此。在 Rails 4.2 中,实例方法的调用被推迟到调用 deliver_now 或 deliver_later 为止。例如
class Notifier < ActionMailer::Base
def notify(user)
puts "Called"
mail(to: user.email)
end
end
mail = Notifier.notify(user) # Notifier#notify is not yet called at this point
mail = mail.deliver_now # Prints "Called"
对于大多数应用程序来说,这不应该导致任何明显的差异。但是,如果您需要同步执行一些非邮件程序方法,并且您以前依赖同步代理行为,您应该将它们直接定义为邮件程序类上的类方法。
class Notifier < ActionMailer::Base
def self.broadcast_notifications(users, ...)
users.each { |user| Notifier.notify(user, ...) }
end
end
12.12. 外键支持
迁移 DSL 已扩展以支持外键定义。如果您一直在使用 Foreigner gem,您可能需要考虑将其移除。请注意,Rails 的外键支持是 Foreigner 的一个子集。这意味着并非每个 Foreigner 定义都可以完全被其 Rails 迁移 DSL 对应项替换。
迁移过程如下
- 从
Gemfile中移除gem "foreigner"。 - 运行
bundle install。 - 运行
bin/rake db:schema:dump。 - 确保
db/schema.rb包含所有必要选项的每个外键定义。
13. 从 Rails 4.0 升级到 Rails 4.1
13.1. 远程 <script> 标签的 CSRF 保护
或者,“我的测试怎么失败了!!?”或“我的 <script> 小部件坏了!!”
跨站点请求伪造 (CSRF) 保护现在也涵盖了带有 JavaScript 响应的 GET 请求。这可以防止第三方站点远程引用您的 JavaScript,并使用 <script> 标签提取敏感数据。
这意味着您的功能和集成测试使用
get :index, format: :js
现在将触发 CSRF 保护。切换到
xhr :get, :index, format: :js
显式测试 XmlHttpRequest。
您自己的 <script> 标签也被视为跨源并默认阻止。如果您确实打算从 <script> 标签加载 JavaScript,您现在必须显式跳过这些操作的 CSRF 保护。
13.2. Spring
如果您想使用 Spring 作为应用程序预加载器,您需要
- 将
gem "spring", group: :development添加到您的Gemfile。 - 使用
bundle install安装 spring。 - 使用
bundle exec spring binstub生成 Spring binstub。
用户定义的 rake 任务默认将在 development 环境中运行。如果您希望它们在其他环境中运行,请查阅 Spring README。
13.3. config/secrets.yml
如果您想使用新的 secrets.yml 约定来存储您的应用程序的秘密,您需要
在您的
config文件夹中创建一个secrets.yml文件,其中包含以下内容development: secret_key_base: test: secret_key_base: production: secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>使用您现有
secret_token.rb初始化程序中的secret_key_base,为在生产环境中运行 Rails 应用程序的任何用户设置SECRET_KEY_BASE环境变量。或者,您可以简单地将secret_token.rb初始化程序中现有的secret_key_base复制到secrets.yml的production部分,替换<%= ENV["SECRET_KEY_BASE"] %>。移除
secret_token.rb初始化程序。使用
rake secret为development和test部分生成新密钥。重启您的服务器。
13.4. 测试辅助程序的变化
如果您的测试辅助程序包含对 ActiveRecord::Migration.check_pending! 的调用,则可以将其移除。当您 require "rails/test_help" 时,此检查现在会自动完成,尽管将此行留在您的辅助程序中不会造成任何损害。
13.5. Cookies 序列化器
在 Rails 4.1 之前创建的应用程序使用 Marshal 将 cookie 值序列化到签名和加密的 cookie jar 中。如果您想在应用程序中使用新的基于 JSON 的格式,您可以添加一个初始化程序文件,其中包含以下内容
Rails.application.config.action_dispatch.cookies_serializer = :hybrid
这将透明地将您现有的 Marshal 序列化 cookie 迁移到新的基于 JSON 的格式。
当使用 :json 或 :hybrid 序列化器时,您应该注意并非所有 Ruby 对象都可以序列化为 JSON。例如,Date 和 Time 对象将序列化为字符串,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 会话存储,这也适用于 session 和 flash 哈希。
13.6. Flash 结构更改
Flash 消息键被规范化为字符串。它们仍然可以使用符号或字符串访问。循环遍历 flash 总是会生成字符串键
flash["string"] = "a string"
flash[:symbol] = "a symbol"
# Rails < 4.1
flash.keys # => ["string", :symbol]
# Rails >= 4.1
flash.keys # => ["string", "symbol"]
确保您将 Flash 消息键与字符串进行比较。
13.7. JSON 处理中的更改
Rails 4.1 中与 JSON 处理相关的几个主要更改。
13.7.1. MultiJSON 移除
MultiJSON 已达到其生命周期结束,并已从 Rails 中移除。
如果您的应用程序当前直接依赖 MultiJSON,您有几个选择
将 'multi_json' 添加到您的
Gemfile。请注意,这将来可能会停止工作通过使用
obj.to_json和JSON.parse(str)替换 MultiJSON。
不要简单地将 MultiJson.load 替换为 JSON.load,因为该 API 用于反序列化任意 Ruby 对象,并且通常不安全。优先使用 JSON.parse。
13.7.2. JSON gem 兼容性
历史上,Rails 在 JSON gem 方面存在一些兼容性问题。在 Rails 应用程序中使用 JSON.generate 和 JSON.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)
=> "\"#<FooBar:0x007fa80a481610>\""
13.7.3. 新的 JSON 编码器
Rails 4.1 中的 JSON 编码器已重写,以利用 JSON gem。对于大多数应用程序来说,这应该是一个透明的更改。但是,作为重写的一部分,以下功能已从编码器中移除
- 循环数据结构检测
- 支持
encode_json钩子 - 将
BigDecimal对象编码为数字而不是字符串的选项
如果您的应用程序依赖于这些功能之一,您可以通过将 activesupport-json_encoder gem 添加到您的 Gemfile 来恢复它们。
13.7.4. 时间对象的 JSON 表示
具有时间组件的对象(Time、DateTime、ActiveSupport::TimeWithZone)的 #as_json 现在默认返回毫秒精度。如果您需要保持没有毫秒精度的旧行为,请在初始化程序中设置以下内容
ActiveSupport::JSON::Encoding.time_precision = 0
13.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)。
有关更多详细信息,请参阅 此拉取请求。
13.9. Active Record fixture 中定义的方法
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
13.10. I18n 强制执行可用区域设置
Rails 4.1 现在将 I18n 选项 enforce_available_locales 默认为 true。这意味着它将确保传递给它的所有区域设置都必须在 available_locales 列表中声明。
要禁用它(并允许 I18n 接受任何区域设置选项),请将以下配置添加到您的应用程序中
config.i18n.enforce_available_locales = false
请注意,此选项作为安全措施添加,以确保用户输入不能用作区域设置信息,除非事先已知。因此,除非您有充分理由这样做,否则建议不要禁用此选项。
13.11. 在 Relation 上调用的变更方法
Relation 不再具有 #map! 和 #delete_if 等变更方法。在使用这些方法之前,通过调用 #to_a 转换为 Array。
它旨在防止直接在 Relation 上调用变更方法的代码中出现奇怪的错误和混淆。
# Instead of this
Author.where(name: "Hank Moody").compact!
# Now you have to do this
authors = Author.where(name: "Hank Moody").to_a
authors.compact!
13.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'
要获得以前的行为,需要使用 unscoped、unscope、rewhere 或 except 显式移除 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'
13.13. 从字符串渲染内容
Rails 4.1 为 render 引入了 :plain、:html 和 :body 选项。这些选项现在是渲染基于字符串内容的优选方式,因为它允许您指定要发送的响应内容类型。
render :plain会将内容类型设置为text/plainrender :html会将内容类型设置为text/htmlrender :body将不设置内容类型头。
从安全角度来看,如果您不希望响应正文中包含任何标记,则应使用 render :plain,因为大多数浏览器会为您转义响应中的不安全内容。
我们将在未来版本中弃用 render :text 的使用。因此,请开始使用更精确的 :plain、:html 和 :body 选项。使用 render :text 可能会带来安全风险,因为内容将以 text/html 形式发送。
13.14. PostgreSQL JSON 和 hstore 数据类型
Rails 4.1 会将 json 和 hstore 列映射到字符串键的 Ruby Hash。在早期版本中,使用了 HashWithIndifferentAccess。这意味着不再支持符号访问。对于基于 json 或 hstore 列的 store_accessors 也是如此。请确保始终使用字符串键。
13.15. ActiveSupport::Callbacks 的显式块使用
Rails 4.1 现在期望在调用 ActiveSupport::Callbacks.set_callback 时传入一个显式块。此更改源于 ActiveSupport::Callbacks 在 4.1 版本中进行了大幅重写。
# Previously in Rails 4.0
set_callback :save, :around, ->(r, &block) { stuff; result = block.call; stuff }
# Now in Rails 4.1
set_callback :save, :around, ->(r, block) { stuff; result = block.call; stuff }
14. 从 Rails 3.2 升级到 Rails 4.0
如果您的应用程序当前是 Rails 3.2.x 之前的任何版本,您应该在尝试升级到 Rails 4.0 之前升级到 Rails 3.2。
以下更改旨在将您的应用程序升级到 Rails 4.0。
14.1. HTTP PATCH
Rails 4 现在使用 PATCH 作为更新的主要 HTTP 动词,当在 config/routes.rb 中声明 RESTful 资源时。update 动作仍在使用,并且 PUT 请求也将继续路由到 update 动作。因此,如果您只使用标准 RESTful 路由,则无需进行任何更改。
resources :users
<%= form_for @user do |f| %>
class UsersController < ApplicationController
def update
# No change needed; PATCH will be preferred, and PUT will still work.
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
# Change needed; form_for will try to use a non-existent PATCH route.
end
end
如果该操作未在公共 API 中使用,并且您可以自由更改 HTTP 方法,则可以将路由更新为使用 patch 而不是 put。
resources :users do
patch :update_name, on: :member
end
Rails 4 中对 /users/:id 的 PUT 请求会像今天一样路由到 update。因此,如果您有一个接收真实 PUT 请求的 API,它将正常工作。路由器还将 PATCH 请求路由到 /users/:id 到 update 操作。
如果该操作正在公共 API 中使用并且您无法更改所使用的 HTTP 方法,则可以将表单更新为使用 PUT 方法。
<%= form_for [ :update_name, @user ], method: :put do |f| %>
有关 PATCH 以及为什么进行此更改的更多信息,请参阅 Rails 博客上的此文章。
14.1.1. 关于媒体类型的一个注意事项
PATCH 动词的勘误表指定应将“diff”媒体类型与 PATCH 一起使用。一种这样的格式是 JSON Patch。虽然 Rails 不原生支持 JSON Patch,但添加支持足够容易。
# in your controller:
def update
respond_to do |format|
format.json do
# perform a partial update
@article.update params[:article]
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 是其中一个 gem,但它没有完全支持规范中的最后几项更改。
14.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)
14.3. vendor/plugins
Rails 4.0 不再支持从 vendor/plugins 加载插件。您必须通过将它们提取到 gem 并将其添加到 Gemfile 来替换任何插件。如果您选择不将它们制作成 gem,您可以将它们移动到(例如)lib/my_plugin/* 中,并在 config/initializers/my_plugin.rb 中添加适当的初始化程序。
14.4. Active Record
Rails 4.0 已从 Active Record 中移除了身份映射,原因是与关联存在一些不一致。如果您已在应用程序中手动启用它,则必须移除以下不再起作用的配置:
config.active_record.identity_map。集合关联中的
delete方法现在除了记录之外,还可以接收Integer或String参数作为记录 ID,与destroy方法非常相似。以前,它会为此类参数引发ActiveRecord::AssociationTypeMismatch。从 Rails 4.0 开始,delete会在删除记录之前自动尝试查找与给定 ID 匹配的记录。在 Rails 4.0 中,当列或表重命名时,相关的索引也会重命名。如果您有重命名索引的迁移,则不再需要它们。
Rails 4.0 已将
serialized_attributes和attr_readonly更改为仅限类方法。您不应使用实例方法,因为它现在已弃用。您应该将它们更改为使用类方法,例如self.serialized_attributes更改为self.class.serialized_attributes。当使用默认编码器时,将
nil分配给序列化属性会将其作为NULL保存到数据库中,而不是通过 YAML 传递nil值("--- \n...\n")。Rails 4.0 已移除
attr_accessible和attr_protected功能,转而使用 Strong Parameters。您可以使用 Protected Attributes gem 来平稳升级。如果您不使用 Protected Attributes,则可以移除与此 gem 相关的任何选项,例如
whitelist_attributes或mass_assignment_sanitizer选项。Rails 4.0 要求作用域使用可调用对象,例如 Proc 或 lambda
scope :active, where(active: true) # becomes 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(...)返回一个关系,而不是像旧查找器那样的数组。如果您需要一个Array,请使用where(...).to_a。这些等效方法可能不会执行与以前实现相同的 SQL。
要重新启用旧查找器,您可以使用 activerecord-deprecated_finders gem。
Rails 4.0 已将
has_and_belongs_to_many关系的默认连接表更改为从第二个表名中去除通用前缀。模型之间具有通用前缀的任何现有has_and_belongs_to_many关系必须使用join_table选项指定。例如class CatalogCategory < ActiveRecord::Base has_and_belongs_to_many :catalog_products, join_table: "catalog_categories_catalog_products" end class CatalogProduct < ActiveRecord::Base has_and_belongs_to_many :catalog_categories, join_table: "catalog_categories_catalog_products" end请注意,前缀也考虑了作用域,因此
Catalog::Category和Catalog::Product或Catalog::Category和CatalogProduct之间的关系需要类似地更新。
14.5. Active Resource
Rails 4.0 将 Active Resource 提取到其自己的 gem 中。如果您仍然需要该功能,您可以在 Gemfile 中添加 Active Resource gem。
14.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
14.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。这是因为在 Rails 4.x 中基于新的secret_key_base签名的 cookie 与 Rails 3.x 不向后兼容。您可以自由地保留现有的secret_token,不设置新的secret_key_base,并忽略弃用警告,直到您合理确定您的升级已完成。如果您依赖外部应用程序或 JavaScript 读取您的 Rails 应用程序的签名会话 cookie(或通用签名 cookie)的能力,则在解耦这些关注点之前不应设置
secret_key_base。如果已设置
secret_key_base,Rails 4.0 会加密基于 cookie 的会话的内容。Rails 3.x 签名但未加密基于 cookie 的会话的内容。签名 cookie 是“安全”的,因为它们被验证是由您的应用程序生成的并且是防篡改的。但是,内容可以被最终用户查看,加密内容消除了这个警告/问题,而不会显着降低性能。请阅读 拉取请求 #9978 以获取有关转向加密会话 cookie 的详细信息。
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_cachinggem 才能在控制器中使用caches_action,并添加actionpack-page_caching才能使用caches_page。Rails 4.0 已移除了 XML 参数解析器。如果您需要此功能,则需要添加
actionpack-xml_parsergem。Rails 4.0 更改了使用符号或返回 nil 的 proc 的默认
layout查找集。要获得“无布局”行为,请返回 false 而不是 nil。Rails 4.0 将默认的 memcached 客户端从
memcache-client更改为dalli。要升级,只需将gem "dalli"添加到您的Gemfile。Rails 4.0 弃用了控制器中的
dom_id和dom_class方法(它们在视图中正常)。您需要在需要此功能的控制器中包含ActionView::RecordIdentifier模块。Rails 4.0 弃用了
link_to辅助方法的:confirm选项。您应该改为依赖数据属性(例如data: { confirm: 'Are you sure?' })。此弃用也涉及基于此的辅助方法(例如link_to_if或link_to_unless)。Rails 4.0 更改了
assert_generates、assert_recognizes和assert_routing的工作方式。现在所有这些断言都引发Assertion而不是ActionController::RoutingError。如果定义了冲突的命名路由,Rails 4.0 会引发
ArgumentError。这可以通过显式定义的命名路由或resources方法触发。以下是两个与名为example_path的路由冲突的示例get "one" => "test#example", as: :example get "two" => "test#example", as: :exampleresources :examples get "clashing/:id" => "test#example", as: :example在第一种情况下,您只需避免为多个路由使用相同的名称。在第二种情况下,您可以使用
resources方法提供的only或except选项来限制创建的路由,如路由指南中所述。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" # becomes match "/" => "root#index", via: :get # or 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。请记住,您还必须从应用程序代码中移除对中间件的任何引用,例如
# Raise exception 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-Options设置为ALLOW-FROM ...或ALLOWALL。在 Rails 4.0 中,预编译资产不再自动从
vendor/assets和lib/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。
14.8. Active Support
Rails 4.0 移除了 ERB::Util#json_escape 的 j 别名,因为 j 已用于 ActionView::Helpers::JavaScriptHelper#escape_javascript。
14.8.1. 缓存
Rails 3.x 和 4.0 之间的缓存方法发生了变化。您应该更改缓存命名空间并使用冷缓存进行部署。
14.9. 辅助加载顺序
Rails 4.0 中,从多个目录加载辅助文件的顺序已更改。以前,它们会先收集然后按字母顺序排序。升级到 Rails 4.0 后,辅助文件将保留加载目录的顺序,并且只在每个目录内按字母顺序排序。除非您显式使用 helpers_path 参数,否则此更改只会影响从引擎加载辅助文件的方式。如果您依赖排序,则应检查升级后是否提供了正确的方法。如果您想更改引擎的加载顺序,可以使用 config.railties_order= 方法。
14.10. Active Record 观察者和 Action Controller 清理器
ActiveRecord::Observer 和 ActionController::Caching::Sweeper 已提取到 rails-observers gem。如果您需要这些功能,则需要添加 rails-observers gem。
14.11. sprockets-rails
assets:precompile:primary和assets:precompile:all已被移除。请改用assets:precompile。config.assets.compress选项应更改为config.assets.js_compressor,例如config.assets.js_compressor = :uglifier
14.12. sass-rails
- 带有两个参数的
asset-url已弃用。例如:asset-url("rails.png", image)变为asset-url("rails.png")。
15. 从 Rails 3.1 升级到 Rails 3.2
如果您的应用程序当前是 Rails 3.1.x 之前的任何版本,您应该在尝试更新到 Rails 3.2 之前升级到 Rails 3.1。
以下更改旨在将您的应用程序升级到最新的 Rails 3.2.x 版本。
15.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
15.2. config/environments/development.rb
您应该在开发环境中添加几个新的配置设置
# Raise exception on mass assignment protection for Active Record models
config.active_record.mass_assignment_sanitizer = :strict
# Log the query plan for queries taking more than this (works
# with SQLite, MySQL, and PostgreSQL)
config.active_record.auto_explain_threshold_in_seconds = 0.5
15.3. config/environments/test.rb
mass_assignment_sanitizer 配置设置也应添加到 config/environments/test.rb
# Raise exception on mass assignment protection for Active Record models
config.active_record.mass_assignment_sanitizer = :strict
15.4. vendor/plugins
Rails 3.2 弃用了 vendor/plugins,Rails 4.0 将完全移除它们。虽然作为 Rails 3.2 升级的一部分并非严格必要,但您可以开始通过将任何插件提取到 gem 并将其添加到您的 Gemfile 来替换它们。如果您选择不将它们制作成 gem,您可以将它们移动到(例如)lib/my_plugin/* 中,并在 config/initializers/my_plugin.rb 中添加适当的初始化程序。
15.5. Active Record
:dependent => :restrict 选项已从 belongs_to 中移除。如果您想在存在任何关联对象时阻止删除对象,您可以设置 :dependent => :destroy 并在检查任何关联对象的销毁回调中是否存在关联后返回 false。
16. 从 Rails 3.0 升级到 Rails 3.1
如果您的应用程序当前是 Rails 3.0.x 之前的任何版本,您应该在尝试更新到 Rails 3.1 之前升级到 Rails 3.0。
以下更改旨在将您的应用程序升级到 Rails 3.1.12(Rails 的最后一个 3.1.x 版本)。
16.1. Gemfile
对您的 Gemfile 进行以下更改。
gem "rails", "3.1.12"
gem "mysql2"
# Needed for the new asset pipeline
group :assets do
gem "sass-rails", "~> 3.1.7"
gem "coffee-rails", "~> 3.1.1"
gem "uglifier", ">= 1.0.3"
end
# jQuery is the default JavaScript library in Rails 3.1
gem "jquery-rails"
16.2. config/application.rb
资产管道需要以下补充
config.assets.enabled = true
config.assets.version = "1.0"
如果您的应用程序正在使用 "/assets" 路由作为资源,您可能需要更改用于资产的前缀以避免冲突
# Defaults to '/assets'
config.assets.prefix = "/asset-files"
16.3. config/environments/development.rb
移除 RJS 设置 config.action_view.debug_rjs = true。
如果您启用资产管道,请添加这些设置
# Do not compress assets
config.assets.compress = false
# Expands the lines which load the assets
config.assets.debug = true
16.4. config/environments/production.rb
同样,下面的大部分更改都是针对资产管道的。您可以在 资产管道 指南中阅读更多相关信息。
# Compress JavaScripts and CSS
config.assets.compress = true
# Don't fallback to assets pipeline if a precompiled asset is missed
config.assets.compile = false
# Generate digests for assets URLs
config.assets.digest = true
# Defaults to Rails.root.join("public/assets")
# config.assets.manifest = YOUR_PATH
# Precompile additional assets (application.js, application.css, and all non-JS/CSS are already added)
# config.assets.precompile += %w( admin.js admin.css )
# Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
# config.force_ssl = true
16.5. config/environments/test.rb
您可以通过在测试环境中添加以下内容来帮助测试性能
# Configure static asset server for tests with Cache-Control for performance
config.public_file_server.enabled = true
config.public_file_server.headers = {
"Cache-Control" => "public, max-age=3600"
}
16.6. config/initializers/wrap_parameters.rb
如果您希望将参数包装到嵌套哈希中,请添加此文件并包含以下内容。这在新应用程序中默认启用。
# Be sure to restart your server when you modify this file.
# This file contains settings for ActionController::ParamsWrapper which
# is enabled by default.
# Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
ActiveSupport.on_load(:action_controller) do
wrap_parameters format: [:json]
end
# Disable root element in JSON by default.
ActiveSupport.on_load(:active_record) do
self.include_root_in_json = false
end
16.7. config/initializers/session_store.rb
您需要将您的会话密钥更改为新的,或者移除所有会话
# in config/initializers/session_store.rb
AppName::Application.config.session_store :cookie_store, key: "SOMETHINGNEW"
或
$ bin/rake db:sessions:clear
16.8. 移除视图中资产辅助程序引用中的 :cache 和 :concat 选项
- 使用资产管道,不再使用 :cache 和 :concat 选项,从视图中删除这些选项。