首页
/ Rails Railties 变更全解读:从生成器、凭据到测试命令的演进实录

Rails Railties 变更全解读:从生成器、凭据到测试命令的演进实录

2026-09-07 19:38:44作者:仰钰奇

本篇文章以当前仓库中保存的一份 Railties 历史 CHANGELOG 片段 railties_06e9fbd.md 为主体,逐条解读 Ruby on Rails 在 7.0 开发周期内对应用脚手架(rails new)、凭据(credentials)、bin/rails test、配置项与加载器等领域做的一系列工程化改进。你不仅能完整掌握这些命令与配置项的用法、默认值与背后动机,还能通过 railties 与 rail_inspector 的源码与测试,看清每一条变更在真实实现中如何落地。文中涉及的命令可直接在生成的 Rails 应用中验证。

1. 这份文档是什么:CHANGELOG 片段与其双重价值

被研究的文件位于 tools/rail_inspector/test/fixtures/railties_06e9fbd.md,它本质上是一份 Railties 框架的 CHANGELOG 片段,覆盖了从「应用内自定义凭据模板」到「移除默认 X-Download-Options 响应头」等 21 条变更记录,其时间背景为 Rails 7.0 的发布窗口——文档末尾的 Please check [7-0-stable] 页脚明确指向 7-0-stable 分支,其中「Since Rails 7 does not fully support Internet Explorer」的表述也印证了这一点。

它同时承担第二个职责:作为 rail_inspector(Rails 官方用于检查 CHANGELOG.md 格式的静态检查工具)的测试夹具(fixture)。在 changelog_test.rb 中,它被用于断言解析器能将这份文件正确切分为 21 条独立条目。因此,这份文档既记录了功能变迁,又充当了校验 changelog 格式规范的黄金样本——它本身就演示了 Rails 官方的 CHANGELOG 书写纪律(见本文第 9 节)。

2. 凭据体系:模板、默认密钥与解密 Diff

2.1 应用内自定义凭据模板

第一条变更解决了开源/分发型 Rails 应用的一个真实痛点:加密凭据文件(config/credentials.yml.enc)通常不会进入版本库,因此当用户 clone 一个开源 Rails 应用并执行 rails credentials:edit 时,拿到的往往是一份空模板。

变更后的行为是:当凭据文件不存在时,rails credentials:edit 会优先尝试使用应用内的 lib/templates/rails/credentials/credentials.yml.tt 生成凭据文件,找不到时才回退到默认模板。这让开源应用可以随仓库提交一份「预填好的凭据模板」,用户安装后运行一次 rails credentials:edit 即可获得定制化的凭据文件。

这一机制的实现沉淀在 railties 的凭据生成器中。从 credentials_generator.rb 可以看到,生成器接收默认内容路径 config/credentials.yml.enc,通过 render_template_to_encrypted_file 在临时目录渲染 credentials.yml 模板后再加密落地;而 rails credentials:edit 命令本身(credentials_command.rb)在编辑前会依次完成 ensure_encryption_key_has_been_addedensure_credentials_have_been_added 等初始化步骤。自定义模板正是挂接在这一流程中。

2.2 分环境凭据文件的 secret_key_base

第二条变更针对分环境凭据文件(如 config/credentials/production.yml.enc):devtest 环境外,新建的分环境凭据文件现在会像主凭据文件 config/credentials.yml.enc 一样,默认附带一个 secret_key_base,方便部署时直接使用,省去额外配置步骤。

2.3 decrypted diffs 默认开启

新生成的应用默认启用「凭据的解密 Diff(decrypted diffs)」能力,即让 git 对 .enc 凭据文件展示解密后的内容差异,方便审查凭据变更。如果不想要该行为,可用应用生成器的 --skip-decrypted-diffs 标志显式关闭。

这一点在今天的源码中仍有完整保留:app_generator.rbunless options[:skip_decrypted_diffs] 条件下才写入 git 的 diff 驱动配置,且该选项被声明为布尔型 class_option(见同文件 L320),生成时与 skip_git 逻辑联动(L324)。

3. rails new--minimal--no-* 选项的精确化

