Skip to content

ChecksController内のnewspaperをActiveSupport::Notificationsに移行#9247

Merged
komagata merged 6 commits intomainfrom
chore/replace-check-events-with-notifications
Dec 3, 2025
Merged

ChecksController内のnewspaperをActiveSupport::Notificationsに移行#9247
komagata merged 6 commits intomainfrom
chore/replace-check-events-with-notifications

Conversation

@sjabcdefin
Copy link
Copy Markdown
Contributor

@sjabcdefin sjabcdefin commented Oct 9, 2025

Issue

概要

  • ChecksController内のnewspaperActiveSupport::Notificationsに置き換え
    ※ 経緯については、:check_createと:check_cancelの件について を参照
  • 例外ハンドリングを追加(CodeRabbitの指摘対応)
    WebUI上の表示とDB状態の不整合を防ぐため、ChecksController#createおよびChecksController#destroyにおいて、以下の処理を同一トランザクション内で実行し、いずれかが失敗した場合は両方をロールバックするように変更
    • Checkの作成・削除
    • ActiveSupport::Notifications.instrumentによるイベント発行(プラクティスのステータス更新)

issueに記載がある最終作業については、newspaper に関する Issue が全てマージされていないため、別issueにて登録予定です。

変更確認方法

  • chore/replace-check-events-with-notificationsをローカルに取り込む
  • foreman start -f Procfile.devでローカル環境を立ち上げる

提出物の確認(ChecksController)

  1. komagataさんでログインする

  2. PC性能の見方を知るの提出物にアクセスする

  3. 提出物欄の「提出物を合格にする」ボタンをクリックする

  4. 「提出物を合格にしました。」メッセージが表示されることを確認する

  5. kimuraさんでログインし、以下を確認する

    • 「komagataさんが「PC性能の見方を知る」の提出物を確認しました。」の通知がきていること
    • Letters にアクセスし、「「PC性能の見方を知る」の提出物が確認されました」のメールがきていること
    • プラクティス一覧画面の「PC性能の見方を知る」が「修了」となっていること
  6. komagataさんでログインする

  7. PC性能の見方を知るの提出物にアクセスする

  8. 提出物欄の「提出物の合格を取り消す」をクリックする

  9. kimuraさんでログインし、以下を確認する

提出物の確認(API::ChecksController)

  1. komagataさんでログインする
  2. telnetを使ってget, postを試し、HTTPの基礎を理解するの提出物にアクセスする
  3. コメント欄にコメントを入力し、コメント欄下部の「合格にする」ボタンをクリックする
  4. kimuraさんでログインし、以下を確認する
    • 「komagataさんが「telnetを使ってget, postを試し、HTTPの基礎を理解する」の提出物を確認しました。」の通知がきていること
    • Letters にアクセスし、「「telnetを使ってget, postを試し、HTTPの基礎を理解する」の提出物が確認されました」のメールがきていること
    • プラクティス一覧画面の「telnetを使ってget, postを試し、HTTPの基礎を理解する」が「修了」となっていること

日報の確認(ChecksController)

  1. komagataさんでログインする
  2. テスト日報5ですの日報にアクセスする
  3. 「日報を確認」ボタンをクリックする
  4. 「日報を確認済みにしました。」メッセージが表示されることを確認する
  5. 「日報の確認を取り消す」をクリックし、以下を確認する
    • エラーメッセージ等が表示されないこと

日報の確認(API::ChecksController)

  1. komagataさんでログインする
  2. テスト日報4ですの日報にアクセスする
  3. コメント欄にコメントを入力し、コメント欄下部の「確認OKにする」ボタンをクリックし、以下を確認する
    • 「日報を確認済みにしました。」メッセージが表示されることを確認する

Screenshot

  • 見た目の変更はないためスクリーンショットはありません。

Summary by CodeRabbit

Summary by CodeRabbit

リリースノート

  • バグ修正

    • チェック作成・削除処理をトランザクション内で安全に実行し、失敗時は一貫したエラーハンドリングとログ出力で処理されます。
    • 永続化失敗時に例外を発生させるよう変更し、不整合検出を強化しました。
  • 改善

    • 作成・削除の通知と後処理をトランザクションコミット後に実行するよう変更し、データ整合性と通知の信頼性を向上しました。
    • APIのエラー応答が統一されたメッセージ形式になりました。

@sjabcdefin sjabcdefin self-assigned this Oct 9, 2025
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Oct 9, 2025

Walkthrough

チェックの作成・削除処理をトランザクション内で save!/destroy! に置き換え、イベント発行を Newspaper.publish から ActiveSupport::Notifications.instrument('check.create'/'check.cancel') に差し替え、エラーハンドリングを統一し、チェックモデルのコールバックをコミット後実行に変更しています。

変更内容

コホート / ファイル 変更の要約
コントローラー: トランザクション化と計測移行
app/controllers/checks_controller.rb, app/controllers/api/checks_controller.rb
create/destroy をトランザクション内で save!/destroy! に置換。Newspaper.publishActiveSupport::Notifications.instrument('check.create'/'check.cancel') に置換。Begin/Rescue による StandardError ハンドリングとログ/ユーザー応答の統一を追加。チェック作成時の直接的な学習状態変更や即時リダイレクトを除去。
モデル: コールバックのコミット後実行
app/models/check.rb
after_create / after_destroyafter_create_commit / after_destroy_commit に変更し、CheckCallbacks.new をコミット後にラムダ経由で呼び出すよう修正。
モデル: 永続化例外の明示化
app/models/product.rb
change_learning_status 内の learning.updatelearning.update! に変更し、保存失敗時に例外を発生させるように変更。

シーケンス図

sequenceDiagram
    actor User
    participant Controller
    participant DB
    participant Notifications as "ActiveSupport::Notifications"
    participant Callbacks

    User->>Controller: POST /checks または DELETE /checks/:id
    Controller->>DB: begin transaction
    alt 成功
        DB->>DB: save! / destroy!
        Controller->>Notifications: instrument('check.create'/'check.cancel')
        DB-->>Controller: commit
        Notifications-->>Callbacks: after_*_commit を発火
        Callbacks->>DB: コールバック内副作用(例: 学習状態変更)
        Controller->>User: 201 / 204 (またはリダイレクト)
    else エラー
        DB-->>DB: rollback
        Controller->>Controller: rescue StandardError, log
        Controller->>User: エラー応答 (JSON またはリダイレクト with alert)
    end
Loading

推定コードレビュー工数

🎯 3 (中程度) | ⏱️ ~20 minutes

  • 注意点: product#change_learning_statusupdate! による例外伝播、after_*_commit に移動したコールバックの副作用(例: 期待するタイミングでの DB 状態・通知発火)、およびコントローラーのリダイレクト/レスポンス差分。

Possibly related issues

Possibly related PRs

Suggested reviewers

  • komagata
  • matuaya

Poem

🐰 トランザクションの畑をぴょんと越え、
計測の鈴は instrument で鳴るよ。
エラーはやさしくキャッチして、
コールバックはコミットのあとに踊る。
ぴょんと一跳び、デプロイへ ✨

Pre-merge checks and finishing touches

✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed タイトルはPRの主な変更内容(newspaper から ActiveSupport::Notifications への移行)を正確に要約しており、具体的で明確です。
Description check ✅ Passed PR説明はテンプレートの全必須セクション(Issue、概要、変更確認方法、Screenshot)を備えており、詳細で具体的な検証手順が記載されています。
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch chore/replace-check-events-with-notifications

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 459fd65 and 05be5d6.

