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
。 - 更改
package.json
中 Rails JavaScript 包的版本,如果运行 jsbundling-rails,则运行bin/rails javascript:install
。 - 运行 更新任务.
- 运行您的测试。
您可以在 此处找到所有已发布的 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_8_0.rb
...
不要忘记查看差异,以查看是否存在任何意外更改,并注意在此过程中使用的 diff 和合并工具可以通过 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 7.2 升级到 Rails 8.0
有关对 Rails 8.0 所做的更改的更多信息,请参阅 发行说明.
3 从 Rails 7.1 升级到 Rails 7.2
有关对 Rails 7.2 所做的更改的更多信息,请参阅 发行说明.
3.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 从 Rails 7.0 升级到 Rails 7.1
有关对 Rails 7.1 所做的更改的更多信息,请参阅 发行说明.
4.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 附件)失效。
生产和其他环境不受影响。
4.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
中。
4.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))
使用这一行代码,lib
中的(非忽略的)代码如果 config.eager_load
为 true
(production
模式下的默认值),也将被急切加载。这通常是您想要的,但是如果 lib
之前没有添加到急切加载路径中,并且您仍然希望保持这种方式,请选择退出
Rails.autoloaders.main.do_not_eager_load(config.root.join("lib"))
config.autoload_lib_once
方法是类似的方法,如果应用程序在 config.autoload_once_paths
中包含 lib
。
4.4 ActiveStorage::BaseController
不再包含流式传输关注项
继承自 ActiveStorage::BaseController
并使用流式传输来实现自定义文件服务逻辑的应用程序控制器现在必须显式包含 ActiveStorage::Streaming
模块。
4.5 MemCacheStore
和 RedisCacheStore
现在默认使用连接池
connection_pool
gem 已作为 activesupport
gem 的依赖项添加,MemCacheStore
和 RedisCacheStore
现在默认使用连接池。
如果您不想使用连接池,请在配置缓存存储时将 :pool
选项设置为 false
config.cache_store = :mem_cache_store, "cache.example.com", { pool: false }
有关更多信息,请参阅 使用 Rails 缓存 指南。
4.6 SQLite3Adapter
现在配置为在严格的字符串模式下使用
使用严格的字符串模式会禁用双引号字符串文字。
SQLite 在双引号字符串文字方面存在一些怪癖。它首先尝试将双引号字符串视为标识符名称,但如果它们不存在,则将其视为字符串文字。因此,错别字可能会被默默地忽略。例如,可以为不存在的列创建索引。有关更多详细信息,请参阅 SQLite 文档。
如果您不想在严格模式下使用 SQLite3Adapter
,可以禁用此行为
# config/application.rb
config.active_record.sqlite3_adapter_strict_strings_by_default = false
4.7 支持 ActionMailer::Preview
的多个预览路径
选项 config.action_mailer.preview_path
已弃用,取而代之的是 config.action_mailer.preview_paths
。将路径追加到此配置选项将导致在搜索邮件预览时使用这些路径。
config.action_mailer.preview_paths << "#{Rails.root}/lib/mailer_previews"
4.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
或自定义异常处理程序。
4.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
将不会在其之前运行。
4.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(...)
4.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]
4.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
如果您将
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 进行加密。
默认情况下,在 Rails 7.1 中禁用config.active_record.encryption.support_sha1_for_non_deterministic_encryption
。如果您有在 Rails < 7.1 版本中加密的数据,并且您认为可能受上述错误影响,则应启用此配置。
config.active_record.encryption.support_sha1_for_non_deterministic_encryption = true
如果您正在处理加密数据,请仔细查看上述内容。
4.13 在控制器测试、集成测试和系统测试中处理异常的新方法
config.action_dispatch.show_exceptions
配置控制 Action Pack 如何处理在响应请求时引发的异常。
在 Rails 7.1 之前,将config.action_dispatch.show_exceptions = true
设置为 true 会将 Action Pack 配置为捕获异常并呈现相应的 HTML 错误页面,例如使用 404 Not found
状态代码呈现public/404.html
,而不是引发ActiveRecord::RecordNotFound
异常。将config.action_dispatch.show_exceptions = false
设置为 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
以利用新行为,或者用相应的新的值替换旧的值(true
替换:all
,false
替换:none
)。
5 从 Rails 6.1 升级到 Rails 7.0
有关对 Rails 7.0 进行的更改的更多信息,请参阅发布说明。
5.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)))
5.2 Spring
如果您的应用程序使用 Spring,则需要将其升级到至少 3.0.0 版本。否则您将收到
undefined method `mechanism=' for ActiveSupport::Dependencies:Module
此外,请确保config.cache_classes
在config/environments/test.rb
中设置为false
。
5.3 Sprockets 现在是可选依赖项
gem rails
不再依赖于sprockets-rails
。如果您的应用程序仍然需要使用 Sprockets,请确保将sprockets-rails
添加到您的 Gemfile 中。
gem "sprockets-rails"
5.4 应用程序需要在zeitwerk
模式下运行
仍然在classic
模式下运行的应用程序必须切换到zeitwerk
模式。有关详细信息,请查看从 Classic 切换到 Zeitwerk 的方法指南。
5.5 setter config.autoloader=
已删除
在 Rails 7 中,没有配置点可以设置自动加载模式,config.autoloader=
已删除。如果您出于任何原因将其设置为:zeitwerk
,只需将其删除即可。
5.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
等。
5.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
。
由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
5.8 能够配置config.autoload_once_paths
config.autoload_once_paths
可以在config/application.rb
中定义的应用程序类的主体中或在config/environments/*
中环境的配置中设置。
类似地,引擎可以在引擎类的类主体中或环境的配置中配置该集合。
之后,集合被冻结,您可以从这些路径自动加载。特别是,您可以在初始化期间从那里自动加载。它们由Rails.autoloaders.once
自动加载器管理,该自动加载器不重新加载,只自动加载/急切加载。
如果您在环境配置处理完后配置了此设置,并且收到FrozenError
,请将代码移至适当位置即可。
5.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"
5.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
5.11 ActiveSupport::Digest 的摘要类更改为 SHA256
ActiveSupport::Digest 的默认摘要类正在从 SHA1 更改为 SHA256。这对诸如 Etags 等内容有影响,这些内容将发生更改,缓存键也会发生更改。更改这些键会影响缓存命中率,因此在升级到新的哈希时要小心并注意这一点。
5.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 能够读取这两种格式,因此缓存不会在升级期间失效。
5.13 Active Storage 视频预览图像生成
视频预览图像生成现在使用 FFmpeg 的场景变化检测来生成更有意义的预览图像。以前,会使用视频的第一帧,如果视频从黑色淡入,就会造成问题。此更改需要 FFmpeg v3.4+。
5.14 Active Storage 默认变体处理器已更改为:vips
对于新的应用程序,图像转换将使用 libvips 而不是 ImageMagick。这将减少生成变体所需的时间以及 CPU 和内存使用量,从而改善依赖 Active Storage 来提供图像的应用程序的响应时间。
:mini_magick
选项没有被弃用,因此可以继续使用它。
要将现有应用程序迁移到 libvips,请设置
Rails.application.config.active_storage.variant_processor = :vips
然后,您需要将现有的图像转换代码更改为image_processing
宏,并将 ImageMagick 的选项替换为 libvips 的选项。
5.14.1 将 resize 替换为 resize_to_limit
- variant(resize: "100x")
+ variant(resize_to_limit: [100, nil])
如果不这样做,切换到 vips 时,您会看到以下错误:no implicit conversion to float from string
。
5.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
。
5.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
5.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]])
5.14.5 删除任何基于 EXIF 的旋转
Vips 在处理变体时会自动使用 EXIF 值旋转图像。如果您以前将用户上传的照片的旋转值存储起来以使用 ImageMagick 应用旋转,则必须停止这样做
- variant(format: :jpg, rotate: rotation_value)
+ variant(format: :jpg)
5.14.6 将 monochrome 替换为 colourspace
Vips 使用不同的选项来制作单色图像
- variant(monochrome: true)
+ variant(colourspace: "b-w")
5.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 })
5.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 %>
5.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
# ...
end
您第一次使用 Rails 7.0 转储架构时,将看到该文件中许多更改,包括一些列信息。请务必查看新架构文件内容并将其提交到您的仓库。
6 从 Rails 6.0 升级到 Rails 6.1
有关对 Rails 6.1 进行的更改的更多信息,请参阅 发行说明。
6.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")
6.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 }
6.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
6.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
之前加载它。
6.5 从 HTTP 重定向到 HTTPS 将使用 308 HTTP 状态代码
在 ActionDispatch::SSL
中从 HTTP 重定向非 GET/HEAD 请求到 HTTPS 时使用的默认 HTTP 状态代码已更改为 308
,如 https://tools.ietf.org/html/rfc7538 中所定义。
6.6 Active Storage 现在需要 Image Processing
在 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])
6.7 新的 ActiveModel::Error
类
错误现在是新的 ActiveModel::Error
类实例,API 已发生更改。其中一些更改可能会根据您操作错误的方式抛出错误,而另一些则会打印弃用警告,以便在 Rails 7.0 中修复。
有关此更改和 API 更改详细信息的更多信息,请参阅 此 PR。
7 从 Rails 5.2 升级到 Rails 6.0
有关对 Rails 6.0 进行的更改的更多信息,请参阅 发行说明。
7.1 使用 Webpacker
Webpacker 是 Rails 6 的默认 JavaScript 编译器。但如果您要升级应用程序,它默认情况下不会被激活。如果您想使用 Webpacker,则将其包含在您的 Gemfile 中并安装它
gem "webpacker"
$ bin/rails webpacker:install
7.2 强制 SSL
控制器上的 force_ssl
方法已被弃用,将在 Rails 6.1 中删除。建议您启用 config.force_ssl
以在整个应用程序中强制 HTTPS 连接。如果您需要将某些端点免于重定向,可以使用 config.ssl_options
来配置该行为。
7.3 目的和过期元数据现在嵌入在签名和加密的 cookie 中,以提高安全性
为了提高安全性,Rails 将目的和过期元数据嵌入在加密或签名 cookie 值中。
Rails 然后可以阻止尝试复制 cookie 的签名/加密值并将其用作另一个 cookie 的值的攻击。
这种新的嵌入元数据使这些 cookie 与低于 6.0 版本的 Rails 不兼容。
如果您需要您的 cookie 被 Rails 5.2 和更早版本读取,或者您仍然在验证 6.0 部署并希望能够回滚,请将 Rails.application.config.action_dispatch.use_cookies_with_metadata
设置为 false
。
7.4 所有 npm 包都已移至 @rails
范围
如果您以前通过 npm/yarn 加载了任何 actioncable
、activestorage
或 rails-ujs
包,则必须更新这些依赖项的名称才能将它们升级到 6.0.0
actioncable → @rails/actioncable
activestorage → @rails/activestorage
rails-ujs → @rails/ujs
7.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
7.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"
7.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 根本不会验证主机。如果要在生产环境中验证主机,可以选择添加它们。
7.8 自动加载
Rails 6 的默认配置
# config/application.rb
config.load_defaults 6.0
在 CRuby 上启用 zeitwerk
自动加载模式。在这种模式下,自动加载、重新加载和急切加载由 Zeitwerk 管理。
如果您使用的是以前 Rails 版本的默认值,您可以像这样启用 zeitwerk
# config/application.rb
config.autoloader = :zeitwerk
7.8.1 公共 API
通常,应用程序不需要直接使用 Zeitwerk 的 API。Rails 会根据现有契约进行设置:config.autoload_paths
、config.cache_classes
等。
虽然应用程序应该坚持使用该接口,但实际的 Zeitwerk 加载器对象可以像这样访问
Rails.autoloaders.main
如果您需要预加载单表继承 (STI) 类或配置自定义词形分析器,这可能很方便。
7.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!
7.8.3 require_dependency
已消除所有已知的 require_dependency
用例,您应该使用 grep 搜索项目并删除它们。
如果您的应用程序使用单表继承,请参阅自动加载和重新加载常量 (Zeitwerk 模式) 指南的单表继承部分。
7.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
7.8.5 关注点
您可以从像这样的标准结构中自动加载和急切加载
app/models
app/models/concerns
在这种情况下,app/models/concerns
被假定为根目录(因为它属于自动加载路径),并且它被忽略为命名空间。因此,app/models/concerns/foo.rb
应该定义 Foo
,而不是 Concerns::Foo
。
Concerns::
命名空间在经典自动加载器中作为实现的副作用起作用,但这并不是预期的行为。使用 Concerns::
的应用程序需要重命名这些类和模块才能在 zeitwerk
模式下运行。
7.8.6 在自动加载路径中包含 app
一些项目希望在 app/api/base.rb
中定义 API::Base
,并在 classic
模式下将 app
添加到自动加载路径中以实现此目的。由于 Rails 会自动将 app
的所有子目录添加到自动加载路径中,因此我们遇到了另一种嵌套根目录的情况,因此这种设置不再有效。与上面的 concerns
说明的原理类似。
如果您想保留这种结构,则需要在初始化器中从自动加载路径中删除子目录。
ActiveSupport::Dependencies.autoload_paths.delete("#{Rails.root}/app/api")
7.8.7 自动加载的常量和显式命名空间
如果命名空间在文件中定义,例如 Hotel
在这里
app/models/hotel.rb # Defines Hotel.
app/models/hotel/pricing.rb # Defines Hotel::Pricing.
则必须使用 class
或 module
关键字设置 Hotel
常量。例如
class Hotel
end
很好。
像这样的替代方案
Hotel = Class.new
或
Hotel = Struct.new
将不起作用,子对象如 Hotel::Pricing
将找不到。
此限制仅适用于显式命名空间。没有定义命名空间的类和模块可以使用这些习惯用法定义。
7.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
。
7.8.9 Spring 和 test
环境
如果出现更改,Spring 会重新加载应用程序代码。在 test
环境中,您需要启用重新加载才能使其正常工作。
# config/environments/test.rb
config.cache_classes = false
否则,您将收到此错误
reloading is disabled because config.cache_classes is true
7.8.10 Bootsnap
Bootsnap 应至少为 1.4.2 版本。
除此之外,如果运行 Ruby 2.5,Bootsnap 需要由于解释器中的错误而禁用 iseq 缓存。在这种情况下,请确保至少依赖于 Bootsnap 1.4.4。
7.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 的工作量和内存消耗,因为它不需要为这些目录建立索引。
7.8.12 线程安全
在经典模式下,常量自动加载不是线程安全的,尽管 Rails 在例如 Web 请求中启用了自动加载的情况下,仍然存在锁来确保线程安全,这在开发环境中很常见。
在 zeitwerk
模式下,常量自动加载是线程安全的。例如,您现在可以在由 runner
命令执行的多线程脚本中自动加载。
7.8.13 config.autoload_paths
中的通配符
注意像这样的配置
config.autoload_paths += Dir["#{config.root}/lib/**/"]
config.autoload_paths
的每个元素都应该代表顶级命名空间(Object
),因此它们不能嵌套(除了上面解释的 concerns
目录)。
要修复此问题,只需删除通配符即可。
config.autoload_paths << "#{config.root}/lib"
7.8.14 提前加载和自动加载是一致的
在 classic
模式下,如果 app/models/foo.rb
定义了 Bar
,您将无法自动加载该文件,但提前加载会起作用,因为它会盲目地递归加载文件。如果您先测试提前加载,这可能是错误的来源,执行可能在以后自动加载时失败。
在 zeitwerk
模式下,两种加载模式都是一致的,它们都会在相同的文件中失败并报错。
7.8.15 如何在 Rails 6 中使用经典自动加载器
应用程序可以加载 Rails 6 默认值,并通过以下方式设置 config.autoloader
来继续使用经典自动加载器
# config/application.rb
config.load_defaults 6.0
config.autoloader = :classic
在 Rails 6 应用程序中使用经典自动加载器时,建议在开发环境中将 Web 服务器和后台处理器的并发级别设置为 1,以解决线程安全问题。
7.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 中删除。
7.10 自定义异常处理应用程序
无效的 Accept
或 Content-Type
请求头现在会引发异常。默认的 config.exceptions_app
会专门处理该错误并进行补偿。自定义异常应用程序也需要处理该错误,否则此类请求会导致 Rails 使用回退异常应用程序,该应用程序返回 500 内部服务器错误
。
8 从 Rails 5.1 升级到 Rails 5.2
有关对 Rails 5.2 进行的更改的更多信息,请参阅 发行说明。
8.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。
8.2 签名或加密 cookie 中的过期时间现在嵌入到 cookie 值中
为了提高安全性,Rails 现在还在加密或签名 cookie 值中嵌入过期时间信息。
这种新的嵌入信息使这些 cookie 与低于 5.2 版本的 Rails 版本不兼容。
如果您需要旧的 5.1 版本或更低版本读取您的 cookie,或者您仍在验证 5.2 部署,并且希望允许您回滚,请将 Rails.application.config.action_dispatch.use_authenticated_cookie_encryption
设置为 false
。
9 从 Rails 5.0 升级到 Rails 5.1
有关对 Rails 5.1 进行的更改的更多信息,请参阅 发行说明。
9.1 顶级 HashWithIndifferentAccess
已被软弃用
如果您的应用程序使用顶级 HashWithIndifferentAccess
类,您应该慢慢地将您的代码迁移到改用 ActiveSupport::HashWithIndifferentAccess
。
它只是被软弃用,这意味着您的代码目前不会中断,也不会显示弃用警告,但该常量将在将来被删除。
此外,如果您拥有包含此类对象转储的非常旧的 YAML 文档,您可能需要再次加载和转储它们,以确保它们引用了正确的常量,并且将来加载它们不会中断。
9.2 application.secrets
现在以所有键为符号加载
如果您的应用程序将嵌套配置存储在 config/secrets.yml
中,现在所有键都以符号加载,因此使用字符串访问应该更改。
从
Rails.application.secrets[:smtp_settings]["address"]
到
Rails.application.secrets[:smtp_settings][:address]
9.3 删除了对 render
中的 :text
和 :nothing
的弃用支持
如果您的控制器使用 render :text
,它们将不再起作用。使用 text/plain
MIME 类型的文本渲染新方法是使用 render :plain
。
类似地,render :nothing
也被删除,您应该使用 head
方法来发送仅包含头的响应。例如,head :ok
发送一个 200 响应,没有要渲染的正文。
9.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)
10 从 Rails 4.2 升级到 Rails 5.0
有关对 Rails 5.0 进行的更改的更多信息,请参阅 发行说明。
10.1 要求 Ruby 2.2.2+
从 Ruby on Rails 5.0 开始,Ruby 2.2.2+ 是唯一支持的 Ruby 版本。在继续之前,请确保您使用的是 Ruby 2.2.2 版本或更高版本。
10.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
然后确保所有模型都继承自它。
10.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。
10.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。
10.5 Rails 控制器测试
10.5.1 将一些辅助方法提取到 rails-controller-testing
assigns
和 assert_template
已被提取到 rails-controller-testing
gem 中。要在您的控制器测试中继续使用这些方法,请将 gem "rails-controller-testing"
添加到您的 Gemfile
中。
如果您使用 RSpec 进行测试,请参阅 gem 文档中所需的额外配置。
10.5.2 上传文件时的新的行为
如果您在测试中使用ActionDispatch::Http::UploadedFile
上传文件,您需要更改为使用类似的Rack::Test::UploadedFile
类。
有关更多详细信息,请参见#26404。
10.6 生产环境中启动后自动加载已禁用
默认情况下,生产环境中启动后,自动加载现已禁用。
急切加载应用程序是启动过程的一部分,因此顶级常量是可以的,并且仍然可以自动加载,无需在其文件中进行要求。
在运行时仅执行的更深位置的常量(如常规方法体)也是可以的,因为定义它们的将已在启动时急切加载。
对于绝大多数应用程序,此更改无需任何操作。但在极少数情况下,您的应用程序需要在生产环境中运行时自动加载,请将Rails.application.config.enable_dependency_loading
设置为true。
10.7 XML序列化
ActiveModel::Serializers::Xml
已从Rails提取到activemodel-serializers-xml
gem中。要继续在您的应用程序中使用XML序列化,请在您的Gemfile
中添加gem "activemodel-serializers-xml"
。
10.8 删除对旧版mysql
数据库适配器的支持
Rails 5 删除了对旧版mysql
数据库适配器的支持。大多数用户应该能够使用mysql2
代替。当我们找到有人维护它时,它将被转换为一个单独的 gem。
10.9 删除对调试器的支持
Rails 5 要求使用 Ruby 2.2,因此不再支持debugger
。请改用byebug
。
10.10 使用bin/rails
运行任务和测试
Rails 5 添加了通过bin/rails
而不是rake运行任务和测试的功能。通常情况下,这些更改与rake并行进行,但有些更改已经完全移植过来。
要使用新的测试运行器,只需键入bin/rails test
。
rake dev:cache
现在是bin/rails dev:cache
。
在您的应用程序的根目录中运行bin/rails
,以查看可用的命令列表。
10.11 ActionController::Parameters
不再继承自HashWithIndifferentAccess
在您的应用程序中调用params
现在将返回一个对象,而不是一个哈希。如果您的参数已经获得许可,那么您将不需要进行任何更改。如果您正在使用map
和其他依赖于能够读取哈希的方法,无论permitted?
如何,您都需要将您的应用程序升级到首先允许,然后转换为哈希。
params.permit([:proceed_to, :return_to]).to_h
10.12 protect_from_forgery
现在默认情况下为prepend: false
protect_from_forgery
默认情况下为prepend: false
,这意味着它将被插入到回调链中,在您在应用程序中调用它的位置。如果您希望protect_from_forgery
始终首先运行,那么您应该将应用程序更改为使用protect_from_forgery prepend: true
。
10.13 默认模板处理程序现在为RAW
扩展名中没有模板处理程序的文件将使用原始处理程序呈现。以前,Rails 将使用 ERB 模板处理程序呈现文件。
如果您不希望您的文件通过原始处理程序进行处理,那么您应该向文件添加一个扩展名,该扩展名可以由相应的模板处理程序解析。
10.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/* %>
10.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。
10.16 删除了对protected_attributes
gem的支持
Rails 5 中不再支持protected_attributes
gem。
10.17 删除了对activerecord-deprecated_finders
gem的支持
Rails 5 中不再支持activerecord-deprecated_finders
gem。
10.18 ActiveSupport::TestCase
默认测试顺序现在为随机
在您的应用程序中运行测试时,默认顺序现在是:random
而不是:sorted
。使用以下配置选项将其设置回:sorted
。
# config/environments/test.rb
Rails.application.configure do
config.active_support.test_order = :sorted
end
10.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
10.20 新的框架默认值
10.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
10.20.2 每个表单的 CSRF 令牌
Rails 5 现在支持每个表单的 CSRF 令牌,以减轻 JavaScript 创建的表单的代码注入攻击。启用此选项后,您的应用程序中的表单将分别具有自己的 CSRF 令牌,该令牌特定于该表单的操作和方法。
config.action_controller.per_form_csrf_tokens = true
10.20.3 带有来源检查的伪造保护
您现在可以将应用程序配置为检查 HTTP Origin
标头是否应针对网站的来源进行检查,作为额外的 CSRF 防御。在您的配置中将以下设置设置为 true
config.action_controller.forgery_protection_origin_check = true
10.20.4 允许配置 Action Mailer 队列名称
默认邮件队列名称为mailers
。此配置选项允许您全局更改队列名称。在您的配置中设置以下内容
config.action_mailer.deliver_later_queue_name = :new_queue_name
10.20.5 支持 Action Mailer 视图中的片段缓存
在您的配置中设置config.action_mailer.perform_caching
,以确定您的 Action Mailer 视图是否应支持缓存。
config.action_mailer.perform_caching = true
10.20.6 配置db:structure:dump
的输出
如果您使用的是schema_search_path
或其他 PostgreSQL 扩展,则可以控制如何转储模式。设置为:all
以生成所有转储,或设置为:schema_search_path
以从模式搜索路径生成。
config.active_record.dump_schemas = :all
10.20.7 配置 SSL 选项以启用带有子域的 HSTS
在您的配置中设置以下内容,以在使用子域时启用 HSTS
config.ssl_options = { hsts: { subdomains: true } }
10.20.8 保留接收者的时区
在使用 Ruby 2.4 时,您可以在调用to_time
时保留接收者的时区。
ActiveSupport.to_time_preserves_timezone = false
10.21 JSON/JSONB 序列化更改
在 Rails 5.0 中,JSON/JSONB 属性的序列化和反序列化方式发生了变化。现在,如果您将列设置为String
,Active Record 将不再将该字符串转换为Hash
,而是仅返回该字符串。这不仅限于与模型交互的代码,还会影响db/schema.rb
中的:default
列设置。建议您不要将列设置为String
,而是传递一个Hash
,它将自动转换为 JSON 字符串并从 JSON 字符串转换回来。
11 从 Rails 4.1 升级到 Rails 4.2
11.1 Web 控制台
首先,在您的Gemfile
中将gem "web-console", "~> 2.0"
添加到:development
组,然后运行bundle install
(在您升级 Rails 时,它不会包含在内)。安装完成后,您只需将对控制台助手的引用(即<%= console %>
)放到您要为其启用任何视图中。在开发环境中查看的任何错误页面上,也将提供一个控制台。
11.2 响应者
respond_with
和类级别的respond_to
方法已提取到responders
gem 中。要使用它们,只需在您的Gemfile
中添加gem "responders", "~> 2.0"
。在没有在您的依赖项中包含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。
11.3 事务回调中的错误处理
目前,Active Record 会抑制在after_rollback
或after_commit
回调中引发的错误,并且只会将它们打印到日志中。在下一个版本中,这些错误将不再被抑制。相反,错误将像其他 Active Record 回调一样正常传播。
当您定义after_rollback
或after_commit
回调时,您将收到有关此即将发生的更改的弃用警告。当您准备就绪时,您可以选择新的行为并通过将以下配置添加到您的config/application.rb
中来删除弃用警告
config.active_record.raise_in_transactional_callbacks = true
11.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
11.5 序列化属性
在使用自定义编码器(例如serialize :metadata, JSON
)时,将nil
分配给序列化属性将将其保存到数据库中,作为NULL
而不是通过编码器传递nil
值(例如,使用JSON
编码器时为"null"
)。
11.6 生产日志级别
在 Rails 5 中,生产环境的默认日志级别将从:info
更改为:debug
。要保留当前默认值,请将以下行添加到您的production.rb
中
# Set to `:info` to match the current default, or set to `:debug` to opt-into
# the future default.
config.log_level = :info
11.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
11.8 Rails HTML 清理器
在您的应用程序中,您可以选择新的方法来清理 HTML 片段。现在,著名的 html-scanner 方法正式被弃用,取而代之的是Rails HTML 清理器
。
这意味着方法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"
11.9 Rails DOM 测试
TagAssertions
模块(包含 assert_tag
等方法)已弃用,取而代之的是 SelectorAssertions
模块中的 assert_select
方法,该模块已提取到 rails-dom-testing gem 中。
11.10 掩盖身份验证令牌
为了减轻 SSL 攻击,form_authenticity_token
现在被掩盖,因此它在每次请求中都会有所不同。 因此,令牌通过取消掩盖然后解密进行验证。 结果,任何依赖于静态会话 CSRF 令牌的策略都必须考虑这一点,以验证来自非 rails 表单的请求。
11.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
11.12 外键支持
迁移 DSL 已扩展为支持外键定义。 如果您一直使用 Foreigner gem,您可能希望考虑将其删除。 请注意,Rails 的外键支持是 Foreigner 的一个子集。 这意味着并非所有 Foreigner 定义都可以完全用其 Rails 迁移 DSL 等效项替换。
迁移过程如下
- 从
Gemfile
中删除gem "foreigner"
。 - 运行
bundle install
。 - 运行
bin/rake db:schema:dump
。 - 确保
db/schema.rb
包含所有具有必要选项的外键定义。
12 从 Rails 4.0 升级到 Rails 4.1
12.1 来自远程 <script>
标签的 CSRF 保护
或者,“我的测试为什么失败了!?!” 或者 “我的 <script>
小部件坏了!!“
跨站点请求伪造 (CSRF) 保护现在也涵盖了带有 JavaScript 响应的 GET 请求。 这可以防止第三方站点通过 <script>
标签远程引用您的 JavaScript 以提取敏感数据。
这意味着您的使用以下内容的功能和集成测试
get :index, format: :js
现在将触发 CSRF 保护。 切换到
xhr :get, :index, format: :js
以明确测试 XmlHttpRequest
。
您自己的 <script>
标签默认情况下也被视为跨域并被阻止。 如果您确实要从 <script>
标签加载 JavaScript,则现在必须明确跳过这些操作的 CSRF 保护。
12.2 Spring
如果您想使用 Spring 作为您的应用程序预加载器,您需要
- 将
gem "spring", group: :development
添加到您的Gemfile
中。 - 使用
bundle install
安装 spring。 - 使用
bundle exec spring binstub
生成 Spring binstub。
默认情况下,用户定义的 rake 任务将在 development
环境中运行。 如果您想让他们在其他环境中运行,请参考 Spring README。
12.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
部分生成新的密钥。重新启动您的服务器。
12.4 测试帮助程序的更改
如果您的测试帮助程序包含对 ActiveRecord::Migration.check_pending!
的调用,则可以将其删除。 现在当您 require "rails/test_help"
时会自动执行此检查,尽管将此行保留在您的帮助程序中不会造成任何伤害。
12.5 Cookies 序列化器
在 Rails 4.1 之前创建的应用程序使用 Marshal
将 Cookie 值序列化到签名和加密的 Cookie 罐中。 如果您想在您的应用程序中使用新的基于 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
哈希。
12.6 闪存结构更改
闪存消息键被规范化为字符串。 它们仍然可以通过符号或字符串访问。 遍历闪存将始终生成字符串键
flash["string"] = "a string"
flash[:symbol] = "a symbol"
# Rails < 4.1
flash.keys # => ["string", :symbol]
# Rails >= 4.1
flash.keys # => ["string", "symbol"]
确保您正在将闪存消息键与字符串进行比较。
12.7 JSON 处理中的更改
Rails 4.1 中的 JSON 处理存在一些重大更改。
12.7.1 MultiJSON 移除
MultiJSON 已达到 使用寿命终点,已从 Rails 中移除。
如果您的应用程序目前直接依赖于 MultiJSON,您有几个选择
将 'multi_json' 添加到您的
Gemfile
中。 请注意,这在将来可能不再有效通过使用
obj.to_json
和JSON.parse(str)
从 MultiJSON 迁移。
不要简单地将 MultiJson.dump
和 MultiJson.load
替换为 JSON.dump
和 JSON.load
。 这些 JSON gem API 用于序列化和反序列化任意 Ruby 对象,通常不安全。
12.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, quirks_mode: true)
=> "\"#<FooBar:0x007fa80a481610>\""
12.7.3 新的 JSON 编码器
Rails 4.1 中的 JSON 编码器已重写,以利用 JSON gem。 对于大多数应用程序来说,这应该是一个透明的更改。 但是,作为重写的一部分,以下功能已从编码器中移除
- 循环数据结构检测
- 对
encode_json
hook 的支持 - 将
BigDecimal
对象编码为数字而不是字符串的选项
如果您的应用程序依赖于其中一项功能,您可以通过将 activesupport-json_encoder
gem 添加到您的 Gemfile
中来找回它们。
12.7.4 Time 对象的 JSON 表示
带有时间组件的对象(Time
、DateTime
、ActiveSupport::TimeWithZone
)的 #as_json
现在默认情况下返回毫秒精度。 如果您需要保留旧的行为,没有毫秒精度,请在初始化器中设置以下内容
ActiveSupport::JSON::Encoding.time_precision = 0
12.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
)。
有关更多详细信息,请参阅 此拉取请求。
12.9 在 Active Record 固定文件中定义的方法
Rails 4.1 在单独的上下文中评估每个固定文件的 ERB,因此在固定文件中定义的帮助程序方法将不可用于其他固定文件。
在多个固定文件中使用的帮助程序方法应在模块中定义,这些模块包含在 test_helper.rb
中新引入的 ActiveRecord::FixtureSet.context_class
中。
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
12.10 I18n 强制执行可用语言环境
Rails 4.1 现在将 I18n 选项 enforce_available_locales
的默认值设置为 true
。 这意味着它将确保传递给它的所有语言环境都必须在 available_locales
列表中声明。
要禁用它(并允许 I18n 接受任何语言环境选项),请将以下配置添加到您的应用程序中
config.i18n.enforce_available_locales = false
请注意,此选项是作为安全措施添加的,以确保用户输入不能用作语言环境信息,除非它事先已知。 因此,建议您不要禁用此选项,除非您有充分的理由这样做。
12.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!
12.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'
12.13 从字符串渲染内容
Rails 4.1 引入了 :plain
、:html
和 :body
选项到 render
。这些选项现在是渲染基于字符串的内容的首选方式,因为它允许你指定想要将响应发送的哪种内容类型。
render :plain
将把内容类型设置为text/plain
render :html
将把内容类型设置为text/html
render :body
将 不 设置内容类型头。
从安全角度来看,如果你不希望在响应主体中包含任何标记,你应该使用 render :plain
,因为大多数浏览器会为你转义响应中的不安全内容。
我们将在未来版本中弃用 render :text
的使用。因此,请开始使用更精确的 :plain
、:html
和 :body
选项。使用 render :text
可能存在安全风险,因为内容将作为 text/html
发送。
12.14 PostgreSQL JSON 和 hstore 数据类型
Rails 4.1 将把 json
和 hstore
列映射到一个字符串键的 Ruby Hash
。在早期版本中,使用的是 HashWithIndifferentAccess
。这意味着不再支持符号访问。对于基于 json
或 hstore
列的 store_accessors
也是如此。确保始终使用字符串键。
12.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 }
13 从 Rails 3.2 升级到 Rails 4.0
如果你的应用程序当前运行在任何低于 3.2.x 版本的 Rails 版本上,你应该先升级到 Rails 3.2,然后再尝试升级到 Rails 4.0。
以下更改用于将你的应用程序升级到 Rails 4.0。
13.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 中,PUT
请求到 /users/:id
会被路由到 update
,就像今天一样。因此,如果你有一个接收真实 PUT 请求的 API,它将继续工作。路由器还会将 PATCH
请求到 /users/:id
路由到 update
操作。
如果操作在公共 API 中使用,并且你无法更改正在使用的 HTTP 方法,你可以更新你的表单,使用 PUT
方法代替
<%= form_for [ :update_name, @user ], method: :put do |f| %>
有关 PATCH 的更多信息以及为什么做出这种改变,请参阅 Rails 博客上的 这篇文章。
13.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,但它并不完全支持规范中的最后几次更改。
13.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)
13.3 vendor/plugins
Rails 4.0 不再支持从 vendor/plugins
加载插件。你必须将所有插件替换为提取到 gem 并将它们添加到 Gemfile
中。如果你选择不将它们制作成 gem,你可以将它们移动到例如 lib/my_plugin/*
中,并在 config/initializers/my_plugin.rb
中添加一个适当的初始化器。
13.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
,而不是将nil
值传递给 YAML("--- \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
之间的关系需要类似地更新。
13.5 Active Resource
Rails 4.0 将 Active Resource 提取到了自己的 gem 中。如果你仍然需要该功能,你可以在 Gemfile
中添加 Active Resource gem。
13.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
13.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"
请注意,你应该等到你的所有用户都迁移到 Rails 4.x 并且你确信不需要回滚到 Rails 3.x 时再设置
secret_key_base
。这是因为在 Rails 4.x 中基于新的secret_key_base
生成的签名 Cookie 与 Rails 3.x 不兼容。你可以在不设置新的secret_key_base
的情况下保留现有的secret_token
,并忽略弃用警告,直到你确信你的升级在其他方面已经完成。如果你依赖外部应用程序或 JavaScript 能够读取 Rails 应用程序的签名会话 Cookie(或签名 Cookie ),则你不应该设置
secret_key_base
,直到你将这些问题解耦。如果设置了
secret_key_base
,Rails 4.0 会对基于 Cookie 的会话内容进行加密。Rails 3.x 会对基于 Cookie 的会话内容进行签名,但不会加密。签名后的 Cookie 是“安全的”,因为它们经过验证,是由您的应用生成的,并且是防篡改的。但是,最终用户可以查看内容,对内容进行加密可以消除这种弊端/担忧,并且不会带来显著的性能损失。请阅读Pull Request #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_caching
gem 才能在控制器中使用caches_action
,并添加actionpack-page_caching
才能使用caches_page
。Rails 4.0 已删除 XML 参数解析器。如果您需要此功能,则需要添加
actionpack-xml_parser
gem。Rails 4.0 更改了默认
layout
查找集,使用符号或返回 nil 的 proc。要获得“无布局”行为,请返回 false 而不是 nil。Rails 4.0 将默认 memcached 客户端从
memcache-client
更改为dalli
。要升级,只需在您的Gemfile
中添加gem "dalli"
即可。Rails 4.0 弃用了控制器中的
dom_id
和dom_class
方法(在视图中使用是可以的)。您需要在需要此功能的控制器中包含ActionView::RecordIdentifier
模块。Rails 4.0 弃用了
link_to
helper 的:confirm
选项。您应该改用数据属性(例如data: { confirm: 'Are you sure?' }
)。此弃用也涉及基于此 helper 的 helper(例如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: :example
resources :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 中,预编译资产不再自动将非 JS/CSS 资产从
vendor/assets
和lib/assets
复制。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
。
13.8 Active Support
Rails 4.0 删除了j
对ERB::Util#json_escape
的别名,因为j
已经用于ActionView::Helpers::JavaScriptHelper#escape_javascript
。
13.8.1 Cache
Rails 3.x 和 4.0 之间的缓存方法有所不同。您应该更改缓存命名空间并使用冷缓存进行推广。
13.9 Helpers 加载顺序
在 Rails 4.0 中,从多个目录加载 helper 的顺序已更改。以前,它们会被收集,然后按字母顺序排序。升级到 Rails 4.0 后,helper 会保留加载目录的顺序,并且只会在每个目录内按字母顺序排序。除非您显式使用helpers_path
参数,否则此更改只会影响从引擎加载 helper 的方式。如果您依赖于排序,则应在升级后检查是否可以使用正确的 method。如果您想更改引擎的加载顺序,可以使用config.railties_order=
method。
13.10 Active Record Observer 和 Action Controller Sweeper
ActiveRecord::Observer
和ActionController::Caching::Sweeper
已被提取到rails-observers
gem 中。如果您需要这些功能,则需要添加rails-observers
gem。
13.11 sprockets-rails
assets:precompile:primary
和assets:precompile:all
已被删除。请改用assets:precompile
。config.assets.compress
选项应更改为config.assets.js_compressor
,例如config.assets.js_compressor = :uglifier
13.12 sass-rails
- 带有两个参数的
asset-url
已弃用。例如:asset-url("rails.png", image)
变为asset-url("rails.png")
。
14 从 Rails 3.1 升级到 Rails 3.2
如果您的应用程序当前使用的是任何低于 3.1.x 版本的 Rails,则应升级到 Rails 3.1,然后再尝试更新到 Rails 3.2。
以下更改适用于将您的应用程序升级到最新 3.2.x 版本的 Rails。
14.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
14.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
14.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
14.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
中添加一个适当的初始化程序。
14.5 Active Record
belongs_to
中已删除:dependent => :restrict
选项。如果您想在存在任何关联对象时阻止删除对象,则可以设置:dependent => :destroy
,并在从任何关联对象的 destroy 回调中检查关联是否存在后返回false
。
15 从 Rails 3.0 升级到 Rails 3.1
如果您的应用程序当前使用的是任何低于 3.0.x 版本的 Rails,则应升级到 Rails 3.0,然后再尝试更新到 Rails 3.1。
以下更改适用于将您的应用程序升级到 Rails 3.1.12,它是最后一个 3.1.x 版本的 Rails。
15.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"
15.2 config/application.rb
资产管道需要以下添加
config.assets.enabled = true
config.assets.version = "1.0"
如果您的应用程序使用“/assets”路由来访问资源,您可能想要更改用于资产的前缀,以避免冲突
# Defaults to '/assets'
config.assets.prefix = "/asset-files"
15.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
15.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
15.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"
}
15.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
15.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
15.8 删除视图中对资产 helper 引用中的 :cache 和 :concat 选项
- 使用资产管道时,不再使用 :cache 和 :concat 选项,请从您的视图中删除这些选项。