node-kyoto-tycoon

node-kyoto-tycoon
http://github.com/swdyh/node-kyoto-tycoon

KyotoTycoonのNode.js用ライブラリを作りました。まだだいぶ荒削りな状態で、KyotoTycoonのAPIを簡単にラップした感じのものです。APIも変わるかもしれないし、バグもたぶんあると思いますが試してみてください。

Node.jsが新しめでないとHTTPのKeep-Aliveが使えないので、古いNode.jsを使っている場合は、新しいNode.jsを入れて使ってください。(Keep-Aliveが使えなくても動きますが、カーソル関連は使えないと思います)

インストール

npmを使う

 % npm install kyoto-tycoon

ソースから

 % git clone http://github.com/swdyh/node-kyoto-tycoon.git
 % cp -r node-kyoto-tycoon ~/.node_libraries/kyoto-tycoon

使い方

    var KyotoTycoon = require('kyoto-tycoon').KyotoTycoon

    var kt = new KyotoTycoon()
    kt.set('key1', 'val1', function() {
        kt.get('key1', function(err ,data) {
            console.log(data)
            kt.end()
        })
    })

Node.jsらしく非同期なAPIしかなくて、なにをするにもコールバック関数を渡します。
ただこれだと、いくつかの処理を順にやろうとすると、コールバック関数がネストしていってつらいので、JSDeferredを使えるようにしています。ソースの中に入れてあるのでインストールする必要はありません。

    var kt3 = new KyotoTycoon({ deferred : true })
    kt3.set('key3', 'val3').next(function() {
        return kt3.get('key3')
    }).next(function(err, data) {
        console.log(data)
        return kt3.remove('key3')
    }).next(function(err, data) {
        return kt3.get('key3')
    }).next(function(err, data) {
        console.log(err)
        kt3.end()
    })

newのときにdeferredオプションを付けておくと、Deferredオブジェクトが返ってくるようになるので、nextで次の処理をつないでいけば順番に処理していけます。

実装の話

KyotoTycoonはhttpでアクセスするで、Node.jsについているhttpのライブラリをそのまま使っていています。データはTSVで返ってくるTSV-RPCという方式らしいので、TSV-RPCを処理するtsv-rpc.jsと、それを使ったkyoto-tycoon.jsのという構成にしています。
KyotoTycoonにははREST APIもあるんだけど、こっとは機能が少ないのと、REST用ライブラリだったら汎用のものがありそうで、KyotoTycoon用のライブラリとして書く意味がないという気がしたので使ってません。

JSDeferredを使えるようにしたところは、Deferred.connectというコールバックを取る関数を渡すと、Deferredを使った関数返をしてくれるという魔法みたいな関数があるので、それを呼びまくって関数を入れ替えています。

テストはVowsというので書いていて、正直あまり分かってないんだけど、非同期な関数のテストを想定してあったり、出力が綺麗だったりするので使ってみました。

AutoPagerizeのFirefox拡張をJetpack SDKで書き直した

Jetpack SDKが0.8になってpage-mod API(content script)がついたので、それを使ってAutPagerizeのFirefox拡張をつくり直しました。

前に作っていたFirefox拡張とは別の拡張になるので(Jetpackの制約のため)、以前のFirefox拡張を使っている場合は、それをアンインストールしてからこちらをインストールしてください。

インストール
https://relucks-org.appspot.com/autopagerize/autopagerize.xpi

ソースコード
http://github.com/swdyh/autopagerize_for_firefox


page-mod APIはけっこういい感じにできていて、JetpackAPIが使えるmain.jsとcontent scriptの間をpostMessageでやりとりできるようになっていて、ChromeSafariの拡張と同じような感じで使えます。

ただpage-mod APIは、content scriptを文字列で指定するようになっているのが難点で、これはselfモジュールを使って読み込むようにするといいと思います。

    pageMod.add(new pageMod.PageMod({
        include: ['http://*', 'https://*'],
        contentScriptWhen: 'ready',
        contentScript: self.data.load('autopagerize.user.js'),
        onAttach: onAttach
    }))

AutoPagerize for Safari

Safari5がリリースされ、拡張機能が追加できるようになったので、AutoPagerizeSafari版をつくってみました。Safariの拡張の仕組みは、Chromeの拡張とほとんど同じなのでわりと簡単につくれました。

いまのSafari5で拡張機能を使うには、環境設定の詳細で開発メニューを表示するようにして、開発メニュー「拡張機能を有効にする」をチェックする必要があります。あとは拡張用のファイルをダウンロードして開くとインストールできます。

http://autopagerize.net/

URLのトラッキング用パラメータを無視するFirefox拡張 Through utm_

URLのトラッキング用パラメータを無視するFirefox拡張(Through utm_) を作りました。インストールすると、utm_から始まるパラメータがついたURLにアクセスしようとした場合に、そのアクセスをキャンセルし、そのパラメータを削除したURLへアクセスします。

Through utm_
https://relucks-org.appspot.com/through_utm_/through_utm_.xpi

Jetpack SDKを使っていて、ソースはいつものようにGithubに置いてあります。

swdyh's Through-utm_ at master - GitHub
http://github.com/swdyh/Through-utm_

Jetpack SDKを使ってちょっと書いてみたという感じのもので、いま思うと他のトラッキング用パラメータ(?ref=rssとか)も対象にしたり、設定で変更できるようにしたり、もうちょっと汎用的にしてもよかったかなあと思ってます。

前からhttpまわりを簡単に触れるとけっこう面白いんじゃないかなあと思っていて、このへんはもうちょっといろいろできそうだなあという感じはあるので、またべつのものを作るかもしれないです。

