コマンドラインランチャー iceberg のv0.9.6 をリリースしました。 GitHubの リリース一覧 よりダウンロードできます。

v0.9.5からのバージョンアップ方法は同梱ドキュメントの更新履歴を参照してください。

更新点は以下です。

  • FIXED : 一部のパスでアイコンが正しく読み込まれない問題を修正
  • FIXED : 一部のパスを読み込んだ際にクラッシュする問題を修正
  • CHANGED : 使用するコンパイラをMinGW-W64 4.9.1にアップグレード
  • IMPROVED : 単一キーをホットキーに割り当てられるようになった
  • NEW : パス補完とオプション補完でオートコンプリートを有効にする path_autocomplete と option_autocomplete を追加

ようやく重い腰をあげてgccのバージョンアップをしました…。icebergは今までmingw gcc4.5系を使っていて、これWindowsのくせにLP64なんですね。まぁそのおかげで*nix生まれのライブラリでも割と普通に使えていました。

で4.9にあげたのでWindowsらしくLLP64になったわけで、fltkなんかは平気で longvoid* にキャストしてるのでワーニング出まくりでした…。そんなクリティカルな場所はなかったので簡単なパッチ当てて終了。

鬼車も今まで使ってたバージョンは long のポインタキャストがあって落ちるので最新に。

なんやかんやあったけど今のところきちんと動いている気がします。なにか問題ありましたらGithubの Issues までどうぞ。

あと、どうやらicebergはWindows8で動かないっぽいです(Windows7互換モードにすると動く)。 8の環境持ってないのでなんで動かないのかわかりません。コンパイラ変えただけで動くようになってたり…しないかなあ。というわけでWindows8使ってる方、もし互換モードなしで動いたらコメントください。


Go言語は標準の net/http が結構よくできてるので、WEBフレームワークはなくてもいいだとかそういう話がありますし、 net/http をラップした俺俺フレームワークが大量に作られています。

というわけで、俺も遅ればせながら俺俺フレームワークを作りました。

読み方は「シードル」。お酒の名前つけるのが慣習ですからね。よくあるこんな感じ。

package main

import (
  "github.com/yuin/cidre"
  "net/http"
)

func main() {
    app := cidre.NewApp(cidre.DefaultAppConfig())
    root := app.MountPoint("/")

    root.Get("show_welcome", "welcome", func(w http.ResponseWriter, r *http.Request) {
        app.Renderer.Text(w, "Welcome!")
    })

    app.Run()
}

特徴

  • よくあるSinatraチックなAPI
  • できるだけ標準インタフェースを使用。いろんな既存ライブラリとの相性が良い。
  • 他の薄いフレームワークではオプションな機能も一部内包。

    • セッション、フラッシュメッセージ
    • レイアウト機能をサポートした html/template のラッパー
  • フック機能を提供していて、より柔軟に外部から拡張可能。

開発経緯

そもそもPythonistaの御多聞にもれず、2と3のはざまでもだえる中でGo言語書くことが多くなってたんですね。んでWEBもGo言語でさらっと書きたい、と。

Go言語のWEBフレームワークはいっぱいあって、軽量だとMartiniだとかGinだとかnegroniだとか、重量級だとbeegoだとかrevelだとか。俺の好みとしてやっぱりシンプルなものが好きなので軽量フレームワークを使いたいところだけど、Martiniはtoo magicだし、Ginは40 times fasterってのが詐欺っぽいし、negroniはツールであってフレームワークじゃないと言っているしで、あんまりしっくりくるのがありませんでした。

なので自分が最低限必要と思う機能を組み込んだフレームワークを作ったわけです。ミドルウェアで対応できるけど組み込まれてたほうが楽だし。SPAが流行ってるって言ってもさらっと作るときはフラッシュメッセージが楽だし、設定は外出ししときたいし、とか。

基本的に http.Handler (もしくは http.HandlerFunc )で構成されるので他のいわゆる「ミドルウェア」と呼ばれているものもすんなり組み込めます。せっかく組み込みライブラリがよくできてるんだから、なるべくフレームワーク特有のことは覚えたくないのもある。

テンプレートエンジンやSessionストアやロガーはInterfaceなので差し替え可能です。

あと、適当に今風のプロジェクトページ作りました。

