Rubyでcliを作るときに定番のgem thorではコマンドのオプション(--key value みたいなやつ)を定義するときにmethod_optionを利用する.
しかし,いくつかのブログやドキュメント等を参照すると他にもいくつかの方法がある.
この違いがよくわからなかったのでいろいろ確認した.
オプション定義方法を解説する2種類のドキュメント
thorのドキュメントとしては,GitHub wikiと公式webサイトの2種類存在する.
GitHub wikiでは method_option と 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.
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:
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 と 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.
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
結論としてはどちらも同じ.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 でエイリアスが張られている.
同様に, method_option で実装されて optionでエイリアスが張られてもいる.
alias_method :options, :method_options
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つしか対応していないように読める.
その他の指定可能な項目として,公式webサイトでは :desc, :banner を,
GitHub wikiでは上記に加えて :lazy_default, :enum を,
オプション項目の生成処理の実装を見ると上記に加えて :hide を
それぞれ指定可能のように見える.
method_options と class_options
class_options および class_option は説明の通り,指定したクラスに定義されたコマンドすべてに適用される.
利用可能な項目は method_options および method_option と等しい.
なお,class_option実装のコメントを見ると lazy_default の記載がない.
しかし実際には class_option も method_option も共通の build_optionで生成しているので
おそらく 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サイトがあるが,どちらも古いので両方とも鵜呑みにできない.