--minimal 选项用于预配置一个去掉可选组件的精简 Rails 应用。在 7.0 中,--no-* 形式(即「取消某 skip 选项」)开始与 --minimal 正确协同,其语义既全面又精确。文档给出了两组可以直接照抄验证的示例输出。

第一组:仅使用 --minimal,此时大量组件被级联跳过:

$ rails new my_cool_app --minimal
Based on the specified options, the following options will also be activated:

  --skip-active-job [due to --minimal]
  --skip-action-mailer [due to --skip-active-job, --minimal]
  --skip-active-storage [due to --skip-active-job, --minimal]
  --skip-action-mailbox [due to --skip-active-storage, --minimal]
  --skip-action-text [due to --skip-active-storage, --minimal]
  --skip-javascript [due to --minimal]
  --skip-hotwire [due to --skip-javascript, --minimal]
  --skip-action-cable [due to --minimal]
  --skip-bootsnap [due to --minimal]
  --skip-dev-gems [due to --minimal]
  --skip-system-test [due to --minimal]

  ...

第二组:在 --minimal 基础上追加 --no-skip-active-storage(即取消跳过 Active Storage),此时依赖链随之解开:由于 Active Storage 不再被跳过,Active Job 也不得不保留,于是 Active Storage 不再出现在跳过列表里,但它的下游(Action Mailer、Action Mailbox、Action Text)仍被跳过:

$ rails new my_cool_app --minimal --no-skip-active-storage
Based on the specified options, the following options will also be activated:

  --skip-action-mailer [due to --minimal]
  --skip-action-mailbox [due to --minimal]
  --skip-action-text [due to --minimal]
  --skip-javascript [due to --minimal]
  --skip-hotwire [due to --skip-javascript, --minimal]
  --skip-action-cable [due to --minimal]
  --skip-bootsnap [due to --minimal]
  --skip-dev-gems [due to --minimal]
  --skip-system-test [due to --minimal]

  ...

注意对比两组输出的差异点,就能直观理解级联依赖的解除规则。这套联动逻辑在应用生成器中有对应实现:app_generator.rb 定义了 minimal 选项及其 META_OPTIONSminimal 被声明为「元选项」,它依据 --minimal 自动推导出其他 --skip-* 选项的默认值;Active Job、Active Storage、Action Mailer 之间则遵循「跳过 Active Job 就一并跳过 Active Storage 与 Action Mailer」的依赖约束。从 Gemfile.tt 等模板中的 <% unless options.minimal? -%><% unless skip_active_storage? -%> 条件也能看出,模板渲染直接消费这些选项的最终取值。

3.1 相关新增的 skip 选项与别名

  • --skip-dev-gems:跳过向 Gemfile 添加开发类 gem(如 web-console)。该选项定义于 app_base.rb,而模板端则通过 <% unless options.skip_dev_gems? -%>Gemfile.tt 控制开发组 gem 是否写入。
  • --js / --skip-js:为 rails new 增加更短的新别名。--js--javascript(即 -j)的别名,例如 rails new --js esbuild ...--skip-js--skip-javascript(即 -J)的别名,例如 rails new --skip-js ...
  • --skip-decrypted-diffs:见 2.3 节。
  • 另外,跳过 Active Job 会自动连带跳过 Active Storage 与 Action Mailer(模板中可见 unless options[:skip_active_job]skip_active_storage? 的联动),这条规则使上述 --minimal 输出中的级联提示成为可能。

3.2 app:update 的框架禁用判断修复

当应用的部分框架被禁用(例如去掉了 Active Storage)后执行 app:update 时,此前对「框架是否被禁用」的检查存在缺陷;本次修复确保更新流程能正确感知每个框架的启停状态,避免在已禁用框架上做无效操作。该变更由 Étienne Barrié 与 Paulo Barros 完成。

4. bin/rails test 的可用性大修

本片段在测试命令上有四条相互呼应的改进,共同提升 bin/rails test 的日常使用体验。

4.1 声明式测试名的直接过滤

Rails 风格的声明式测试用例一般写作:

class MyTest < ActiveSupport::TestCase
  test "does something" do
    # ...
  end
end

过去想只跑这条用例,必须写出其内部展开后的方法名 test_does_something;现在可以直接用声明名过滤:

$ bin/rails test test/my_test.rb -n "does something"

替代原来的写法:

