`rails plugin` コマンドに不正なサブコマンドのバリデーションを追加
rails plugin foo bar のように無効なサブコマンドを渡した際に、サイレントに無視して rails plugin new bar として処理されていた問題が修正されました。無効なサブコマンドはエラーメッセージを出力してステータス1で終了するようになります。
背景
rails plugin コマンドは、有効なサブコマンドとして new のみをサポートしていますが、それ以外のサブコマンドが渡された場合でもエラーが発生しませんでした。#57430 で報告されたように、rails plugin foo bar を実行すると "foo" が無視され、rails plugin new bar と同等の動作として "bar" という名前のプラグインが生成されていました。
この挙動はタイポやサブコマンドの誤認識が気づかれないまま意図しないプラグイン生成につながる危険性があり、混乱を招くものでした。
技術的な変更
PluginCommand#perform に type 引数のバリデーションが追加されました。変更前は type == "new" でない場合に --help を差し込むだけの単純な1行の条件分岐でしたが、変更後は type が存在するかどうかを先に判定する構造になっています。
変更前:
def perform(type = nil, *plugin_args)
plugin_args << "--help" unless type == "new"
# ...
end
変更後:
def perform(type = nil, *plugin_args)
if type
unless type == "new"
error "'#{type}' is not a valid plugin subcommand. Valid subcommand: new."
error "Run '#{self.class.executable} -h' for help."
exit 1
end
else
plugin_args << "--help"
end
# ...
end
type が nil(サブコマンドなし)の場合は従来どおり --help を付与してヘルプを表示します。type が存在し、かつ "new" 以外である場合はエラーメッセージを2行出力してステータス1で終了します。
テストファイル railties/test/commands/plugin_test.rb が新規追加され、以下のケースが網羅されています:
- 無効なサブコマンド(
foo bar)指定時にステータス1で終了し、エラーメッセージが含まれること -
rails plugin newでパスなし実行時にヘルプが表示されること -
--helpオプションでヘルプが表示されること -
--mountable・--full・--skip-test等のオプション付きnewが正常動作すること
設計判断
type の有無と有効性を分離して判定する構造が採用されました。変更前の unless type == "new" という1つの条件では nil と無効な文字列を区別できませんでしたが、if type による先行チェックを挟むことで両者の挙動を明確に分岐させています。
exit 1 を使用した即時終了という実装は、Unix的なCLIの慣習に沿っており、シェルスクリプトやCI環境でのエラーハンドリングとも親和性が高い選択です。また、エラーメッセージに self.class.executable を使用することで、コマンド名をハードコードせずに動的に参照する設計となっています。
まとめ
本PRはCLIのフェイルファスト原則を rails plugin コマンドに適用した修正です。無効な入力をサイレントに無視する代わりに明示的なエラーで終了させることで、意図しないプラグイン生成を防ぐとともに、コマンドの仕様をユーザーに正確に伝えられるようになりました。