結論
Rails.cache.fetch("page_#{page}", expires_in: 24.hours) do
items = Item.all.page(page).per(50)
Kaminari::PaginatableArray.new(items.to_a, limit: items.limit_value, offset: items.offset_value, total_count: items.total_count)
end
概要
Kaminariを使ってページネーションをしていて、データが膨大なのでキャッシュをすることになりました。
しかし、 Item.all
をキャッシュするのもまたデータが大きすぎるので、ページごとに絞り込んだ結果でキャッシュしたいということに。
いつも通りの方法で以下の様にやってみたところエラーが…
Rails.cache.fetch("page_#{page}", expires_in: 24.hours) do
items = Item.all.page(page).per(50)
end
=> can't dump anonymous class #<Module:0x00000001197139b8>
ということで調べました。
なぜエラーになるのか?
ChatGPTによると、エラーの原因は以下になります。
”TypeError: can't dump anonymous class
というエラーは、Railsのキャッシュ機構で、キャッシュに保存できない(シリアライズできない)オブジェクトを扱っている際に発生します。この場合、キャッシュ対象のオブジェクト(Item.al
l.page(page).per(50)
)の中に、キャッシュできない匿名クラスやモジュールが含まれている可能性があります。”
Kaminariのメソッドを使ったことで余計なデータが入ってしまった様です。
解決策
Kaminari::PaginatableArray
を使ってKaminariに互換性のある配列に変換します。
単純な配列であればキャッシュができるので、色々調べると絞り込んだデータを .to_a
で配列にする様言われました。
ですが、配列にすると今度は paginate
で使われる total_pages
などが使えなくなってしまうのが問題でした。
それをKaminariに互換性のある配列にすることでどちらの問題も解決することができます。
items = Item.all.page(page).per(50)
Kaminari::PaginatableArray.new(items.to_a, limit: items.limit_value, offset: items.offset_value, total_count: items.total_count)
各パラメータの解説は以下です。
- 第1引数:こちらは配列型にしたオブジェクトを渡します。
- limit:Kaminariが設定している1ページあたりの件数(
per(50)
の50
)を取得します。これにより、Kaminari::PaginatableArray
に「1ページあたりの件数」が渡されます。 - offset:現在のページの先頭から何件目までスキップするかを示します。例えば、2ページ目の場合、
per(50)
だとoffset_value
は50になります。これにより、正確にページのオフセット位置が適用されます。 - total_count:全体のレコード数を示します。これにより、ページネーションが全体で何ページ必要なのかを計算することができます。
最終的なコード
# controller
def index
@items = Rails.cache.fetch("page_#{page}", expires_in: 24.hours) do
items = Item.all.page(page).per(50)
Kaminari::PaginatableArray.new(items.to_a, limit: items.limit_value, offset: items.offset_value, total_count: items.total_count)
end
end
# view
<%= paginate @items %>
コメント