Kredisを使ってRailsアプリに一時的なUI状態を簡単に保存する方法
Kredis(Keyed Redis)は、Rails開発者のツールキットに最近追加されたライブラリです。Redis上での構造化データの保存とアクセスを簡素化することを目指しています。
この2部構成シリーズの第1回では、まずKredisの仕組みについて詳しく解説します。その後、独自のRedisキーを使用して一時的なUI状態を保存する実用的なユースケースを紹介します。
それでは始めましょう!
RailsにおけるKredisの概要
KredisはRailtieとして提供されており、以下の3つの方法でRedisの利用を効率化する便利なラッパーを備えています。
- RubyらしいAPI:例えば
Kredis.listやKredis.setのようなコレクション型は、ネイティブなRubyの型(およびそれぞれのAPI)を可能な限り模倣しています。 - 型付け(Typings):特にコレクションで便利な機能で、Kredisは要素の型キャストを標準的なデータ型(
datetimeやjsonなど)との間で自動的に処理できます。 - ActiveRecord DSL:おそらくこのライブラリ最大の強みで、任意のRedisデータ構造を特定のモデルインスタンスと簡単に紐付けることができます。
READMEからのサンプルがこちらです:
Kredisの大きなメリットは、特定のレコードに関連付けられた一時的な情報を、セッションとは独立して保存できる手軽さにあります。通常、Railsでデータを永続化する必要がある場合、いくつかの選択肢がありますが、最も一般的なのは次の2つです。
- ActiveRecord:多くの場合、カラムの追加やデータモデルへの変更が必要になります。マイグレーションが必要で、既存レコードのバックフィル(過去データの埋め戻し)も必要になることがあります。
- セッション:すべてのRailsアプリに標準搭載されているキー/バリューストアで、セットアップはほぼ不要です。ただし、ここに保存したデータはログイン/ログアウトのサイクルをまたいでは保持されません。
Kredisはこれらに代わる第三の選択肢をもたらします。モデル内でDSLを呼び出す以外には、ほとんどセットアップが不要です。しかも、Redisインスタンスがダウンしない限り、データはセッションを超え、さらにはデバイスを超えても保持されます。したがって、Kredisの良いユースケースとしては、Webアプリとモバイルアプリなど、デバイスの垣根を越えて共有したい重要度の低い情報の保存が挙げられます。
ケーススタディ:Kredisを使って折りたたみ/展開のUI状態を永続化・復元する
Kredisが活きる典型的なユースケースの一つが、UI状態の永続化です。例えば以下のようなものがあります。
- サイドバーの開閉状態
- ツリービューの開閉状態
- アコーディオンの折りたたみ/展開状態
- カスタムダッシュボードのレイアウト
- データテーブルの表示行数
今回は例として、<details>要素の折りたたみ/展開状態を管理する方法を見ていきます。
まず、新しいRailsアプリを作成し、kredisをGemfileに追加してインストーラーを実行しましょう:
注:これにより、config/redis/shared.ymlにRedis設定ファイルが作成されます。
この記事の残りの部分では、ローカルでRedisインスタンスが稼働していることを前提とします。Homebrewが入ったmacOSであれば、以下のコマンドだけで起動できます:
お使いのOSへのRedisのインストール方法については、公式の「Getting Started」ガイドをご参照ください。
ユーザー認証
UI状態情報を保存するエンティティとして、Userモデルを使用します。細かい議論を避けるため、Deviseがデフォルトで提供するものをそのまま使いましょう:
次に、Railsコンソールでサンプルユーザーを作成します:
サンプルアプリ:オンラインストア
Kredisが複雑なツリー構造の状態を永続化するのにどう役立つかを示すため、オンライン百貨店を運営していると仮定しましょう。そのために、Department(部署)とProduct(商品)モデルをscaffoldで生成します。部署から部署への自己結合(self join)を含めることで、2階層のネスト構造を作ります:
ツリー構造のルートを許容するため、親をnullにできるようにしておく必要があります:
DepartmentモデルとProductモデルは次のように定義します:
最後に、fakerを使ってシードデータを生成します:
ストアフロントの構築
ショップの店頭となる、非常にシンプルなHomeControllerを作成します。
部署の子に対して自己結合を行い、実際にサブ部署を持つもの(つまりツリーのルート)だけを取得します:
indexビューでは、2階層の<details>要素を使って部署のネストされたツリービューを構築します:
現時点では、開閉して探索できる、わざとらしい商品名を持つ部署のツリービューができあがりました:

次に、個々のカテゴリの開閉状態を永続化できるようにしていきます。
カテゴリのUI状態をKredisに保存する
これから行うことを、ステップごとに整理します。
-
Userモデルにkredis_setという名前のopen_department_idsを追加します。セットを使用する理由は、重複が許されないため、部署を安全に追加・削除できるからです。 -
以下のパラメータを受け取る
UIStateControllerを作成します:department_id- その部署の
open状態
そして、現在ログイン中のユーザーの
kredis_setに対して、該当する部署を追加または削除します。 -
<details>要素のtoggleイベントを監視し、対応するペイロードを送信するStimulusコントローラを作成します。
それでは進めていきましょう!
UserモデルにKredisデータ構造を追加するのは、kredis_setを呼び出して識別子を渡すだけと非常に簡単です:
次に、UI状態の更新を受け取るUIStateControllerを生成します。生成されたルートはpatchエンドポイントとして設定する必要がある点に注意してください:
KredisのAPIに初めて触れるのはコントローラの中です。Ruby開発者の期待に沿うよう設計されていることが分かります。セットへの追加は<<を使い、削除はremoveを使います。
ここで行っているのは、クライアントから渡されるopenパラメータに基づいて、セット内の特定のdepartment_idの有無を切り替える処理です。全体像を完成させるには、クライアント側でこれらのUI状態変更を送信するコードを書く必要があります。
アクションの実行には@rails/request.jsを使用するので、まずpinで追加します:
新しいStimulusコントローラ(特定の<details>要素にアタッチします)では、部署IDとそのopen状態をFormDataオブジェクトに追加して送信します:
ビューコードを前述のように編集し、各<details>要素のtoggleイベントを監視してUI状態の更新をトリガーします:
DOMの手動復元(Rehydrate)
一連の流れを完結させるために残っているのは、ユーザーがページを更新した際にDOMを目的の状態へ復元(rehydrate)することだけです。これは、Kredisのセットに部署IDが存在する場合に、<details>ノードにopen属性を手動で追加することで実現します:
最後に結果をご覧ください。個々のツリーノードの開閉状態が2階層にわたって保持されていることに注目してください。
次回予告:UI状態のための汎用ユーザーローカルコンテナ
この2部構成シリーズの第1回では、Kredisを紹介し、折りたたみ/展開のUI状態をKredisで永続化・復元する方法を探りました。
オンライン百貨店の例を通じて、複雑なツリー構造の状態をKredisで永続化する方法を示し、最後にDOMを手動で復元しました。
しかし、このアプローチでは多くのKredisキーを自作する必要があります。次回は、UI状態のための汎用的なユーザーローカルコンテナによって、この課題をどう解決できるかを掘り下げます。
それまで、Happy Coding!
P.S. Ruby Magicの記事を公開と同時にお読みになりたい方は、Ruby Magicニュースレターを購読して、記事を見逃さないようにしましょう!
-
Rubyのメソッド委譲(デリゲーション)をマスターする実践ガイド
プログラミングにおける「委譲(デリゲーション)」とは、プログラムのある部分から別の部分へタスクを引き渡すことを指します。オブジェクト指向プログラミングにおいて不可欠なテクニックであり、各オブジェクトやメソッドが特定のタスクや振る舞いに責任を持つことで、クリーンで保守しやすいコードを実現します。 委譲を理解し活用することは、Rubyをはじめとするオブジェクト指向言語を習得するうえで重要です。委譲は関心の分離を促進し、コードのモジュール性を高めることで、理解・テスト・リファクタリングがしやすいコードへと導きます。 本記事では、Rubyで委譲を実現する3つの方法——明示的な委譲、Forwardabl
-
rack-mini-profilerとフレームグラフでRailsアプリのボトルネックを可視化する方法
あなたのRailsアプリは遅くなっていませんか? 本来シンプルに表示されるはずのビューの読み込みに数秒かかるなら、原因を掘り下げて調査すべきサインです。 原因としては、データベースへの呼び出しが多すぎたり、処理の遅いメソッドがあったり、あるいは誰かがコードに仕込んだまま忘れられてしまった無駄なループだったりします。 アプリの遅さの原因を突き止めるためのツールは数多く存在します。以前このブログでもrbtraceについて紹介しましたし、New Relicのrpm gemもアプリの高速化に役立ってくれました。 しかし、私がパフォーマンス問題の調査に最も愛用しているツールは、それ以上のことができるので