開発中に思ったこと

俺俺フレームワークを書きたい方のために、cidreを書いてる時に思ったこと、検討したことを書いておきます。

Contextの持ち方

どのフレームワークも Context という構造体がだいたいある。これは入れ子になってる http.Handler 間でデータを受け渡すのが主目的だ。

でContextの考え方は3種類ある。

  • 1. http.Handler インタフェースを使わず独自インタフェースをつくって引数として渡す。
  • 2. Gorilla context のようなスレッドローカル変数を使う。
  • 1. http.Handler#ServeHTTP の引数である http.ResponseWriterhttp.Request のどこかに埋め込む。

1はGinやnegroniなど大半のフレームワークが採用している方法。これはこれでシンプルでよい。ただし独自インタフェースになる。

2はスレッドローカルにするためにgoなのにLock, Unlockが走りまくるのが難点だが見た目すごくクリーン。

3は生成時にトリックが必要だけど標準インタフェースを使えて、ロックも発生しない。というわけでcidreは3の方式をとっている。Goで外部からオブジェクトを埋め込むためには

  • それがインタフェースで
  • Public

じゃないといけない。というわけで http.Request#Body に埋め込んでいる。

拡張性

Writing HTTP Middleware in Go という記事があるように、 http.Handler をPythonのWSGIミドルウェアのように扱う、というのは標準的な考え方だろう。

ただ、結局この方式はただのフィルタであって柔軟性がない。HTTPボディを書く前に処理を差し込みたい、とかできない。正確にはできないことないけどめんどくさい(独自ResponseWriterを作って次のミドルウェアに渡すことになる)。Martiniでは独自ResponseWriterにコールバックが設定できるようになっていてHTTPボディ書く前にヘッダ書くというのができる。

でも結局そういうポイントって随所にあって、統一的に扱える仕組みがあったほうがよいと思う。のでHookの仕組みをつくってサーバ起動時、とかいろんなところをフックできるようにしてある。

設定オブジェクト

これは何を今さら、な話で設定を表すオブジェクトをどう扱うか、ということ。例えば以下のようなstructがあるとして

type Config struct {
    Host string
    Port int
    Timeout time.Duration
}

これにどうデフォルト値を適応するかっていうこと。

スクリプト言語ならundefinedなりnullなりnilなり未初期化を表す共通の値があるので、よいのだがCやGoではintは初期値0だし、0と設定したのか未設定なのかわからない。

なので以下のようなデフォルト設定を返す関数をつくってそれに設定を追加していく形がよいと思う。

func DefaultConfig(init ...func(*Config)) *Config {
    self := &Config {
        Host: "localhost",
        Port: "8080",
        Timeout: 180 * time.Second,
    }
    if len(init) > 0 {
        init[0](self)
    }
    return self
}

config := DefaultConfig()
config.Timeout = 0

あと利便性のためこういう書き方もできるようにしてある。

app := NewApp(DefaultConfig(func (config *Config){
    config.Timeout = 0
})

といろいろあるけどこんなところで。

今後について

今後も細々メンテしていくつもりですし、ミドルウェアなんかも追加していきたいなあと思っています。やっぱGo言語はさらっと書けるそれなりに速いし、いいっす。


フロントエンド開発者向けのAlfred Workflow という記事を見かけたので、それ iceberg でもできる かも よ!ということでいくつか作ってみました(仕様上実装できないものもあったのでiceberg自体も改修して v0.9.4としてリリース しています)。

簡単にできそうなものをピックアップしたつもりが以外と苦労したかもしれない…

iceberg-ip

image

IPアドレス一覧を表示するコマンド。

iceberg-worldtime

image

世界主要都市の現在時刻を表示する。頑張ってサマータイム対応したのだけど、これがめんどくさかった…

iceberg-encodedecode

image

Base64, URL, HTML entityでのエンコード結果を表示。

というわけで

これらのコマンドはLuaのみで書かれています。

以下の weather コマンドのようにHTTPで結果を取得してそれをリストに表示することもできます。加えてmigemoに対応しているので絞り込みも簡単です。

image

Windows7以降はOS自体にWin+Rより高度なコマンドラインランチャー的な機能が内蔵されたこともあって、こういうのがあるとコマンドラインランチャを使う意義みたいなのも高まるかな、と。