Ruboty体験記(2) - 外部コマンドを実行させてみた

ChatOps 実現のために外部コマンド実行したい
-----------

基本的に ruboty になにかタスクを追加するためには gem を作らないといけないわけです(よね?）。

私としてはロボットにデプロイやらなんやらやらせたいわけです。となると、ansible 叩いたり YAML 作ったり dhcpd.conf 更新したり、とにかくいろいろやらせたいわけで、それをすべて ruboty の gem にするのは、ちょっとキツすぎる。ruboty-serverspec とか名前だけで楽しそうですが。。。

最初は [ruboty-aun](https://github.com/blockgiven/ruboty-aun) を使わせてもらおうと思ったのですが、ちょっと思ってるのと違うなーというのと、勉強も兼ねて自分で作ってみることにしました。

ということで、その作った [ruboty-exec_command](https://github.com/norobust/ruboty-exec_command) の説明です。

```rb:Gemfile
gem 'ruboty-exec_command'
```

と書けまして

```bash:commands/example/hello
#!/bin/bash

if [ "$1" == "-h" ]; then
  echo "Say hello to something"
  exit
fi

echo "Hello! $*"
```

と置きます。

その心は！

```shell-session
$ bundle exec ruboty
Type `exit` or `quit` to end the session.
> ruboty help
ruboty /example hello/i        - Say hello to something
ruboty /help( me)?\z/i - Show this help message
ruboty /ping\z/i       - Return PONG to PING
ruboty /who am i\?/i   - Answer who you are
> ruboty example hello world
Hello!
```

というように、commands の下のパスをハンドラとして登録し、それを実行した結果を取得します。いまのところ引数を取ってませんが・・・

デフォルトではカレントディレクトリを探しますが、```${RUBOTY_ROOT}/commands``` に実行ファイルを置くことも出来ます。

と、説明はこんなところで、以下体験記（２）です。

gem 工作体験記
------------

なにぶん初めてなのでいろいろハマりましたが・・今後も役に立ちそうなところをピックアップしてみました。

仕様は以下のとおり。

- commands/ ディレクトリ以下の実行ファイルのパス名をハンドラとして登録
- つまり commands/hello/world がある場合 ```@ruboty hello world``` でこのファイルを実行する

ひな形は ruboty-gen で作成。こういうコマンドがあるのはとってもありがたい。先人たちに感謝です。

```shell-session:
$ ruboty-gen g exec_command command
```

せっかくなので、guard も追加。

```rb:gemspec追記
  spec.add_development_dependency "guard-rspec"
  spec.add_development_dependency "growl"
```

```shell-session:
$ guard init rspec
$ guard
```

lib しか見ないので Guardfile はシンプル。

```rb:Guardfile
guard :rspec, cmd: 'bundle exec rspec' do
  watch(%r{^spec/.+_spec\.rb$})
  watch(%r{^lib/(.+)\.rb$})     { |m| "spec/lib/#{m[1]}_spec.rb" }
  watch('spec/spec_helper.rb')  { "spec" }
end
```

ハンドラの rspec は ruboty のコードをパク、、じゃなくて参考にしながら以下のようなものを書いてみました。（```let(:to)```など一部省略)

```rb:spec/lib/ruboty/handlers/command_spec.rb
  let(:said) do
    "@ruboty example hello"
  end

  let(:replied) do
    "hello!"
  end

  describe "#command_handler" do
    it "run example command" do
      robot.should_receive(:say).with(
      body: replied,
      from: to,
      to: from,
      original: {
        body: said,
        from: from,
        robot: robot,
        to: to,
      },
      )
      robot.receive(body: said, from: from, to: to)
    end
  end
```

ハンドラ書くのが一番大変だったわけで
-----------------

ハンドラを書くわけですよ。

動かないわけですよ。

print デバッグするわけです。

なにも間違ってない。

guard が rspec 実行しますわな。

うん！動かない！

いろいろ悩んだですが、フックにハットが要らないということがわかりました。なんという初歩的ミス！

最初こう書いたわけです。

```rb:
    on /^hello world/i, name: "command_handler", description "hello world"
```

どーにもひっかからないなーと思ったらハットが要らなかったという。ただそれだけなんですが、悩んでしまいました。ハットがなくてもメッセージの先頭にマッチするみたいです。先頭以外をフックしたい場合は ```.*``` とかつければいいんでしょうか。

最終的には実行ファイルを処理する Command クラスを作ったので、こんな風になりました。

```rb:
        Ruboty::ExecCommand::Command.all.each do |e|
          on /#{e.command}/i, name: "command_handler", description: e.help
        end
```

続いて、アクションを書くのも、また躓いてました。そう簡単にはいかない。

アクションに渡されるメッセージは、```"@ruboty hello world"``` とメンション含め全部渡されるわけですね。私としては、/path/to/hello/world を生成したいので ```@ruboty``` が不要なわけです。単に 

```message.body.sub(ロボット名, '')``` すればいいんですが、ロボット名がわからない。

ruboty のコードを読みながらしばらく頭をひねった結果、Ruboty::Action に prefix_pattern という素敵な関数が用意されてまして、これをそのまま使うことになりました。Ruboty::Messsage もくまなく読んで（print デバッグして）ロボット名も取れることがわかりました。

```rb:
odule Ruboty
  module Extender
    module Actions
      class Command < Ruboty::Actions::Base

        def robot_name
          Ruboty::Action.prefix_pattern(message.original[:robot].name)
        end

        def command_body
          message.body.sub(robot_name,'')
        end
```

出来上がったコードがなんとも Command だらけで、シンボルネームの管理としてどうなのかと不安になりつつ・・・今回はここらで終わりにしたいと思います。

過去記事は以下のとおりです。

- [Ruboty 体験記](http://t.co/401wQNmXrL)