Rails 8.1 代表了自上次主要版本发布以来,由超过 500 名贡献者通过 2500 次提交所完成的工作。在人们试用了 beta 版和 release candidates 版本几周后,我们很高兴地发布最终版本。
此版本展示了 Rails 的稳定性,Shopify 和 HEY 等应用程序已经运行了数月。
以下是一些亮点:
现在可以将长时间运行的任务分解为离散的步骤,以便在重启后从最后一个已完成的步骤继续执行,而不是从头开始。当使用 Kamal 进行部署时,这尤其有用,因为 Kamal 默认只给运行任务的容器三十秒的关闭时间。
示例
class ProcessImportJob < ApplicationJob
include ActiveJob::Continuable
def perform(import_id)
@import = Import.find(import_id)
# block format
step :initialize do
@import.initialize
end
# step with cursor, the cursor is saved when the job is interrupted
step :process do |step|
@import.records.find_each(start: step.cursor) do |record|
record.process
step.advance! from: record.id
end
end
# method format
step :finalize
end
private
def finalize
@import.finalize
end
end
Active Job Continuations 由37signals的 Donal McBreen 主导。
Rails 中的默认日志记录器非常适合人类阅读,但不太适合事后处理。新的 Event Reporter 为 Rails 应用程序生成结构化事件提供了一个统一的接口。
Rails.event.notify("user.signup", user_id: 123, email: "user@example.com")
它支持为事件添加标签。
Rails.event.tagged("graphql") do
# Event includes tags: { graphql: true }
Rails.event.notify("user.signup", user_id: 123, email: "user@example.com")
end
以及上下文。
# All events will contain context: {request_id: "abc123", shop_id: 456}
Rails.event.set_context(request_id: "abc123", shop_id: 456)
事件会分发给订阅者。应用程序注册订阅者来控制事件的序列化和分发方式。订阅者必须实现一个 #emit 方法,该方法接收事件哈希。
class LogSubscriber
def emit(event)
payload = event[:payload].map { |key, value| "#{key}=#{value}" }.join(" ")
source_location = event[:source_location]
log = "[#{event[:name]}] #{payload} at #{source_location[:filepath]}:#{source_location[:lineno]}"
Rails.logger.info(log)
end
end
结构化事件报告由Shopify的 Adrianna Chang 主导。
开发人员的机器现在速度非常快,拥有大量的核心,这使得它们非常适合运行本地测试,即使是相对大型的测试套件。以前,HEY拥有超过 30,000 个断言的测试套件,在云端运行时,算上协调、镜像构建和并行运行,需要超过 10 分钟。现在,在 Framework Desktop AMD Linux 机器上本地运行只需要 1 分 23 秒,在 M4 Max 上需要 2 分 22 秒。
这使得摆脱所有 CI 的云端设置不仅可行,而且对许多中小型应用程序来说是理想的选择,因此 Rails 添加了一个默认的 CI 声明 DSL,它定义在 config/ci.rb 中,并由 bin/ci 运行。它看起来像这样:
CI.run do
step "Setup", "bin/setup --skip-server"
step "Style: Ruby", "bin/rubocop"
step "Security: Gem audit", "bin/bundler-audit"
step "Security: Importmap vulnerability audit", "bin/importmap audit"
step "Security: Brakeman code analysis", "bin/brakeman --quiet --no-pager --exit-on-warn --exit-on-error"
step "Tests: Rails", "bin/rails test"
step "Tests: Seeds", "env RAILS_ENV=test bin/rails db:seed:replant"
# Requires the `gh` CLI and `gh extension install basecamp/gh-signoff`.
if success?
step "Signoff: All systems go. Ready for merge and deploy.", "gh signoff"
else
failure "Signoff: CI failed. Do not merge or deploy.", "Fix the issues and try again."
end
end
与 gh 的可选集成确保了 PR 必须通过 CI 运行才能合并。
本地 CI 工作由37signals的 Jeremy Daer 主导。
Markdown 已成为 AI 的通用语言,Rails 通过使其更容易响应 markdown 请求并直接渲染它们来拥抱这一趋势。
class Page
def to_markdown
body
end
end
class PagesController < ActionController::Base
def show
@page = Page.find(params[:id])
respond_to do |format|
format.html
format.md { render markdown: @page }
end
end
end
Kamal 现在可以轻松地从加密的 Rails 凭据存储中获取用于部署的密钥。这使其成为外部密钥存储的一个低成本替代方案,只需要 master key 即可工作。
# .kamal/secrets
KAMAL_REGISTRY_PASSWORD=$(rails credentials:fetch kamal.registry_password)
这是Shopify的 Matthew Nguyen 和 Jean Boussier 的工作。
Active Record 关联现在可以标记为已弃用。
class Author < ApplicationRecord
has_many :posts, deprecated: true
end
这样,将会报告对 posts 关联的使用情况。这包括显式的 API 调用,例如:
author.posts
author.posts = ...
和其他用法,以及间接用法,例如:
author.preload(:posts)
通过嵌套属性的使用等。
支持三种报告模式(:warn、:raise 和 :notify),并且可以启用或禁用回溯,尽管您始终会获得报告使用情况的位置。默认值为 :warn 模式和禁用回溯。
这是 Xavier Noria 在为Gusto提供咨询时上游提交的。
Kamal 不再需要像 Docker Hub 或 GHCR 这样的远程注册表来进行基本部署。默认情况下,Kamal 2.8 现在将使用本地注册表进行简单部署。对于大规模部署,您仍然需要使用远程注册表,但这使得入门更容易,并能将您的第一个“Hello World”部署发布到实际环境中。
自上次发布以来,Rails 8.1 包含了 2500 次提交,其中包含大量的修复、次要功能和改进。请查看 CHANGELOG 文件以获取详细信息。