自上次主要版本发布以来,Rails 8.1 在过去十个月中集成了超过 500 位贡献者、2500 次提交的成果,我们很高兴能赶在 Rails World 的第一天发布第一个 Beta 版本。以下是其中的一些亮点:
现在,长时间运行的作业可以分解为离散的步骤,允许执行在重启后从最后一个完成的步骤继续,而不是从头开始。这在使用 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
private
def finalize
@import.finalize
end
end
end
Active Job 连续性由 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 凭据存储中获取其部署所需的秘密。这使其成为外部秘密存储的一个低配置替代方案,只需要主密钥即可工作。
# .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 咨询时向上游贡献。
自上次发布以来,Rails 8.1 共有 2500 次提交,其中包含了大量的修复、次要功能和改进。请查阅 CHANGELOG 文件以获取详细信息。