GLI を使って Ruby で簡単にターミナルから呼べるコマンドを作る

[GLI](https://github.com/davetron5000/gli) を使うと、自分が作った Ruby アプリをターミナルから操作するためのコマンド、CLI（Command Line Interface）を簡単に作る事が出来ます。
CLI とは、例えば `git add` とか `gem install` みたいな、ターミナルから呼べるコマンドのことですね。

# 1. まず骨組みを用意

早速 GLI を使って作ってみましょう！まず骨組みを用意します。

```rb:gli_sample
#!/usr/bin/env ruby

require 'gli'
include GLI::App

program_desc 'サンプルアプリ'

version 1.0
subcommand_option_handling :normal

exit run(ARGV)
```

それぞれのコードの意味は以下です。

* ターミナルから実行できるようにコメント（shebang）
* GLI を使うために include
* `program_desc` にアプリの説明を記述
* `version` にアプリのバージョンを記述
* `subcommand_option_handling :normal` は GLI の下位互換のために必要とのこと
* `run(ARGV)` でスクリプト実行

これだけで既にそれっぽくなります。

```
$ ./gli_sample
NAME
    gli_sample - サンプルアプリ

SYNOPSIS
    gli_sample [global options] command [command options] [arguments...]

VERSION
    1.0

GLOBAL OPTIONS
    --help    - Show this message
    --version - Display the program version

COMMANDS
    help - Shows a list of commands or help for one command
```

help と version オプションと、help コマンドが自動的に追加されていることが分かります。

```
$ ./gli_sample --version
gli_sample version 1.0
```

ショート形式も

```
$ ./gli_sample -v
gli_sample version 1.0
```

素晴らしい！

# 2. コマンドを追加

では試しに、超単純な足し算をするコマンドを作ってみます。

```rb:gli_sample
#!/usr/bin/env ruby

...

desc 'calculate a total of numbers'
arg_name 'number1 number2'
command :add do |c|
  c.action do |global_options, options, args|
    sum = args[0].to_i + args[1].to_i
    puts "#{args[0]} + #{args[1]} = #{sum}"
  end
end

exit run(ARGV)
```
簡単に説明すると、

* desc はコマンドの説明
* arg_name は、引数名や、数の渡し方などを説明
* args は、実行時に渡した引数を配列で格納

です！たったこれだけ！
これでスクリプトを実行してみると

```
$ ./gli_sample
NAME
    gli_sample - サンプルアプリ

SYNOPSIS
    gli_sample [global options] command [command options] [arguments...]

VERSION
    1.0

GLOBAL OPTIONS
    --help    - Show this message
    --version - Display the program version

COMMANDS
    add  - calculate a total of numbers
    help - Shows a list of commands or help for one command
```

add が追加されています！
GLOBAL OPTIONS として、help が用意されているので、add の help も見る事が出来ます。

```
$ ./gli_sample add -h
NAME
    add - calculate a total of numbers

SYNOPSIS
    gli_sample [global options] add number1 number2
```

SYNOPSIS を見ると add をどのように実行すれば良いか分かりますね。

```
$ ./gli_sample add 1 2
1 + 2 = 3
```

ここで、`gli_sample` のコードに戻りますと、`global_options` と `options` がブロックの引数に渡されています。これは名前から推測できる通り、グローバルオプションと add 固有のオプションを受け取っています。

試しにオプションを作って、変数を使ってみます。

```rb:gli_sample
desc 'calculate a total of numbers'
arg_name 'number1 number2'
command :add do |c|
  c.desc 'reverse a meaning'
  c.switch [:r, :reverse]

  c.action do |global_options, options, args|
    if options[:reverse]
      diff = args[0].to_i - args[1].to_i
      puts "#{args[0]} - #{args[1]} = #{diff}"
    else
      sum = args[0].to_i + args[1].to_i
      puts "#{args[0]} + #{args[1]} = #{sum}"
    end
  end
end
```

コマンドの意味を反対にする reverse オプションを作りました。

`desc` は他の場所と同様に、説明を記述しています。
実際にオプションを定義しているのは `switch` メソッドです。`switch` に定義したいオプション名を配列を渡すだけ！
オプション定義には `flag` も用意されているので、引数を渡すオプションの場合はそちらで定義しましょう。

実行して見ると、

```
$ ./gli_sample add -h
NAME
    add - calculate a total of numbers

SYNOPSIS
    gli_sample [global options] add [command options] number1 number2

COMMAND OPTIONS
    -r, --[no-]reverse - reverse a meaning
```

COMMAND OPTIONS が追加されました。

```
$ ./gli_sample add -r 1 2
1 - 2 = -1
```

options[:reverse] で受け取れている事が分かります。今回はコマンドオプションを追加しましたが、同じ方法でグローバルオプションも定義できます。

# 3. コマンドに hook させる

最後に hook メソッドについて説明します。
GLI には、コマンド実行前に hook される `pre` や、実行後に hook される `post` など、いくつか hook メソッドが用意されています。

試しに `pre` で引数にバリデーションをかけてみます。

```rb:gli_sample
...

pre do |global_options, command, options, args|
  if command.names == 'add'
    args.size == 2
  else
    true
  end
end
```

`pre` メソッドはブロックを引数に持ち、ブロックは四つの引数を持ちます。左から

* global_options : グローバルオプションの情報
* command : コマンドの情報
* options : コマンドオプションの情報
* args : 引数

を受け取ります。
command は GLI::Command のインスタンスで、`names` メソッドを呼ぶと、コマンド名が返ってきます。

上の例では、add コマンドの際に、引数が 2 つある事を確認しています。`pre` メソッドが false を返すと PreconditionFailed エラーになるため、これでバリデーションをかけることが出来ます。

実行してみます。

```
$ ./gli_sample add
error: preconditions failed

$ ./gli_sample add 1 2 3
error: preconditions failed

$ ./gli_sample add 1 2
1 + 2 = 3
```

2 つ以外の引数を渡すとエラーになっているのが分かります。

```
$ ./gli_sample help
NAME
    gli_sample - サンプルアプリ

SYNOPSIS
    gli_sample [global options] command [command options] [arguments...]

VERSION
    1.0

GLOBAL OPTIONS
    --help    - Show this message
    --version - Display the program version

COMMANDS
    add  - calculate a total of numbers
    help - Shows a list of commands or help for one command
```

また、help コマンドでは、引数を与えなくても実行できることが分かります。
便利！

# おわり

以上のように、GLI を使うと簡単にコマンドを作る事が出来ます。上記だけでも十分に意図したコマンドを作る事が出来ますが、他にもいろいろと便利なメソッドが用意されているので、是非使ってみて下さい！