Ruby の OptionParser チートシート

## テンプレ

おれのテンプレ。class 化して require したただけでは、オプション処理が発動しないようにしている。

```rb:lib/test/cli.rb
require 'optparse'

class Test
  class CLI
    def parse_options(argv = ARGV)
      op = OptionParser.new
  
      # op がローカル変数なので `define_method` で `usage` を定義しているが、ただのこだわりに過ぎないと言えばその通り
      self.class.module_eval do
        define_method(:usage) do |msg = nil|
          puts op.to_s
          puts "error: #{msg}" if msg
          exit 1
        end
      end

      # default value
      opts = {
        boolean: false,
        string: '',
        integer: 0,
        array: [],
      }

      # boolean
      op.on('-b', '--[no-]boolean', "boolean value (default: #{opts[:boolean]})") {|v|
        opts[:boolean] = v
      }
      # string argument
      op.on('-s', '--string VALUE', "string value (default: #{opts[:string]})") {|v|
        opts[:string] = v
      }
      # array argument
      op.on('-a', '--array one,two,three', Array, "array value (default: #{opts[:array]})") {|v|
        opts[:array] = v
      }
      # optional integer argument (just cast)
      op.on('-i', '--integer [VALUE]', "integer value (default: #{opts[:integer]})") {|v|
        opts[:integer] = v.to_i
      }

      op.banner += ' ARG1 ARG2'
      # op.parse! は破壊的だが、op.parse は破壊的ではない (ARGV を改変しない)
      begin
        args = op.parse(argv)
      rescue OptionParser::InvalidOption => e
        usage e.message
      end
  
      if args.size < 2
        usage 'number of arguments is less than 2'
      end

      [opts, args]
    end

    def run
      opts, args = parse_options
      puts "opts: #{opts.to_s}"
      puts "args: #{args.to_s}"
    end
  end
end
```

```rb:bin/test.rb
#!/usr/bin/env ruby

require_relative '../lib/test/cli'
Test::CLI.new.run
```

## help

help は `-h` または `--help` で表示される。デフォルトで**予約されている**。
こんなかんじの表示になる。

```
$ bin/test.rb -h
Usage: test [options] ARG1 ARG2
    -b, --[no-]boolean               boolean value (default: false)
    -s, --string VALUE               string value (default: )
    -i, --integer VALUE              integr value (default: 0)
    -a, --array one,two,three        array value (default: [])
```

間違ったオプションを指定した場合は、(このテンプレでは)次のようになる

```
$ bin/test.rb --hoge
Usage: test [options] ARG1 ARG2
    -b, --[no-]boolean               boolean value (default: false)
    -s, --string VALUE               string value (default: )
    -i, --integer VALUE              integr value (default: 0)
    -a, --array one,two,three        array value (default: [])
error: invalid option: --hoge
```

後述しているが `-h` は暗黙的に予約されているので、`--host` オプションなどを生やしたくなった場合に注意が必要。

## bolean オプション

引数がないオプションは boolean オプションとして使うことができる。

v には true が入る。

```ruby
op.on('-b', '--boolean', "boolean value") {|v|
  opts[:boolean] = v
}
```

```bash
$ bin/test.rb --boolean ARG1 ARG2
opts: {:boolean=>true, :string=>"", :integer=>0, :array=>[]}
args: ["ARG1", "ARG2"]
```

## boolean 否定(no) オプション

boolean なオプションを、`--[no-]...` とすることで否定形オプションを作成することができる。

```ruby
op.on('-b', '--[no-]boolean', "boolean value") {|v|
  opts[:boolean] = v
}
```

`--boolean` と指定すると、v が true になり、`--no-boolean` と指定すると、v が false になる。

```bash
$ bin/test.rb --boolean ARG1 ARG2
opts: {:boolean=>true, :string=>"", :integer=>0, :array=>[]}
args: ["ARG1", "ARG2"]
```

```bash
$ bin/test.rb --no-boolean ARG1 ARG2
opts: {:boolean=>false, :string=>"", :integer=>0, :array=>[]}
args: ["ARG1", "ARG2"]
```

## 文字列引数オプション

```rb
op.on('-s', '--string VALUE', "string value") {|v|
  opts[:string] = v
}
```

のようにオプション定義で末尾に何かを書くと、そのオプションは引数を受け付けることができるようになる。例では `VALUE` としているが、この文字はなんでもよくて、`help` の見た目にのみ影響する。

## 整数引数オプション


integer などなんらかの型処理をしたい場合は、受け取った値を自分で加工して処理しても良いし、

```rb
op.on('-i', '--integer VALUE', "integer value") {|v|
  opts[:integer] = v.to_i # integer に cast
}
```

以下のように型指定しておくと、勝手に変換処理をしてくれるようにもできる。

```rb
op.on('-i', '--integer VALUE', Integer, "integer value") {|v|
  opts[:integer] = v
}
```

他にも指定可能な型がいくつかあるので http://docs.ruby-lang.org/ja/2.1.0/method/OptionParser/i/on.html を見ておいても良い(し、自分で処理しても良い)。

## Enum引数オプション

```ruby
op.on("-e", "--enum VALUE", [:http, :ftp, :https], "enum value") {|v|
  opts[:enum] = v
}
```

のようにすると、オプションの値に指定可能な文字列を列挙することができる。

列挙されてない文字列を指定すると `OptionParser::InvalidArgument` 例外が発生する。


## 配列引数オプション

