前置き
Go で EventEmitter/Dispatcher 的なことをしたい場合、 emission や go-pubsub などの既存のライブラリを使うのが最も手っ取り早いと思いますが、
golang で event emitter/dispatcher 的なもの | tech - 氾濫原
リフレクションで型変換は隠蔽されているが、もしリスナーとエミッターとで型が食い違っていると、実行時エラーになる。
「イベント名」というパラメータが存在せず、型の選択によって自動的にディスパッチされる。よって型がマッチしなければリスナーは実行されないので、原理的に型変換の実行時エラーは発生しない (もし間違えた場合単に実行時になって「呼ばれない」ことに気付く)
で解説されている通り、上記ライブラリではコードにミスがあった場合、実行時でないとその問題が露見しません。
やはり静的型付け言語を書いている以上、コンパイル時にミスは発見できてほしい…ということで、コンパイル時に型チェックできるような EventEmitter を go generate
で自動生成するツールがあったら、それなりに需要があるんじゃないだろうかと思い、作ってみました。
インストール
$ go get -u github.com/shiwano/genem
使い方
ヘルプテキストをべたり。
genem
Usage:
genem [options] <file>
genem -h | --help
genem --version
Options:
-n, --emitter-name string Specify the event emitter type name (default: EventEmitter).
-o, --output-file-name string Specify the output file name (default: event_emitter.go).
-p, --print Print the generated code without file output.
-h, --help Output help information.
-v, --version Output version.
このツールは、指定した Go のファイルから lower camel case で最後に Event
で終わる名前の struct を探してきて(例: fooEvent
)、その struct の定義に対応した EventEmitter のコードを自動生成します。
例えば
以下のようなファイルを作り、
//go:generate genem -n Emitter -o emitter.go $GOFILE
package event
type fooEvent struct {
bar string
baz int
}
type quxEvent struct {
quux float32
}
go generate
をすると、そのファイルのあるディレクトリに emitter.go
というファイル名で以下のようなコードが自動生成されます。
package event
type Emitter struct {}
func NewEmitter() *Emitter
func (_e *Emitter) EmitFooEvent(bar string, baz int)
func (_e *Emitter) AddFooEventListener(listener func(bar string, baz int))
func (_e *Emitter) AddFooEventListenerOnce(listener func(bar string, baz int))
func (_e *Emitter) RemoveFooEventListener(listener func(bar string, baz int))
func (_e *Emitter) EmitQuxEvent(quux float32)
func (_e *Emitter) AddQuxEventListener(listener func(quux float32))
func (_e *Emitter) AddQuxEventListenerOnce(listener func(quux float32))
func (_e *Emitter) RemoveQuxEventListener(listener func(quux float32))
生成された EventEmitter は、以下のような感じで使えます。
package main
import (
"fmt"
"example.com/event"
)
func main() {
e := event.NewEmitter()
e.AddFooEventListener(func(bar string, baz int) {
fmt.Println("foo: ", bar, baz) // Output: "foo: bar 3"
})
e.EmitFooEvent("bar", 3)
}