at posts/single.html

キャッシュ機能付き PStore を作ってみた

Web サービス経由でデータを取得するときに、毎回データを取得するのではなく、一定期間はキャッシュさせておきたい場合がある。 そういう時は PStore に更新時刻付きでデータを保存し、データの読み込み時に更新時刻をチェックするようなプログラムを書く。 でも、毎回似たようなコードを書くのが面倒になってきたので、キャッシュ機能付きの PStore (CachedPStore) を作ってみた。

tDiary での Web サービス呼び出し系のプラグインでも使えると思う。

使い方

CachedPStore の生成時にブロックを渡すと、初回および有効期限切れのときにブロックが呼ばれてデータが更新される。 RubyのHashオブジェクトをブロック付きで new した場合と同じような使い方になる。 PStore を継承しているので、それ以外は PStore と同じように使える。

例えば、 Twitter のタイムラインを定期的に取得する処理は以下のように書ける。 有効期限を10分に設定しているので、10分以内はPStoreに保存したキャッシュを使い、10分を越えたらTwitterから再取得する。

require 'twitter'
require 'cached_pstore'
require 'pp'

# twitterクライアントの初期化処理は省略
twitter = Twitter::Base.new(oauth)

# 有効期間は10分(600秒)
db = CachedPStore.new('twitter.dat', :expire => 60 * 10) {|db, key|
  # 有効期間切れの時の更新処理
  client.__send__(key)
}

db.transaction {|db|
  # newで指定したブロックを呼び出し、タイムラインのデータをPStoreに格納
  puts db['user_timeline']
  # 普通にPStoreとして使うことも可能
  db['others'] = 'other data'
}

# ...5分後
db.transaction {|db|
  # 有効期間内なのでPStoreに保存したキャッシュが使われる
  puts db['user_timeline']
}

# ...さらに5分後
db.transaction {|db|
  # 有効期間が切れているので、newで指定したブロックを呼び出してデータを更新
  puts db['user_timeline']
}

仕組み

PStore#[] と PStore#[]= を再定義して、更新時刻とデータ本体を持つハッシュを使ってキャッシュ管理しているだけ。 ソースと spec ファイルは gist に置いた。

関連する日記