$ bin/rails test test/my_test.rb -n test_does_something

这对中文/带空格的长测试名尤其友好,不再需要心智换算「声明名 → snake_case 方法名」。

4.2 支持 ./ 前缀的相对路径

修复了 rails test ./test/model/post_test.rb 只能匹配全部文件而非单个文件的问题——现在允许把带前导点斜杠的相对路径直接传给 rails test,路径解析更贴近 shell 习惯。

4.3 测试子任务不再二次启动应用

此前运行任意 rails test:* 子任务(如 test:systemtest:models)都会经由 Rake 调度,导致应用被启动两次(先以 development 启动、再以 test 启动)。变更后,所有 test:* 子任务被重写为 Thor 任务,直接加载 test 环境,避免重复启动、显著缩短测试任务的启动耗时。

4.4 无对应文件夹时的表现(同源后续演进)

虽然该片段未收录,但沿此方向的后续演进(可参见 railties/CHANGELOG.md 顶部条目)把同样的思路扩展到 CI:当 bin/rails test:* 命令指向一个应用并不存在的文件夹(如新应用没有 test/system)时,现在会报告零个测试而不是抛 LoadError,从而避免生成的 .github/workflows/ci.yml 在首次推送时失败。

5. 运行与加载语义:executor、$LOAD_PATH 与 reloading

5.1 rails runner 在 executor 内执行

rails runner 脚本现在运行在应用的 executor 上下文内,因此脚本执行期间能够获得与请求处理同等的框架能力——包括**错误上报(error reporting)、查询缓存(query cache)**等围绕 executor 生命周期的机制,避免出现「用 runner 跑脚本时这些能力悄然失效」的落差。

5.2 不再把 autoload 路径加入 $LOAD_PATH

