Amazon WebサービスのレスポンスをActiveRecordでキャッシュ

前回RailsでAmazonのWebサービスを使ってみたけれども、何かあるたびに毎回リクエストを送るのはいろいろ無駄があって忍びないのでレスポンスをキャッシュしなければならない。

ということで今回はActiveRecordを使ってざっくりキャッシュしてみる。
キャッシュテーブルにたくさんカラムを定義するのは面倒なので検索キーにしたいものだけカラムとして定義して、あとは結果をシリアライズして突っ込む方針で。

キャッシュ用のテーブルとモデルを作る

まずはキャッシュするためのテーブルとモデルを準備。

テーブル

とりあえずマイグレーションでテーブルを作る。

class CreateAmazonCaches < ActiveRecord::Migration
  def self.up
    create_table :amazon_caches do |t|
      t.string :asin
      t.string :title
      t.text :response_dump

      t.timestamps
    end
  end

  def self.down
    drop_table :amazon_caches
  end
end
モデル

キャッシュを参照するためのモデル。
データを参照するときにレスポンスの内容をデシリアライズする。

class AmazonCache < ActiveRecord::Base
  @reponse_hash = nil
  @search_limit = 10

  def get(name)
    @response_hash = Marshal.restore(response_dump) if @response_hash.nil?
    @response_hash[name.to_sym]
  end

  def self.search(title, order = 'id', limit = nil)
    @search_limit = limit if limit
    self.find(:all, :conditions => ["title like ?", "%#{title}%"], :order => order, :limit => @search_limit)
  end
end

ハッシュのデシリアライズのタイミングはafter_findでもいいかもしれないなぁ。

キャッシュの保存と参照

テーブルとモデルができたので次はそれを使って実際にキャッシュする。

レスポンスをキャッシュする

以下がレスポンスをキャッシュするためのコード。

get_hashで取得したデータのハッシュだけシリアライズして突っ込む。
(itemのままだとなにやらうまいことMarshalでシリアライズできなかった)

res = Amazon::Ecs.item_search("Rails", :response_group => 'Large')
res.items.each do |item|
  AmazonCache.create(:asin => item.get('asin'), :title => item.get('title'), :response_dump => Marshal.dump(item.get_hash))
end
キャッシュを参照する

参照は以下のように。

items = AmazonCache.search("Rails")
items.first.get(:title) # => Railsの何かの本とかそんなタイトル

smallimageのように子要素を持つ要素はXMLの断片が保存されている。

items.first.get(:smallimage) # => "<url>http://ecx.images-amazon.com/images/I/hogehogehoge-.jpg</url><height units=\"pixels\">75</height><width units=\"pixels\">58</width>"

これは正規表現とかで値を取り出すのが楽かね。
モデルにそれ用のメソッドを追加してしまってもいいかもしれない。

まあひとまず今回はこんなところで。