タイトルの通りの備忘録です。
今回は何かしら事前にGoでツールでも書いて参加しようという目標があった。単に自分で何か書いてみたかっただけなんだけど 当日得られた知見によって何を改善することが出来たのかを文章+コードのdiff(=pull request)として>残しておきたかった。ここに至ったキッカケは、当日想定外の繰り上がりが発生し何の準備もせぬまま慌てて参加した5月のspringで、一方的に話を聞くというスタンスに終始してしまい勿体無いことをしたという想いがあった。
自分が書いたツールはごくごくシンプルなもので、ゴルーチンをガンガン使ってミドルを書くとかそこまでの意識の高さはまだ持ち合わせていなかったわけだが、何か作って勉強会に行く という狙いはまあアリだったと思う。GoConで得られた知見も織り交ぜつつ振り返ってみる。
gat というツールを書いた
goldeneggg/gat - "Concatnating and printing file to various services by Go"
ファイルの内容をリモートサービスにCat(Post)するだけのツール。今のところ
- gist (github.com OR intra内のgithub enter priseのgistにPOSTして、生成したURLを出力する) * slack (webhook integrationで設定したUSERが、同じく設定したROOM/CHANNELにPOSTする)
- play.golang.org (指定した goファイル の内容をplay.golangへシェアして、生成したURLを出力する)
に対応させている。
利用マシンに何かしらセットアップ作業を施す必要はなくて、READMEに記載してるBinary Download Linkにクロスコンパイルで出力した各プラットフォーム毎のバイナリがあるので、自環境に合ったバイナリをPATHが通っているディレクトリにダウンロー>ドして実行権限を付与すれば使える。
クロスコンパイルも含めてCIにはCI paasの一つであるdrone.io を使った。ビルド処理のスクリプトをDSLではなくシェル・スクラッチで書く等の特徴がある。travisほど機能が充実しているわけでは無いが、ツール自体がシンプル>なものなので、(ある意味)シンプルに纏まっているdroneを使ってみる事にした。
詳細はREADMEを見ていただくとして、使い方の概要としては
- 設定ファイル(
~/.gat/conf.json
)に必要な設定を記述し、
{
"gist" : {
"api-domain" : "https://api.github.com",
"access-token" : "YOUR_GITHUB_TOKEN"
},
"slack" : {
"webhook-url" : "YOUR_WEBHOOK_URL"
},
"playgo" : {
}
}
-
gat <gist | slack | playgo> FILE
でFILEの内容をCat(Post)する。 - 成功時は、生成されたURLやレスポンス文字列を結果として標準出力に出す。
- 複数ファイル指定や、前段コマンドをパイプで受け取る事にも対応させている。
- 自分では
alias ggist='gat gist'
みたいなalias切って使ってる
なぜこのツールを書いたか
- GoCon参加のモチベーションとして
- そもそも自分が欲しいツールだった
- Go製が欲しかった
- 既にGo製の類似ツールが公開されているのかも知れないが、自分で作ってもすぐ出来る気がした
- 類似ツールのググラビリティが低い という説も無くはなし
- 自分の場合何かしら新しい言語に触れ始める取っ掛かりでは HTTP API を叩くクライアントを自作してみるケースが多い。これは "1つ何か作ってみて先々別件でそのノウハウを流用できる場面が多い" という経験から
- シンプルであるが故にGoの目玉?機能であるゴルーチンを使う機会は無かったのだけど、「ゴルーチンを使って何かを作る」事よりも「今一番欲しいツールをGoで書く」事を重視
所感
GoCon前日までは、ミドル・インフラ周りでGoを導入した話 を中心に聞きたいなと思っていて、実際当日もそうした話を色々と聞けたのだけれど、参加後はそれよりも寧ろ "Goらしくシンプルに書きつつ、チームで開発する事になった場合に留意すべき事は何か?" という事を考えるようになった。 それは自分でツールを書いてみて躓いたりした経験もあっての事なのだけれど、取っ掛かりで同じように躓く人ちょくちょくいるんじゃないかなという気がするので、イマイマの考えを纏めてみる。
メタボリックmainシンドロームに陥った
メタボリックmainシンドローム と勝手に名付けていた事象なのだが、元々gistだけ対応させる事を考えていてmain以外のパッケージを切らずにガリガリ処理を書き、気がついたらmainパッケージがカオスな状況に陥ってしまっていた。言語仕様のシンプルさとコーディングの気持ち良さへの甘えと言うか、サクサク書けるが故に設計への意識が薄れてしまったのは否めない。
mainパッケージがメタボ化してくると、否応なしに自分のソースが "cliツールに密結合" な状態に陥ってきて、「あ、ここ外出ししてライブラリ化出来そう」って考えが浮かんだ時には泥臭いリファクタリング地獄と戦う、なんて羽目になり得る。(というか、なった)。パッケージを引っ越ししたらメソッド・変数の頭文字をUpperにしないと引越元からはアクセス出来なくなるから置換しなきゃ等々。
プログラムを作りつつ、ライブラリ(化)を意識する
例え作っているのが(最初は)シンプルなプログラムであっても、API likeな設計を 事前に 意識して{interfaceを使う,パッケージを切る}事に留意し、API化出来そうな機能はpublicなAPIとして実装していくのが大事だと実感した。これは別にGoに限った話ではないけど、Goではinterfaceとパッケージの設計が肝になると思っている。
自分の感覚値としてだが、構想段階ではinterface,パッケージの構成(≒API設計)を少々細かめにしておき、リファクタリングの段階で纏められそうな部分は纏める、というアプローチでも良い気がした。「ちょっと細分化し過ぎたかな、と思ってたけど、出来上がりを見たらそんな事も無かった」という結末になる事が割りと多いし、テストも書きやすい。
利用する側に何をgo get
,go install
してもらいたいのか?を考える
例えばプログラムが欲しいならgo get github.com/goldeneggg/gat
だけど、内部で使ってる機能だけ流用したい場合であれば go get github.com/goldeneggg/gat/client
すれば良い場合とか(import github.com/goldeneggg/gat/client
して使う)。
この場合gat/client
というパッケージを作ることを設計段階で予め決めておき、ライブラリ化可能な機能はこのパッケージの下に実装していくというアプローチを取る。初期段階からライブラリ化駆動で開発が進むので、main下に一纏めにするより見通>しが良く綺麗な設計を保ち続けられる。
"シンプルな上に分かりやすいパッケージ名" を付けるのも手を抜いてはいけない部分なのだが、これがなかなか手強くて、 J●va時代の名残りから commonとかutilとか安直な名前に逃げてしまいそうになるんだけど、これは良くないですねすみませ
んすみません
ライブラリ化するならドキュメント(コメント)大事
Golint が便利すぎて濡れた。
Golintはコードチェッカーで、ココGoコード的にイケてない書き方してるよ直しときなはれ、と教えてくれるツールなのだが、変数名やコメントの書き方についてもツッコミを入れてくれる。変数名については"API"や"URL"といった単語はCamelCaseではなく大文字3文字で書きましょうね とか、コメントについては publicな変数や関数にコメントが無いよ, "メソッドのコメントはMethodName..."で始めろ とかとか。
書いたコメントの見栄えを確認する際はgodoc -http[:PORT]
コマンドを使うと良い。godocコマンドはGoインストール時に標準でインストールされている。$GOPATH
直下で`godoc -http:6060'を叩くとlocalhostにGoのドキュメントサイトが立ち上がる。ブラウザから http://localhost:6060/ にアクセスすれば公式サイトと同じ見た目のページが見える。ここから Packages => 自分のpackage と辿れば良い。
まとめ
- ライブラリ化を意識してinterfaceやパッケージの設計を最初から真面目に考えとかないと、後で泣く