Node.jsの0.12でES6のgeneratorが使えるようになっているので勉強。
正しい使い方かわからないが、hubotで使うと対話モード風になる。
コード
calendar.coffee
module.exports = (robot) ->
cal = ->
title = yield 'Creating a schedule. \nWhat?'
attendees = yield 'With whom?'
date = yield 'When?'
while date == 'sat' or date == 'sun'
date = yield 'No meeting on weekend. When?'
place = yield 'Where?'
delete robot.cal
'Set ' + title + ' with ' + attendees+ ' on ' + date + ' at ' + place + '. Enjoy!'
next_value = (input) ->
robot.cal.next(input).value
# respond block
robot.respond /cal$/i, (msg) ->
robot.ignore_input = true
robot.cal = cal()
msg.send(next_value())
# hear block
robot.hear /(.*)\s*$/i, (msg) ->
if !('cal' of robot and robot.cal)
return
if 'ignore_input' of robot and robot.ignore_input
delete robot.ignore_input
return
msg.send(next_value msg.match[1])
- generatorは
robot.cal
として登録されている。 -
yield
でgeneratorから呼び出し元に処理が移る。 - generatorが存在する間は
hear
のたびにgeneratorのnext
が呼び出される。
結果
コンパイル
hubotの挙動確認としては不要だが、初めはコンパイルしながら確認したほうがよいかもしれない。
http://coffeescript.org/ のTRY COFFEESCRIPTでもできるし/path/to/coffee -c scripts/calendar.coffee
でも可能。
yield
を入れておくと自動でgenerator(function*
)が生成されるのがミソ。
感想とか
- ステートマシンっぽくステートとそのネットワークをうまく管理するのがよいのだろうが、ステートにいちいち名前つけてフロー修正を試行錯誤するのは面倒
-
yield
とnext
なら、ただの入力待ちのような感じで使えて手軽 - generatorの保存は
robot.brain
でもがんばればできそうだったが、プロセス再起動で確実に挙動を切り替えてほしいのでとりあえずrobot
のメンバーとしてみた(永続化するほどのデータは扱わない前提) - コードの臭い
-
respond
とhear
が複数マッチする場合、コードに書いた順に呼ばれる。書く順を変えると破綻する -
respond
の直後にhear
も呼ばれるがそのままreturnしてほしい、みたいな余計な意図のコードが入る - 手戻りややり直しが数回程度の簡単な状態遷移であれば見通しよくコンパクトに収まるが、気がついたらモンスター化してそう
-
- ステートパターン上手く使えないかな?
環境構築
本題ではない。使ったのは以下のバージョン。
- node@0.12.7
- coffee-script@1.9.3
- hubot@2.11.4
- hubot-slack@3.3.0
実際にgeneratorを利用できるようにするところで四苦八苦。
バッドノウハウっぽいが動いているので、ちゃんとした手順にするのはいったん後回しにする。
- 使っているcoffeeの実行ファイルを探す。
- coffeeのshebangでのnode呼び出し時に
--harmony
オプションを渡したいのだが、shebangではファイル名以外の引数が渡せなかったので間接ファイルを作って呼び出す。
$ vi /path/to/node-harmony
# !/bin/bash
/path/to/node --harmony "$@"
$ vi /path/to/coffee
# !/usr/bin/env /path/to/node-harmony
(もちろんパス通してもよいと思う)