Thor CLI 高级用法
Hello Ruby 的命令行工具基于 Thor 构建。Thor 是 Ruby 生态中最流行的 CLI 框架之一,Rails 的命令行也是用 Thor 实现的。这一章带你深入了解 Thor 的高级功能,包括全局选项、命令专属选项、子命令注册、参数解析和自定义帮助。
掌握 Thor 后,你可以为自己的 Ruby 项目构建功能完整的命令行工具。运行 hello advance cli_advanced 可以查看完整演示代码。
class_option vs method_option
Thor 提供两种选项声明方式:class_option 和 method_option。理解两者的区别是构建复杂 CLI 的第一步。
class_option 定义一个类内所有命令共享的选项。适合全局配置项,比如 --verbose 和 --config:
class MyCLI < Thor
class_option :verbose, type: :boolean, default: false,
desc: "显示详细信息"
class_option :config, aliases: ["-c"],
desc: "指定配置文件路径"
desc "build", "构建项目"
def build
puts "verbose: #{options[:verbose]}"
puts "config: #{options[:config]}"
end
desc "deploy", "部署项目"
def deploy
puts "verbose: #{options[:verbose]}"
# class_option 在所有命令中都可用
end
end
method_option 只影响单个命令。适合命令专属的配置项:
class MyCLI < Thor
desc "build [INPUT]", "构建项目"
method_option :output, aliases: ["-o"], default: "./dist",
desc: "输出目录"
method_option :minify, type: :boolean, default: false,
desc: "压缩输出文件"
method_option :format, aliases: ["-f"], default: "esm",
enum: ["esm", "cjs"],
desc: "输出格式"
def build(input = ".")
puts "输入: #{input}"
puts "输出: #{options[:output]}"
puts "压缩: #{options[:minify]}"
puts "格式: #{options[:format]}"
end
end
调用方式:
mycli build --output ./out --minify --format esm
mycli build -o ./out -f cjs
mycli --verbose build -o ./out
规则很简单:全局共享的用 class_option,单个命令专属的用 method_option。
选项类型
Thor 支持多种选项类型,每种类型有不同的解析行为:
class AppCLI < Thor
# string(默认类型)— 接受任意字符串
method_option :name, type: :string
# 用法: --name Alice
# boolean — 标志位,无需值
method_option :verbose, type: :boolean, default: false
# 用法: --verbose(启用) 或 --no-verbose(禁用)
# numeric — 数字类型,自动转换
method_option :port, type: :numeric, default: 3000
# 用法: --port 8080
# hash — 键值对
method_option :headers, type: :hash
# 用法: --headers key1=value1 --headers key2=value2
# array — 数组,可多次出现
method_option :files, type: :array
# 用法: --files a.txt --files b.txt --files c.txt
# default — 允许 nil,nil 不转默认值
method_option :timeout, type: :default
# 用法: --timeout(nil) 或 --timeout 30(30)
end
enum 约束选项只能取特定值。这对于有固定选项的场景非常有用,Thor 会自动校验:
method_option :env, enum: ["dev", "staging", "prod"], default: "dev"
# 如果传入 --env test,Thor 会自动报错并显示有效值
所有选项都在 options hash 中访问,例如 options[:verbose]、options[:config]。
Subcommands:子命令注册
当命令数量增长时,把所有方法放在一个类中会变得臃肿。Thor 的 register 允许你将子命令拆分到独立的类中:
class AppCLI < Thor
register(UserCommands, "user", "user [CMD]", "用户管理相关命令")
register(BuildCommands, "build", "build [CMD]", "构建工具相关命令")
end
class UserCommands < Thor
desc "create NAME EMAIL", "创建用户"
def create(name, email)
puts "创建用户: #{name} <#{email}>"
end
desc "delete ID", "删除用户"
def delete(id)
puts "删除用户: #{id}"
end
end
class BuildCommands < Thor
desc "production", "构建生产版本"
def production
puts "构建生产版本..."
end
desc "development", "构建开发版本"
def development
puts "构建开发版本..."
end
end
注册后,用户可以这样调用:
app user create Alice alice@example.com
app build production
app user delete 42
register 的四个参数分别是:子命令类、子命令名、横幅描述、简短描述。这种方式让 CLI 的结构保持清晰,每个子命令类只关心自己的命令。
参数解析
Thor 支持多种参数模式:位置参数、剩余参数(splat)、必须参数。
class DeployCLI < Thor
# 位置参数 — 按顺序映射
desc "transfer FROM TO AMOUNT", "转账"
def transfer(from, to, amount)
puts "从 #{from} 转 #{amount} 到 #{to}"
end
# 剩余参数(splat)— 收集所有剩余参数
desc "add FILES...", "添加文件"
def add(*files)
files.each { |f| puts "添加: #{f}" }
end
# 必须参数 — 没有默认值 = 必填
desc "create NAME EMAIL", "创建用户"
def create(name, email)
# 缺少参数时 Thor 自动报错并显示帮助
puts "创建: #{name} <#{email}>"
end
# 可选参数 — 有默认值
desc "greet [NAME]", "打招呼"
def greet(name = "World")
puts "Hello, #{name}!"
end
end
调用方式:
app transfer alice bob 100
app add file1.txt file2.txt file3.txt
app create Alice alice@example.com
app greet Ruby # Hello, Ruby!
app greet # Hello, World!
参数也可以通过 args 数组访问。Thor 自动把位置参数映射为方法参数,不需要手动解析。
帮助与文档
Thor 自动生成 --help 输出。你需要提供的是命令描述和长描述:
class MyCLI < Thor
desc "build [INPUT]", "构建项目"
long_desc <<-DESC
将指定目录中的源代码构建为可部署的产物。
默认输出到 ./dist 目录,可以用 --output 自定义。
支持的格式: esm, cjs, iife。默认 esm。
示例:
mycli build
mycli build ./src -o ./out --minify
mycli build --format cjs --verbose
DESC
method_option :output, aliases: ["-o"], default: "./dist"
def build(input = ".")
end
# 自定义横幅
banner "mycli build [OPTIONS]"
# 短选项映射
map "-T" => :tasks
# 隐藏命令(不出现在 --help 中)
# 使用 method_option 的 hidden: true 或 desc nil
end
生成的帮助输出:
$ mycli --help
Commands:
mycli build [INPUT] # 构建项目
$ mycli help build # 查看 build 命令的详细帮助(显示 long_desc)
desc 提供简短描述,出现在命令列表中。long_desc 提供详细文档,只有在单独查看某个命令的帮助时才显示。map 创建短选项别名。
最佳实践
构建 CLI 工具时遵循以下原则:
- 每个命令一个方法,保持方法体简洁。超过 30 行的逻辑应该抽离到独立的服务类。
- 用 class_option 共享全局选项,如
--verbose、--config、--dry-run。这些选项几乎在每个命令中都需要。 - 用 method_option 定义命令专属选项,避免无关选项污染其他命令。
- 复杂命令抽离为独立类,通过
register挂载。一个类管理一组相关的命令。 - 设置 exit_on_failure? = true。确保未知子命令或参数错误时程序退出而不是静默忽略。
- 自定义版本命令。Thor 的默认
--version输出不够自定义,建议用desc "version"重写。 - 所有用户输入都校验。如果参数必须是数字、枚举值或文件路径,在方法内部校验而不是依赖 Thor 的默认行为。
本章要点
- class_option 定义类内所有命令共享的选项
- method_option 定义单个命令专属的选项
- 选项类型包括 string、boolean、numeric、hash、array,每种有不同的解析行为
- register 将子命令注册到独立的类中,保持代码组织清晰
- 参数模式包括位置参数、剩余参数(splat)、必须参数和可选参数
- desc + long_desc 生成
--help和thor help COMMAND的文档输出 - 每个命令保持方法体简洁,复杂逻辑抽离到服务类
- 运行
hello advance cli_advanced查看完整 Thor 高级用法演示