📒 Files selected for processing (4)
  • app/controllers/api/checks_controller.rb (1 hunks)
  • app/controllers/checks_controller.rb (2 hunks)
  • app/models/check.rb (1 hunks)
  • app/models/product.rb (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • app/controllers/api/checks_controller.rb
  • app/models/check.rb
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rb

⚙️ CodeRabbit configuration file

**/*.rb: # refactoring

  • まずFat Controllerを避け、次にFat Modelを避ける。
  • Serviceクラスの乱用を避ける。
  • controller concernを作ろうとしたらPORO(Plain Old Ruby Object)やActiveRecordモデルでの実装で代替できないか検討する。

Rails Patterns

  • ViewHelperにメソッドを実装する時にはまずDecoratorパターンを使うことを検討する。(active_decorator gemを導入しているのでそれを使う)
  • 複雑なActiveRecordクエリがあり、再利用できそうな場合はQueryObjectパターンを検討する。(rails-patterns gemを導入しているのでそれのQuery機能を使う)
  • Viewにpartialを作る場合はViewComponentを使うことを検討する。
  • 複数のActiveRecordモデルを操作する1つの責務がある時や外部APIとやりとりする処理がある場合にはInteractorオブジェクトパターンを検討する。(interactor gemを導入しているのでそれを使う)
  • 複数のInteractorを実行するような処理がある場合Organizerオブジェクトパターンを検討する。(interactor gemを導入しており、その中にOrganizerの機能があるので使う)

Files:

  • app/models/product.rb
  • app/controllers/checks_controller.rb
🧠 Learnings (25)
📓 Common learnings
Learnt from: sjabcdefin
Repo: fjordllc/bootcamp PR: 9247
File: app/controllers/checks_controller.rb:0-0
Timestamp: 2025-10-22T06:04:36.036Z
Learning: ChecksController#createおよび#destroyでは、Checkの作成・削除とActiveSupport::Notifications.instrumentによるイベント発行(プラクティスのステータス更新)を同一トランザクション内で実行し、いずれかが失敗した場合は両方をロールバックする。これによりWebUI表示とDB状態の整合性を保証している。
Learnt from: komagata
Repo: fjordllc/bootcamp PR: 9101
File: app/models/graduation_notifier.rb:23-28
Timestamp: 2025-09-12T21:16:47.639Z
Learning: Rails upgrade PRにおいて、configuration systemの変更やwebhook設定の改善提案も、アップグレードに直接関連しない場合は別PRで対応すべきである。PRのスコープ維持が重要。
Learnt from: komagata
Repo: fjordllc/bootcamp PR: 9101
File: app/notifiers/discord_notifier.rb:131-135
Timestamp: 2025-09-12T21:18:00.834Z
Learning: Rails アップグレードPRにおいて、product_review_not_completed メソッドの webhook URL 設定の shared フォールバック追加も、設定システム全体の変更として別PRで対応すべきである。
Learnt from: sjabcdefin
Repo: fjordllc/bootcamp PR: 9247
File: app/controllers/checks_controller.rb:0-0
Timestamp: 2025-10-22T12:22:33.168Z
Learning: CheckCallbacks内のメール通知・サイト内通知で例外が発生した場合、例外を握りつぶさず上位に伝搬させる。これにより通知失敗を検知し、運用上の混乱を防ぐ。CheckCallbacksはafter_create_commitで実行されるため、例外発生時にはCheckとステータス更新はすでにコミット済みであり、ユーザーにはエラーメッセージが表示されるが、DBには変更が残る状態になる。
Learnt from: Miya096jp
Repo: fjordllc/bootcamp PR: 9102
File: app/controllers/mentor/buzzes_controller.rb:0-0
Timestamp: 2025-09-04T01:39:22.261Z
Learning: fjordllc/bootcamp プロジェクトでは、更新処理の成功時の通知メッセージについて、I18n を使用せずに日本語文字列を直接記述する方針で一貫性を保っている。
Learnt from: matuaya
Repo: fjordllc/bootcamp PR: 9108
File: app/models/after_user_retirement.rb:0-0
Timestamp: 2025-09-01T22:31:57.345Z
Learning: fjordllc/bootcampプロジェクトにおいて、`Newspaper.publish(:retirement_create, { user: user })`はUnfinishedDataDestroyer(未チェックProduct・WIP Report削除、career_pathリセット)とTimesChannelDestroyer(Discord分報チャンネル削除)による重要なデータクリーンアップ処理を実行するため、退会のトリガーが何であろうと(admin、hibernation、user)必ず実行する必要がある。
Learnt from: matuaya
Repo: fjordllc/bootcamp PR: 9108
File: app/models/after_user_retirement.rb:0-0
Timestamp: 2025-09-01T22:31:57.345Z
Learning: fjordllc/bootcampプロジェクトにおいて、`Newspaper.publish(:retirement_create, { user: user })`は通知処理ではなく、UnfinishedDataDestroyerやTimesChannelDestroyer等による重要なデータクリーンアップ処理を実行するため、管理者による退会(triggered_by: 'admin')の場合でも必ず実行する必要がある。
Learnt from: komagata
Repo: fjordllc/bootcamp PR: 9101
File: app/notifiers/discord_notifier.rb:52-56
Timestamp: 2025-09-12T21:17:45.295Z
Learning: Rails upgrade PRにおいて、configuration systemの改善提案(shared configuration fallback等)も、アップグレードに直接関連しない場合は別PRで対応すべきである。PRのスコープ維持が重要。
Learnt from: jun-kondo
Repo: fjordllc/bootcamp PR: 0
File: :0-0
Timestamp: 2025-07-23T21:11:21.826Z
Learning: fjordllc/bootcampプロジェクトの`app/models/inquiry_notifier.rb`において、`NOTIFICATION_SENDER_NAME`で指定される管理者ユーザーは必ず存在することがシステムの前提条件として保証されているため、senderがnilになることは想定されておらず、エラーハンドリングは不要である。
Learnt from: komagata
Repo: fjordllc/bootcamp PR: 9101
File: app/notifiers/discord_notifier.rb:19-23
Timestamp: 2025-09-12T21:17:26.029Z
Learning: Rails アップグレードPRにおいて、config_for(:secrets) を使った webhook URL 設定の shared セクション未参照問題は、設定システム全体の変更として別PRで対応すべきである。
Learnt from: jun-kondo
Repo: fjordllc/bootcamp PR: 8977
File: app/controllers/reports_controller.rb:63-63
Timestamp: 2025-07-23T20:31:13.856Z
Learning: fjordllc/bootcampプロジェクトの`app/controllers/reports_controller.rb`において、`create`と`update`アクションは両方とも`report.save_uniquely`を使用し、同じ`:report_save`イベントと`'report.save'`イベントを発行する。これは両方とも本質的に「レポートの保存」操作であり、作成と更新を区別する必要がないためである。
📚 Learning: 2025-10-22T06:04:36.036Z
Learnt from: sjabcdefin
Repo: fjordllc/bootcamp PR: 9247
File: app/controllers/checks_controller.rb:0-0
Timestamp: 2025-10-22T06:04:36.036Z
Learning: ChecksController#createおよび#destroyでは、Checkの作成・削除とActiveSupport::Notifications.instrumentによるイベント発行(プラクティスのステータス更新)を同一トランザクション内で実行し、いずれかが失敗した場合は両方をロールバックする。これによりWebUI表示とDB状態の整合性を保証している。

Applied to files:

  • app/controllers/checks_controller.rb
📚 Learning: 2025-09-04T01:27:23.422Z
Learnt from: Miya096jp
Repo: fjordllc/bootcamp PR: 9102
File: db/fixtures/buzzes.yml:0-0
Timestamp: 2025-09-04T01:27:23.422Z
Learning: The buzz_controller.rb and related data migration files were deleted as part of the Buzz page redesign, not just modified. The old single buzz page was completely replaced with a new buzzes (plural) listing page with year-based navigation and metadata structure.

Applied to files:

  • app/controllers/checks_controller.rb
📚 Learning: 2025-09-04T01:27:23.422Z
Learnt from: Miya096jp
Repo: fjordllc/bootcamp PR: 9102
File: db/fixtures/buzzes.yml:0-0
Timestamp: 2025-09-04T01:27:23.422Z
Learning: The buzz_controller.rb was indeed deleted, but there are still two remaining items from the old buzz single page implementation that need cleanup: the data migration file db/data/20230726024528_create_buzz.rb and the old buzz route in config/routes.rb line 136. These should be updated/removed to complete the buzz page redesign migration.

Applied to files:

  • app/controllers/checks_controller.rb
📚 Learning: 2025-10-22T12:22:33.168Z
Learnt from: sjabcdefin
Repo: fjordllc/bootcamp PR: 9247
File: app/controllers/checks_controller.rb:0-0
Timestamp: 2025-10-22T12:22:33.168Z
Learning: CheckCallbacks内のメール通知・サイト内通知で例外が発生した場合、例外を握りつぶさず上位に伝搬させる。これにより通知失敗を検知し、運用上の混乱を防ぐ。CheckCallbacksはafter_create_commitで実行されるため、例外発生時にはCheckとステータス更新はすでにコミット済みであり、ユーザーにはエラーメッセージが表示されるが、DBには変更が残る状態になる。

Applied to files:

  • app/controllers/checks_controller.rb
📚 Learning: 2025-09-01T22:31:57.345Z
Learnt from: matuaya
Repo: fjordllc/bootcamp PR: 9108
File: app/models/after_user_retirement.rb:0-0
Timestamp: 2025-09-01T22:31:57.345Z
Learning: fjordllc/bootcampプロジェクトにおいて、`Newspaper.publish(:retirement_create, { user: user })`はUnfinishedDataDestroyer(未チェックProduct・WIP Report削除、career_pathリセット)とTimesChannelDestroyer(Discord分報チャンネル削除)による重要なデータクリーンアップ処理を実行するため、退会のトリガーが何であろうと(admin、hibernation、user)必ず実行する必要がある。

Applied to files:

  • app/controllers/checks_controller.rb
📚 Learning: 2025-09-01T22:31:57.345Z
Learnt from: matuaya
Repo: fjordllc/bootcamp PR: 9108
File: app/models/after_user_retirement.rb:0-0
Timestamp: 2025-09-01T22:31:57.345Z
Learning: fjordllc/bootcampプロジェクトにおいて、`Newspaper.publish(:retirement_create, { user: user })`は通知処理ではなく、UnfinishedDataDestroyerやTimesChannelDestroyer等による重要なデータクリーンアップ処理を実行するため、管理者による退会(triggered_by: 'admin')の場合でも必ず実行する必要がある。

Applied to files:

  • app/controllers/checks_controller.rb
📚 Learning: 2025-09-12T21:18:00.834Z
Learnt from: komagata
Repo: fjordllc/bootcamp PR: 9101
File: app/notifiers/discord_notifier.rb:131-135
Timestamp: 2025-09-12T21:18:00.834Z
Learning: Rails アップグレードPRにおいて、product_review_not_completed メソッドの webhook URL 設定の shared フォールバック追加も、設定システム全体の変更として別PRで対応すべきである。

Applied to files:

  • app/controllers/checks_controller.rb
📚 Learning: 2025-07-23T21:11:21.826Z
Learnt from: jun-kondo
Repo: fjordllc/bootcamp PR: 0
File: :0-0
Timestamp: 2025-07-23T21:11:21.826Z
Learning: fjordllc/bootcampプロジェクトの`app/models/work_notification_destroyer.rb`において、このクラスは`app/controllers/works_controller.rb`の`destroy`アクションから呼び出され、`before_action :set_my_work`でIDの妥当性チェックが事前に行われるため、payloadのworkオブジェクトは常に有効であることが保証されている。そのためエラーハンドリングは不要。

Applied to files:

  • app/controllers/checks_controller.rb
📚 Learning: 2025-09-12T21:16:47.639Z
Learnt from: komagata
Repo: fjordllc/bootcamp PR: 9101
File: app/models/graduation_notifier.rb:23-28
Timestamp: 2025-09-12T21:16:47.639Z
Learning: Rails upgrade PRにおいて、configuration systemの変更やwebhook設定の改善提案も、アップグレードに直接関連しない場合は別PRで対応すべきである。PRのスコープ維持が重要。

Applied to files:

  • app/controllers/checks_controller.rb
📚 Learning: 2025-09-15T07:39:39.350Z
Learnt from: ryufuta
Repo: fjordllc/bootcamp PR: 9155
File: app/controllers/api/correct_answers_controller.rb:20-22
Timestamp: 2025-09-15T07:39:39.350Z
Learning: API::CorrectAnswersController#update では、ベストアンサー取り下げという単純な状態変更のため、update! を使用して DB エラー時に 500 を返し、成功時はデフォルトレスポンスを使用するのが適切。クライアント側のバリデーションエラーは発生しにくく、明示的なエラーハンドリングは不要。

Applied to files:

  • app/controllers/checks_controller.rb
📚 Learning: 2025-09-12T21:17:45.295Z
Learnt from: komagata
Repo: fjordllc/bootcamp PR: 9101
File: app/notifiers/discord_notifier.rb:52-56
Timestamp: 2025-09-12T21:17:45.295Z
Learning: Rails upgrade PRにおいて、configuration systemの改善提案(shared configuration fallback等)も、アップグレードに直接関連しない場合は別PRで対応すべきである。PRのスコープ維持が重要。

Applied to files:

  • app/controllers/checks_controller.rb
📚 Learning: 2025-09-12T21:17:26.029Z
Learnt from: komagata
Repo: fjordllc/bootcamp PR: 9101
File: app/notifiers/discord_notifier.rb:19-23
Timestamp: 2025-09-12T21:17:26.029Z
Learning: Rails アップグレードPRにおいて、config_for(:secrets) を使った webhook URL 設定の shared セクション未参照問題は、設定システム全体の変更として別PRで対応すべきである。

Applied to files:

  • app/controllers/checks_controller.rb
📚 Learning: 2025-09-04T01:39:22.261Z
Learnt from: Miya096jp
Repo: fjordllc/bootcamp PR: 9102
File: app/controllers/mentor/buzzes_controller.rb:0-0
Timestamp: 2025-09-04T01:39:22.261Z
Learning: fjordllc/bootcamp プロジェクトでは、更新処理の成功時の通知メッセージについて、I18n を使用せずに日本語文字列を直接記述する方針で一貫性を保っている。

Applied to files:

  • app/controllers/checks_controller.rb
📚 Learning: 2025-07-23T21:11:21.826Z
Learnt from: jun-kondo
Repo: fjordllc/bootcamp PR: 0
File: :0-0
Timestamp: 2025-07-23T21:11:21.826Z
Learning: fjordllc/bootcampプロジェクトの`app/models/inquiry_notifier.rb`において、`NOTIFICATION_SENDER_NAME`で指定される管理者ユーザーは必ず存在することがシステムの前提条件として保証されているため、senderがnilになることは想定されておらず、エラーハンドリングは不要である。

Applied to files:

  • app/controllers/checks_controller.rb
📚 Learning: 2025-07-23T20:31:13.856Z
Learnt from: jun-kondo
Repo: fjordllc/bootcamp PR: 8977
File: app/controllers/reports_controller.rb:63-63
Timestamp: 2025-07-23T20:31:13.856Z
Learning: fjordllc/bootcampプロジェクトの`app/controllers/reports_controller.rb`において、`create`と`update`アクションは両方とも`report.save_uniquely`を使用し、同じ`:report_save`イベントと`'report.save'`イベントを発行する。これは両方とも本質的に「レポートの保存」操作であり、作成と更新を区別する必要がないためである。

Applied to files:

  • app/controllers/checks_controller.rb
📚 Learning: 2025-07-15T12:58:02.735Z
Learnt from: reckyy
Repo: fjordllc/bootcamp PR: 8356
File: app/models/notification.rb:45-47
Timestamp: 2025-07-15T12:58:02.735Z
Learning: fjordllc/bootcampプロジェクトの`app/models/notification.rb`において、`training_completed`通知種別は`TARGETS_TO_KINDS`ハッシュに含めない設計になっている。これは`training_completed`が通知一覧のフィルタ対象ではないためで、意図的な設計判断である。

Applied to files:

  • app/controllers/checks_controller.rb
📚 Learning: 2025-08-24T07:39:34.701Z
Learnt from: sekito1107
Repo: fjordllc/bootcamp PR: 9081
File: app/jobs/transcode_job.rb:11-24
Timestamp: 2025-08-24T07:39:34.701Z
Learning: In the bootcamp application's TranscodeJob, exceptions are not re-raised after logging/notification because the job runs via after_create_commit. The Movie creation has already succeeded, so re-raising would cause users to see a creation failure screen, degrading user experience. Instead, failures are logged and sent to Rollbar for monitoring only.

Applied to files:

  • app/controllers/checks_controller.rb
📚 Learning: 2025-09-04T01:50:47.773Z
Learnt from: Miya096jp
Repo: fjordllc/bootcamp PR: 9102
File: db/migrate/20250831234516_remove_unique_index_from_buzzes_title.rb:1-5
Timestamp: 2025-09-04T01:50:47.773Z
Learning: fjordllc/bootcampプロジェクトでは、マイグレーションにおいてdisable_ddl_transaction!やalgorithm: :concurrentlyは使用せず、標準的なRailsのchangeメソッド内でremove_indexを実行する慣例がある。

Applied to files:

  • app/controllers/checks_controller.rb
📚 Learning: 2025-06-29T03:44:15.179Z
Learnt from: hirokiej
Repo: fjordllc/bootcamp PR: 8740
File: app/helpers/reports_helper.rb:56-64
Timestamp: 2025-06-29T03:44:15.179Z
Learning: このプロジェクト(fjordllc/bootcamp)では、ja.ymlファイルで一部の単語や文章のみI18n対応されているが、多くのテキストは日本語でハードコーディングされており、完全な国際化対応は行われていない。新しい機能でもI18n対応は不要と判断される。

Applied to files:

  • app/controllers/checks_controller.rb
📚 Learning: 2025-07-26T15:24:14.799Z
Learnt from: su-su-su-su
Repo: fjordllc/bootcamp PR: 0
File: :0-0
Timestamp: 2025-07-26T15:24:14.799Z
Learning: fjordllc/bootcampプロジェクトでは、Rubyの述語メソッドには?を付ける命名規則(例: can_be_checker?)を徹底している。

Applied to files:

  • app/controllers/checks_controller.rb
📚 Learning: 2025-09-02T19:01:08.804Z
Learnt from: matuaya
Repo: fjordllc/bootcamp PR: 9108
File: app/models/after_user_retirement.rb:43-45
Timestamp: 2025-09-02T19:01:08.804Z
Learning: fjordllc/bootcampプロジェクトの`app/models/card.rb`は、ActiveRecordモデルではなくStripe APIをラップしたカスタムクラスである。`Card.destroy_all(customer_id)`メソッドは、指定されたcustomer_idに関連付けられたカードのみを削除するカスタム実装で、ActiveRecordの`destroy_all`とは動作が異なり安全である。

Applied to files:

  • app/controllers/checks_controller.rb
📚 Learning: 2025-07-23T20:42:19.974Z
Learnt from: jun-kondo
Repo: fjordllc/bootcamp PR: 0
File: :0-0
Timestamp: 2025-07-23T20:42:19.974Z
Learning: fjordllc/bootcampプロジェクトでは、hタグ(見出し)の文言は日本語でハードコーディングする方針が確立されており、I18n対応は行わない。例:app/views/welcome/logo.html.slimでh2、h3タグが日本語でハードコーディングされている。

Applied to files:

  • app/controllers/checks_controller.rb
📚 Learning: 2025-08-01T12:57:10.349Z
Learnt from: mousu-a
Repo: fjordllc/bootcamp PR: 8566
File: test/mailers/activity_mailer_test.rb:0-0
Timestamp: 2025-08-01T12:57:10.349Z
Learning: came_pair_work機能は現在テスト運用段階で、メンターと関係者のみに通知される仕様のため、メール通知OFF時の制御テストは現時点では実装不要と判断されている。

Applied to files:

  • app/controllers/checks_controller.rb
📚 Learning: 2025-11-03T04:53:33.395Z
Learnt from: jun-kondo
Repo: fjordllc/bootcamp PR: 9202
File: config/initializers/active_support_notifications.rb:6-7
Timestamp: 2025-11-03T04:53:33.395Z
Learning: app/controllers/api/answers_controller.rb の destroy アクションにおいて、answer.destroy イベントは `if answer.type == 'CorrectAnswer'` 条件付きで発行される。通常の Answer 削除では未解決問題数に影響しないため、ベストアンサー (CorrectAnswer) 削除時のみ AnswerCacheDestroyer によるキャッシュクリアが必要。

Applied to files:

  • app/controllers/checks_controller.rb
🧬 Code graph analysis (1)
app/models/product.rb (1)
app/models/practice.rb (1)
  • learning (126-128)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build_and_test
🔇 Additional comments (3)
app/models/product.rb (1)

94-100: LGTM! トランザクション整合性を保証する重要な変更です。

update から update! への変更により、学習ステータスの更新失敗時に例外が発生し、ChecksController内のトランザクション全体がロールバックされます。これにより、Checkの作成・削除とプラクティスのステータス更新が確実に同期され、WebUIの表示とDB状態の整合性が保証されます。

Based on learnings

app/controllers/checks_controller.rb (2)

17-27: LGTM! トランザクション境界とエラーハンドリングが適切に実装されています。

Checkの作成とイベント発行を同一トランザクション内で実行し、失敗時は両方をロールバックする設計により、WebUI表示とDB状態の整合性が保証されています。エラーログも適切に出力されており、障害発生時の原因調査が可能です。

Based on learnings


34-43: LGTM! createメソッドと一貫性のある実装です。

Checkの削除とイベント発行をトランザクション内で実行し、エラーログも適切に出力されています。createメソッドと同じパターンで実装されており、コードベース全体の一貫性が保たれています。

Based on learnings

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 RuboCop (1.81.7)
app/controllers/api/checks_controller.rb

rubocop-minitest extension supports plugin, specify plugins: rubocop-minitest instead of require: rubocop-minitest in /.rubocop.yml.
For more information, see https://docs.rubocop.org/rubocop/plugin_migration_guide.html.
rubocop-capybara extension supports plugin, specify plugins: rubocop-capybara instead of require: rubocop-capybara in /.rubocop.yml.
For more information, see https://docs.rubocop.org/rubocop/plugin_migration_guide.html.
Unable to find gem rubocop-fjord; is the gem installed? Gem::MissingSpecError
/var/lib/gems/3.1.0/gems/rubocop-1.81.7/lib/rubocop/config_loader_resolver.rb:310:in rescue in gem_config_path' /var/lib/gems/3.1.0/gems/rubocop-1.81.7/lib/rubocop/config_loader_resolver.rb:293:in gem_config_path'
/var/lib/gems/3.1.0/gems/rubocop-1.81.7/lib/rubocop/config_loader_resolver.rb:84:in block (2 levels) in resolve_inheritance_from_gems' /var/lib/gems/3.1.0/gems/rubocop-1.81.7/lib/rubocop/config_loader_resolver.rb:82:in reverse_each'
/var/lib/gems/3.1.0/g

... [truncated 255 characters] ...

fig_loader_resolver.rb:76:in resolve_inheritance_from_gems' /var/lib/gems/3.1.0/gems/rubocop-1.81.7/lib/rubocop/config_loader.rb:58:in load_file'
/var/lib/gems/3.1.0/gems/rubocop-1.81.7/lib/rubocop/config_store.rb:34:in options_config=' /var/lib/gems/3.1.0/gems/rubocop-1.81.7/lib/rubocop/config_store.rb:29:in apply_options!'
/var/lib/gems/3.1.0/gems/rubocop-1.81.7/lib/rubocop/cli.rb:167:in act_on_options' /var/lib/gems/3.1.0/gems/rubocop-1.81.7/lib/rubocop/cli.rb:49:in block in run'
/var/lib/gems/3.1.0/gems/rubocop-1.81.7/lib/rubocop/cli.rb:87:in profile_if_needed' /var/lib/gems/3.1.0/gems/rubocop-1.81.7/lib/rubocop/cli.rb:45:in run'
/var/lib/gems/3.1.0/gems/rubocop-1.81.7/exe/rubocop:15:in <top (required)>' /usr/local/bin/rubocop:25:in load'
/usr/local/bin/rubocop:25:in `

'

app/models/product.rb

rubocop-minitest extension supports plugin, specify plugins: rubocop-minitest instead of require: rubocop-minitest in /.rubocop.yml.
For more information, see https://docs.rubocop.org/rubocop/plugin_migration_guide.html.
rubocop-capybara extension supports plugin, specify plugins: rubocop-capybara instead of require: rubocop-capybara in /.rubocop.yml.
For more information, see https://docs.rubocop.org/rubocop/plugin_migration_guide.html.
Unable to find gem rubocop-fjord; is the gem installed? Gem::MissingSpecError
/var/lib/gems/3.1.0/gems/rubocop-1.81.7/lib/rubocop/config_loader_resolver.rb:310:in rescue in gem_config_path' /var/lib/gems/3.1.0/gems/rubocop-1.81.7/lib/rubocop/config_loader_resolver.rb:293:in gem_config_path'
/var/lib/gems/3.1.0/gems/rubocop-1.81.7/lib/rubocop/config_loader_resolver.rb:84:in block (2 levels) in resolve_inheritance_from_gems' /var/lib/gems/3.1.0/gems/rubocop-1.81.7/lib/rubocop/config_loader_resolver.rb:82:in reverse_each'
/var/lib/gems/3.1.0/g

... [truncated 255 characters] ...

fig_loader_resolver.rb:76:in resolve_inheritance_from_gems' /var/lib/gems/3.1.0/gems/rubocop-1.81.7/lib/rubocop/config_loader.rb:58:in load_file'
/var/lib/gems/3.1.0/gems/rubocop-1.81.7/lib/rubocop/config_store.rb:34:in options_config=' /var/lib/gems/3.1.0/gems/rubocop-1.81.7/lib/rubocop/config_store.rb:29:in apply_options!'
/var/lib/gems/3.1.0/gems/rubocop-1.81.7/lib/rubocop/cli.rb:167:in act_on_options' /var/lib/gems/3.1.0/gems/rubocop-1.81.7/lib/rubocop/cli.rb:49:in block in run'
/var/lib/gems/3.1.0/gems/rubocop-1.81.7/lib/rubocop/cli.rb:87:in profile_if_needed' /var/lib/gems/3.1.0/gems/rubocop-1.81.7/lib/rubocop/cli.rb:45:in run'
/var/lib/gems/3.1.0/gems/rubocop-1.81.7/exe/rubocop:15:in <top (required)>' /usr/local/bin/rubocop:25:in load'
/usr/local/bin/rubocop:25:in `

'

app/controllers/checks_controller.rb

rubocop-minitest extension supports plugin, specify plugins: rubocop-minitest instead of require: rubocop-minitest in /.rubocop.yml.
For more information, see https://docs.rubocop.org/rubocop/plugin_migration_guide.html.
rubocop-capybara extension supports plugin, specify plugins: rubocop-capybara instead of require: rubocop-capybara in /.rubocop.yml.
For more information, see https://docs.rubocop.org/rubocop/plugin_migration_guide.html.
Unable to find gem rubocop-fjord; is the gem installed? Gem::MissingSpecError
/var/lib/gems/3.1.0/gems/rubocop-1.81.7/lib/rubocop/config_loader_resolver.rb:310:in rescue in gem_config_path' /var/lib/gems/3.1.0/gems/rubocop-1.81.7/lib/rubocop/config_loader_resolver.rb:293:in gem_config_path'
/var/lib/gems/3.1.0/gems/rubocop-1.81.7/lib/rubocop/config_loader_resolver.rb:84:in block (2 levels) in resolve_inheritance_from_gems' /var/lib/gems/3.1.0/gems/rubocop-1.81.7/lib/rubocop/config_loader_resolver.rb:82:in reverse_each'
/var/lib/gems/3.1.0/g

... [truncated 255 characters] ...

fig_loader_resolver.rb:76:in resolve_inheritance_from_gems' /var/lib/gems/3.1.0/gems/rubocop-1.81.7/lib/rubocop/config_loader.rb:58:in load_file'
/var/lib/gems/3.1.0/gems/rubocop-1.81.7/lib/rubocop/config_store.rb:34:in options_config=' /var/lib/gems/3.1.0/gems/rubocop-1.81.7/lib/rubocop/config_store.rb:29:in apply_options!'
/var/lib/gems/3.1.0/gems/rubocop-1.81.7/lib/rubocop/cli.rb:167:in act_on_options' /var/lib/gems/3.1.0/gems/rubocop-1.81.7/lib/rubocop/cli.rb:49:in block in run'
/var/lib/gems/3.1.0/gems/rubocop-1.81.7/lib/rubocop/cli.rb:87:in profile_if_needed' /var/lib/gems/3.1.0/gems/rubocop-1.81.7/lib/rubocop/cli.rb:45:in run'
/var/lib/gems/3.1.0/gems/rubocop-1.81.7/exe/rubocop:15:in <top (required)>' /usr/local/bin/rubocop:25:in load'
/usr/local/bin/rubocop:25:in `

'

  • 1 others

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@sjabcdefin
Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Oct 9, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 26ad439 and 9767ae1.

📒 Files selected for processing (1)
  • app/controllers/checks_controller.rb (2 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rb

⚙️ CodeRabbit configuration file

**/*.rb: # refactoring

  • まずFat Controllerを避け、次にFat Modelを避ける。
  • Serviceクラスの乱用を避ける。
  • controller concernを作ろうとしたらPORO(Plain Old Ruby Object)やActiveRecordモデルでの実装で代替できないか検討する。

Rails Patterns

  • ViewHelperにメソッドを実装する時にはまずDecoratorパターンを使うことを検討する。(active_decorator gemを導入しているのでそれを使う)
  • 複雑なActiveRecordクエリがあり、再利用できそうな場合はQueryObjectパターンを検討する。(rails-patterns gemを導入しているのでそれのQuery機能を使う)
  • Viewにpartialを作る場合はViewComponentを使うことを検討する。
  • 複数のActiveRecordモデルを操作する1つの責務がある時や外部APIとやりとりする処理がある場合にはInteractorオブジェクトパターンを検討する。(interactor gemを導入しているのでそれを使う)
  • 複数のInteractorを実行するような処理がある場合Organizerオブジェクトパターンを検討する。(interactor gemを導入しており、その中にOrganizerの機能があるので使う)

Files:

  • app/controllers/checks_controller.rb
🧠 Learnings (1)
📓 Common learnings
Learnt from: matuaya
PR: fjordllc/bootcamp#9108
File: app/models/after_user_retirement.rb:0-0
Timestamp: 2025-09-01T22:31:57.345Z
Learning: fjordllc/bootcampプロジェクトにおいて、`Newspaper.publish(:retirement_create, { user: user })`は通知処理ではなく、UnfinishedDataDestroyerやTimesChannelDestroyer等による重要なデータクリーンアップ処理を実行するため、管理者による退会(triggered_by: 'admin')の場合でも必ず実行する必要がある。
Learnt from: matuaya
PR: fjordllc/bootcamp#9108
File: app/models/after_user_retirement.rb:0-0
Timestamp: 2025-09-01T22:31:57.345Z
Learning: fjordllc/bootcampプロジェクトにおいて、`Newspaper.publish(:retirement_create, { user: user })`はUnfinishedDataDestroyer(未チェックProduct・WIP Report削除、career_pathリセット)とTimesChannelDestroyer(Discord分報チャンネル削除)による重要なデータクリーンアップ処理を実行するため、退会のトリガーが何であろうと(admin、hibernation、user)必ず実行する必要がある。
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build_and_test
🔇 Additional comments (1)
app/controllers/checks_controller.rb (1)

18-18: 通知タイミングが適切です。

@check.saveの成功後にのみイベント通知を送信しており、適切な実装です。イベント名も Rails の命名規則に従っています。

@sjabcdefin
Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Oct 12, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
app/controllers/checks_controller.rb (1)

50-54: ログメッセージのイベント名を動的にしてください。

Line 53のログメッセージが'check.create'とハードコードされていますが、eventパラメータを使用すべきです。現在の実装では、check.cancelイベントでエラーが発生した場合も'check.create'と表示されてしまいます。

以下のdiffを適用してください:

   def safe_instrument(event, check)
     ActiveSupport::Notifications.instrument(event, check)
   rescue StandardError => e
-    Rails.logger.warn "[Notifications] 'check.create'サブスクライブ処理でエラーが発生しました。:#{e.message}"
+    Rails.logger.warn "[Notifications] '#{event}'サブスクライブ処理でエラーが発生しました。:#{e.message}"
   end
🧹 Nitpick comments (1)
app/models/check.rb (1)

16-33: キャッシュクリアロジックの実装が適切です。

以下の点で良い実装になっています:

  • clear_cacheメソッドで両方のキャッシュクリア処理を呼び出し、統一的なインターフェースを提供
  • delete_report_cachedelete_product_cachecheckable_typeに応じた適切な処理を実行
  • ガード句により不要な処理を早期にスキップ

より厳密なカプセル化を行う場合、clear_cachedelete_report_cachedelete_product_cacheprivateにすることを検討してください(現在はコールバックから呼ばれるためpublicで問題ありませんが、外部から直接呼ばれることを想定していない場合)。

   alias sender user
 
   validates :user_id, uniqueness: { scope: %i[checkable_id checkable_type] }
 
   def receiver
     checkable.respond_to?(:user) ? checkable.user : nil
   end
+
+  private
 
   def clear_cache
     delete_report_cache
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9767ae1 and f75af79.

📒 Files selected for processing (6)
  • app/controllers/checks_controller.rb (3 hunks)
  • app/models/check.rb (1 hunks)
  • app/models/check_callbacks.rb (0 hunks)
  • app/models/check_notifier_for_submitter.rb (1 hunks)
  • app/models/product.rb (1 hunks)
  • config/initializers/active_support_notifications.rb (1 hunks)
💤 Files with no reviewable changes (1)
  • app/models/check_callbacks.rb
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rb

⚙️ CodeRabbit configuration file

**/*.rb: # refactoring

  • まずFat Controllerを避け、次にFat Modelを避ける。
  • Serviceクラスの乱用を避ける。
  • controller concernを作ろうとしたらPORO(Plain Old Ruby Object)やActiveRecordモデルでの実装で代替できないか検討する。

Rails Patterns

  • ViewHelperにメソッドを実装する時にはまずDecoratorパターンを使うことを検討する。(active_decorator gemを導入しているのでそれを使う)
  • 複雑なActiveRecordクエリがあり、再利用できそうな場合はQueryObjectパターンを検討する。(rails-patterns gemを導入しているのでそれのQuery機能を使う)
  • Viewにpartialを作る場合はViewComponentを使うことを検討する。
  • 複数のActiveRecordモデルを操作する1つの責務がある時や外部APIとやりとりする処理がある場合にはInteractorオブジェクトパターンを検討する。(interactor gemを導入しているのでそれを使う)
  • 複数のInteractorを実行するような処理がある場合Organizerオブジェクトパターンを検討する。(interactor gemを導入しており、その中にOrganizerの機能があるので使う)

Files:

  • app/models/check_notifier_for_submitter.rb
  • app/controllers/checks_controller.rb
  • config/initializers/active_support_notifications.rb
  • app/models/check.rb
  • app/models/product.rb
🧠 Learnings (2)
📓 Common learnings
Learnt from: sjabcdefin
PR: fjordllc/bootcamp#9247
File: app/controllers/checks_controller.rb:18-18
Timestamp: 2025-10-12T03:08:58.326Z
Learning: ChecksController#createでは、check.saveが成功した後の副作用(ステータス更新、アプリ内通知)で例外が発生しても、checkオブジェクトはロールバックせず、ログ出力のみで対応する方針。
Learnt from: matuaya
PR: fjordllc/bootcamp#9108
File: app/models/after_user_retirement.rb:0-0
Timestamp: 2025-09-01T22:31:57.345Z
Learning: fjordllc/bootcampプロジェクトにおいて、`Newspaper.publish(:retirement_create, { user: user })`は通知処理ではなく、UnfinishedDataDestroyerやTimesChannelDestroyer等による重要なデータクリーンアップ処理を実行するため、管理者による退会(triggered_by: 'admin')の場合でも必ず実行する必要がある。
Learnt from: matuaya
PR: fjordllc/bootcamp#9108
File: app/models/after_user_retirement.rb:0-0
Timestamp: 2025-09-01T22:31:57.345Z
Learning: fjordllc/bootcampプロジェクトにおいて、`Newspaper.publish(:retirement_create, { user: user })`はUnfinishedDataDestroyer(未チェックProduct・WIP Report削除、career_pathリセット)とTimesChannelDestroyer(Discord分報チャンネル削除)による重要なデータクリーンアップ処理を実行するため、退会のトリガーが何であろうと(admin、hibernation、user)必ず実行する必要がある。
📚 Learning: 2025-10-12T03:08:58.326Z
Learnt from: sjabcdefin
PR: fjordllc/bootcamp#9247
File: app/controllers/checks_controller.rb:18-18
Timestamp: 2025-10-12T03:08:58.326Z
Learning: ChecksController#createでは、check.saveが成功した後の副作用(ステータス更新、アプリ内通知)で例外が発生しても、checkオブジェクトはロールバックせず、ログ出力のみで対応する方針。

Applied to files:

  • app/controllers/checks_controller.rb
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build_and_test
🔇 Additional comments (6)
app/models/product.rb (1)

94-100: change_learning_status は現状呼び出し箇所が存在しないため例外懸念は発生しません 呼び出し元実装時に必要に応じて例外ハンドリングを検討してください。

Likely an incorrect or invalid review comment.

config/initializers/active_support_notifications.rb (1)

36-36: LGTM!

check.createイベントに対する新しいサブスクライバーの登録が適切に実装されています。既存のパターンに沿っており、LearningStatusUpdaterとの併用も問題ありません(同一イベントに複数のサブスクライバーを登録することはActiveSupport::Notificationsの標準的な使い方です)。

app/models/check_notifier_for_submitter.rb (1)

1-14: LGTM!

CheckNotifierForSubmitterの実装が適切です:

  • callメソッドのシグネチャがActiveSupport::Notificationsの標準に準拠しています
  • ガード句により、通知が必要な条件を明確に定義しています(receiverの存在、sender≠receiver、checkable_type≠'Report')
  • ActivityDeliveryを使用した通知処理が正しく実装されています

過去のレビューコメントより、例外処理はコントローラー側のsafe_instrumentで行う方針のため、この実装で問題ありません。

Based on learnings

app/controllers/checks_controller.rb (2)

18-20: LGTM!

check.createイベントの通知処理が適切に実装されています:

  • safe_instrumentにより例外が捕捉され、ログ出力のみで処理が継続される設計になっています
  • @checkableの型に応じた適切なメッセージが表示されます
  • 過去の議論で合意された方針(check保存後の副作用で例外が発生してもロールバックしない)に沿った実装です

Based on learnings


30-35: LGTM!

@check.destroyの結果を確認してから通知を送信する実装が正しく追加されています。過去のレビューコメントで指摘された問題が適切に修正されています。

app/models/check.rb (1)

6-7: LGTM!

コールバックの定義が:clear_cacheメソッドを使用するように変更され、CheckCallbacksへの依存が削除されています。この変更により、キャッシュクリア処理がCheckモデル内に集約され、コードの見通しが良くなっています。

@sjabcdefin sjabcdefin force-pushed the chore/replace-check-events-with-notifications branch from f75af79 to 3fb7549 Compare October 12, 2025 13:58
@sjabcdefin
Copy link
Copy Markdown
Contributor Author

sjabcdefin commented Oct 12, 2025

@coderabbitai
指摘いただいた内容(Duplicate comments と Nitpick Comments)について修正いたしました。
#9247 (review)

ご確認お願いいたします。

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Oct 12, 2025

@sjabcdefin さん、ご対応ありがとうございます!

前回の指摘事項(Duplicate comments と Nitpick Comments)を修正していただいたとのこと、承知いたしました。変更内容を確認させていただきます。

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
app/controllers/checks_controller.rb (1)

50-54: パラメータ名をより明確にすることを検討してください。

メソッドのパラメータ名checkは実際にはハッシュ{ check: @check }を受け取るため、payloadという名前の方がより明確です。

以下のdiffを適用してパラメータ名を変更できます:

-  def safe_instrument(event, check)
-    ActiveSupport::Notifications.instrument(event, check)
+  def safe_instrument(event, payload)
+    ActiveSupport::Notifications.instrument(event, payload)
   rescue StandardError => e
     Rails.logger.warn "[Notifications] '#{event}'サブスクライブ処理でエラーが発生しました。:#{e.message}"
   end
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f75af79 and 3fb7549.

📒 Files selected for processing (6)
  • app/controllers/checks_controller.rb (3 hunks)
  • app/models/check.rb (1 hunks)
  • app/models/check_callbacks.rb (0 hunks)
  • app/models/check_notifier_for_submitter.rb (1 hunks)
  • app/models/product.rb (1 hunks)
  • config/initializers/active_support_notifications.rb (1 hunks)
💤 Files with no reviewable changes (1)
  • app/models/check_callbacks.rb
🚧 Files skipped from review as they are similar to previous changes (2)
  • app/models/product.rb
  • app/models/check_notifier_for_submitter.rb
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rb

⚙️ CodeRabbit configuration file

**/*.rb: # refactoring

  • まずFat Controllerを避け、次にFat Modelを避ける。
  • Serviceクラスの乱用を避ける。
  • controller concernを作ろうとしたらPORO(Plain Old Ruby Object)やActiveRecordモデルでの実装で代替できないか検討する。

Rails Patterns

  • ViewHelperにメソッドを実装する時にはまずDecoratorパターンを使うことを検討する。(active_decorator gemを導入しているのでそれを使う)
  • 複雑なActiveRecordクエリがあり、再利用できそうな場合はQueryObjectパターンを検討する。(rails-patterns gemを導入しているのでそれのQuery機能を使う)
  • Viewにpartialを作る場合はViewComponentを使うことを検討する。
  • 複数のActiveRecordモデルを操作する1つの責務がある時や外部APIとやりとりする処理がある場合にはInteractorオブジェクトパターンを検討する。(interactor gemを導入しているのでそれを使う)
  • 複数のInteractorを実行するような処理がある場合Organizerオブジェクトパターンを検討する。(interactor gemを導入しており、その中にOrganizerの機能があるので使う)

Files:

  • app/controllers/checks_controller.rb
  • app/models/check.rb
  • config/initializers/active_support_notifications.rb
🧠 Learnings (2)
📓 Common learnings
Learnt from: sjabcdefin
PR: fjordllc/bootcamp#9247
File: app/controllers/checks_controller.rb:18-18
Timestamp: 2025-10-12T03:08:58.326Z
Learning: ChecksController#createでは、check.saveが成功した後の副作用(ステータス更新、アプリ内通知)で例外が発生しても、checkオブジェクトはロールバックせず、ログ出力のみで対応する方針。
📚 Learning: 2025-10-12T03:08:58.326Z
Learnt from: sjabcdefin
PR: fjordllc/bootcamp#9247
File: app/controllers/checks_controller.rb:18-18
Timestamp: 2025-10-12T03:08:58.326Z
Learning: ChecksController#createでは、check.saveが成功した後の副作用(ステータス更新、アプリ内通知)で例外が発生しても、checkオブジェクトはロールバックせず、ログ出力のみで対応する方針。

Applied to files:

  • app/controllers/checks_controller.rb
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build_and_test
🔇 Additional comments (5)
config/initializers/active_support_notifications.rb (1)

36-36: LGTM!

CheckNotifierForSubmitterの購読が適切に追加されています。LearningStatusUpdaterと並行して動作する構成になっており、Newspaperからの移行方針に沿っています。

app/controllers/checks_controller.rb (2)

18-20: LGTM!

safe_instrumentラッパーを使用してイベント通知を行い、例外が発生してもcheckオブジェクトのロールバックは行わない実装になっています。過去の議論で合意した方針に沿っています。

Based on learnings


30-35: LGTM!

destroyの成功を確認してから通知を送信する実装になっており、過去のレビューコメントで指摘された問題が適切に修正されています。

app/models/check.rb (2)

6-7: LGTM!

クラスベースのコールバックからシンボルベースのコールバックへの移行により、コードがシンプルになっています。Railsの慣例に従った実装です。


18-35: LGTM!

キャッシュクリアロジックがモデル内に適切に実装されています。checkable_typeによる分岐も正しく、ReportとProductそれぞれに対応するキャッシュ削除処理が呼ばれています。

@sjabcdefin sjabcdefin force-pushed the chore/replace-check-events-with-notifications branch from 3fb7549 to b8ff744 Compare October 21, 2025 10:49
@sjabcdefin sjabcdefin marked this pull request as ready for review October 21, 2025 10:50
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
app/models/check_notifier_for_submitter.rb (1)

4-6: payload の妥当性チェックを追加してください。

payload[:check]nil の場合、Line 6 で NoMethodError が発生します。instrument 呼び出し側の実装ミスを早期検知するため、ガード句を追加することを推奨します。

以下の diff を適用して、payload の妥当性チェックを追加してください:

 def call(_name, _started, _finished, _id, payload)
   check = payload[:check]
+  return unless check
   return unless check.receiver && check.sender != check.receiver && check.checkable_type != 'Report'
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3fb7549 and b8ff744.

📒 Files selected for processing (7)
  • app/controllers/api/checks_controller.rb (2 hunks)
  • app/controllers/checks_controller.rb (3 hunks)
  • app/models/check.rb (1 hunks)
  • app/models/check_callbacks.rb (0 hunks)
  • app/models/check_notifier_for_submitter.rb (1 hunks)
  • app/models/product.rb (1 hunks)
  • config/initializers/active_support_notifications.rb (1 hunks)
💤 Files with no reviewable changes (1)
  • app/models/check_callbacks.rb
🚧 Files skipped from review as they are similar to previous changes (3)
  • app/models/check.rb
  • app/models/product.rb
  • config/initializers/active_support_notifications.rb
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rb

⚙️ CodeRabbit configuration file

**/*.rb: # refactoring

  • まずFat Controllerを避け、次にFat Modelを避ける。
  • Serviceクラスの乱用を避ける。
  • controller concernを作ろうとしたらPORO(Plain Old Ruby Object)やActiveRecordモデルでの実装で代替できないか検討する。

Rails Patterns

  • ViewHelperにメソッドを実装する時にはまずDecoratorパターンを使うことを検討する。(active_decorator gemを導入しているのでそれを使う)
  • 複雑なActiveRecordクエリがあり、再利用できそうな場合はQueryObjectパターンを検討する。(rails-patterns gemを導入しているのでそれのQuery機能を使う)
  • Viewにpartialを作る場合はViewComponentを使うことを検討する。
  • 複数のActiveRecordモデルを操作する1つの責務がある時や外部APIとやりとりする処理がある場合にはInteractorオブジェクトパターンを検討する。(interactor gemを導入しているのでそれを使う)
  • 複数のInteractorを実行するような処理がある場合Organizerオブジェクトパターンを検討する。(interactor gemを導入しており、その中にOrganizerの機能があるので使う)

Files:

  • app/controllers/checks_controller.rb
  • app/controllers/api/checks_controller.rb
  • app/models/check_notifier_for_submitter.rb
🧠 Learnings (2)
📓 Common learnings
Learnt from: sjabcdefin
PR: fjordllc/bootcamp#9247
File: app/controllers/checks_controller.rb:18-18
Timestamp: 2025-10-12T03:08:58.343Z
Learning: ChecksController#createでは、check.saveが成功した後の副作用(ステータス更新、アプリ内通知)で例外が発生しても、checkオブジェクトはロールバックせず、ログ出力のみで対応する方針。
📚 Learning: 2025-10-12T03:08:58.343Z
Learnt from: sjabcdefin
PR: fjordllc/bootcamp#9247
File: app/controllers/checks_controller.rb:18-18
Timestamp: 2025-10-12T03:08:58.343Z
Learning: ChecksController#createでは、check.saveが成功した後の副作用(ステータス更新、アプリ内通知)で例外が発生しても、checkオブジェクトはロールバックせず、ログ出力のみで対応する方針。

Applied to files:

  • app/controllers/checks_controller.rb
  • app/controllers/api/checks_controller.rb
🧬 Code graph analysis (1)
app/models/check_notifier_for_submitter.rb (1)
app/models/check.rb (1)
  • receiver (12-14)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build_and_test
🔇 Additional comments (4)
app/controllers/checks_controller.rb (2)

50-54: LGTM!安全な通知処理が実装されています。

safe_instrument メソッドにより、通知処理の例外が適切に捕捉され、ログ出力されています。event パラメータを使用することで、check.createcheck.cancel の両方のイベントで正確なログが出力される実装になっています。

Based on learnings


30-35: destroy の成功確認後に通知を送信する実装が適切です。

@check.destroy の戻り値を確認してから safe_instrument を呼び出すことで、実際に削除されたチェックに対してのみサブスクライバーが処理を実行するようになっています。失敗時のエラーハンドリングも適切です。

app/controllers/api/checks_controller.rb (2)

38-42: LGTM!Web コントローラと同じ安全な通知処理が実装されています。

safe_instrument メソッドの実装により、通知処理の例外が適切に捕捉され、ログ出力されています。API 応答に影響を与えずに通知エラーを処理できる設計になっています。

Based on learnings


26-27: destroy! 使用時のコールバック失敗を考慮してください。

destroy! はバリデーション失敗時だけでなく、コールバック(before_destroy など)で false を返した場合や例外が発生した場合にも ActiveRecord::RecordNotDestroyed を raise します。現在の実装では、コールバック失敗時に例外が API クライアントに伝播しますが、これが意図した動作か確認してください。

Web コントローラでは destroy を使用し、失敗時は alert 付きでリダイレクトしています。API でも同様に、削除失敗を制御したい場合は以下のパターンを検討してください:

@check = Check.find(params[:id])
if @check.destroy
  safe_instrument('check.cancel', check: @check)
  head :no_content
else
  render json: { message: 'チェックの削除に失敗しました。' }, status: :unprocessable_entity
end

現在の destroy! を使用するアプローチが意図的(削除失敗時は 500 エラーとして扱う)であれば、このコメントは無視してください。

Comment on lines +8 to +12
ActivityDelivery.with(
sender: check.sender,
receiver: check.receiver,
check:
).notify(:checked)
Copy link
Copy Markdown

@coderabbitai coderabbitai bot Oct 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

ActivityDelivery の例外を捕捉してログ出力してください。

ActivityDelivery.notify 内部で Notification.create! が呼ばれるため、DB エラーやバリデーションエラー時に例外が発生します。学習済みの方針に従い、check オブジェクトの保存は成功しているため、通知失敗時は例外を伝播させず、ログ出力のみで対応すべきです。

Based on learnings

以下の diff を適用して、例外処理を追加してください:

-    ActivityDelivery.with(
-      sender: check.sender,
-      receiver: check.receiver,
-      check:
-    ).notify(:checked)
+    ActivityDelivery.with(
+      sender: check.sender,
+      receiver: check.receiver,
+      check:
+    ).notify(:checked)
+  rescue StandardError => e
+    Rails.logger.warn "[CheckNotifierForSubmitter] 通知作成でエラーが発生しました。check_id=#{check.id}, error=#{e.message}"
   end
 end
🤖 Prompt for AI Agents
In app/models/check_notifier_for_submitter.rb around lines 8 to 12, wrap the
ActivityDelivery.with(...).notify(:checked) call in a begin/rescue that catches
StandardError (or more specific errors if desired) and logs the failure instead
of re-raising; include useful context such as the check.id (or other
identifiers), the exception message and backtrace in the log (e.g.,
Rails.logger.error "ActivityDelivery.notify failed for check=#{check.id}:
#{e.message}\n#{e.backtrace.join("\n")}"), and do not propagate the exception so
normal flow continues.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

app/models/check_notifier_for_submitter.rbの呼び出し元であるapp/controllers/checks_controller.rbapp/controllers/api/checks_controller.rbで例外を捕捉し、ログ出力している。
上記方針で問題があるでしょうか?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# コントローラー側のsafe_instrumentメソッドの実装を確認
echo "=== ChecksController の safe_instrument 実装 ==="
rg -nA 10 "def safe_instrument" app/controllers/checks_controller.rb

echo -e "\n=== API::ChecksController の safe_instrument 実装 ==="
rg -nA 10 "def safe_instrument" app/controllers/api/checks_controller.rb

echo -e "\n=== safe_instrumentの呼び出し箇所 ==="
rg -n "safe_instrument" app/controllers/checks_controller.rb app/controllers/api/checks_controller.rb

Length of output: 1133


@sjabcdefin さん、コントローラー側での例外処理で問題ありません。むしろ良い設計だと思います。

現在の実装が妥当な理由:

  1. ActiveSupport::Notifications の動作特性

    • ActiveSupport::Notifications.instrument は同期的に実行されるため、subscriber(CheckNotifierForSubmitter)で発生した例外は確実に instrument の呼び出し元に伝播します
    • つまり、コントローラー側の safe_instrument で捕捉できます
  2. 設計上の利点

    • 例外処理を一箇所(コントローラー)で管理できるため、保守性が高い
    • 将来 check.create イベントに複数の subscriber を追加しても、コントローラー側の処理で一括してハンドリングできる
    • subscriber 側の実装がシンプルになり、責務が明確になる
  3. 学習済みの方針との整合性

    • check.save 成功後の副作用で例外が発生してもログ出力のみで対応する方針に従っており、一貫性がある

補足:
より詳細なログが必要な場合は、subscriber 側でも rescue して詳細情報(例:check.id, check.sender.id など)をログ出力した後に raise で再送出することも可能ですが、現状のログメッセージにはイベント名が含まれているため、トラブルシューティングには十分と考えられます。

したがって、私の当初のコメントは誤りでした。現在の実装方針で進めてください。


🧠 Learnings used
Learnt from: sjabcdefin
PR: fjordllc/bootcamp#9247
File: app/controllers/checks_controller.rb:18-18
Timestamp: 2025-10-12T03:08:58.343Z
Learning: ChecksController#createでは、check.saveが成功した後の副作用(ステータス更新、アプリ内通知)で例外が発生しても、checkオブジェクトはロールバックせず、ログ出力のみで対応する方針。

Learnt from: jun-kondo
PR: fjordllc/bootcamp#0
File: :0-0
Timestamp: 2025-07-23T21:11:21.826Z
Learning: fjordllc/bootcampプロジェクトの`app/models/inquiry_notifier.rb`において、`NOTIFICATION_SENDER_NAME`で指定される管理者ユーザーは必ず存在することがシステムの前提条件として保証されているため、senderがnilになることは想定されておらず、エラーハンドリングは不要である。

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

例外ハンドリングに関する指摘の修正方針変更により、新規に作成していたapp/models/check_notifier_for_submitter.rbは削除いたしました。

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sjabcdefin さん、了解しました。check_notifier_for_submitter.rb の削除を確認しました。

例外ハンドリングの方針変更により、別のアプローチを採用されたとのことですね。もし新しい実装について確認が必要でしたら、お気軽にレビューをご依頼ください。

PRの他の部分で何かお手伝いできることがあれば、お知らせください。


🧠 Learnings used
Learnt from: jun-kondo
PR: fjordllc/bootcamp#0
File: :0-0
Timestamp: 2025-07-23T21:11:21.826Z
Learning: fjordllc/bootcampプロジェクトの`app/models/inquiry_notifier.rb`において、`NOTIFICATION_SENDER_NAME`で指定される管理者ユーザーは必ず存在することがシステムの前提条件として保証されているため、senderがnilになることは想定されておらず、エラーハンドリングは不要である。

Learnt from: jun-kondo
PR: fjordllc/bootcamp#0
File: :0-0
Timestamp: 2025-07-23T21:11:21.826Z
Learning: fjordllc/bootcampプロジェクトの`app/models/work_notification_destroyer.rb`において、このクラスは`app/controllers/works_controller.rb`の`destroy`アクションから呼び出され、`before_action :set_my_work`でIDの妥当性チェックが事前に行われるため、payloadのworkオブジェクトは常に有効であることが保証されている。そのためエラーハンドリングは不要。

Learnt from: sjabcdefin
PR: fjordllc/bootcamp#9247
File: app/controllers/checks_controller.rb:0-0
Timestamp: 2025-10-22T06:04:36.027Z
Learning: ChecksController#createおよび#destroyでは、Checkの作成・削除とActiveSupport::Notifications.instrumentによるイベント発行(プラクティスのステータス更新)を同一トランザクション内で実行し、いずれかが失敗した場合は両方をロールバックする。これによりWebUI表示とDB状態の整合性を保証している。

Learnt from: reckyy
PR: fjordllc/bootcamp#8356
File: app/models/notification.rb:45-47
Timestamp: 2025-07-15T12:58:02.735Z
Learning: fjordllc/bootcampプロジェクトの`app/models/notification.rb`において、`training_completed`通知種別は`TARGETS_TO_KINDS`ハッシュに含めない設計になっている。これは`training_completed`が通知一覧のフィルタ対象ではないためで、意図的な設計判断である。

@sjabcdefin sjabcdefin force-pushed the chore/replace-check-events-with-notifications branch from b8ff744 to c6f1a97 Compare October 22, 2025 05:53
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (1)
app/models/check_callbacks.rb (1)

6-14: 通知エラーのログに詳細情報を追加してください。

エラーメッセージのみをログ出力していますが、デバッグ時に以下の情報があると問題の特定が容易になります:

  • check.id
  • check.checkable_type / check.checkable_id
  • check.sender_id / check.receiver_id
  • バックトレース(e.backtrace.first(5) など)

以下の差分を適用して、ログの詳細度を向上させることを検討してください:

 rescue StandardError => e
-  Rails.logger.warn("[CheckCallbacks] 通知処理でエラー: #{e.message}")
+  Rails.logger.warn(
+    "[CheckCallbacks] 通知処理でエラー: #{e.message} " \
+    "(check_id: #{check.id}, checkable: #{check.checkable_type}##{check.checkable_id}, " \
+    "sender_id: #{check.sender_id}, receiver_id: #{check.receiver&.id})"
+  )
+  Rails.logger.warn(e.backtrace.first(5).join("\n")) if e.backtrace
 end
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b8ff744 and c6f1a97.

📒 Files selected for processing (5)
  • app/controllers/api/checks_controller.rb (1 hunks)
  • app/controllers/checks_controller.rb (2 hunks)
  • app/models/check.rb (1 hunks)
  • app/models/check_callbacks.rb (1 hunks)
  • app/models/product.rb (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • app/models/product.rb
  • app/models/check.rb
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rb

⚙️ CodeRabbit configuration file

**/*.rb: # refactoring

  • まずFat Controllerを避け、次にFat Modelを避ける。
  • Serviceクラスの乱用を避ける。
  • controller concernを作ろうとしたらPORO(Plain Old Ruby Object)やActiveRecordモデルでの実装で代替できないか検討する。

Rails Patterns

  • ViewHelperにメソッドを実装する時にはまずDecoratorパターンを使うことを検討する。(active_decorator gemを導入しているのでそれを使う)
  • 複雑なActiveRecordクエリがあり、再利用できそうな場合はQueryObjectパターンを検討する。(rails-patterns gemを導入しているのでそれのQuery機能を使う)
  • Viewにpartialを作る場合はViewComponentを使うことを検討する。
  • 複数のActiveRecordモデルを操作する1つの責務がある時や外部APIとやりとりする処理がある場合にはInteractorオブジェクトパターンを検討する。(interactor gemを導入しているのでそれを使う)
  • 複数のInteractorを実行するような処理がある場合Organizerオブジェクトパターンを検討する。(interactor gemを導入しており、その中にOrganizerの機能があるので使う)

Files:

  • app/controllers/checks_controller.rb
  • app/models/check_callbacks.rb
  • app/controllers/api/checks_controller.rb
🧠 Learnings (2)
📓 Common learnings
Learnt from: matuaya
PR: fjordllc/bootcamp#9108
File: app/models/after_user_retirement.rb:0-0
Timestamp: 2025-09-01T22:31:57.345Z
Learning: fjordllc/bootcampプロジェクトにおいて、`Newspaper.publish(:retirement_create, { user: user })`は通知処理ではなく、UnfinishedDataDestroyerやTimesChannelDestroyer等による重要なデータクリーンアップ処理を実行するため、管理者による退会(triggered_by: 'admin')の場合でも必ず実行する必要がある。
Learnt from: matuaya
PR: fjordllc/bootcamp#9108
File: app/models/after_user_retirement.rb:0-0
Timestamp: 2025-09-01T22:31:57.345Z
Learning: fjordllc/bootcampプロジェクトにおいて、`Newspaper.publish(:retirement_create, { user: user })`はUnfinishedDataDestroyer(未チェックProduct・WIP Report削除、career_pathリセット)とTimesChannelDestroyer(Discord分報チャンネル削除)による重要なデータクリーンアップ処理を実行するため、退会のトリガーが何であろうと(admin、hibernation、user)必ず実行する必要がある。
📚 Learning: 2025-10-12T03:08:58.343Z
Learnt from: sjabcdefin
PR: fjordllc/bootcamp#9247
File: app/controllers/checks_controller.rb:18-18
Timestamp: 2025-10-12T03:08:58.343Z
Learning: ChecksController#createでは、check.saveが成功した後の副作用(ステータス更新、アプリ内通知)で例外が発生しても、checkオブジェクトはロールバックせず、ログ出力のみで対応する方針。

Applied to files:

  • app/controllers/checks_controller.rb
  • app/models/check_callbacks.rb
  • app/controllers/api/checks_controller.rb
🧬 Code graph analysis (1)
app/models/check_callbacks.rb (1)
app/models/check.rb (1)
  • receiver (12-14)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build_and_test
🔇 Additional comments (1)
app/controllers/checks_controller.rb (1)

17-26: トランザクション設計の確認:意図した動作になっています。

過去のレビューコメントでの議論を踏まえ、以下の設計が正しく実装されていることを確認しました:

  • ✅ Check の作成とステータス更新(LearningStatusUpdater 経由)が同一トランザクション内で実行され、いずれかが失敗すると両方ロールバック
  • ✅ 通知処理(CheckCallbacks#after_create 内の ActivityDelivery)は例外を捕捉してログ出力のみ行い、トランザクションに影響しない
  • Product#change_learning_statusupdate! を使用し、ステータス更新の失敗が適切に例外として伝播

この設計により、UI表示に影響するステータス更新の整合性が保証される一方、副次的な通知処理の失敗がシステム全体に波及しない適切なバランスが取れています。

Based on learnings

Comment on lines +14 to +22
begin
Check.transaction do
@check = Check.create!(user: current_user, checkable:)
ActiveSupport::Notifications.instrument('check.create', check: @check)
end
head :created
rescue StandardError
render json: { message: 'エラーが発生しました。' }
end
Copy link
Copy Markdown

@coderabbitai coderabbitai bot Oct 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

エラー時のログ出力を追加してください。

rescue StandardError で例外を捕捉していますが、エラー内容をログに記録していないため、障害発生時の原因特定が困難です。ユーザーには汎用的なエラーメッセージを返しつつ、ログには詳細な情報を記録することを推奨します。

以下の差分を適用して、エラーログを追加してください:

-    rescue StandardError
+    rescue StandardError => e
+      Rails.logger.error(
+        "[API::ChecksController#create] チェック作成でエラー: #{e.message} " \
+        "(checkable: #{params[:checkable_type]}##{params[:checkable_id]}, user_id: #{current_user.id})"
+      )
+      Rails.logger.error(e.backtrace.first(10).join("\n")) if e.backtrace
       render json: { message: 'エラーが発生しました。' }
     end
🤖 Prompt for AI Agents
In app/controllers/api/checks_controller.rb around lines 14 to 22, the rescue
block swallows exceptions without logging; change the rescue to capture the
exception (rescue StandardError => e) and add a server-side error log (e.g.
Rails.logger.error or logger.error) that includes the exception message and
backtrace (and optional context such as current_user.id and checkable) before
rendering the generic JSON error to the client.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

エラー時のログ出力を追加いたしました。
コミット:2b0fa50

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sjabcdefin さん、エラーログの追加をありがとうございます!

例外のキャッチとログ出力が適切に実装されていることを確認しました。1点だけ気づいた点があります:

ログメッセージ内のコントローラ名が [ChecksController#create] となっていますが、このファイルは API::ChecksController なので、以下のように修正するとより正確になります:

create メソッド(Line 20):

Rails.logger.error("[API::ChecksController#create] チェック作成でエラー: #{e.message}")

destroy メソッド(Line 32):

Rails.logger.error("[API::ChecksController#destroy] チェック削除でエラー: #{e.message}")

これにより、ログからエラーの発生箇所を特定しやすくなります。

 /\_/\
( o.o )
 > ^ <  ナイスです!
/|   |\

(| |)


🧠 Learnings used
Learnt from: sjabcdefin
PR: fjordllc/bootcamp#9247
File: app/controllers/checks_controller.rb:0-0
Timestamp: 2025-10-22T06:04:36.027Z
Learning: ChecksController#createおよび#destroyでは、Checkの作成・削除とActiveSupport::Notifications.instrumentによるイベント発行(プラクティスのステータス更新)を同一トランザクション内で実行し、いずれかが失敗した場合は両方をロールバックする。これによりWebUI表示とDB状態の整合性を保証している。

Learnt from: Miya096jp
PR: fjordllc/bootcamp#9102
File: app/controllers/mentor/buzzes_controller.rb:0-0
Timestamp: 2025-09-04T01:39:22.261Z
Learning: fjordllc/bootcamp プロジェクトでは、更新処理の成功時の通知メッセージについて、I18n を使用せずに日本語文字列を直接記述する方針で一貫性を保っている。

If you found this review helpful, would you consider giving us a shout-out on X?

Thank you for using CodeRabbit!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[API::ChecksController#create], [API::ChecksController#destroy]に修正いたしました。
ご確認お願いいたします。
コミット:459fd65

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sjabcdefin さん、修正ありがとうございます!

ログメッセージのコントローラ名が [API::ChecksController#create][API::ChecksController#destroy] に正しく更新されていることを確認しました。これでログから発生箇所を正確に特定できるようになりました。完璧です!✨

 /\_/\
( ^.^ )
 > ♥ <  完璧です!
/|   |\

(| |)


🧠 Learnings used
Learnt from: sjabcdefin
PR: fjordllc/bootcamp#9247
File: app/controllers/checks_controller.rb:0-0
Timestamp: 2025-10-22T06:04:36.027Z
Learning: ChecksController#createおよび#destroyでは、Checkの作成・削除とActiveSupport::Notifications.instrumentによるイベント発行(プラクティスのステータス更新)を同一トランザクション内で実行し、いずれかが失敗した場合は両方をロールバックする。これによりWebUI表示とDB状態の整合性を保証している。

Comment on lines 28 to 36
def destroy
@check = Check.find(params[:id]).destroy
ActiveSupport::Notifications.instrument('check.cancel', check: @check)

Check.transaction do
@check = Check.find(params[:id]).destroy!
ActiveSupport::Notifications.instrument('check.cancel', check: @check)
end
head :no_content
rescue StandardError
render json: { message: 'エラーが発生しました。' }
end
Copy link
Copy Markdown

@coderabbitai coderabbitai bot Oct 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

エラー時のログ出力を追加してください。

create アクションと同様に、destroy アクションでも例外の詳細をログに記録することで、障害対応が容易になります。

以下の差分を適用して、エラーログを追加してください:

   rescue StandardError => e
+    Rails.logger.error(
+      "[API::ChecksController#destroy] チェック削除でエラー: #{e.message} " \
+      "(check_id: #{params[:id]}, user_id: #{current_user.id})"
+    )
+    Rails.logger.error(e.backtrace.first(10).join("\n")) if e.backtrace
     render json: { message: 'エラーが発生しました。' }
   end

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In app/controllers/api/checks_controller.rb around lines 28 to 36, the destroy
action rescues StandardError but does not log the exception details; update the
rescue to capture the exception (e.g., rescue StandardError => e) and log the
error message and backtrace (using Rails.logger.error or logger.error) before
rendering the JSON error response so operators have the exception context for
troubleshooting.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

修正いたしました。ご確認お願いいたします。
コミット:459fd65

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sjabcdefin さん、修正ありがとうございます!確認いたしました。

エラーログの追加が正しく実装されています。destroy アクションでも StandardError => e で例外をキャッチし、Rails.logger.error でログ出力するようになりました。また、create アクションと同じログ形式で統一されているため、コードの一貫性も保たれています。

これで、エラー発生時の調査がしやすくなります。対応お疲れ様でした!


🧠 Learnings used
Learnt from: sjabcdefin
PR: fjordllc/bootcamp#9247
File: app/controllers/checks_controller.rb:0-0
Timestamp: 2025-10-22T06:04:36.027Z
Learning: ChecksController#createおよび#destroyでは、Checkの作成・削除とActiveSupport::Notifications.instrumentによるイベント発行(プラクティスのステータス更新)を同一トランザクション内で実行し、いずれかが失敗した場合は両方をロールバックする。これによりWebUI表示とDB状態の整合性を保証している。

Learnt from: Miya096jp
PR: fjordllc/bootcamp#9102
File: app/controllers/mentor/buzzes_controller.rb:0-0
Timestamp: 2025-09-04T01:39:22.261Z
Learning: fjordllc/bootcamp プロジェクトでは、更新処理の成功時の通知メッセージについて、I18n を使用せずに日本語文字列を直接記述する方針で一貫性を保っている。

If you found this review helpful, would you consider giving us a shout-out on X?

Thank you for using CodeRabbit!

@sjabcdefin sjabcdefin force-pushed the chore/replace-check-events-with-notifications branch 2 times, most recently from 2b0fa50 to 459fd65 Compare October 22, 2025 11:59
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
app/controllers/checks_controller.rb (1)

34-43: destroyアクションも同様にエラーログの改善が推奨されます。

createアクションと同様に、過去のレビューコメントでエラーログの詳細化が提案されています。以下の情報を追加することで、障害発生時の原因特定が迅速化されます:

  • check_id
  • checkableの型とID
  • user_id
  • スタックトレース

過去コメントの提案を適用し、createdestroyで一貫したエラーログ出力を実現することをご検討ください。

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2b0fa50 and 459fd65.

📒 Files selected for processing (2)
  • app/controllers/api/checks_controller.rb (1 hunks)
  • app/controllers/checks_controller.rb (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/controllers/api/checks_controller.rb
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rb

⚙️ CodeRabbit configuration file

**/*.rb: # refactoring

  • まずFat Controllerを避け、次にFat Modelを避ける。
  • Serviceクラスの乱用を避ける。
  • controller concernを作ろうとしたらPORO(Plain Old Ruby Object)やActiveRecordモデルでの実装で代替できないか検討する。

Rails Patterns

  • ViewHelperにメソッドを実装する時にはまずDecoratorパターンを使うことを検討する。(active_decorator gemを導入しているのでそれを使う)
  • 複雑なActiveRecordクエリがあり、再利用できそうな場合はQueryObjectパターンを検討する。(rails-patterns gemを導入しているのでそれのQuery機能を使う)
  • Viewにpartialを作る場合はViewComponentを使うことを検討する。
  • 複数のActiveRecordモデルを操作する1つの責務がある時や外部APIとやりとりする処理がある場合にはInteractorオブジェクトパターンを検討する。(interactor gemを導入しているのでそれを使う)
  • 複数のInteractorを実行するような処理がある場合Organizerオブジェクトパターンを検討する。(interactor gemを導入しており、その中にOrganizerの機能があるので使う)

Files:

  • app/controllers/checks_controller.rb
🧠 Learnings (4)
📓 Common learnings
Learnt from: sjabcdefin
PR: fjordllc/bootcamp#9247
File: app/controllers/checks_controller.rb:0-0
Timestamp: 2025-10-22T06:04:36.027Z
Learning: ChecksController#createおよび#destroyでは、Checkの作成・削除とActiveSupport::Notifications.instrumentによるイベント発行(プラクティスのステータス更新)を同一トランザクション内で実行し、いずれかが失敗した場合は両方をロールバックする。これによりWebUI表示とDB状態の整合性を保証している。
Learnt from: sjabcdefin
PR: fjordllc/bootcamp#9247
File: app/controllers/checks_controller.rb:0-0
Timestamp: 2025-10-22T06:04:36.027Z
Learning: CheckCallbacksはafter_create_commitで実行されるため、Checkのトランザクションがコミットされた後に動作する。CheckCallbacks内のメール通知・サイト内通知でエラーが発生した場合は、副次的な処理とみなし、例外を発生させずワーニングログのみ出力する設計になっている。
Learnt from: matuaya
PR: fjordllc/bootcamp#9108
File: app/models/after_user_retirement.rb:0-0
Timestamp: 2025-09-01T22:31:57.345Z
Learning: fjordllc/bootcampプロジェクトにおいて、`Newspaper.publish(:retirement_create, { user: user })`は通知処理ではなく、UnfinishedDataDestroyerやTimesChannelDestroyer等による重要なデータクリーンアップ処理を実行するため、管理者による退会(triggered_by: 'admin')の場合でも必ず実行する必要がある。
📚 Learning: 2025-10-22T06:04:36.027Z
Learnt from: sjabcdefin
PR: fjordllc/bootcamp#9247
File: app/controllers/checks_controller.rb:0-0
Timestamp: 2025-10-22T06:04:36.027Z
Learning: ChecksController#createおよび#destroyでは、Checkの作成・削除とActiveSupport::Notifications.instrumentによるイベント発行(プラクティスのステータス更新)を同一トランザクション内で実行し、いずれかが失敗した場合は両方をロールバックする。これによりWebUI表示とDB状態の整合性を保証している。

Applied to files:

  • app/controllers/checks_controller.rb
📚 Learning: 2025-10-22T06:04:36.027Z
Learnt from: sjabcdefin
PR: fjordllc/bootcamp#9247
File: app/controllers/checks_controller.rb:0-0
Timestamp: 2025-10-22T06:04:36.027Z
Learning: CheckCallbacksはafter_create_commitで実行されるため、Checkのトランザクションがコミットされた後に動作する。CheckCallbacks内のメール通知・サイト内通知でエラーが発生した場合は、副次的な処理とみなし、例外を発生させずワーニングログのみ出力する設計になっている。

Applied to files:

  • app/controllers/checks_controller.rb
📚 Learning: 2025-09-04T01:39:22.261Z
Learnt from: Miya096jp
PR: fjordllc/bootcamp#9102
File: app/controllers/mentor/buzzes_controller.rb:0-0
Timestamp: 2025-09-04T01:39:22.261Z
Learning: fjordllc/bootcamp プロジェクトでは、更新処理の成功時の通知メッセージについて、I18n を使用せずに日本語文字列を直接記述する方針で一貫性を保っている。

Applied to files:

  • app/controllers/checks_controller.rb
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build_and_test
🔇 Additional comments (1)
app/controllers/checks_controller.rb (1)

17-27: トランザクション実装は適切です。エラーログの改善提案が過去コメントにあります。

Based on learnings: トランザクション内でsave!とイベント発行を実行する実装は、DB状態とWebUI表示の整合性を保証する設計として適切です。

ただし、過去のレビューコメントで提案されているように、エラーログに以下の情報を追加することで障害分析が容易になります:

  • checkableの型とID
  • user_id
  • スタックトレース(バックトレース)

過去コメントの提案を適用することをご検討ください。

@sjabcdefin
Copy link
Copy Markdown
Contributor Author

@yokomaru

お疲れ様です!
お手すきの際に、こちらのPRをレビューいただけると嬉しいです。😊🙏
ご負担になりそうでしたら、遠慮なく教えていただければと思います!😌👌

また、FjordChoiceに、引き受けたレビューが反映されない場合があるようです。
他にレビューがある等も遠慮なく教えていただければと思います!

@sjabcdefin sjabcdefin requested a review from yokomaru October 22, 2025 21:55
@yokomaru
Copy link
Copy Markdown
Contributor

yokomaru commented Oct 26, 2025

@sjabcdefin
お疲れ様です!返信遅れて申し訳ありませんっ🙏
レビュー承知いたしました!1週間以内にご連絡させていただきます!

@sjabcdefin
Copy link
Copy Markdown
Contributor Author

@yokomaru

お疲れさまです!
レビュー引き受けてくださり、ありがとうございます!
よろしくお願いいたします。😌🙏

Copy link
Copy Markdown
Contributor

@yokomaru yokomaru left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sjabcdefin
お疲れ様です!指定した期限より少し遅くなってしまい申し訳ありません…!🙇‍♀️

動作確認いたしましたが、問題ありませんでした!
検証項目がとても丁寧で流れも分かりやすかったので、とても検証しやすかったです 🙌☺️

1点だけ軽くコメントさせていただきましたが、個人的に気になった程度の内容なので、最終的な判断はお任せします!

また、トランザクションや例外処理まわりのハンドリングも非常に勉強になりました!
例外処理関連のコミットメッセージが少しシンプル(ChecksController の例外処理を改善)に感じたので、以下あたりの変更内容や背景(Why)などどのように改善されたかを残していただけたら、後から履歴を追いやすいかなと思いました 💡

  • after_create_commit / update! への変更の理由
  • rescue StandardError を採用した背景
    • rescue StandardErrorはかなり広い範囲のエラーを捕捉する認識なのですが、メンターの方や CodeRabbit とのやり取りを踏まえた上でこの形になっていると思うので、その経緯や理由を軽く残していただくと、後からコードを読む方にも意図が伝わりやすいかなと思いました 💡

※ CodeRabbit とのやり取りを拝見し、議論を踏まえた実装だと伝わってきたので、その経緯がコミットログに軽く残っているとより親切かも!というくらいの温度感です 🙏

お手数おかけいたしますが、よろしくお願いいたします。

head :created
rescue StandardError => e
Rails.logger.error("[API::ChecksController#create] チェック作成でエラー: #{e.message}")
render json: { message: 'エラーが発生しました。' }
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[IMO]
renderはステータスを指定しない場合、デフォルトで200(成功)を返すので、
StandardError が発生している=サーバー側の処理に失敗した状態でレスポンスを返す場合は、 status: :internal_server_errorなどのエラーステータスを付与した方がAPIとして適切かな?と思ったのですが、tomokaさん的にどう思われますかっ?

※API を叩いているJS 側(comment-chekable.js)では、HTTP ステータスではなくmessage の有無でエラーを判定しているようなので、現状このステータスがあってもなくても動作上は問題なさそうなため最終的な判断はお任せいたします!🙏

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ご指摘ありがとうございます!
エラー時に200(成功)を返すのは不適切だと思い、status: :internal_server_errorを返すように修正いたしました。😌🙏

コミット:05be5d6

過去のissue対応での置き換え漏れを修正
- 画面上の不整合が発生しないよう、チェック保存/削除とステータス更新を同一トランザクション内で実行し、
  いずれかが失敗した場合はロールバックされるように変更
- サイト内通知・メール通知は、画面上の不整合が起きないため、トランザクション外での実行でも問題ないと判断
- 例外を検知できるよう、update / save を update! / save! に変更
- トランザクション完了後にコールバックを実行するため、
  after_create / after_destroy を after_create_commit / after_destroy_commit に変更
- 画面上の不整合が発生しないよう、チェック保存/削除とステータス更新を同一トランザクション内で実行し、
  いずれかが失敗した場合はロールバックされるように変更
- サイト内通知・メール通知は、画面上の不整合が起きないため、トランザクション外での実行でも問題ないと判断
- 例外を検知できるよう、destroy を destroy! に変更
例外発生時に200を返すのは不適切なため
削除処理については現状使用されていないが併せて同様の対応を実施
@sjabcdefin sjabcdefin force-pushed the chore/replace-check-events-with-notifications branch from 459fd65 to 05be5d6 Compare November 4, 2025 14:17
@sjabcdefin
Copy link
Copy Markdown
Contributor Author

@yokomaru
お疲れさまです!
ご指摘事項について修正いたしましたので、再度ご確認お願いいたします。😌🙏

また、例外処理関連のコミットメッセージについて、変更内容や判断理由などを追記してみました!
こちらも併せてご確認いただければと思います!🙂‍↕️
94b9dc1
d5d4bee

Copy link
Copy Markdown
Contributor

@yokomaru yokomaru left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sjabcdefin

お疲れ様です!ご対応いただきありがとうございました〜!🙌
コミットメッセージ、丁寧にまとめられていてとても読みやすく、非常に勉強になりました!

こちらApproveさせていただきます!!

引き続きどうぞよろしくお願いいたします!!

@sjabcdefin
Copy link
Copy Markdown
Contributor Author

@yokomaru
お疲れさまです!
レビューありがとうございました!😌🙏

@okuramasafumi
お疲れさまです!
メンバレビューが完了しましたので、お手すきの際にレビューをお願いいたします。😌🙏

Copy link
Copy Markdown
Contributor

@okuramasafumi okuramasafumi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@sjabcdefin
Copy link
Copy Markdown
Contributor Author

@okuramasafumi
ご確認ありがとうございました!😌🙏

@komagata
お疲れさまです。
レビューが完了しましたので、お手すきの際にマージお願いします。😌🙏

@komagata komagata merged commit dae9279 into main Dec 3, 2025
22 checks passed
@komagata komagata deleted the chore/replace-check-events-with-notifications branch December 3, 2025 05:30
@komagata
Copy link
Copy Markdown
Member

komagata commented Dec 3, 2025

@sjabcdefin マージしました〜

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants