Rubyのoptparseについて

# はじめに
今回はRubyのoptparseについての記事を書こうと思います。
カレンダーをRubyで作成する際にoptparseを使用する仕様にする際にoptparseについて調べたのでまとめて記事にしようと思いました。


# optparseとは
* `optparse`は標準ライブラリの1つで、コマンドラインのオプションを取り扱うために使われる
* `optparse`を使うときは基本的に以下のステップで使用する
①`OptionParser`オブジェクト`opt`を作成
②オプションを扱うブロックを作成
③opt.parse(ARGV)でコマンドラインをパースする


# 挙動の確認
```sample.rb
require 'optparse'
opt = OptionParser.new

opt.on('-a') {|v| p v}
opt.on('-b') {|v| p v}

opt.parse!(ARGV)
p ARGV
```

実行結果
```
$ ruby sample.rb -a foo bar -b baz
# => true
     true
     ["foo", "bar", "baz"]
```

`on`メソッドは、`opt`オブジェクトに登録するオプションを定義している。
`ARGV`にはコマンドラインで渡されたオプション、引数が配列として渡される。
パースすることで引数だけ取り出すことができる。

# ロングオプションを定義する
ショートオプションだけでなく、ロングオプションを定義したい場合は、`on`メソッドでオプション定義するときにショートオプションと一緒にロングオプションとして渡したい値を指定する。


```sample.rb
require 'optparse'
opt = OptionParser.new

opt.on('-a', '--all') {|v| p v}
opt.on('-b', '--before') {|v| p v}
opt.parse(ARGV)

p ARGV
```

実行結果
```
$ ruby sample.rb --all foo --before bar
# => true
     true
     ["--all", "foo", "--before", "bar"]
```

# どのオプションが指定されたか記憶する
`OptionParser`はどのオプションが指定されたかは記憶されていない。
そのため、どのオプションがコマンドランで渡されたか覚えておく必要がある場合は、コンテナに格納しておく。

```sample.rb
require 'optparse'
opt = OptionParser.new

params = {}

opt.on('-a') {|v| params[:a] = v}
opt.on('-b') {|v| params[:b] = v}

opt.parse!(ARGV)
p ARGV
p params
```

実行結果
```
$ ruby sample.rb -a foo bar -b baz
# => ["foo", "bar", "baz"]
     {:a=>true, :b=>true}
```

オプションが指定された場合は、コンテナにkey,valueが保存される。オプションが指定されなかった場合は、nilが返る。

明示的にコンテナへ格納する以外に、parse(及びparse!)メソッドの引数に:intoオプションを指定することでハッシュへ自動的に値を格納することができる。
この時ハッシュのキーとして利用される名前はロングオプションが定義されていればロングオプションの値を、ショートオプションのみの場合はショートオプションの値から、先頭の"-"を除いてシンボル化した値が仕様される。


```sample.rb
require 'optparse'
opt = OptionParser.new

params = {}

opt.on('-a') {|v| v}
opt.on('-b', '--bbb') {|v| v}

opt.parse!(ARGV, into: params) # intoオプションにハッシュを渡す
p ARGV
p params
```
実行結果
```
$ ruby sample.rb -a foo bar -b baz
# => ["foo", "bar", "baz"]
     {:a=>true, :bbb=>true}
```

# オプションの引数
`on`メソッドのオプション定義で末尾に何かを書くと、そのオプションは引数を受け付けることの指定となる。

```sample.rb
require 'optparse'
opt = OptionParser.new

opt.on('-a VAL') {|v| p v}  # <- "VAL"を追加
opt.on('-b') {|v| p v}

opt.parse!(ARGV)
p ARGV
```
実行結果
```
$ ruby sample.rb -a foo bar -b baz
# => "foo"
     true
     ["bar", "baz"]
```

オプションの末尾の書き方の基準は、「ヘルプの見栄えが良くなるように書く」こと。オプションの引数を省略すると例外`OptionParser::MissingArgument`が発生する。

オプションの引数が必須でないことを表すには、[ を付ける。

```sample.rb
require 'optparse'
opt = OptionParser.new

opt.on('-a [VAL]') {|v| p v}  # <- [VAL]を追加
opt.on('-b') {|v| p v}

opt.parse!(ARGV)
p ARGV
```

実行結果
```
$ ruby sample.rb -a
# => nil
     []
```

# 受け取る引数のクラスを指定する
あるオプションの引数で受け取るクラスを限定したい場合は、以下のように記述する。

```sample.rb
require 'optparse'
opt = OptionParser.new

option = {}
# 引数はIntegerのみ
opt.on('-a', '--all=N', Integer, 'description:a') {|v| option[:a] = v }
# 引数はStringのみ
opt.on('-b', '--before=S', String, 'description:b') {|v| option[:b] = v }
opt.on('-c [VAL]', '--ccc', 'description:c') {|v| option[:c] = v }
opt.parse!(ARGV)
p option
```

実行結果
```
$ ruby sample.rb -a 1 -b aaa
{:a=>1, :b=>"aaa"}

$ ruby sample.rb -a aaa
sample3.rb:7:in `<main>': invalid argument: -a aaa (OptionParser::InvalidArgument)

$ ruby sample.rb -b 1
{:b=>"1"}
```

`Integer`クラスを指定すると、文字列が引数として渡された時はエラーを返す。
`String`クラスを指定すると、数値を引数に渡した場合でも文字列として受け取ることができる。


# ARGV
ARGVにコマンドラインから渡された引数が配列として渡される。
ARGVでオプションを定義することができる。
複雑なオプションがない場合はARGVを使うのが良い。
オプションの指定は`getopts`メソッドでオプションを指定することができる。

##### ショートネームのオプション
ショートネームのオプションを文字列で指定する。
オプションが引数を取る場合は直後に`:`を付ける。
* aという引数をとるオプションを追加する場合：`a:`
* aとbという引数をとるオプションを追加する場合：`a:b:`
* aという引数をとらないオプションとbという引数をとるオプションを追加する場合：`ab:`


##### ロングネームのオプション
ショートネームのオプションを文字列で指定する。
オプションが引数をとる場合は直後に`:`を付ける。
デフォルト値は `:` の直後に指定する。
* bufsizeという引数をとらないオプションを追加する場合：`bufsize`
* bufsizeという引数をとるオプションを追加する場合：`bufsize:`
デフォルト値1024のbufsizeという引数をとるオプションを追加する場合：`bufsize:1024`

```sample.rb
require 'optparse'

params = ARGV.getopts("a:b:")
p params
```

実行結果
```
$ ruby sample.rb -a foo -b bar
# => {"a"=>"foo", "b"=>"bar"}
```


# 最後に
今回はoptparseについて学習したことをまとめてみました。実際に全てを利用したわけではないので、理解が浅い部分もありますが、そこに関してはまた実際に使う機会に復習しなおしたりして、しっかりと使えるようになりたいと思いました。

今回も最後までご覧いただきありがとうございました。


# 参考文献
[library optparse (Ruby 3.2 リファレンスマニュアル)](https://docs.ruby-lang.org/ja/latest/library/optparse.html)