Rails 默认 autoload 的应用代码目录(如 app/*)此前会出现在 $LOAD_PATH 中,本变更取消了这一行为。含义有二:

  • 无法再用手工 require 去加载这些代码,需要改为直接引用对应类或模块(让 autoloader 工作);
  • 收益是显著缩小 $LOAD_PATH:对未使用 bootsnap 的应用可加速 require 调用,对使用 bootsnap 的应用则缩小其缓存体积。

5.3 config.enable_reloadingconfig.enable_dependency_loading 的去留

  • 新增 config.enable_reloading,其语义定义为 !config.cache_classes,即「是否在请求间重载常量」有了更直观的名字。官方推荐今后使用 config.enable_reloadingconfig.reloading_enabled?,但 config.cache_classes 仍为向后兼容保留。
  • 弃用 config.enable_dependency_loading。该开关本是为了规避 classic 自动加载器的局限,而如今已无实际作用,遇到弃用警告时直接删除相关配置即可(由 Xavier Noria 提交)。

这两条在 8.x 源码中都已稳定落地:应用默认骨架里,development.rb.tt 写入 config.enable_reloading = true,而 production.rb.tttest.rb.tt 写入 config.enable_reloading = false。配置实现位于 configuration.rbenable_reloading 的读/写方法),并由 finisher.rb 在应用启动完成阶段通过 autoloader.enable_reloading 真正生效。

6. 日志文件大小配置:config.log_file_size

本次变更允许对 local 与 test 环境配置日志文件的大小上限,新增配置项 config.log_file_size,默认值为 100 MB

从源码可确认其完整语义:configuration.rb 在默认配置中将其设为 100 * 1024 * 1024(字节),而 bootstrap.rb 中,应用启动时若 config.log_file_size 已设置,则按 ActiveSupport::Logger.new(config.default_log_file, 1, config.log_file_size) 构造带轮转上限的 logger——其中 1 表示保留的日志文件数量,第三个参数即单文件大小的轮转阈值。也就是说,达到上限后日志会自动轮转,避免单个日志文件无限膨胀。

7. 生成器输出的正确性修复

7.1 命名空间控制器注释中的 route_url

rails generate 的命名空间控制器生成此前存在一个 bug:当用顶层的 model 配合命名空间生成时,控制器动作上方的注释路由会写错。文档给出前后对照:

修复前,执行

bin/rails generate scaffold_controller Admin/Post --model-name Post

生成的控制器注释是:

# GET /posts
def index
  @posts = Post.all
end

修复后正确反映命名空间:

# GET /admin/posts
def index
  @posts = Post.all
end

根因与修复都在生成器的路由计算逻辑:Rails::Generators::NamedBase#route_url 改为使用 controller_class_path。这在今天源码中可验证:named_base.rbroute_url 定义为 controller_class_path.collect { |dname| "/" + dname }.join + "/" + plural_file_name;而 controller_class_pathresource_helpers.rb 提供,可同时处理 / 分隔与 :: 分隔的命名空间写法。模板端 controller.rb.tt 也正是通过 # GET <%= route_url %> 把该值渲染进注释。

7.2 模型生成器描述与 deprecation 更名

  • 模型生成器的帮助描述(如 rails generate model --help 的说明文字)不再由模型生成器自行硬编码,而是委托给 ORM 挂接的生成器输出,保证帮助信息与用户实际使用的 ORM(如 ActiveRecord)保持一致。
  • 弃用 Rails::Generators::Testing::Behaviour,改用拼写更标准的 Rails::Generators::Testing::Behavior(美式拼写)。如代码中仍在引用旧常量,会收到弃用提示,迁移只需改写常量名。

8. 收尾细节:bin/setup 与默认响应头

  • bin/setup 安装 JavaScript 依赖:当应用使用 esbuild、webpack 或 rollout 等 JS 打包方案时,生成的 bin/setup 会自动加入 yarn install,让新环境搭建一步到位。
  • 移除默认的 X-Download-Options 响应头:该响应头原本仅服务于 Internet Explorer,而 IE 于 2022 年停止支持,且 Rails 7 已不再完整支持 IE,因此从默认响应头集合中移除,由需要它的应用自行显式添加。

9. 附录:RailInspector 如何守护这份 CHANGELOG 的格式

这份 fixture 同时是 rail_inspector 的输入样例,而 changelog.rb 中的解析与校验逻辑恰好把「Rails 官方 CHANGELOG 格式」以代码形式固定了下来。读懂它可以反过来帮你理解为什么本文件长成这个样子:

  • 条目结构:每条变更由「* 后跟 3 个空格开头的标题行」+「缩进 4 个空格的正文行」+「以 *作者名* 结尾的署名行」组成;footer 文本必须是 Please check x-y-stable 的形式(L148、L162-L165)。
  • 规则化检查Entry 会依次校验署名缺失(CHANGELOG entry is missing authors.,L38-L52)、标题是否以 * 加 3 空格开头(L54-L62)、正文是否统一缩进 4 空格(L64-L75)以及行尾空白(L78-L89)。
  • 解析策略Parser 通过 peek_probably_header? 识别新条目起点——对 * 开头行,若整行 * 数量为奇数或行尾不以 * 结尾,则判定为新条目(L168-L178),因此正文中的加粗内容不会被误判成新条目。
  • 测试闭环:本 fixture 之所以文件名带哈希后缀、内容规整,正是因为它要满足 changelog_test.rb 中断言「恰好解析出 21 个条目、零 offense」的前提。

若想亲自验证,可在仓库中运行 rail_inspector 针对各 */CHANGELOG.md 的检查(参见其 RunnerDir[rails_path.join("*/CHANGELOG.md")] 的遍历),输出形如 N changelogs inspected, M offense(s) detected 的统计,这正是 Rails 维护者保持每个框架 changelog 机器可读、风格统一的手段。

10. 小结

这份 railties CHANGELOG 片段虽然只是 Rails 7.0 庞大变更集的缩影,却精准勾勒出 Rails 团队当时的三条主线:rails new 的选项系统做得更精确可组合--minimal × --no-*、级联 skip、--js 别名)、把凭据与测试的日常开发体验打磨得更顺滑(自定义凭据模板、declarative 测试名过滤、./ 相对路径、避免二次启动)、以及持续收敛运行期与配置的语义(executor、$LOAD_PATHenable_reloadinglog_file_size)。以上每条变更如今都还能在当前仓库对应源码与测试中找到明确的实现证据,是理解 Rails 命令链演进的一份不可多得的原始资料。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.13 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
897
5.8 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
529
593
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
915
1.83 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.58 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.35 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.01 K
515
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
547
388