先月転職し、久しぶりに 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 ペアプログラミングで学習しました。具体的な流れは以下のとおりです。

  1. GitHub Issue を作成する(例: #14 フォロー機能
  2. Claude Code に計画書を作成させる(例: docs/plan-for-issue-14.md
  3. 計画書を元に 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 を消化しながら、学習を継続していきます。

参考情報