1 升级到 Rails 7.1
如果您要升级现有应用程序,建议您在开始升级之前拥有良好的测试覆盖率。您还应该先升级到 Rails 7.0(如果您还没有升级),并确保应用程序按预期运行,然后再尝试升级到 Rails 7.1。有关升级时需要注意的事项列表,请参阅 升级 Ruby on Rails 指南。
2 主要功能
2.1 为新的 Rails 应用程序生成 Dockerfile
默认 Docker 支持 新的 Rails 应用程序。在生成新应用程序时,Rails 现在将在应用程序中包含与 Docker 相关的文件。
这些文件作为使用 Docker 在生产环境中部署 Rails 应用程序的基础设置。需要注意的是,这些文件并非用于开发目的。
以下是如何使用这些 Docker 文件构建和运行 Rails 应用程序的示例
$ docker build -t app .
$ docker volume create app-storage
$ docker run --rm -it -v app-storage:/rails/storage -p 3000:3000 --env RAILS_MASTER_KEY=<your-config-master-key> app
您也可以从此 Docker 镜像启动控制台或运行器
$ docker run --rm -it -v app-storage:/rails/storage --env RAILS_MASTER_KEY=<your-config-master-key> app console
对于希望创建多平台镜像(例如,用于 AMD 或 Intel 部署的 Apple Silicon),并将镜像推送到 Docker Hub 的用户,请按照以下步骤操作
$ docker login -u <your-user>
$ docker buildx create --use
$ docker buildx build --push --platform=linux/amd64,linux/arm64 -t <your-user/image-name> .
此增强功能简化了部署流程,为在生产环境中启动和运行 Rails 应用程序提供了便捷的起点。
2.2 添加 ActiveRecord::Base.normalizes
ActiveRecord::Base.normalizes
声明一个属性规范化。规范化在分配或更新属性时应用,规范化的值将被持久化到数据库中。规范化也应用于查询方法的相应关键字参数,允许使用非规范化值查询记录。
例如
class User < ActiveRecord::Base
normalizes :email, with: -> email { email.strip.downcase }
normalizes :phone, with: -> phone { phone.delete("^0-9").delete_prefix("1") }
end
user = User.create(email: " [email protected]\n")
user.email # => "[email protected]"
user = User.find_by(email: "\t[email protected] ")
user.email # => "[email protected]"
user.email_before_type_cast # => "[email protected]"
User.where(email: "\t[email protected] ").count # => 1
User.where(["email = ?", "\t[email protected] "]).count # => 0
User.exists?(email: "\t[email protected] ") # => true
User.exists?(["email = ?", "\t[email protected] "]) # => false
User.normalize_value_for(:phone, "+1 (555) 867-5309") # => "5558675309"
2.3 添加 ActiveRecord::Base.generates_token_for
ActiveRecord::Base.generates_token_for
定义了为特定目的生成令牌。生成的令牌可以过期,也可以嵌入记录数据。使用令牌获取记录时,将比较来自令牌的数据和记录的当前数据。如果两者不匹配,令牌将被视为无效,与令牌过期相同。
以下是如何实现单次使用密码重置令牌的示例
class User < ActiveRecord::Base
has_secure_password
generates_token_for :password_reset, expires_in: 15.minutes do
# `password_salt` (defined by `has_secure_password`) returns the salt for
# the password. The salt changes when the password is changed, so the token
# will expire when the password is changed.
password_salt&.last(10)
end
end
user = User.first
token = user.generate_token_for(:password_reset)
User.find_by_token_for(:password_reset, token) # => user
user.update!(password: "new password")
User.find_by_token_for(:password_reset, token) # => nil
2.4 添加 perform_all_later
以一次性排队多个作业
Active Job 中的 perform_all_later
方法 旨在简化同时排队多个作业的过程。这个强大的功能允许您有效地排队作业,而不会触发回调。这在您需要一次性排队一批作业时特别有用,可以减少对队列数据存储的多次往返的开销。
以下是如何使用 perform_all_later
# Enqueueing individual jobs
ActiveJob.perform_all_later(MyJob.new("hello", 42), MyJob.new("world", 0))
# Enqueueing an array of jobs
user_jobs = User.pluck(:id).map { |id| UserJob.new(user_id: id) }
ActiveJob.perform_all_later(user_jobs)
通过使用 perform_all_later
,您可以优化作业排队过程,并利用更高的效率,尤其是在处理大量作业时。值得注意的是,对于支持新 enqueue_all
方法的队列适配器(例如 Sidekiq 适配器),排队过程将通过 push_bulk
进一步优化。
请注意,此新方法引入了单独的事件 enqueue_all.active_job
,并且不使用现有的 enqueue.active_job
事件。这确保了对批量排队过程的准确跟踪和报告。
2.5 复合主键
现在在数据库和应用程序级别都支持复合主键。Rails 能够直接从模式中推断出这些键。此功能对于多对多关系和其他复杂数据模型特别有用,在这些模型中,单个列不足以唯一地识别记录。
Active Record 中的查询方法(例如 #reload
、#update
、#delete
)生成的 SQL 将包含复合主键的所有部分。#first
和 #last
等方法将在 ORDER BY
语句中使用完整的复合主键。
query_constraints
宏可以用作“虚拟主键”,以在不修改数据库模式的情况下实现相同的效果。示例
class TravelRoute < ActiveRecord::Base
query_constraints :origin, :destination
end
类似地,关联接受 query_constraints:
选项。此选项用作复合外键,用于配置用于访问关联记录的列列表。
示例
class TravelRouteReview < ActiveRecord::Base
belongs_to :travel_route, query_constraints: [:travel_route_origin, :travel_route_destination]
end
2.6 引入 Trilogy
的适配器
一个 新的适配器 已经引入,以促进 Trilogy
(一个与 MySQL 兼容的数据库客户端)与 Rails 应用程序的无缝集成。现在,Rails 应用程序可以选择通过配置它们的 config/database.yml
文件来包含 Trilogy
功能。例如
development:
adapter: trilogy
database: blog_development
pool: 5
或者,可以使用 DATABASE_URL
环境变量来实现集成
ENV["DATABASE_URL"] # => "trilogy://127.0.0.1/blog_development?pool=5"
2.7 添加 ActiveSupport::MessagePack
ActiveSupport::MessagePack
是一个序列化器,它与 msgpack
gem 集成。ActiveSupport::MessagePack
可以序列化 msgpack
支持的基本 Ruby 类型,以及一些其他类型,例如 Time
、ActiveSupport::TimeWithZone
和 ActiveSupport::HashWithIndifferentAccess
。与 JSON
和 Marshal
相比,ActiveSupport::MessagePack
可以减小有效负载大小并提高性能。
ActiveSupport::MessagePack
可以用作 消息序列化器
config.active_support.message_serializer = :message_pack
# Or individually:
ActiveSupport::MessageEncryptor.new(secret, serializer: :message_pack)
ActiveSupport::MessageVerifier.new(secret, serializer: :message_pack)
作为 cookie 序列化器
config.action_dispatch.cookies_serializer = :message_pack
以及作为 缓存序列化器
config.cache_store = :file_store, "tmp/cache", { serializer: :message_pack }
# Or individually:
ActiveSupport::Cache.lookup_store(:file_store, "tmp/cache", serializer: :message_pack)
2.8 介绍 config.autoload_lib
和 config.autoload_lib_once
用于增强自动加载
一个 新的配置方法 config.autoload_lib(ignore:)
已经引入。此方法用于通过包含 lib
目录来增强应用程序的自动加载路径,该目录默认情况下不包括在内。此外,还为新应用程序生成了 config.autoload_lib(ignore: %w(assets tasks))
。
当从 config/application.rb
或 config/environments/*.rb
中调用时,此方法会将 lib
目录添加到 config.autoload_paths
和 config.eager_load_paths
中。需要注意的是,此功能不适用于引擎。
为了确保灵活性,可以使用 ignore
关键字参数来指定不应由自动加载器管理的 lib
目录中的子目录。例如,您可以通过将 assets
、tasks
和 generators
等目录传递给 ignore
参数来排除这些目录
config.autoload_lib(ignore: %w(assets tasks generators))
config.autoload_lib_once
方法 与 config.autoload_lib
类似,只是它将 lib
添加到 config.autoload_once_paths
中,而不是 config.autoload_paths
。
请参阅 自动加载指南 中的更多详细信息
2.9 用于通用异步查询的 Active Record API
Active Record API 引入了重大改进,扩展了其对异步查询的支持。此改进解决了对更有效地处理速度不快的查询的需求,特别是关注聚合(例如 count
、sum
等)以及所有返回单个记录或除 Relation
之外的任何内容的方法。
新 API 包含以下异步方法
async_count
async_sum
async_minimum
async_maximum
async_average
async_pluck
async_pick
async_ids
async_find_by_sql
async_count_by_sql
以下是一个简短的示例,说明如何使用其中一种方法 async_count
以异步方式计算已发布帖子的数量
# Synchronous count
published_count = Post.where(published: true).count # => 10
# Asynchronous count
promise = Post.where(published: true).async_count # => #<ActiveRecord::Promise status=pending>
promise.value # => 10
这些方法允许以异步方式执行这些操作,这可以显着提高某些类型数据库查询的性能。
2.10 允许模板设置严格的 locals
引入一项新功能,允许模板设置显式 locals
。此改进在将变量传递到模板时提供了更大的控制和清晰度。
默认情况下,模板将接受任何 locals
作为关键字参数。但是,现在可以通过在模板文件开头添加 locals
幻数注释来定义模板应该接受哪些 locals
。
以下是它的工作原理
<%# locals: (message:) -%>
<%= message %>
您还可以为这些 locals 设置默认值
<%# locals: (message: "Hello, world!") -%>
<%= message %>
可选关键字参数可以被 splat
<%# locals: (message: "Hello, world!", **attributes) -%>
<%= tag.p(message, **attributes) %>
如果要完全禁用 locals 的使用,可以按以下方式操作
<%# locals: () %>
Action View 将处理任何支持 #
前缀注释的模板引擎中的 locals:
幻数注释,并将从部分中的任何行读取幻数注释。
仅支持关键字参数。定义位置参数或块参数将在渲染时引发 Action View 错误。
2.11 添加 Rails.application.deprecators
新的 Rails.application.deprecators
方法 返回应用程序中管理的弃用器集合,并允许您轻松添加和检索单个弃用器
Rails.application.deprecators[:my_gem] = ActiveSupport::Deprecation.new("2.0", "MyGem")
Rails.application.deprecators[:other_gem] = ActiveSupport::Deprecation.new("3.0", "OtherGem")
集合的配置设置会影响集合中的所有弃用器。
Rails.application.deprecators.debug = true
Rails.application.deprecators[:my_gem].debug
# => true
Rails.application.deprecators[:other_gem].debug
# => true
在某些情况下,您可能希望对特定代码块的所有弃用警告进行静音。使用弃用器集合,您可以轻松地在块内静音所有弃用警告
Rails.application.deprecators.silence do
Rails.application.deprecators[:my_gem].warn # No warning (silenced)
Rails.application.deprecators[:other_gem].warn # No warning (silenced)
end
2.12 支持 JSON response.parsed_body
的模式匹配
当 ActionDispatch::IntegrationTest
测试块为 JSON 响应调用 response.parsed_body
时,它们的有效负载将以无差异访问的方式提供。这使您可以与 Ruby 的模式匹配 和内置的 Minitest 对模式匹配的支持 集成。
get "/posts.json"
response.content_type # => "application/json; charset=utf-8"
response.parsed_body.class # => Array
response.parsed_body # => [{"id"=>42, "title"=>"Title"},...
assert_pattern { response.parsed_body => [{ id: 42 }] }
get "/posts/42.json"
response.content_type # => "application/json; charset=utf-8"
response.parsed_body.class # => ActiveSupport::HashWithIndifferentAccess
response.parsed_body # => {"id"=>42, "title"=>"Title"}
assert_pattern { response.parsed_body => [{ title: /title/i }] }
2.13 扩展 response.parsed_body
以使用 Nokogiri 解析 HTML
扩展 ActionDispatch::Testing
模块 以支持将 HTML response.body
的值解析为 Nokogiri::HTML5::Document
实例
get "/posts"
response.content_type # => "text/html; charset=utf-8"
response.parsed_body.class # => Nokogiri::HTML5::Document
response.parsed_body.to_html # => "<!DOCTYPE html>\n<html>\n..."
新添加的 Nokogiri 对模式匹配的支持 以及内置的 Minitest 对模式匹配的支持 为对 HTML 响应的结构和内容进行测试断言提供了机会
get "/posts"
html = response.parsed_body # => <html>
# <head></head>
# <body>
# <main><h1>Some main content</h1></main>
# </body>
# </html>
assert_pattern { html.at("main") => { content: "Some main content" } }
assert_pattern { html.at("main") => { content: /content/ } }
assert_pattern { html.at("main") => { children: [{ name: "h1", content: /content/ }] } }
2.14 引入 ActionView::TestCase.register_parser
扩展 ActionView::TestCase
以支持将视图部分渲染的内容解析为已知结构。默认情况下,定义 rendered_html
以将 HTML 解析为 Nokogiri::XML::Node
,并将 rendered_json
解析为 ActiveSupport::HashWithIndifferentAccess
test "renders HTML" do
article = Article.create!(title: "Hello, world")
render partial: "articles/article", locals: { article: article }
assert_pattern { rendered_html.at("main h1") => { content: "Hello, world" } }
end
test "renders JSON" do
article = Article.create!(title: "Hello, world")
render formats: :json, partial: "articles/article", locals: { article: article }
assert_pattern { rendered_json => { title: "Hello, world" } }
end
要将渲染的内容解析为 RSS,请注册对 RSS::Parser.parse
的调用
register_parser :rss, -> rendered { RSS::Parser.parse(rendered) }
test "renders RSS" do
article = Article.create!(title: "Hello, world")
render formats: :rss, partial: article, locals: { article: article }
assert_equal "Hello, world", rendered_rss.items.last.title
end
要将渲染的内容解析为 Capybara::Simple::Node,请使用对 Capybara.string
的调用重新注册 :html
解析器
register_parser :html, -> rendered { Capybara.string(rendered) }
test "renders HTML" do
article = Article.create!(title: "Hello, world")
render partial: article
rendered_html.assert_css "main h1", text: "Hello, world"
end
3 Railties
有关详细更改,请参阅 变更日志。
3.1 删除
删除已弃用的
bin/rails secrets:setup
命令。删除默认
X-Download-Options
标头,因为它仅供 Internet Explorer 使用。
3.2 弃用
弃用
Rails.application.secrets
的使用。弃用
secrets:show
和secrets:edit
命令,改用credentials
。弃用
Rails::Generators::Testing::Behaviour
,改用Rails::Generators::Testing::Behavior
。
3.3 值得注意的更改
向
sandbox_by_default
选项添加用于默认以沙箱模式启动 rails 控制台。添加了用于支持按行范围过滤测试的新语法。
添加了
DATABASE
选项,该选项在执行rails railties:install:migrations
命令以复制迁移时,允许指定目标数据库。在
rails new --javascript
生成器中添加了对 Bun 的支持。$ rails new my_new_app --javascript=bun
添加了向测试运行器显示缓慢测试的功能。
4 Action Cable
有关详细更改,请参阅 变更日志。
4.1 删除
4.2 弃用
4.3 值得注意的更改
添加了
capture_broadcasts
测试助手以捕获块中广播的所有消息。添加了 Redis 发布/订阅适配器在 Redis 连接断开时自动重新连接的功能。
向
ActionCable::Connection::Base
添加了命令回调before_command
、after_command
和around_command
。
5 Action Pack
有关详细更改,请参阅 变更日志。
5.1 删除
删除
Request#content_type
上的已弃用行为删除将单个值分配给
config.action_dispatch.trusted_proxies
的已弃用功能。删除用于系统测试的已弃用的
poltergeist
和webkit
(capybara-webkit)驱动程序注册。
5.2 弃用
弃用
config.action_dispatch.return_only_request_media_type_on_content_type
。弃用
AbstractController::Helpers::MissingHelperError
。弃用
ActionDispatch::IllegalStateError
。弃用
speaker
、vibrate
和vr
权限策略指令。弃用
config.action_dispatch.show_exceptions
中的true
和false
值,改用:all
、:rescuable
或:none
。
5.3 值得注意的更改
向
ActionController::Parameters
添加了exclude?
方法。它是include?
方法的逆方法。添加了
ActionController::Parameters#extract_value
方法以允许从参数中提取序列化值。添加了用于存储和检索 CSRF 令牌的自定义逻辑的功能。
为系统测试屏幕截图助手添加了
html
和screenshot
kwarg。
6 Action View
有关详细更改,请参阅 变更日志。
6.1 删除
删除已弃用的常量
ActionView::Path
。删除将实例变量作为 locals 传递给部分的已弃用支持。
6.2 弃用
6.3 值得注意的更改
checkbox_tag
和radio_button_tag
现在接受checked
作为关键字参数。添加了
picture_tag
助手以生成 HTML<picture>
标签。simple_format
助手现在处理:sanitize_options
功能,允许添加用于清理过程的额外选项。simple_format("<a target=\"_blank\" href=\"http://example.com\">Continue</a>", {}, { sanitize_options: { attributes: %w[target href] } }) # => "<p><a target=\"_blank\" href=\"http://example.com\">Continue</a></p>"
7 Action Mailer
有关详细更改,请参阅 变更日志。
7.1 删除
7.2 弃用
弃用
config.action_mailer.preview_path
。弃用通过
:args
kwarg 将参数传递给assert_enqueued_email_with
。现在支持:params
kwarg,因此请使用它来传递参数。
7.3 值得注意的更改
添加了
config.action_mailer.preview_paths
以支持多个预览路径。在测试助手添加了
capture_emails
以捕获块中发送的所有电子邮件。在
ActionMailer::TestHelper
中添加了deliver_enqueued_emails
以传递所有排队的电子邮件作业。
8 Active Record
有关详细更改,请参阅 变更日志。
8.1 删除
删除对
ActiveRecord.legacy_connection_handling
的支持。删除已弃用的
ActiveRecord::Base
配置访问器删除对
configs_for
上的:include_replicas
的支持。改用:include_hidden
。删除已弃用的
config.active_record.partial_writes
。删除已弃用的
Tasks::DatabaseTasks.schema_file_type
。删除 PostgreSQL 结构转储中的
--no-comments
标志。
8.2 弃用
弃用
#remove_connection
上的name
参数。弃用
check_pending!
,改用check_all_pending!
。弃用
add_foreign_key
中的deferrable: true
选项,改用deferrable: :immediate
。弃用
TestFixtures#fixture_path
,改用TestFixtures#fixture_paths
。弃用从
Base
到connection_handler
的委托。弃用
config.active_record.suppress_multiple_database_warning
。弃用在 SQL 字符串模板中将
ActiveSupport::Duration
作为内插绑定参数使用。弃用
all_connection_pools
并使connection_pool_list
更明确。弃用
read_attribute(:id)
在主键不是:id
时返回主键。弃用
#merge
上的rewhere
参数。弃用使用
alias_attribute
对非属性进行别名化。
8.3 值得注意的更改
添加了
TestFixtures#fixture_paths
以支持多个 fixture 路径。在使用
has_secure_password
时添加了authenticate_by
。向
ActiveRecord::Persistence
添加了update_attribute!
,它类似于update_attribute
,但当before_*
回调引发:abort
时,它会引发ActiveRecord::RecordNotSaved
。允许将别名属性与
insert_all
/upsert_all
一起使用。向
add_index
添加了:include
选项。添加了
#regroup
查询方法作为.unscope(:group).group(fields)
的简写形式。向
SQLite3
适配器添加了对自动填充列和自定义主键的支持。为
SQLite3
数据库连接添加了现代、高性能的默认值。允许使用列元组语法指定 where 子句。
Topic.where([:title, :author_name] => [["The Alchemist", "Paulo Coelho"], ["Harry Potter", "J.K Rowling"]])
现在自动生成的索引名称限制为 62 个字节,这适合 MySQL、PostgreSQL 和 SQLite 的默认索引名称长度限制。
为 Trilogy 数据库客户端引入了适配器。
添加了
ActiveRecord.disconnect_all!
方法以立即关闭来自所有池的所有连接。为枚举重命名、添加值和重命名值添加了 PostgreSQL 迁移命令。
添加了
ActiveRecord::Base#id_value
别名以访问记录 id 列的原始值。为
enum
添加了验证选项。
9 Active Storage
有关详细更改,请参阅 变更日志。
9.1 删除
删除 Active Storage 配置中已弃用的无效默认内容类型。
删除已弃用的
ActiveStorage::Current#host
和ActiveStorage::Current#host=
方法。删除将分配到附件集合时已弃用的行为。现在,不再将内容追加到集合中,而是替换整个集合。
删除附件关联中已弃用的
purge
和purge_later
方法。
9.2 弃用
9.3 值得注意的更改
ActiveStorage::Analyzer::AudioAnalyzer
现在在输出metadata
哈希中输出sample_rate
和tags
。添加了在附件上调用
preview
或representation
方法时使用预定义变体的选项。在声明变体以预处理变体时添加了
preprocessed
选项。添加了销毁 Active Storage 变体的功能。
User.first.avatar.variant(resize_to_limit: [100, 100]).destroy
10 Active Model
有关详细更改,请参阅 变更日志。
10.1 删除
10.2 弃用
10.3 值得注意的变更
为
LengthValidator
的:in
/:within
选项添加对无限范围的支持。validates_length_of :first_name, in: ..30
为
inclusivity/exclusivity
验证器添加对无起始范围的支持。validates_inclusion_of :birth_date, in: -> { (..Date.today) }
validates_exclusion_of :birth_date, in: -> { (..Date.today) }
为
has_secure_password
添加对密码挑战的支持。设置后,验证密码挑战是否与持久化的password_digest
匹配。允许验证器接受没有记录参数的 lambda 表达式。
# Before validates_comparison_of :birth_date, less_than_or_equal_to: ->(_record) { Date.today } # After validates_comparison_of :birth_date, less_than_or_equal_to: -> { Date.today }
11 Active Support
有关详细的变更,请参阅 变更日志。
11.1 删除
删除
Enumerable#sum
的已弃用重写。删除已弃用的
ActiveSupport::PerThreadRegistry
。删除
Array
、Range
、Date
、DateTime
、Time
、BigDecimal
、Float
和Integer
中将格式传递给#to_s
的已弃用选项。删除
ActiveSupport::TimeWithZone.name
的已弃用重写。删除已弃用的
active_support/core_ext/uri
文件。删除已弃用的
active_support/core_ext/range/include_time_with_zone
文件。删除
ActiveSupport::SafeBuffer
对对象隐式转换为String
的支持。删除在提供不是
Digest::UUID
上定义的常量的命名空间 ID 时生成不正确的 RFC 4122 UUID 的已弃用支持。
11.2 弃用
弃用
config.active_support.disable_to_s_conversion
。弃用
config.active_support.remove_deprecated_time_with_zone_name
。弃用
config.active_support.use_rfc4122_namespaced_uuids
。弃用
SafeBuffer#clone_empty
。弃用对单例
ActiveSupport::Deprecation
的使用。弃用使用
Dalli::Client
实例初始化ActiveSupport::Cache::MemCacheStore
。弃用
Notification::Event
的#children
和#parent_of?
方法。
11.3 值得注意的变更
12 Active Job
有关详细的变更,请参阅 变更日志。
12.1 删除
- 删除
QueAdapter
。
12.2 弃用
12.3 值得注意的变更
添加
perform_all_later
以一次性排队多个作业。添加作业生成器的
--parent
选项以指定作业的父类。添加
after_discard
方法到ActiveJob::Base
,以便在作业即将被丢弃时运行回调。添加对记录后台作业排队调用者的支持。
13 Action Text
有关详细的变更,请参阅 变更日志。
13.1 删除
13.2 弃用
13.3 值得注意的变更
14 Action Mailbox
有关详细的变更,请参阅 变更日志。
14.1 删除
14.2 弃用
14.3 值得注意的变更
将
X-Forwarded-To
地址添加到收件人。添加
bounce_now_with
方法到ActionMailbox::Base
,以便在不通过邮件队列的情况下发送退回邮件。
15 Ruby on Rails 指南
有关详细的变更,请参阅 变更日志。
15.1 值得注意的变更
16 致谢
查看 Rails 的完整贡献者列表,了解所有为 Rails 投入大量时间,使其成为稳定可靠框架的人们。感谢所有贡献者。