RailsのAction Cache

RailsでAction cacheでlayoutの中身だけをchaceする Action cacheはactionを丸々キャッシュするのですが、 フィルターは実行してくれるのでそこに「キャッシュを何時どうやって消すか?」という ロジックを書くことが出来るという利点があります。

また、Layoutの中身だけキャッシュするという機構があるので、レイアウトの外側に ログインしているユーザ名を書いたりする動的なページも作ることができます。

これらどちらも Page cacheでは難しい問題なんですよね。 さて、このAction cacheですが 3.Xまでは標準パッケージに含まれていましたが、 4.xからは標準パッケージに含まれなくなったので、gemで追加してやる必要があります。 やり方は他と同様 Gemfileに次の一行を書いてbundleをしなおすことでOKです。

gem 'actionpack-action_caching'

使い方ですが、controllerにキャッシュしたい actionを以下のように書けばOK

  caches_action :show

これで 初回アクセス時のみshow アクションが実行され、2回目からは 初回アクセス時に renderされたHTMLが帰ってきます。 内容が更新されない場合は、これだけでいいのですが更新もある場合は 以下のように chaceのパラメータに何か更新する情報を渡してやるのがよいでせう。

caches_action :show, cache_path: Proc.new{ params.merge(:t => @blog.updated_at.to_f) } 

@blogが更新される度に updated_atが更新されれば、 cache_pathパラメータが 毎回変わるので古いキャッシュが使用されることなく新しいキャッシュファイルが 作られるというわけです。 ただしこれは tmp/cachesの下にどんどんファイルが溜っていくので、 定期的に日付を見てファイルを削除してやるロジックが必要になります。 Layoutの中身だけCacheしたい場合はさらに layoutパラメータを追加し

  caches_action :show, layout: false, cache_path: Proc.new{
   params.merge(:t => @blog.updated_at.to_f)
  }

とします。但しこの場合

  • Controllerはデフォルトのレイアウトを持っていなければならない
  • render時に layoutのパラメータを指定してはならない

という条件がつきます。Filterの評価が全て終ったあとに、どのlayoutが 使われるのあ分からないとキャッシュできないので、これは致し方のないところで ありましょう。 通常はデフォルトのLayoutは applicationになっていると思いますが、 例えばuser毎にlayoutを変える必要がある場合は

  layout :custom_layout
...
...
  private
  def custom_layout
    @user.laytout
  end

のように layoutにメソッド名をシンボルで指定してやればOKです。 さて、ここまで書いて、いろいろなところで @userやら @blogなどというインスタンス変数が登場してますが、 これらを Actionで取得することはできません。つまり、

  def show
    @blog = Blog.find(params[:id])
  end

とか書くのはNGです。何故なら cacheを使うかどうかは actionよりも先に実行されるので、 その前に@blogなどは取得できてないといけないわけです。 つまり、

  before_action :set_blog, if: [:show]

  private 
  def before_action
    @blog = Blog.find(parmas[:id])
  end

のようなFilterを書いておかないといけないと。 また、Layoutの外側に書く内容も当然filterで全て処理をしておかない いけなくなります。 結果 Action本体よりも Filterばっかり書くハメになり、コードの見通しが悪くなるという 問題があるんですよね。

関連