あと今回は、ファイルをGoogle App Engineに置いています。Jetpack SDKでxpiを作る場合に署名を仕込むのが難しそうだったので、httpsが使えるApp Engineにしました。ファイルを置くだけならyamlを書くだけでいいし、なかなか良さそうです。App Engineでファイルを配布する場合、.xpi用のmime typeが用意されていないので、設定ファイルでmime type(mime_type: application/x-xpinstall)を指定しておくといいと思います。

Jepack SDKがリリースされたので使ってみる

Mozilla Labs » jetpack » Blog Archive » Announcing the Jetpack SDK: First Milestone Release
http://mozillalabs.com/jetpack/2010/03/09/announcing-the-jetpack-sdk/

環境は、MacOS 10.5.8、python2.5(macportsで入れたやつ、もとから入ってるのでもいいと思う)

SDKをダウンロードして解凍

 % wget https://ftp.mozilla.org/pub/mozilla.org/labs/jetpack/jetpack-sdk-0.1.tar.gz
 % tar zxvf jetpack-sdk-0.1.tar.gz
 % cd jetpack-sdk-0.1

SDKの環境をロード

 % source bin/activate

ドキュメントの説明通りにパッケージをつくってみる

 (jetpack-sdk-0.1)% mkdir packages/my-first-package
 (jetpack-sdk-0.1)% cd packages/my-first-package
 (jetpack-sdk-0.1)% vim package.json
 {
   "description": "This is my first package, it's tiny.",
   "author": "Me (http://me.org)"
 }

モジュールをつくる。モジュールはCommonJSスタイル。exportのプロパティに追加しておくと、他のファイルからrequireしたときに呼び出せる。

 (jetpack-sdk-0.1)% mkdir lib
 (jetpack-sdk-0.1)% vim lib/my-module.js
 exports.add = function add(a, b) {
   return a + b;
 }

モジュールのテストを書く

 (jetpack-sdk-0.1)% mkdir tests
 (jetpack-sdk-0.1)% vim tests/test-my-module.js
 var myModule = require('my-module')
 exports.ensureAdditionWorks = function(test) {
     test.assertEqual(myModule.add(1, 1), 2, '1 + 1 = 2')
 }

テストの実行。

 (jetpack-sdk-0.1)% cfx test -v
 info: executing 'test-my-module.ensureAdditionWorks'
 info: pass: 1 + 1 = 2

 Malloc bytes allocated (in use by application): 6436928
 Malloc bytes mapped (not necessarily committed): 14262272
 Malloc bytes committed (r/w) in default zone: 6446624
 Malloc bytes allocated (in use) in default zone: 13213696
 Tracked memory objects in testing sandbox: 2

 1 of 1 tests passed.
 OK
 Total time: 1.215288 seconds
 Program terminated successfully.

ドキュメントを書く。Markdown形式。

 (jetpack-sdk-0.1)% vim README.md
 This is my *first* package. It contains:

 * A tiny module.
 * A tiny test suite.
 * Some meager documentation.

モジュールのドキュメントを書く。

 (jetpack-sdk-0.1)% mkdir docs
 (jetpack-sdk-0.1)% vim docs/my-module.md
 # my-module documentation!!

ドキュメントを開く

 (jetpack-sdk-0.1)% cfx docs

ブラウザにSDKのドキュメントが表示される。左下のへんを見ると、自分のパッケージも追加されていて、ドキュメントも表示される。

起動部分を書く

 (jetpack-sdk-0.1)% vim lib/main.js
 exports.main = function(options, callbacks) {
     console.log("Hello World!")
     callbacks.quit()
 }

動かしてみる

 (jetpack-sdk-0.1)% cfx run
 info: Hello World!
 OK
 Total time: 0.559730 seconds
 Program terminated successfully.

モジュールを使ってみる

 var myModule = require('my-module')
 
 exports.main = function(options, callbacks) {
     console.log("Hello World!")
     console.log(myModule.add(1,2))
     callbacks.quit()
 }
 (jetpack-sdk-0.1)% cfx run
 info: Hello World!
 info: 3
 OK
 Total time: 0.562395 seconds
 Program terminated successfully.

もうちょっと書いてみる

 exports.add = function add(a, b) {
     return a + b;
 }
 exports.notify = function notify(options) {
     Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService).
     showAlertNotification(options['icon'], options['title'], options['body'])
 }

CcやCiは定義されていて使える。(セキュリティ関係で扱い方がかわるかも。)

 var myModule = require('my-module')

 exports.main = function(options, callbacks) {
     myModule.notify({title: 'Run', body: 'This is my first Jetpack SDK app.'})
     callbacks.quit()
 }

xpiにしてみる

 (jetpack-sdk-0.1)% cfx xpi
 Exporting extension to my-first-package.xpi.

my-first-package.xpiをブラウザにドロップして、再起動。通知が出てきたら、成功

雑感

けっこう良い気がする。APIがそろってくれば、もっと書きやすくなるだろうし、APIが足りない部分はCcとかCi使って、ごりごり書けばなんとかなりそうだし。やっぱりそこが、Chromeの拡張に対するアドバンテージだろうなあ。
Jetpackから入るひとにCcとかCi使え、というのは酷だけど、もとから拡張書いてるひとが、そのへんをラップしたライブラリ書けばいいと思う。

今までだって、そういうラップしたライブラリは書こうと思えば書けたし、書いてるひとももちろんいるんだけど、そのへんが共有されてない感じがあって、結局、再発明したりMDCからコピペしたりとかになっちゃってたから、CPANとかGem的なライブラリを共有する仕組みがあるとJetpackはもう一歩先に進めるんじゃないかと思う。