```rb
op.on('-a', '--array one,two,three', Array, "array value") {|v|
  opts[:array] = v
}
```

Array と指定したオプションには、, (カンマ)区切りで複数の値を指定することができる。

```bash
$ bin/test.rb -a 1,2,3 ARG1 ARG2
opts: {:boolean=>false, :string=>"", :integer=>0, :array=>["1", "2", "3"]}
args: ["ARG1", "ARG2"]
```

※ thor gem の場合、 array は スペース区切りで指定するので仕様が異なる．

perl のとあるオプションライブラリのように `-a foo -a bar` のようにして複数指定したい場合は、以下のようにして自前で処理すればできなくはない。

```rb
opts[:array] = []
op.on('-a', '--array VALUE', "array value") {|v|
  opts[:array] << v
}
```

## --long-option=VALUE

```rb
op.on('-s', '--string=VALUE', "string value") {|v|
  opts[:string] = v
}
```

のように = を使ってもよいし、

```rb
op.on('-s', '--string VALUE', "string value") {|v|
  opts[:string] = v
}
```

のようにスペースを使ってもよい。`OptionParser#on` での記述は、help の見た目に反映されるだけで、コマンドラインからは実は `--string VALUE` または `--string=VALUE` のどちらでも指定することができる。

```
$ bin/test.rb -h
Usage: test [options] ARG1 ARG2
    -b, --boolean                    boolean value (default: false)
    -s, --string=VALUE               string value (default: )
    -i, --integer VALUE              integr value (default: 0)
    -a, --array one,two,three        array value (default: [])
```

## オプショナル引数

```rb
op.on('-i', '--integer [VALUE]', "integer value") {|v|
  opts[:integer] = v.to_i
}
```

`[VALUE]` のように `[` 始まりの文字で指定したオプションの引数は、オプショナルになる。

オプションはただでさえオプショナルなので、あまり使う用事は思いつかない(し、使ったことがない）。

## オプション引数の位置

オプションはコマンドの直後に指定しないといけない類のオプションパーサも世の中にはあるが、OptionParser の場合は、オプションを引数の後ろに書いても動く。

```bash
$ bin/test.rb ARG1 -a 1,2,3 ARG2
opts: {:boolean=>false, :string=>"", :integer=>0, :array=>["1", "2", "3"]}
args: ["ARG1", "ARG2"]
```

環境変数 POSIXLY_CORRECT を定義することによって、オプションはコマンドの直後に指定しなければならない、と制限することもできる。

```
$ POSIXLY_CORRECT=1 be ruby bin/test.rb ARG1 -a 1,2,3 ARGV2
opts: {:boolean=>false, :string=>"", :integer=>0, :array=>[]}
args: ["ARG1", "-a", "1,2,3", "ARGV2"]
```

ARG1 以降がオプションとして解釈されなくなっていることがわかる。`POSIXLY_CORRECT` は自分は特に使う事はないかな。

## -- 以降はオプションとして扱わない

`--` 以降の引数は、オプションとして扱わない、という機能も備わっている。

```
$ bin/test.rb -a 1,2,3 -- --foo --bar
opts: {:boolean=>false, :string=>"", :integer=>0, :array=>["1", "2", "3"]}
args: ["--foo", "--bar"]
```

## 二文字 short option は無理

`-a1` みたいな perl のとあるオプションライブラリではできてしまうようなオプションは作れない。

※ thor gem でも無理

## short option は暗黙的(勝手)に定義される

```rb
op.on('--host VALUE', "hostname") {|v|
  opts[:host] = v
}
```

のように定義しても、勝手に `-h VALUE` オプションも定義される。ただし、この場合 help には出ないので厄介。

これにより、`-h` オプションが `--help` の short option だったのが、壊れるので厄介。

```
$ bin/test.rb -h
bin/test.rb:44:in `parse_options': missing argument: -h (OptionParser::MissingArgument)
```

これを躱すには、

```rb
op.on('-h', '--help', "help") {|v|
  opts[:help] = v
}
```

のように自分で -h オプションを定義して、

```rb
if opts[:help]
  usage
end
```

のように自分で処理すればよい。

## long オプションの文字列 (_ または -)

`--long-option` と記述すれば `--long-option` だし、`--long_option` と記述すれば `--long_option` と指定した文字列通り。

※ なぜわざわざこんなことを書くかというと、thor の場合は、`:long_option` と指定した場合、`--long_option` でも `--long-option` (ハイフンになっている) でもどちらでもコマンドラインから指定できるようになるため

一般的にはオプション名はアンダースコアよりもハイフンだと思う(アンダースコアだとタイプするのに Shift キーを押さないといけない)。標準があるんだったか？

## おまけ：ARGV.getopts

ARGV.getops という形式もあり、これを使うと Linux の getopts っぽい指定ができる。

```ruby:bin/test.rb
require 'optparse'
params = ARGV.getopts('abc:')
p params
```

```
% bin/test.rb -a -c /tmp/file
{"a"=>true, "b"=>false, "c"=>"/tmp/file"}

% bin/test.rb -h
Usage: test.rb [options]
    -a
    -b
    -c VAL
```

http://qiita.com/genta/items/785900055a5be2ba5d2c の文書が詳しい。


## 参考文献

* http://docs.ruby-lang.org/ja/2.1.0/method/OptionParser/i/on.html
* http://d.hatena.ne.jp/zariganitosh/20140819/ruby_optparser_true_power こっちのほうが詳しいな