💾

汎用ページキャッシュ機能を作ってみた(Ruby on Rails)

2024/10/24に公開

Railsのページキャッシュとして有名な gem actionpack-page_caching

https://github.com/rails/actionpack-page_caching

しかし、これはRails的には非推奨らしいので、似たような汎用ページキャッシュ機能を自作してみました。

コード

# frozen_string_literal: true

module PageCacheable
  extend ActiveSupport::Concern

  included do
    before_action :cache_enabled?, if: -> { action_exists? }
  end

  # ページキャッシュの保存先を指定
  PAGE_CACHE_DIR = Rails.root.join("public", "page_cache")

  # キャッシュ時間を設定
  CACHE_EXPIRATION_TIME = 1.minutes

  private

  def action_exists?
    respond_to?(action_name)
  end

  def cache_enabled?
    return false if Rails.env.development?
    return false unless cache_valid?

    render(layout: false, file: cache_path) and return false
  end

  def cache_valid?
    return false unless File.exist?(cache_path)

    cache_created_at = File.mtime(cache_path)
    Time.now < cache_created_at + CACHE_EXPIRATION_TIME
  end

  def cache_path
    param_key = params_to_cache_key(params)
    "#{Rails.root.join(PAGE_CACHE_DIR, controller_name, action_name, param_key)}.html"
  end

  def params_to_cache_key(params)
    # permitの値はキャッシュしたいパターンに応じて調整
    safe_params = params.permit(:id, :page)
    safe_params.to_h.sort.to_h.to_s.parameterize
  end

  def render_cache_page(view)
    response_body = render(view)
    FileUtils.mkdir_p(File.dirname(cache_path))
    File.write(cache_path, response_body)
  end

  def clear_cache(path = cache_path)
    cache_file = path
    FileUtils.rm_f(cache_file)
  end
end

使い方

class ArticlesController < ApplicationController
  # 使いたいcontrollerでinclude(ApplicationControllerでincludeしても良い)
  include PageCacheable

  def show
    # 既存のページキャッシュを削除したい場合
    clear_cache if hoge_fuga?

    # 返すviewを明示的に指定
    render_cache_page(:show)
  end
end

使いたい人はコピペしてどうぞ。

Discussion