thorでオプションを定義する6つの方法

Rubyでcliを作るときに定番のgem [thor](https://github.com/erikhuda/thor)ではコマンドのオプション(`--key value` みたいなやつ)を定義するときに`method_option`を利用する．
しかし，いくつかのブログやドキュメント等を参照すると他にもいくつかの方法がある．
この違いがよくわからなかったのでいろいろ確認した．

## オプション定義方法を解説する2種類のドキュメント
thorのドキュメントとしては，[GitHub wiki](https://github.com/erikhuda/thor/wiki)と[公式webサイト](http://whatisthor.com/)の2種類存在する．

GitHub wikiでは [`method_option` と `method_options` で定義する方法](https://github.com/erikhuda/thor/wiki/Method-Options)が紹介されている．
こちらのwikiには `option` / `options` を用いて定義する方法については触れられていない．

> Thor allows you to specify options for its tasks, using method_options to supply a hash of options, or method_option to provide more detail about an individual option.

```ruby:GitHub/wiki記載のサンプル
method_option :value, :default => "some value"
#=> Creates a string option with a default value of "some value"
```


一方で，公式webサイトでは `Options and Flags` の章にて `option` と `options` を用いて定義する方法が紹介されている．
こちらのドキュメントには `method_option` / `method_options` を用いて定義する方法については触れられていない．

> Thor makes it easy to specify options and flags as metadata about a Thor command:

```ruby:公式webサイト記載のサンプル
class MyCLI < Thor
  desc "hello NAME", "say hello to NAME"
  option :from
  def hello(name)
    puts "from: #{options[:from]}" if options[:from]
    puts "Hello #{name}"
  end
end
```

なお `method_options` と `method_option` の違いについて GitHub wiki には，
`method_option` は個別のオプションに対してより詳細な項目を定義するために利用すると説明がある．
`options` と `option` の違いについて公式webサイトには，
`options` は複数の `option` を一度に定義できると説明がある．
上記の説明では， `method_option` と `method_options` の関係は `options` と `option` の関係とは異なるように読める．

> You can use a shorthand to specify a number of options at once if you just want to specify the type of the options. 

また，公式webサイトには上記に加えて `Class Options` の章で `class_option` という別の方法が記載されている．
`class_option` はGitHub wikiでは説明はなく，[Generators](https://github.com/erikhuda/thor/wiki/Generators) と [Groups](https://github.com/erikhuda/thor/wiki/Groups)で参照されているだけだった．
ただし，公式webサイトでは触れられていない `class_options` も参照されている．

> You can specify an option that should exist for the entire class by using class_option. Class options take exactly the same parameters as options for individual commands, but apply across all commands for a class.

```ruby:公式webサイト記載のclass_optionサンプル
class MyCLI < Thor
  class_option :verbose, :type => :boolean

  desc "hello NAME", "say hello to NAME"
  options :from => :required, :yell => :boolean
  def hello(name)
    puts "> saying hello" if options[:verbose]
    output = []
    output << "from: #{options[:from]}" if options[:from]
    output << "Hello #{name}"
    output = output.join("\n")
    puts options[:yell] ? output.upcase : output
    puts "> done saying hello" if options[:verbose]
  end

  desc "goodbye", "say goodbye to the world"
  def goodbye
    puts "> saying goodbye" if options[:verbose]
    puts "Goodbye World"
    puts "> done saying goodbye" if options[:verbose]
  end
end
```

## 使い分け
### method_options と options
この疑問についてはまさに同じ内容のissueが報告されている.
参考: [Difference between method_options and options #596](https://github.com/erikhuda/thor/issues/596)

結論としてはどちらも同じ．GitHub wikiは内容が古くなっているということ．
実際，GitHub wikiには内容が古くなっているので公式wbサイトを参照してくれと注意書きがある

> NOTE: a good part of the documentation here is outdated, see the website instead: http://whatisthor.com

実装を見ても， method_optionsで実装されて options で[エイリアスが張られている](https://github.com/erikhuda/thor/blob/34df888d721ecaa8cf0cea97d51dc6c388002742/lib/thor.rb#L128)．
同様に， method_option で実装されて optionで[エイリアスが張られてもいる](https://github.com/erikhuda/thor/blob/34df888d721ecaa8cf0cea97d51dc6c388002742/lib/thor.rb#L165)．

```ruby:lib/thor.rb#128
alias_method :options, :method_options
```

```ruby:lib/thor.rb#165
alias_method :option, :method_option
```

### method_options と method_option
一方で， `method_options` と `method_option` (および `options` と `option`)の違いについて，
単に `method_options` は複数の `method_option` を同時に指定できるというだけでなく，
`method_option` でしか定義できない詳細オプションがある様子．
すなわち GitHub wikiが正しい (公式webサイトの記載を自分が読み違えている？)．

具体的には，`method_options` では `:required`, `:type`, `:default`, `:aliases` の4つしか指定できない．
これは`method_options`を指定したときの処理の実装上，[この4つしか対応していない](https://github.com/erikhuda/thor/blob/34df888d721ecaa8cf0cea97d51dc6c388002742/lib/thor/parser/option.rb#L72)ように読める．
その他の指定可能な項目として，公式webサイトでは `:desc`, `:banner` を，
GitHub wikiでは上記に加えて `:lazy_default`, `:enum` を，
[オプション項目の生成処理の実装](https://github.com/erikhuda/thor/blob/34df888d721ecaa8cf0cea97d51dc6c388002742/lib/thor/parser/option.rb#L7)を見ると上記に加えて `:hide` を
それぞれ指定可能のように見える．

### method_options と class_options
`class_options` および `class_option` は説明の通り，指定したクラスに定義されたコマンドすべてに適用される．
利用可能な項目は `method_options` および `method_option` と等しい．

なお，[class_option実装のコメント](https://github.com/erikhuda/thor/blob/34df888d721ecaa8cf0cea97d51dc6c388002742/lib/thor/base.rb#L312)を見ると `lazy_default` の記載がない．
しかし実際には `class_option` も `method_option` も共通の [build_option](https://github.com/erikhuda/thor/blob/34df888d721ecaa8cf0cea97d51dc6c388002742/lib/thor/base.rb#L590)で生成しているので
おそらく `laze_default`も含めて共通化されている．

## 結論
* `method_options` と `options`, `method_option` と `option` の機能はそれぞれ同じ．どちらを利用してもよい．
* `method_options` と `method_option` は役割が異なる．
    * `method_options` は異なる複数のオプションに対して， `method_option` は単一のオプションに対して項目を指定する．
    * `method_options` は `required`, `type`, `default`, `aliases` の計4項目を指定できる．
    * `method_option` では上記に加えて `desc`, `banner`, `lazy_default`, `enum`, `hide` の計9項目を指定できる．
* `method_options` と `class_options` は対象となるコマンドが違うだけで同じ項目を指定できる． `class_option` も同様.
* ドキュメントは GitHub wikiと公式webサイトがあるが，どちらも古いので両方とも鵜呑みにできない．