RailsでAmazonの商品を, 署名認証に対応した上で扱う

今朝こんなメールがきました.

【重要】Product Advertising API (旧 AmazonアソシエイトWebサービス)の署名認証に関するお知らせ

内容をサラッと言うと:

うちが調べてみたところ, おたくのAWSAccessKeyIDから送られてくるリクエストに電子署名がついていないんですよー.
んでー, 8月15日までに電子署名つけてくれないと, リクエスト受け付けなくなっちゃうんですよー.
だからそれまでにつけておいてね!

です.

いやー, やっぱり催促されちゃいますよね. というわけで署名認証に対応したのもメモメモ.

ruby-aawsのインストール

前回のバージョンで使ったamazon-ecsは署名認証に対応していない(だからメールがきたんだけど)ので, 別のRubyGemsを用意. 前回のバージョンのブコメid:takahashimさんが:

対応済みをうたっている(未確認)ruby-aawsの方がいいような

とおっしゃっていたので, 早速ruby-aawsをインストール.

$ sudo gem install ruby-aaws

ちなみにruby-awsというのもあって, それを間違えて入れていたのは内緒. ruby-awsではなく, ruby-aawsなので注意(自分に言い聞かせるように).

デフォルトの設定を書く

いつものようにこのRubyGemsを使いますよー, っていうのを書く.

# config/environment.rb
config.gem 'ruby-aaws', :lib => 'amazon'

あとデフォルトの設定(AWSAccessKeyIDなど)を書く. 専用のファイルに書くので:

# config/amazonrc
[global]
  key_id        = APIを使うために必要なアクセスキー
  secret_key_id = 秘密キー
  locale        = jp
  cache         = true
  cache_dir     = tmp/amazon

って書いて:

# config/initializers/amazon_aws_env.rb
ENV['AMAZONRCDIR']  = 'config'
ENV['AMAZONRCFILE'] = 'amazonrc'

そのファイルを読みにいくよう, 環境変数を設定しておく.

何気に素敵なのが, このRubyGems自体にキャッシュの機能がついているコト. config/amazonrcのcacheとcache_dirがそれ. キャッシュの保存場所はtmp/amazonにしたので:

$ mkdir tmp/amazon

そのためのディレクトリを作っておく.

モデルを書く

class AmazonItem < ActiveRecord::Base
  validates_presence_of :asin

  def method_missing(name, *args)
    lookup unless @looked
    @item.send name, *args
  end

  protected

  def validate
    lookup unless @looked
    errors.add :asin, "can't find the item from Amazon" unless @item
  end

  private

  def lookup
    page = Amazon::AWS::Search::Request.new.search(
      Amazon::AWS::ItemLookup.new('ASIN', 'ItemId' => asin),
      Amazon::AWS::ResponseGroup.new('Medium')
    )
    @item = page.item_lookup_response.items.item
    @looked = true
  end
end
amazon_item = AmazonItem.find_by_asin '4797336617'
puts amazon_item.item_attributes.title.to_s # => たのしいRuby 第2版 Rubyではじめる気軽なプログラミング

リクエストされたURLは:

http://ecs.amazonaws.jp/onca/xml
?AWSAccessKeyId=XXX
&AssociateTag=calibanorg-20
&IdType=ASIN
&ItemId=4797336617
&Operation=ItemLookup
&ResponseGroup=Medium
&Service=AWSECommerceService
&Timestamp=2009-06-17T09%3A46%3A55Z
&Version=2009-03-31
&Signature=g42E4Xoj%2F9c1peqybJxnN0iuxB8Cr4ZVPwggPcI0SHw%3D

なので, ちゃんと電子署名(Signatureパラメーター)が付与されていますよ, と*1.

ちなみにmemcachedを使わなくても, キャッシュを使うように設定すればruby-aawsだけでもそこそこ速い. あとItemLookup以外のオペレーションもキャッシュするので何かと便利かも.

ただ速度はオンメモリなmemcachedに及ばない(未確認)だろうし, ネットワークを介すこともできないのでそこはケースバイケースかな.

あと気になるのが:

Amazon::AWS::Search::Request.new.search(
  Amazon::AWS::ItemSearch.new('Books', 'Keywords' => 'ruby'),
  Amazon::AWS::ResponseGroup.new('Medium'),
  5
)

ってやると, 1ページ目から5ページ目までの検索結果が返ってくる点. 5ページ目だけじゃなくて?

*1:ちなみになんでAssociateTagがついてんの? つけていないのに……