RubyでCLI Tool を作ってみる

1年くらいRubyを触ってきたが、「そういやCLITool作ったことないや」と思い、簡単に作ってみた。

# アウトプット
* CLIベースでSlackに認証テスト、メッセージ送信、チャンネルリストの取得ができるものをつくる
    * https://github.com/tozu00/slack_notify_cli 
* あくまでCLItoolの作りかたを学ぶ

# 開発環境
* OS:macOS Mojave
* rbenv: 1.1.2
* Ruby: 2.6.2
* rubygems: 3.0.3 

# 手順
今回は、`slack_notify_cli`という名前のgemを作成する。
## 1. bundlerインストール
未インストールの場合のみ。

```
$ gem install bundler 
```

## 2. gemの雛形を作成
今回はテストも書く予定なので、`-t`オプションを指定する。

```
$ bundle gem slack_notify_cli -t rspec
```

## 3. gemspecの修正
基本的に`TODO`のところを編集すればいいらしい。

```diff:slack_notify_cli.gemspec

Gem::Specification.new do |spec|
  spec.name          = "slack_notify_cli"
  spec.version       = SlackNotifyCli::VERSION
+   spec.authors       = "tozu00"
-   spec.summary       = %q{TODO: Write a short summary, because RubyGems requires one.}↲
-   spec.description   = %q{TODO: Write a longer description or delete this line.}↲
-   spec.homepage      = "TODO: Put your gem's website or public repo URL here."↲
+  spec.summary       = %q{slack notification command line interface. }
+  spec.description   = %q{slack notification command line interface. ex.) get channel list, check auth, send message}
+  spec.homepage      = "https://github.com/tozu00/slack_notify_cli"
```
### rubygemsに登録する場合は、以下の設定があるとダメ。
[外部にgemを公開したくない場合のみ、この設定が必要](https://guides.rubygems.org/publishing/#serving-your-own-gems)なので、削除する。

```diff:slack_notify_cli.gemspec
-  # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
-  # to allow pushing to a single host or delete this section to allow pushing to any host.
-  if spec.respond_to?(:metadata)
-    spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"

-    spec.metadata["homepage_uri"] = spec.homepage
-    spec.metadata["source_code_uri"] = "TODO: Put your gem's public repo URL here."
-    spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
-  else
-    raise "RubyGems 2.0 or newer is required to protect against " \
-      "public gem pushes."
-  end
```

### 必要なgemの追加

```ruby:slack_notify_cli.gemspec

  spec.add_development_dependency "aruba"

  spec.add_dependency "thor"
  spec.add_dependency "slack-ruby-client"
```
* [aruba](https://github.com/cucumber/aruba) : テストフレームワーク。CLIのテストが簡単になる。
* [thor](http://whatisthor.com/) : CLItoolを作成する。
* [slack-ruby-client](https://github.com/slack-ruby/slack-ruby-client) : slackの通知機能を実現する。

## 4. コマンドが実行されるクラスの作成
`slack-ruby-client`の以下機能を実装してみる。

* [test-auth](https://github.com/slack-ruby/slack-ruby-client#test-auth)
* [send-messages](https://github.com/slack-ruby/slack-ruby-client#send-messages)
* [list-channels](https://github.com/slack-ruby/slack-ruby-client#list-channels)

```ruby:lib/slack_notify_cli/command.rb
require 'thor'
require 'slack-ruby-client'

Slack.configure do |config|
  config.token = ENV['SLACK_API_TOKEN']
  raise 'Missing ENV[SLACK_API_TOKEN]!' unless config.token
end

module SlackNotifyCli
  class Command < Thor
    desc "slack_notify_cli auth_check", "auth check"
    def auth_check
      client = Slack::Web::Client.new
      puts client.auth_test['ok'] ? "Authentication successed" : "Authentication failed"
    end

    desc "slack_notify_cli channel_list", "get channel list"
    def channel_list
      client = Slack::Web::Client.new
      channels = client.channels_list.channels
      puts channels.map { |c| "##{c.name} #{c.purpose.value}"}
    end

    desc "slack_cli send str:{channel} str:{message}", "send message ex.) slack_cli send \"#general\" \"Hello World\""
    def send(channel, message)
      client = Slack::Web::Client.new
      client.chat_postMessage(channel: channel, text: message, as_user: true)
    end
  end
end
```
### 作成したクラスを読み込む
```diff:lib/slack_notify_cli.rb
require "slack_notify_cli/version"
+ require "slack_notify_cli/command"

module SlackNotifyCli
  class Error < StandardError; end
end
```
## 5. 実行ファイルの作成
`SlackNotifyCli::Command.start`でCLIを起動する。

```ruby:exe/slack_notify_cli
#!/usr/bin/env ruby

require "slack_notify_cli"

SlackNotifyCli::Command.start
```

## 6. bundle install
gemのインストールをディレクトリの範囲に限定的したいので、`--path`オプションをつけて、インストールする。

```
$ bundle install --path .bundle
```
## 7. コマンド実行
環境変数にtokenを設定して、コマンドを実行。

```
$ export SLACK_API_TOKEN=xo...
$ bundle exec exe/slack_notify_cli
Commands:
  slack_notify_cli help [COMMAND]                              # Describe available commands or one specific command
  slack_notify_cli slack_cli send str:{channel} str:{message}  # send message ex.) slack_cli send "#general" "Hello World"![スクリーンショット 2019-05-04 23.59.51.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236702/fdc9373d-0d54-bdbb-534c-ea5737285d5a.jpeg)

  slack_notify_cli slack_notify_cli auth_check                 # auth check
  slack_notify_cli slack_notify_cli channel_list               # get channel list
```

引数を指定しないと、ヘルプを出してくれるっぽい。



# メッセージ送信してみる
```
$ bundle exec exe/slack_notify_cli send "#test" "Hello World"
```
![スクリーンショット 2019-05-05 0.01.52.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236702/37d5d978-b808-8ae6-c9f7-df7d71266c0a.jpeg)
すごい簡単やないかい！
## 所感
* 思ったより簡単にできた
* gemを作って公開するまでやってみる
* CItoolとかつくれそう

# 参考記事
* [RubyでCLIツールを作成する手順まとめ](https://qiita.com/akisame338/items/92379addeb1a17468498)
* [RubyGemsの作り方](https://qiita.com/chamao/items/9813e18d82b51c99422c)
* [Slack APIのTokenの取得・場所](https://qiita.com/ykhirao/items/0d6b9f4a0cc626884dbb)
* [rubygemsに登録するときにはspec.metadata['allowed_push_host']あたりを消しておこう](https://qiita.com/tossh/items/08e7165e730dbc1a0e2e)
* [Aruba gemでCLIのテストを支援する](https://qiita.com/tbpgr/items/41730edcdb07bb5b59ad)