OptionParser
Ruby標準ライブラリの一部で、基本的なコマンドライン引数解析タスクのためのシンプルな選択肢です。
フレームワーク
OptionParser
概要
OptionParserは、Ruby標準ライブラリの一部で、基本的なコマンドライン引数解析タスクのためのシンプルな選択肢です。標準ライブラリとして、追加の依存関係なしで使用できるため、シンプルなスクリプトで引き続き使用されています。学習コストが低く、Rubyの基本的なCLIツール開発に適しています。
詳細
OptionParserは、Rubyに標準で含まれているコマンドライン引数解析ライブラリです。外部gemを追加することなく使用でき、基本的なオプション解析機能を提供します。シンプルなAPIと軽量な実装により、小規模なCLIツールの開発に適しています。
主な特徴
- 標準ライブラリ: 追加のgemインストール不要
- 軽量: 最小限のオーバーヘッド
- 基本機能: 必要十分なオプション解析機能
- 自動ヘルプ生成: ヘルプメッセージの自動生成
- 型変換: 文字列、数値、配列への自動変換
- バリデーション: 基本的な値の検証
- 短縮形・長形式: -v と --verbose の両方をサポート
メリット・デメリット
メリット
- 依存関係なし: 標準ライブラリなので追加インストール不要
- 学習コストが低い: シンプルで理解しやすいAPI
- 軽量: メモリ使用量とパフォーマンスのオーバーヘッドが少ない
- 安定性: Ruby標準ライブラリとして長期サポート
- 十分な機能: 基本的なCLIツールには必要十分
デメリット
- 機能制限: 高度な機能は他のライブラリに劣る
- サブコマンド非対応: 複雑なCLI構造には不向き
- 拡張性: カスタマイズ性に限界がある
- モダンでない: 最新のCLI設計パターンには対応していない
主要リンク
書き方の例
基本的な使用例
#!/usr/bin/env ruby
require 'optparse'
# オプションを格納するハッシュ
options = {}
# OptionParserインスタンスを作成
opt_parser = OptionParser.new do |opts|
opts.banner = "使用法: #{$0} [オプション] ファイル"
# 文字列オプション
opts.on('-n', '--name NAME', '名前を指定') do |name|
options[:name] = name
end
# 数値オプション
opts.on('-c', '--count COUNT', Integer, '繰り返し回数') do |count|
options[:count] = count
end
# フラグオプション
opts.on('-v', '--verbose', '詳細出力') do
options[:verbose] = true
end
# ヘルプオプション
opts.on('-h', '--help', 'ヘルプを表示') do
puts opts
exit
end
end
# 引数を解析
begin
opt_parser.parse!(ARGV)
rescue OptionParser::InvalidOption => e
puts "エラー: #{e.message}"
puts opt_parser
exit 1
end
# 残りの引数(ファイル名など)
files = ARGV
# オプションの使用
name = options[:name] || 'World'
count = options[:count] || 1
verbose = options[:verbose]
# 実行
count.times do |i|
puts "こんにちは、#{name}さん!"
puts "実行回数: #{i + 1}" if verbose
end
if files.any?
puts "処理ファイル: #{files.join(', ')}"
end
より高度な例
#!/usr/bin/env ruby
require 'optparse'
require 'ostruct'
# オプションを格納するオブジェクト
options = OpenStruct.new
options.verbose = false
options.format = 'txt'
options.output = nil
options.force = false
opt_parser = OptionParser.new do |opts|
opts.banner = "ファイル処理ツール v1.0\n使用法: #{$0} [オプション] 入力ファイル..."
opts.separator ""
opts.separator "基本オプション:"
# 必須オプション(後でチェック)
opts.on('-o', '--output FILE', '出力ファイル名') do |output|
options.output = output
end
# 選択肢からの選択
opts.on('-f', '--format FORMAT', %w[txt csv json xml],
'出力形式 (txt, csv, json, xml)') do |format|
options.format = format
end
# 数値レンジ
opts.on('-l', '--limit LIMIT', Integer, '処理制限数 (1-1000)') do |limit|
if limit < 1 || limit > 1000
raise OptionParser::InvalidArgument, "制限数は1-1000の範囲で指定してください"
end
options.limit = limit
end
# 配列オプション
opts.on('-t', '--tags TAG1,TAG2,TAG3', Array, 'タグ一覧(カンマ区切り)') do |tags|
options.tags = tags
end
# 正規表現オプション
opts.on('-p', '--pattern REGEX', Regexp, 'フィルタパターン(正規表現)') do |pattern|
options.pattern = pattern
end
opts.separator ""
opts.separator "フラグオプション:"
opts.on('-v', '--verbose', '詳細出力') do
options.verbose = true
end
opts.on('-q', '--quiet', '静寂モード') do
options.quiet = true
end
opts.on('--force', '強制実行') do
options.force = true
end
opts.on('--dry-run', '実際には実行しない') do
options.dry_run = true
end
opts.separator ""
opts.separator "その他:"
opts.on_tail('-h', '--help', 'このヘルプを表示') do
puts opts
exit
end
opts.on_tail('--version', 'バージョンを表示') do
puts "ファイル処理ツール v1.0"
exit
end
end
# 引数解析とエラー処理
begin
opt_parser.parse!(ARGV)
rescue OptionParser::InvalidOption => e
STDERR.puts "エラー: #{e.message}"
STDERR.puts opt_parser
exit 1
rescue OptionParser::InvalidArgument => e
STDERR.puts "エラー: #{e.message}"
exit 1
end
# 必須パラメータのチェック
if ARGV.empty?
STDERR.puts "エラー: 入力ファイルが指定されていません"
STDERR.puts opt_parser
exit 1
end
# 相互排他オプションのチェック
if options.verbose && options.quiet
STDERR.puts "エラー: --verbose と --quiet は同時に指定できません"
exit 1
end
# 設定の表示
unless options.quiet
puts "処理設定:"
puts " 入力ファイル: #{ARGV.join(', ')}"
puts " 出力ファイル: #{options.output || '標準出力'}"
puts " 出力形式: #{options.format}"
puts " 制限数: #{options.limit || '無制限'}"
puts " タグ: #{options.tags&.join(', ') || 'なし'}"
puts " パターン: #{options.pattern || 'なし'}"
puts " 詳細出力: #{options.verbose ? 'あり' : 'なし'}"
puts " ドライラン: #{options.dry_run ? 'あり' : 'なし'}"
puts
end
# ファイル処理の実行
ARGV.each do |input_file|
unless File.exist?(input_file)
STDERR.puts "警告: ファイルが見つかりません: #{input_file}"
next
end
puts "処理中: #{input_file}" if options.verbose
# ファイル処理のシミュレーション
unless options.dry_run
lines = File.readlines(input_file)
# パターンフィルタリング
if options.pattern
lines = lines.select { |line| line.match?(options.pattern) }
end
# 制限数の適用
if options.limit
lines = lines.first(options.limit)
end
# 出力処理
output_content = case options.format
when 'csv'
lines.map { |line| "\"#{line.chomp}\"" }.join(",\n")
when 'json'
require 'json'
lines.map(&:chomp).to_json
when 'xml'
"<lines>\n" + lines.map { |line| " <line>#{line.chomp}</line>" }.join("\n") + "\n</lines>"
else
lines.join
end
# 出力
if options.output
File.write(options.output, output_content)
puts "出力完了: #{options.output}" unless options.quiet
else
puts output_content
end
end
end
puts "処理完了" unless options.quiet
設定ファイル読み込みとの組み合わせ
#!/usr/bin/env ruby
require 'optparse'
require 'yaml'
require 'json'
class ConfigurableApp
def initialize
@options = {
config_file: nil,
host: 'localhost',
port: 8080,
debug: false,
log_level: 'info'
}
end
def parse_options
opt_parser = OptionParser.new do |opts|
opts.banner = "設定可能アプリケーション\n使用法: #{$0} [オプション]"
opts.on('-c', '--config FILE', '設定ファイル (YAML/JSON)') do |file|
@options[:config_file] = file
end
opts.on('-H', '--host HOST', 'ホスト名') do |host|
@options[:host] = host
end
opts.on('-p', '--port PORT', Integer, 'ポート番号') do |port|
@options[:port] = port
end
opts.on('-d', '--debug', 'デバッグモード') do
@options[:debug] = true
end
opts.on('-l', '--log-level LEVEL', %w[debug info warn error],
'ログレベル (debug, info, warn, error)') do |level|
@options[:log_level] = level
end
opts.on('-h', '--help', 'ヘルプを表示') do
puts opts
exit
end
end
opt_parser.parse!
end
def load_config
return unless @options[:config_file]
unless File.exist?(@options[:config_file])
STDERR.puts "エラー: 設定ファイルが見つかりません: #{@options[:config_file]}"
exit 1
end
begin
config_content = File.read(@options[:config_file])
# ファイル拡張子で形式を判定
config_data = case File.extname(@options[:config_file]).downcase
when '.yml', '.yaml'
YAML.safe_load(config_content)
when '.json'
JSON.parse(config_content)
else
STDERR.puts "エラー: 未対応の設定ファイル形式"
exit 1
end
# 設定ファイルの値をデフォルトとして使用(コマンドライン引数が優先)
config_data.each do |key, value|
sym_key = key.to_sym
if @options.key?(sym_key) && @options[sym_key] == default_value(sym_key)
@options[sym_key] = value
end
end
rescue YAML::SyntaxError, JSON::ParserError => e
STDERR.puts "エラー: 設定ファイルの解析に失敗しました: #{e.message}"
exit 1
end
end
def run
parse_options
load_config
puts "アプリケーション設定:"
puts " ホスト: #{@options[:host]}"
puts " ポート: #{@options[:port]}"
puts " デバッグ: #{@options[:debug] ? '有効' : '無効'}"
puts " ログレベル: #{@options[:log_level]}"
puts " 設定ファイル: #{@options[:config_file] || 'なし'}"
# アプリケーションのメイン処理
start_server
end
private
def default_value(key)
defaults = {
host: 'localhost',
port: 8080,
debug: false,
log_level: 'info'
}
defaults[key]
end
def start_server
puts "\nサーバーを開始中..."
puts "http://#{@options[:host]}:#{@options[:port]}"
if @options[:debug]
puts "デバッグモード: 詳細ログが有効です"
end
# サーバー起動のシミュレーション
puts "サーバーが正常に起動しました (Ctrl+C で停止)"
begin
loop do
sleep 1
end
rescue Interrupt
puts "\nサーバーを停止中..."
puts "サーバーが停止しました"
end
end
end
# アプリケーション実行
app = ConfigurableApp.new
app.run
バリデーションとエラーハンドリングの例
#!/usr/bin/env ruby
require 'optparse'
class ValidatedApp
def initialize
@options = {}
@errors = []
end
def parse_options
opt_parser = OptionParser.new do |opts|
opts.banner = "バリデーション付きアプリケーション"
opts.on('-e', '--email EMAIL', 'メールアドレス') do |email|
@options[:email] = email
end
opts.on('-u', '--url URL', 'URL') do |url|
@options[:url] = url
end
opts.on('-f', '--file FILE', 'ファイルパス') do |file|
@options[:file] = file
end
opts.on('-n', '--number NUMBER', Integer, '数値 (1-100)') do |number|
@options[:number] = number
end
opts.on('-d', '--date DATE', '日付 (YYYY-MM-DD)') do |date|
@options[:date] = date
end
opts.on('-h', '--help', 'ヘルプを表示') do
puts opts
exit
end
end
begin
opt_parser.parse!
rescue OptionParser::InvalidOption, OptionParser::InvalidArgument => e
@errors << e.message
end
validate_options
end
def validate_options
# メールアドレスのバリデーション
if @options[:email]
email_regex = /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
unless @options[:email].match?(email_regex)
@errors << "無効なメールアドレス形式: #{@options[:email]}"
end
end
# URLのバリデーション
if @options[:url]
url_regex = /\Ahttps?:\/\/[\S]+\z/
unless @options[:url].match?(url_regex)
@errors << "無効なURL形式: #{@options[:url]}"
end
end
# ファイル存在チェック
if @options[:file]
unless File.exist?(@options[:file])
@errors << "ファイルが見つかりません: #{@options[:file]}"
end
end
# 数値範囲チェック
if @options[:number]
unless (1..100).include?(@options[:number])
@errors << "数値は1-100の範囲で指定してください: #{@options[:number]}"
end
end
# 日付形式チェック
if @options[:date]
begin
require 'date'
Date.strptime(@options[:date], '%Y-%m-%d')
rescue Date::Error
@errors << "無効な日付形式: #{@options[:date]} (YYYY-MM-DD形式で入力してください)"
end
end
end
def run
parse_options
if @errors.any?
STDERR.puts "バリデーションエラー:"
@errors.each { |error| STDERR.puts " - #{error}" }
exit 1
end
puts "バリデーション成功!"
puts "設定値:"
@options.each do |key, value|
puts " #{key}: #{value}"
end
end
end
# アプリケーション実行
app = ValidatedApp.new
app.run
サンプル設定ファイル
# config.yml
host: "0.0.0.0"
port: 3000
debug: true
log_level: "debug"
{
"host": "0.0.0.0",
"port": 3000,
"debug": true,
"log_level": "debug"
}
実行例
# 基本的な使用
ruby script.rb --name "Ruby" --count 3 --verbose file1.txt file2.txt
# 設定ファイルと組み合わせ
ruby configurable_app.rb --config config.yml --port 9000
# バリデーション例
ruby validated_app.rb --email test@example.com --url https://example.com --number 50