Ruby の CLI ツールの作成を支援する、 thor gem の基本について。 #thor #ruby

# Ruby の CLI ツールの作成を支援する、 thor gem の基本について。
## 概要
Ruby の CLI ツールの作成を支援する、 thor gem の基本について。

## Thor 関連資料
* [thor GitHub](https://github.com/erikhuda/thor)
* [thor RubyGems](https://rubygems.org/gems/thor)
* [Thor Official Document](http://whatisthor.com/)

## CLI のクラス作成
Thor クラスを継承し、 public メソッドを定義すると
該当メソッドがコマンドになります。

~~~ruby
require 'thor'
class SampleCLI < Thor
  desc "command1 usage", "command1 desc"
  def command1(name)
    puts "command1 #{name}"
  end

  desc "command2 usage", "command2 desc"
  def command2(name)
    puts "command2 #{name}"
  end
end

SampleCLI.start(ARGV)
~~~

* 出力

~~~bash
$ ruby thor_class_define.rb help
Commands:
  thor_class_define.rb command1 usage  # command1 desc
  thor_class_define.rb command2 usage  # command2 desc
  thor_class_define.rb help [COMMAND]  # Describe available commands or one specific command

$ ruby thor_class_define.rb help command1
Usage:
  thor_class_define.rb command1 usage

command1 desc
$ ruby thor_class_define.rb help command2
Usage:
  thor_class_define.rb command2 usage

command2 desc

$ ruby thor_class_define.rb command1 hoge
command1 hoge
~~~

## 各種設定
### package_name(name, options = {})
パッケージ名を指定する。
help コマンドを実行した際に表示される。

~~~ruby
require 'thor'
class SampleCLI < Thor
  package_name "package_name"
end

SampleCLI.start(ARGV)
~~~

* 出力

~~~bash
$ ruby thor_class_package_name.rb help
package_name commands:
  thor_class_package_name.rb help [COMMAND]  # Describe available commands or one specific command
~~~

## 各種設定
### default_command(meth = nil)
デフォルトのコマンドを指定する。
コマンド名を省略した際に実行されるコマンドを決定する。
`default_command` を指定しなかった場合は、 `help` コマンドが実行される。

~~~ruby
require 'thor'
class SampleCLI < Thor
  default_command :command1
  desc "command1 usage", "command1 desc"
  def command1(name = 'default')
    puts "command1 #{name}"
  end

  desc "command2 usage", "command2 desc"
  def command2(name)
    puts "command2 #{name}"
  end
end

SampleCLI.start(ARGV)
~~~

* 出力

~~~bash
$ ruby thor_class_default_command.rb
command1 default
$ ruby thor_class_default_command.rb command1
command1 default
$ ruby thor_class_default_command.rb command1 hoge
command1 hoge
~~~

* default_command を無効化

~~~ruby
require 'thor'
class SampleCLI < Thor
  # default_command :command1
  desc "command1 usage", "command1 desc"
  def command1(name = 'default')
    puts "command1 #{name}"
  end

  desc "command2 usage", "command2 desc"
  def command2(name)
    puts "command2 #{name}"
  end
end

SampleCLI.start(ARGV)
~~~

 * 出力

~~~bash
$ ruby thor_class_default_command.rb
Commands:
thor_class_default_command.rb command1 usage  # command1 desc
thor_class_default_command.rb command2 usage  # command2 desc
thor_class_default_command.rb help [COMMAND]  # Describe available commands or one specific command
~~~

### register(klass, subcommand_name, usage, description, options = {})
外部コマンドの登録

* thor_class_register_main.rb

~~~ruby
require 'thor'
require './thor_class_register_sub'
class SampleCLI < Thor
  register(SubCommand, "sub", "command2 usage override", "command2 desc override")
  register(GroupPlugin, "group", "group usage override", "group desc override")

  desc "command1 usage", "command1 desc"
  def command1(name = 'default')
    puts "command1 #{name}"
  end
end

SampleCLI.start(ARGV)
~~~

* thor_class_register_sub.rb

~~~
require 'thor'
class SubCommand < Thor
  desc "command2 usage", "command2 desc"
  def command2(name)
    puts "command2 #{name}"
  end

  desc "command3 usage", "command3 desc"
  def command3(name)
    puts "command3 #{name}"
  end
end

# Thor::Group は定義したコマンドを上から順にすべて実行する
class GroupPlugin < Thor::Group
  desc "hoge1"
  def hoge1
    puts "hoge1"
  end

  desc "hoge2"
  def hoge2
    puts "hoge2"
  end
end
~~~

* 出力

~~~bash
$ ruby thor_class_register_main.rb help
Commands:
  thor_class_register_main.rb command1 usage        # command1 desc
  thor_class_register_main.rb group usage override  # group desc override
  thor_class_register_main.rb help [COMMAND]        # Describe available commands or one specific command
  thor_class_register_main.rb sub usage override    # sub desc override

# サブコマンドの定義を確認
$ ruby thor_class_register_main.rb help sub
Commands:
  thor_class_register_main.rb sub_command command2 usage  # command2 desc
  thor_class_register_main.rb sub_command command3 usage  # command3 desc
  thor_class_register_main.rb sub_command help [COMMAND]  # Describe subcommands or one specific subcommand

$ ruby thor_class_register_main.rb sub command2 hoge
command2 hoge
$ ruby thor_class_register_main.rb sub command3 hige
command3 hige

# Thor::Group を継承したクラスの全メソッドの結果が出力される
$ ruby thor_class_register_main.rb group
hoge1
hoge2
~~~

## desc(usage, description, options = {})
usage と description を定義する。
設定した内容は、 help で確認可能。

~~~
require 'thor'
class SampleCLI < Thor
  desc "command1 usage", "command1 desc"
  def command1(name)
    puts "command1 #{name}"
  end
end

SampleCLI.start(ARGV)
~~~

* 出力

~~~
$ ruby thor_class_desc.rb help
Commands:
  thor_class_desc.rb command1 usage  # command1 desc
  thor_class_desc.rb help [COMMAND]  # Describe available commands or one specific command
~~~

## long_desc(long_description, options = {})
usage と description を定義する。
設定した内容は、 help で確認可能。

~~~
require 'thor'
class SampleCLI < Thor
  desc "command1 usage", "command1 desc"
  long_desc "long desc"
  def command1(name)
    puts "command1 #{name}"
  end
end

SampleCLI.start(ARGV)
~~~

* 出力

~~~
$ ruby thor_class_long_desc.rb help
Commands:
  thor_class_long_desc.rb command1 usage  # command1 desc
  thor_class_long_desc.rb help [COMMAND]  # Describe available commands or one specific command

$ ruby thor_class_long_desc.rb help command1
Usage:
  thor_class_long_desc.rb command1 usage

Description:
  long desc
~~~

## map(mappings = nil)
mappings のマップのキーを値に紐付ける

~~~
require 'thor'
class SampleCLI < Thor
  map "CM1" => "command1"

  desc "command1 usage", "command1 desc"
  def command1(name)
    puts "command1 #{name}"
  end
end

SampleCLI.start(ARGV)
~~~

* 出力

~~~
$ ruby thor_map.rb CM1 test
command1 test
$ ruby thor_map.rb command test
command1 test
~~~

## method_option(name, options = {})
内部で option エイリアスを定義している

|key|value|
|:--|:--|
|:desc    |Description for the argument.|
|:required|If the argument is required or not.|
|:default |Default value for this argument. It cannot be required and have default values.|
|:aliases |Aliases for this option.|
|:type    |The type of the argument, can be :string, :hash, :array, :numeric or :boolean.|
|:banner  |String to show on usage notes.|
|:hide    |If you want to hide this option from the help.|

~~~ruby
require 'thor'
class SampleCLI < Thor
  desc "command1 usage", "command1 desc"
  method_option "opt", desc: 'ops'
  def command1(name)
    print options['opt'], "\n"
    puts "command1 #{name}"
  end

  desc "command2 usage", "command2 desc"
  option "opt", required: true
  def command2(name)
    print options['opt'], "\n"
    puts "command2 #{name}"
  end

  desc "command3 usage", "command3 desc"
  option "opt", aliases: "o"
  def command3(name)
    print options['opt'], "\n"
    puts "command3 #{name}"
  end

  desc "command4 usage", "command4 desc"
  option "opt", type: :string
  def command4(name)
    print options['opt'], "\n"
    puts "command4 #{name}"
  end

  desc "command5 usage", "command5 desc"
  option "opt", type: :array
  def command5(name)
    print options['opt'], "\n"
    puts "command5 #{name}"
  end

  desc "command6 usage", "command6 desc"
  option "opt", banner: "banner"
  def command6(name)
    print options['opt'], "\n"
    puts "command6 #{name}"
  end

  desc "command7 usage", "command7 desc"
  option "opt", hide: true
  def command7(name)
    print options['opt'], "\n"
    puts "command7 #{name}"
  end
end

SampleCLI.start(ARGV)
~~~

* 出力

### desc option

~~~bash
$ ruby thor_method_option.rb help command1
Usage:
  thor_method_option.rb command1 usage

Options:
  [--opt=OPT]  # ops

$ ruby thor_method_option.rb command1 hoge --opt="@@@@@"
@@@@@
command1 hoge
~~~

### require option

~~~bash
$ ruby thor_method_option.rb help command2
Usage:
  thor_method_option.rb command2 usage --opt=OPT

Options:
  --opt=OPT

command2 desc

# require に設定したオプションがないためエラー
$ ruby thor_method_option.rb command2 hoge
No value provided for required options '--opt'

$ ruby thor_method_option.rb command2 hoge --opt=opt_value
opt_value
command2 hoge
~~~

### aliases option

~~~bash
$ ruby thor_method_option.rb help command3
Usage:
  thor_method_option.rb command3 usage

Options:
  o, [--opt=OPT]

command3 desc

$ ruby thor_method_option.rb command3 hoge -o "@@@@@"
@@@@@
command3 hoge

$ ruby thor_method_option.rb command3 hoge --opt="@@@@@"
@@@@@
command3 hoge
~~~

### type option

* String

~~~
$ ruby thor_method_option.rb help command4
Usage:
  thor_method_option.rb command4 usage

Options:
  [--opt=OPT]

command4 desc

$ ruby thor_method_option.rb command4 hoge --opt="string"
string
command4 hoge

$ ruby thor_method_option.rb command4 hoge --opt={key1: 1, key2: 2}
ERROR: "thor_method_option.rb command4" was called with arguments ["hoge", "1,", "key2:", "2}"]
Usage: "thor_method_option.rb command4 usage"
~~~

* Array

~~~
$ ruby thor_method_option.rb help command5
Usage:
  thor_method_option.rb command5 usage

Options:
  [--opt=one two three]

command5 desc
~~~

### banner option

~~~
$ ruby thor_method_option.rb help command6
Usage:
  thor_method_option.rb command6 usage

Options:
  [--opt=banner]

command6 desc

# 配列で受け取る
$ ruby thor_method_option.rb command5 hoge --opt=hoge hige hage
["hoge", "hige", "hage"]
command5 hoge

# 配列以外は配列に変換
$ ruby thor_method_option.rb command5 hoge --opt=1
["1"]
command5 hoge

# 配列以外は配列に変換
$ ruby thor_method_option.rb command5 hoge --opt={key1: 1, key2: 2}
["{key1:", "1,", "key2:", "2}"]
command5 hoge
~~~

### hide option
オプションをヘルプには表示しない。
動作上は利用できる。

~~~
$ ruby thor_method_option.rb command7 hoge --opt="@@@"
@@@
command7 hoge
~~~

## method_options(options = nil)
内部で options エイリアスを定義している

キーにオプション名、 値にデータ型（type）を定義する。

~~~
require 'thor'
class SampleCLI < Thor
  desc "command1 usage", "command1 desc"
  options opt1: :string, opt2: :array
  def command1(name)
    print options, "\n"
    puts "command1 #{name}"
  end
end

SampleCLI.start(ARGV)
~~~

* 出力

~~~
$ ruby thor_method_options.rb command1 hoge --opt1=string --opt2=ary1 ary2 ary3
{"opt1"=>"string", "opt2"=>["ary1", "ary2", "ary3"]}
command1 hoge
~~~

## 参照
* [thor GitHub](https://github.com/erikhuda/thor)
* [thor RubyGems](https://rubygems.org/gems/thor)
* [Thor Official Document](http://whatisthor.com/)