深入 Ruby 00:导读与可重复运行
一个 Ruby 文件可以只有三行,但运行它需要的不只是这三行。解释器版本、加载的 gem、输入字节、当前目录和环境变量都可能改变结果。学习语言时,先把这些条件固定,后面的对象模型和并发结论才有可复核的依据。
本系列使用 Ruby 3.4.11 和 CRuby。这个版本是教学基线,不代表 Ruby 的最新版。官方发布包的 SHA256 是 5c22be44524312b3d433d68739bcc530633b1da5ef8ba0afa0a37680da17d3de;本文工程实际从该包构建。版本冻结、依赖锁和源码 revision 分别记录在工程与研究资料里。Ruby 3.4.11 发布说明
文章卡
| 项目 | 内容 |
|---|---|
| 先修 | 一种通用编程语言;能运行终端命令 |
| 核心问题 | 怎样证明命令使用了所选解释器,并且结果满足约定? |
| 累计工程 | examples/ruby/lib/taskbook.rb,内存任务校验、筛选和统计 |
| 实验入口 | 工程根目录执行 ruby labs/00/check.rb |
| 验收 | 正确筛选、统计、输入拒绝和复制隔离都有可失败断言 |
| 版本边界 | CRuby 3.4.11;Rails 与其他实现不参与本篇 |
从解释器开始确认环境
工程里的 .ruby-version 内容是 3.4.11。它供版本管理器读取,文件本身不会切换解释器;一个终端可以无视它继续调用系统 Ruby。进入工程后先执行:
1 | |
command -v 说明 Shell 找到了哪个入口,ruby -v 说明这个入口实际启动的解释器,RUBY_ENGINE 区分 CRuby 与 JRuby 等实现。路径、版本和实现需要同时记录。只截图终端提示符中的版本名称,不能证明子进程用了同一个 Ruby。
这次实验在 arm64 macOS 上构建独立解释器,安装在临时目录,未替换系统 Ruby。读者可以使用已有的版本管理器,也可以从官方包构建;验收依据仍是实际输出,而不是安装工具的名称。把平台记录下来,是为了避免将后文的信号、进程和性能结果直接套到 Windows 或另一种 CPU 上。
用一个小任务工具贯穿系列
Taskbook 处理合成任务,每项只有五个字段:
1 | |
ID 必须是正整数且在清单内唯一;标题和标签必须是有效 UTF-8 编码的非空字符串;优先级是 1 到 5 的整数;状态只接受 :todo 与 :done。字段不齐全、出现额外字段、用字符串代替优先级,都会被拒绝。JSON 中的字符串键如何转换,留给第 26 篇的外部数据边界处理,核心库不顺便猜测输入意图。
这份约定不复杂,却足以暴露语言问题:0 在条件里是什么值?Hash 默认值会不会被共享?筛选后的标签还能否改动原输入?方法收到的是关键字,还是最后一个 Hash?同一份案例可以逐步验证这些问题,而不必先学数据库和 Web 框架。
核心 API 是三个普通模块方法:
1 | |
require_relative 相对当前源文件定位库,与执行命令时的工作目录不同。这段代码保存到工程根目录才能使用这里的相对路径;正式 lab 已按自己的位置处理路径。加载库只定义能力,不自动读文件、开端口或处理任务,因此测试可以控制每次调用的输入。
filter 保留原始顺序。它先校验并复制任务,再筛选;summary 也先校验。这里选择明确拒绝非法数据,不把格式错误当成“没有结果”。复制不是为了让所有 Ruby 对象都深度不可变,而是针对已声明的五个字段复制可变字符串和标签数组。
成功、失败与别名都需要断言
打印 [1, 3] 只能供人观察。若程序后来打印 [3, 1],终端仍会正常退出。raise unless 将约定转化为退出状态:满足则继续,不满足则抛出异常并返回非零。第 00 篇暂时使用这种最小断言,第 23 篇再加入 Minitest 的用例、统计和统一入口。
1 | |
第二个实验有一个关键位置:raise 'bad priority accepted' 产生的是 RuntimeError,不会被这里的 rescue ArgumentError 吞掉。若把两个异常都统一捕获,反例可能永远通过。验证错误行为时,要捕获约定的异常类型,并让“没有发生预期错误”本身成为失败。
labs/00/check.rb 还验证空筛选、重复 ID、非法标题、错误标签类型、错误状态和未知字段。合法 UTF-16 与坏 UTF-8 都应得到 ArgumentError;筛选参数 tag 遵守同一编码限制。通过说明这些固定输入的结果符合契约,不等于任意规模和任意输入已经被证明正确。后面的测试会补充边界,保持每次证明的范围清楚。
语言实验与累计应用分开组织
工程分成 lib、exe、test、labs 和 evidence。lib 承担实际任务能力,exe 提供 CLI,test 检查应用行为。labs/NN 保存某个语言机制的正反实验;evidence/NN 保存环境、命令、实际输出和判定。
单例类、非局部 return、Fiber scheduler 和字节码观察适合独立 lab,不必全部进入任务工具。为了演示元编程而把五个普通字段改成动态方法生成器,会增加阅读成本;案例只在能力确实需要时增长。
依赖阶段再执行:
1 | |
Gemfile.lock 冻结该工程验证时使用的依赖;gemspec 声明库对外兼容的范围。锁文件不是所有用户安装同一版本的保证,也不证明任何新平台均可安装。第 21、22 与 39 篇分别检查版本激活、包内容和临时目录中的实际执行。
阅读路线与实验材料
第 00 到 08 篇讨论表达式、参数和枚举。第 09 到 17 篇讨论名字绑定、方法查找与元编程。第 18 到 29 篇形成有资源边界和反馈工具的应用。第 30 到 39 篇用受控实验观察并发、内存、执行路径与完整交付。Rails 等框架属于写作计划的选修,不是理解核心语言的前提。
完整源码包提供所有主线实验、固定依赖和运行记录:下载 Taskbook 与 Ruby labs。解压后以 examples/ruby 为工程根目录;先检查 Ruby 版本,再按篇目运行 lab。源码包内的结果文件供对照,重新运行时应保存本机输出,尤其不能把另一台机器的性能数字当成本机结果。
本篇练习是在副本中删掉优先级上界检查,再运行第 00 篇实验。应出现“非法优先级被接受”的失败。随后恢复检查,把一项状态改成字符串 'todo',确认核心库拒绝它。这两个操作分别检查验收是否能发现错误、输入契约是否明确,完成后不用扩充业务功能。
主线目录
主线 40 篇与选修 8 篇均已写成,每篇附独立实验,共 48 篇。主线以 CRuby 3.4.11 为基线;选修另行冻结 Rails、JRuby、原生构建与数据库环境。
选修目录
选修按各篇前置概念进入。Rails 与 SQL 使用各自锁文件,JRuby 需要 JDK 21,原生扩展需要编译器;全部实验入口为 bundle exec ruby verify.rb,首次运行前按各 lab 安装说明准备依赖。
参考资料
系列导航
导读 · 下一篇:01:表达式与控制流 · 完整源码包
