Hatena::ブログ(Diary)

Lazy Technology

2007-05-17

[]静的ファイルを管理するクラス

負荷対策の為にバッチで予めHTMLを作っておくのは多くのサイトでやっていることだと思う。

Railsでは静的ファイルはrhtmlにしてrender :partialで読み込むか、File.readで読み込むかだろう。

そして、毎回ファイルのIOが発生するのは無駄って事で大抵キャッシュする事を考えると思う。Railsキャッシュ簡単だし。


と言うわけで早速検証。

RailsRoot/html/hoge.htmを作成し、適当なビューに以下のように記述。

<% cache do('html/hoge.html') %>
	<%= File.read('html/hoge.html') %>
<% end %>

動作確認。development環境で該当ビューにアクセス。

その後、hoge.htmlを削除して、再度アクセス。すると例外が発生する。

次に、config\environments\development.rbを以下のように変更。

config.action_controller.perform_caching = true

変更後、APサーバ再起動

先程と同様の手順を繰り返すも、例外は発生せずちゃんとhtmlが表示されている。


ただ、これだとキャッシュを削除するタイミングがない。

できればバッチの作成と同期する形でキャッシュをクリアするのが理想だろう。

ActiveRecordに関連するファイルであればsweeperを使えるのだが、DBとは関係のない話なので、今回はキャッシュをクリアする専門のコントローラを作る事にした。

ruby script/generate controller CacheCleaner test

testアクション内にキャッシュを削除するロジックを書く。

def test
  expire_fragment('html/hoge.html')
end

CacheCleanerコントローラへは外部からアクセスできないように、Apacheなどの設定を書く。

一応これで目的は達成できたのだが、ロジックが複数個所に分散してしまっていたり、

<% cache do %>
	<%= File.read('html/hoge.html') %>
<% end %>

毎度毎度こう書くのはどうもかっこ悪い。

ファイルの読み込みロジックをビューに直書きするのもDRYに反している。


と言うわけでキャッシュ用のモジュールを作って見る事に。以下ソース。

module Html
  module HogeRanking
    class << self
      def key
        self.to_s
      end
      def read(controller)
        result = controller.read_fragment(self.key)
        unless result
          result = File.read('html/ranking/hoge.html') 
          controller.write_fragment(self.key, result)
        end
        result
      end
      def reload(controller)
        controller.expire_fragment(self.key)
        self.read(controller)
      end
    end
  end
end

ファイルを読み込むときはビューにこう書く。

<%= Html::HogeRanking.read(controller) %>

キャッシュをクリアするときはコントローラにこう書く。

Html::HogeRanking.reload(self)

一応これでロジックの集約という目的は果たせた。

ただ、コントローラオブジェクトがないとキャッシュの削除ができないのが美しくない。

どうにかならないものかなぁ…。