Google App EngineデータストアをRubyから簡単に使う

前置き

前回前々回あたりでとりあえずGoogle App Engine for Java(以下GAEJ*1)上でRubyスクリプトを動作させることができるようになりました。

が、どうしてもいつものRubyスクリプトとは勝手が違って、いまいちやりにくい気がします。

例えばデータの記録なんかも、両手で数え足りるような数のデータを記録するだけであれば、O/Rマッパー的な仕組みを使ってモデルを定義して…という作業も面倒です。もっと手軽に行いたいものですよね。

大人しくフレームワークでも使っておけ、という話もありますが、まぁそれはそれ…。

GAE Toolkit

そんなこんなで、あまり「GAEのデータストア」という存在を意識せずに手軽にデータの保存・取得を行えるように、簡単なラッパーを書いてみました。GitHubリポジトリに置いてあります。

"Tiny toolkit for JRuby on Google App Engine"などと銘打っていますが、まだデータストアにアクセスするためのクラスしかありません。このくらいのライブラリなら探せば他にいくらでもありそうですが、あともういくつか個人的に用意したい機能もある*2ので、後々追加していけたらな…と思います。

簡単な使い方

例えば次のようにしてGAEデータストア上にデータを保存できます。

GAEKit::Store.put(:name, 'Tanaka Tarou')
GAEKit::Store.put('age', 20) # キーは文字列でもシンボルでも同じ扱い
GAEKit::Store.put(:friends, [ 'Ichiro', 'Hanako', 'John' ])
GAEKit::Store[:location] = 'Tokyo/Japan' # putメソッドの別名

データの取り出しは次のような感じです。

GAEKit::Store.get(:name)
GAEKit::Store.get('age')
GAEKit::Store.get(:friends)
GAEKit::Store[:location] # getメソッドの別名

GAEデータストア上に記録されていますので、もちろんセッションをまたいで永続的に利用できます。

GAEKitモジュールをincludeしてしまえば、もっと手軽にアクセスできます。

include GAEKit
Store[:foo] = 'bar'
value = Store[:name]

その他のメソッドをさらっと。

Store.has_key?(:name) # 引数で指定したプロパティの存在確認
Store.include?(:name) # has_key?メソッドの別名
Store.delete(:name) # 引数で指定したプロパティの削除
Store.keys # 全プロパティの名前を配列で取得
Store.values # 全プロパティの値を配列で取得

インストール方法

gitをインストール済みの場合は

$ git clone git://github.com/shibason/rb-gaekit.git

でチェックアウトできます。

gitがインストールされていない場合は、下のページの"download"ボタンからダウンロードして適当な場所に展開してください。

必要なのはrb-gaekit/gaekit.rbファイル1つだけですので、これをWEB-INFディレクトリ以下の適当な場所に放り込みます。

$ cp rb-gaekit/gaekit.rb sample-application/WEB-INF

あとは上記のファイルをrequireすれば使えるようになります。

require 'gaekit'

もう少し詳しい説明

格納できるデータ

数値や文字列、nil/true/falseなどの基本的な値はもちろん、配列も格納できます。

ただしネストした配列は格納できません。また、ハッシュを始めとしたその他のオブジェクトも格納不可です。*3

サブクラスの活用

GAEKit::Storeクラスはそれ自身がGAEデータストアの一つのエンティティと対応しており、エンティティの識別子として自身のクラス名を使っています。

つまり、GAEKit::Storeを継承したサブクラスを作成すると、それをもう一つの別のエンティティとして利用することができます。

include GAEKit

class MyStore < Store; end

Store[:name] = 'Tanaka Tarou'
MyStore[:name] = 'Suzuki Ichiro'

[ Store[:name], MyStore[:name] ] # => ["Tanaka Tarou", "Suzuki Ichiro"]

なお、GAEKit::Storeクラス及びそのサブクラスに対応するエンティティのkind値は"GAEKit::Datastore"となっています。

GAEデータストアの制限

データの実体はGAEデータストアに保存されますので、GAEKit::Storeに格納可能なデータについても、当然GAEデータストアが持つ各種制限をそのまま受け継ぎます。

GAEデータストアの制限は以下のページなどを参考にしてください。

GAEKit::Storeでは一つのエンティティに全てのデータを突っ込みますので、大量のデータ、もしくはサイズの大きなデータを保存する場合は、きちんとモデルを定義して使うタイプの他のライブラリを利用することをお勧めします。

また、APIの呼び出し回数制限にも注意が必要です。GAEKit::Store#[]=やGAEKit::Store#deleteなどの書き込みメソッドは、その呼出し毎に内部でGAEデータストアのAPIを呼んでいますので、例えばループの中でこれらのメソッドを呼んでいると、APIの呼び出し回数が嵩むばかりでなく、処理速度にも大きな影響を及ぼします。

ループで処理する際は一時変数に退避して、全ての処理が完了してからデータストアに反映する、といった処理を行うようにした方が安全です。

参考資料

GAEデータストアの基本的な使い方はGoogleのドキュメントを参照しました。

JRubyからJavaクラスを呼ぶときの約束事や、オブジェクトの型変換などについては以下のサイトを参考にさせて頂きました。

GitHubの使い方については以下のサイトを参考にさせて頂きました。

*1:GAEjのほうが一般的な表記でしょうか?

*2:むしろそっちが本命だったりもするのですが

*3:Marshal.dumpとMarshal.loadを使えばある程度はいけるかも。