静的ファイルを管理するクラス
負荷対策の為にバッチで予め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)
一応これでロジックの集約という目的は果たせた。
ただ、コントローラのオブジェクトがないとキャッシュの削除ができないのが美しくない。
どうにかならないものかなぁ…。