読者です 読者をやめる 読者になる 読者になる

Kyoto Cabinetのwindowsバイナリパッケージが出ていたのでJRubyでしばいてみた

関連tweet
Javaのbindingがあるってところが個人的にはミソである。JRubyで動かせるじゃん。
kyotocabinetにあるサンプルコードを動かしてみようとしたが、importがうまくできなくて、クラスをフルパスで指定してみたりしているのが俺の残念なJRuby力の限界w。

require 'java'
require 'kyotocabinet.jar'

    # create the object
    db = Java::Kyotocabinet::DB.new()

    # open the database
    if (!db.open("casket.kch", Java::Kyotocabinet::DB::OWRITER | Java::Kyotocabinet::DB::OCREATE)) then
      puts "open error: " + db.error()
    end

    # store records
    if (!db.set("foo", "hop") ||
        !db.set("bar", "step") ||
        !db.set("baz", "jump")) then
      puts "set error: " + db.error()
    end

    # retrieve records
    value = db.get("foo")
    if value then
      puts value
    else 
      puts "set error: " + db.error()
    end

    # traverse records
    cur = db.cursor()
    cur.jump()
    while (rec = cur.get_str(true)) do
      puts rec[0] + ":" + rec[1]
    end
    cur.disable()

    # close the database
    if(!db.close()) then
      puts "close error: " + db.error()
    end

上記のコードをkc.rbとしてセーブして、jkyotocabinet.jarのあるディレクトリにおいて実行してみる。

C:\なんたら\KC\kcwin32\java>jruby kc.rb
hop
foo:hop
bar:step
baz:jump

C:\なんたら\KC\kcwin32\java>dir
 ドライブ C のボリューム ラベルがありません。
 ボリューム シリアル番号は 7049-2730 です

 C:\なんたら\KC\kcwin32\java のディレクトリ

2010/12/12  03:19              .
2010/12/12  03:19              ..
2010/12/12  03:36         6,297,792 casket.kch
2010/12/11  02:03           663,552 jkyotocabinet.dll
2010/12/12  03:35               923 kc.rb
2010/12/11  02:03            38,117 kyotocabinet.jar
               4 個のファイル           7,000,384 バイト
               2 個のディレクトリ     415,764,480 バイトの空き領域

へー、動くじゃん。DBは初期状態でこれくらいのサイズになるんだ。
importのやり方覚えよう。

で、何をたくらんでいるのか

でみかけたTwitter4JをJRubyで使うってところで、バックエンドにどうか、とか思ったのだった。鯖じゃなくて、自分のノートPCのようなtaransportableな環境でデータ扱えないか、ということ。
VMware Serverで鯖立てればええやん、とも思うが。そういうのにちょっと疲れているのでw
待てよ、OAuthをモバイルなPCでってのは無理があるか。

そういやR14BがノートPCで動くから、APIの勉強できるやんか

Erlangとのbindingについていろいろ試せるはずだ。

追記:KeyもValueも文字列扱いが無難かしら

いくつか、普通の文字列以外のvalueについて試してみた。Java APIのドキュメントにはkeyもvalueもbyte列もしくはStringの型だったので、それ以外はダメだろうけど、JRubyがマジックを使っていないかとほのかな期待w
db.set("foo", "hop")のところをdb.set("foo", 1.0)にすると、、、

: no set with arguments matching [class org.jruby.RubyString, class org.jruby.Ru
byFloat] on object # (NameError)

db.set("foo", "hop")のところをdb.set("foo", 1)にしても、

: no set with arguments matching [class org.jruby.RubyString, class org.jruby.Ru
byFixnum] on object # (NameError)

db.set("foo", "hop")のところを、改行を含んだvalueとしてdb.set("foo", "first line\nsecond line")にした場合は特に問題無い。

first line
second line
bar:step
baz:jump
foo:first line
second line

では\0を含んだ場合、すなわちdb.set("foo", "first line\0second line") の場合は?
特におかしな動作にはならなかったが、DOS窓からのコピペがうまくいかなくなることはあったw

first line second line
bar:step
baz:jump
foo:first line second line

これなら、URLをkeyにして、コンテンツをvalueにするようなユースケースは、たいていは大丈夫かもしれん。でも、あくまでもRubyだからな、気をつけないと。
ちうわけで、以下のようなコードは、とりあえず動いた。(ただ失敗だったのは、前のデータを消しておかなかったために、URLとコンテンツ以外のデータもダンプされることがあったことぐらいか。永続化万歳(汗

require 'java'
require 'kyotocabinet.jar'
require 'net/http'
Net::HTTP.version_1_2   # おまじない

    # create the object
    db = Java::Kyotocabinet::DB.new()

    # open the database
    if (!db.open("casket.kch", Java::Kyotocabinet::DB::OWRITER | Java::Kyotocabinet::DB::OCREATE)) then
      puts "open error: " + db.error()
    end

    urllist = {
		'www.ruby-lang.org' => '/ja/man/html/Hash.html',
		'fallabs.com' => '/kyotocabinet/javadoc/'
               }
    urllist.each do |host, path|
      if (! db.set(host + path, (Net::HTTP.get host, path)) ) then
        raise "set error : " + db.error()
      end
    end


    # traverse records
    cur = db.cursor()
    cur.jump()
    while (rec = cur.get_str(true)) do
      puts rec[0] + "\n" + rec[1] + "\n " + ("=" * 72 ) + "\n\n"
    end
    cur.disable()

    # close the database
    if(!db.close()) then
      puts "close error: " + db.error()
    end

文字コードの扱いは,,,

上記のコードで、ruby-lang.orgのドキュメントページを出力して思ったのだけど、文字コードの扱いは誰がやってんだろ。DB内で特殊用途の文字があったりすると、マルチバイトの扱いはencodingを知らずにやるのは難しいような気がする。開きなおってバイナリのバイト列でござんす、というほうがいいのかな。
上で、Javaのbindingにはbyte列とStringの引数をとるsetがどちらも定義されていると書いたけど、JRubyからはどうやって使い分けるんだっけな、確かできたはず...