先月転職し、久しぶりに Rails 開発に戻ってきました。
6 年のブランクがあり、学習に使える期間は 1 ヶ月しかなかったため、効率的に Rails 8 の新機能を把握したり、既存の機能を思い出す必要がありました。
そのため、Rails ガイドを参照しながら、実際に動くアプリを Claude Code と一緒に作って学習することにしました。一時的に Claude Code の Max プラン USD 100/mo を購読しました。
作ったリポジトリはこちらです。
学習の流れ
Rails ガイド を主な参考資料にしました。日本語で読めるのはありがたいですね。
1. Rails ガイドで基本を復習
Rails をはじめよう
をなぞって /products を作成しました。主に Active Storage(画像アップロード)と Action Text(リッチテキスト)を学習しました。
2. Hotwire と Solid Trifecta を学習
Hotwire と Solid Trifecta を学習すべく /chat と /posts を作成しました。
/chat: Action Cable、Solid Cable、Stimulus を学習/posts: Turbo Streams/Frames、Active Job、Solid Queue を学習。よくあるブログ機能に、コメント機能、フォロー機能、通知機能を追加
Claude Code を使った AI ペアプログラミングで学習しました。具体的な流れは以下のとおりです。
- GitHub Issue を作成する(例: #14 フォロー機能 )
- Claude Code に計画書を作成させる(例: docs/plan-for-issue-14.md )
- 計画書を元に PR を作成させながら理解する(例: #38 )
また、CodeRabbit による AI レビューも活用しました。N+1 クエリの指摘やテストの追加提案など、一人開発でも品質を保てました。OSS だと無料で使えるのがありがたいです。
学習した Rails の機能
Hotwire
Hotwire は Rails 7 から導入された、SPA 風の UX を実現するためのフレームワークです。Hotwire は Turbo と Stimulus で構成されています。
Turbo はさらに 3 つで構成されています。
- Turbo Drive: ページ遷移の高速化(リンククリック時に HTML を fetch して置き換え)
- Turbo Frames: ページの一部を更新(コメントのインライン編集など)
- Turbo Streams: 複数箇所を同時に更新(コメント追加時にリストとカウントを更新)
Stimulus は JavaScript のフレームワークですが、HTML を基点に最小限の JS を足すためのごく薄いレイヤです。チャット機能の UI 制御に使用しました。
コメント機能で Turbo Streams を活用しました。コメントを投稿すると、モデルのコールバックで他のユーザーのブラウザにもリアルタイムで反映されます。
# app/models/comment.rb
class Comment < ApplicationRecord
belongs_to :post, counter_cache: true
belongs_to :user
after_create_commit do
# コメント一覧に追加
broadcast_append_to post,
target: "comments",
partial: "comments/comment",
locals: { comment: self, allow_actions: false }
# コメント数を更新
broadcast_replace_to post,
target: "comment_count_#{post.id}",
partial: "posts/comment_count",
locals: { post: post }
end
end
Solid Trifecta
Rails 8 で導入された「Solid Trifecta」は、Redis や Memcached などの外部ミドルウェアが不要になる仕組みです。
- Solid Queue: データベースベースの Active Job バックエンド
- Solid Cable: データベースベースの Action Cable アダプター
- Solid Cache: データベースベースのキャッシュストア(今回は未使用)
予約投稿機能で Active Job + Solid Queue を使用しました。published_at に未来の日時を設定すると、その時刻にジョブが実行されて記事が公開されます。
# app/jobs/publish_post_job.rb
class PublishPostJob < ApplicationJob
queue_as :default
def perform(post_id, scheduled_at)
post = Post.find_by(id: post_id)
return unless post
# 冪等性: 既に公開済みならスキップ
return if post.published
# 公開日時が変更されていたらスキップ
return if post.published_at.blank? || post.published_at.to_i != scheduled_at
post.update!(published: true)
end
end
ジョブの実行状況は Mission Control Jobs で確認できます。開発環境で http://localhost:3000/jobs にアクセスすると、ジョブの履歴やキューの状態を Web UI で確認できます。
その他の機能
- Eager Loading: N+1 クエリを防ぐための
includes/preload/eager_load - has_secure_password + rate_limit: ユーザー認証とレート制限
- Counter Cache: コメント数の効率的な取得(
belongs_to :post, counter_cache: true) - 外部キー制約の
on_delete: :cascade: 親レコード削除時に子レコードも自動削除。Rails のdependent: :destroyと併用して DB レベルでも整合性を保つ - Shallow Routing: ネステッドリソースで
shallow: trueを指定すると、member アクション(show/edit/update/destroy)の URL が短くなる - Polymorphic Association: 通知機能で使用。フォロー通知やコメント通知を統一的に管理
- 自己参照関連(Self-Referential Association): フォロー機能で使用。1 つの
followsテーブルで多対多の関係を表現
まとめ
6 年のブランクはありましたが、Rails ガイドと Claude Code で効率的に学習できました。
なお、今回はテストをあまり書いていません。Rails の機能を学ぶことが目的であり、テストの学習は別の機会にしました。
Turbolinks を一旦無効にして…というあたりで私の頭の中が止まっていたので、JavaScript をほとんど書かずに SPA 風の UX を実現できる Hotwire には割と感動しました。記憶の中の Rails は API サーバーに徹したり、フロントエンドに React を採用することが多い印象でしたが、これからはまず Hotwire を検討したほうが良いと思いました。
Solid Queue と Solid Cable も、初手で RDS だけで構築できるので、登場人物が減って良い感じです。
引き続きオープン中の Issue を消化しながら、学習を継続していきます。