2011/06/09


Google App EngineへのGo言語アップロード権(Trusted Tester for the golang runtime for appengine)を得たので、Webアプリを作り始めたら皆が必ず作るという「おうっふー」をGo言語に移植してみた。
なお、「おうっふー」とはログイン画面のみを持ち、ログインすると有無を言わさず「おうっふー」という単語をログインアカウントにポストするWebアプリケーションの事。知ってない奴はWeb界のモグリだ!って隣の人が言ってた。
以下、Go言語のコードを解説してみる。 ログイン画面からログインハンドラに要求が行われると、OAuth1.0でリクエストトークンがTwitterに要求され、得たテンポラリトークンで認証画面に遷移します。 http.HandleFunc("/login"func(w http.ResponseWriter, r *http.Request) {
    client := urlfetch.Client(appengine.NewContext(r))
    tmp, err := creds.request(client)
    if err != nil {
        http.Error(w, err.String(), 500)
        return
    }
    url := fmt.Sprintf("%s?oauth_token=%s&oauth_callback=%s", authURI, http.URLEscape(tmp.Key), http.URLEscape(callbackURI))
    w.Header().Set("Set-Cookie""tmp="+http.URLEscape(tmp.Key)+"/"+http.URLEscape(tmp.Secret))
    http.Redirect(w, r, url, 302)
})
この時、注意しなければならないのがurlfetchにリクエストから作成したコンテキストが必要である事。urlfechを使いたい関数までhttp.Requestを引きずり回さないといけないという、なんとも煩わしい仕様ですがキマリはキマリ。
また認証画面から戻って来た時に再び照合が行える様に、テンポラリトークンをクッキーに保持しておきます。

認証画面から戻ってくると登録していたコールバックURLが呼び出されます。 client := urlfetch.Client(appengine.NewContext(r))
v := r.FormValue("oauth_verifier")
a := strings.Split(r.Header.Get("Cookie"), "=", -1)
w.Header().Set("Set-Cookie""tmp=")
if len(a) != 2 || a[0] != "tmp" {
    http.Error(w, "invalid request"500)
    return
}
a = strings.Split(a[1], "/", -1)
if len(a) != 2 {
    http.Error(w, "invalid request"500)
    return
}
ak, _ := http.URLUnescape(a[0])
as, _ := http.URLUnescape(a[1])
tmp := &Token{ak, as}
token, err := creds.verify(client, tmp, v)
if err != nil {
    http.Error(w, err.String(), 500)
    return
}
クッキーを読み取り得たテンポラリトークンと、認証画面から戻されたベリファイアを合わせて照合を行います。Twitter APIを使ったアプリをバシバシ書いてる諸君なら、鼻くそほじるより簡単ですね。
後は得られたアクセストークンを使ってTwitterにポストする。 gouffu := []string{"ごうっふ~""おうっふ~""もうっふ~""とうっふ~""もふもっふ~""わっふ~""きゃっふ~"}
data := map[string]string{"status": gouffu[rand.Int()%len(gouffu)]}
creds.sign(token, data, resURI, "POST")
resp, err := client.PostForm(resURI, data)
if err != nil {
    http.Error(w, err.String(), 500)
    return
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
    http.Error(w, resp.Status, 500)
    return
}
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
    http.Error(w, err.String(), 500)
    return
}
var m map[string]interface{}
err = json.Unmarshal(b, &m)
if err != nil {
    http.Error(w, err.String(), 500)
    return
}
screen_name := m["user"].(map[string]interface{})["screen_name"].(string)
id_str := m["id_str"].(string)
url := fmt.Sprintf("https://twitter.com/%s/status/%s", screen_name, id_str)
http.Redirect(w, r, url, 302)
単語1つでは遊び心が無いので、何個か用意した。ポストが完了したらパーマリンクへリダイレクトする様にした。
なお、このサーバではクッキーは一時的に保存する事はしてもデータストア等には何も残していません。

ごうっふ~

...

http://go-ouffu.appspot.com/
良かったら遊んでみて下さい。
ソースはこの辺に置いときます。
mattn/go-ouffu - GitHub

ごうっふ~

https://github.com/mattn/go-ouffu
Posted at by



2011/05/29


Go言語がGoogle App Engineで動くようになった事を先日の記事でご紹介しました。
僕に取っては大きな出来事でした。
さて、Go言語用にSDKが提供されていますが、このSDKに含まれるgo-moustachioはアップロードされた画像にヒゲを付けるWebアプリケーションで、以下のURLでも公開されています。
Welcome to Moustachio - Moustachio
http://moustach-io.appspot.com/
仕組みとしては、アップロードされた画像に対してJSON APIから受け取ったパラメータを元に、ヒゲの位置、ヒゲの上向き下向き度合いを変更出来ます。HTML上で合成するのではなく、freetypeを使った画像合成が行われています。
出来上がった画像はGoogle Buzzやtwitterで公開出来るリンクが用意されています。

とても面白いサンプルなのですが、一つ大きな問題が起こりました。
そう、ヒゲが回転出来ないんです。
moustachio1
どう考えても、時計周りに回転が必要ですよね。これでは全世界のヒゲマニアが安眠出来ません。そこでパッチを書きました。
a820df2a6dda - mattnjp-appengine-go - fork of appengine-go - Google Project Hosting

Log message rotate moustachio.

