ごく簡単なバリデータを書く

コマンドラインツールのオプションは極端に省略されていて、可読性に欠けている面があったりする。(また他のツールと一貫性のない略語が使われていたりするので、なおさらタチが悪い)

Rubyでそういったコマンドラインツールのラッパをメソッドで実装しようと考えたとき、コマンドラインツールに渡すオプションは少し冗長であったとしても、そのオプションがどのような役割を持つのか理解できる十分な長さを持った文字列(あるいはシンボル)で渡したい。ラッパ用の十分に長いオプション名のキーと、ツールに直接渡すオプションの文字列の値が対応するHashを使うと便利そうだ。

SOME_CMD_OPTION = {
  :force_create => '-f'
}

def some_cmd(opts={})
  args = ['some_cmd']
  args << opts.map {|k, v| SOME_CMD_OPTION[k] }
  system args
end

some_cmd :force_create => true

このようなコードが考えられる。

また、オプションの中には任意の引数を取るものがある。

たとえば、あるオプション "-c" を考える。 "-c" はコマンドに渡す設定ファイル(configuration file)のパスを指定するオプションで、任意のファイルへのパスを引数にとる。

$ some_cmd -c ../hoge.rc

このようなコマンドのラッパをRubyで書こうと考えた。文字列置き換えを使ってみる。

SOME_CMD_OPTION = {
  :config => &#39;-c %s&#39;,
  :force_create => &#39;-f&#39;
}

def some_cmd(opts={})
  args = [&#39;some_cmd&#39;]
  args << opts.map {|k, v| SOME_CMD_OPTION[k] % v }
  system args
end

some_cmd :config => &#39;../hoge.rc&#39;

よさげである。

しかし、オプションの中には0個以上の引数を取るものもある。つまり、引数がある場合とない場合の、どちらも考えられる場合。これは、文字列置き換えでは対応できない。

ここまで複雑になってくると、素直にRubyに引数チェックを移譲したほうが楽である。しかし、 Module#define_method などをつかって動的にメソッドを定義するのはいささか大事である。

そこで、必要とする引数に応じた手続オブジェクトを生成するDSL的メソッドを定義してみる。