https://code.google.com/r/mattnjp-appengine-go/source/detail?r=a820df2a6ddab19d7c22fe66f8fc76fc41690018&name=default
仕組みとしては、これまで画像イメージに対してfreetypeでヒゲを描いていたのに対して、同じ大きさのRGBA領域に対してヒゲを描画し、ヒゲ画像を回転して元の画像に移しています。
メインとなる部分のコードは以下。
// transcribe points to image from rotated moustache
ra := math.Pi * 2 * float64(angle) / 360
for i := 0; i < h; i++ {
    for j := 0; j < w; j++ {
        xx := int(float64(j-x)*math.Cos(-ra)-float64(i-y)*math.Sin(-ra)) + x
        yy := int(float64(j-x)*math.Sin(-ra)+float64(i-y)*math.Cos(-ra)) + y
        c := mp.At(xx, yy)
        cr, cg, cb, ca := c.RGBA()
        if cr != 0 || cg != 0 || cb != 0 || ca != 0 {
            mrgba.Set(j, i, c)
        }
    }
}
一般的に画像の回転は各ポイントを回転させるのではなく、回転後のポイントに対して元のポイントを求める事で穴の開かない画像が得られます。 本来ならば、オンラインで公開したい所ですが、先日の「appengineにgoをデプロイ出来る権利50名様プレゼント」に外れてしまいましたので、試したい方はローカルサーバでお楽しみ下さい。
moustachio2
ちゃんと綺麗にヒゲが生えました。
Posted at by



2011/05/24


部下にも何度も説明してて、この辺がC言語のポインタみたいな鬼門なのかなーとか思いながら。
javascriptでhtml内のonclickの内容を書き換えようとしています.. - 人力検索はてな

javascriptでhtml内のonclickの内容を書き換えようとしていますが 変更後の関数に変数を渡すと、変更後の関数(load)自体が実行されてしまい、うまくいきません 現在は以下のように、onclickの内容を変更しようとしていますが、 関数を実行させずに、html内のonclickの内容だけ書き換える場合はどのようにしたらいいですか?

document.getElementById('box').onclick = (function(id){ load(id) })(userid);

http://q.hatena.ne.jp/1305849029
こういうコードになった経緯を考えると、その間違いがなぜ起こったかわかりやすいし、人力検索で回答貰った後で再度間違う事も少ないと思う。

おそらくだが、この人はこのコールバック関数登録をループかなんかの中で行おうとしたんじゃないかと(勝手ながら)思った。 <script>
window.onload = function() {
    var elem = document.getElementsByTagName('a');
    for (var n = 0; n < elem.length; n++) {
        elem[n].onclick = function() {
            alert(n);
        }
    }
}
</script>
<body>
<a href="#">foo1</a>
<a href="#">foo2</a>
<a href="#">foo3</a>
</body>
このHTMLでfoo1,foo2,foo3をクリックするとどうなるだろう。0,1,2と答えた人は、この質問者と同じ間違いを起こすだろう。答えは全て3が表示される。
これが何故起きて、どう解決すべきかが分かると今後の理解も早いと思う。

ループのインデックスであるnは、コールバック関数から見ると外のスコープにある。つまりコールバック関数が呼び出された時にはループは3回回ってしまっていて、結果どれをクリックしても3が表示される。
じゃぁどうするか。
var elem = document.getElementsByTagName('a');
for (var n = 0; n < elem.length; n++) {
    var f = n
    elem[n].onclick = function() {
        alert(f);
    }
}
こうじゃない?って答えた人は×。それ何も変わってないから、どれクリックしてもまた3だよって答えた人も×。答えは全て2。
なぜ前回は3が表示されたのか。それはforループが回りきってループを外れる条件に達した、つまり3になったから。ではなぜ今回は2なのか。ループは0,1,2の時にしか実行されなかったから。
わかりますよね。

じゃぁこれ、どうやって個々に0,1,2の値を表示させるのよ...となる。答えは聞いてない!答えは複数ある。
人によってはeval()使っちゃう人もいるだろうし、昔のコードでは結構見た。未だにそんなコードの保守をやらされる場合もある。出来るだけモダンな書き方したいですよね。
問題はスコープでしたよね。毎回同じスコープが実行されちゃってるのが問題で、コールバック関数から見ると全て同じ変数を見てしまっているのが問題。
そこでループの度にスコープを作ってあげるのです。以下の様なコードを見ることが多いと思います。 (function() {
    // ...
})()
関数を文として扱わず、式として呼び出しているんですよね。最近はこんな書き方もある様です。
+function() {
    // ...
}()
これで新しいスコープが出来ます。でもこれだけでは解決しません。この作ったスコープで変動しない値を保持してあげます。
var elem = document.getElementsByTagName('a');
for (var n = 0; n < elem.length; n++) {
    (function() { // ココが決め手
        var f = n
        elem[n].onclick = function() {
            alert(f);
        }
    })() // ココが決め手
}
変数fにn渡してます。このループが3回実行されると3回スコープが作られ、それぞれにfというスコープ内変数が宣言されます。これにより個々のイベントハンドラから0,1,2が参照出来る様になるという事です。
ちなみにvar宣言したくない人はよく for (var n = 0; n < elem.length; n++) {
    (function(n) {
        elem[n].onclick = function() {
            alert(n);
        }
    })(n)
}
こう書いたりもします。
今回の問題、よくカウントダウンタイマを作ろうとして for (var n = 0; n < 10; n++) {
    setTimeout(function() {
        document.getElementById('timer').innerHTML = '残り' + (10 - n) + '秒'
    }, 1000 * n)
}
こう書いちゃう人もいます。理屈は同じですよね。気をつけて。
Posted at by