Skip to content

ベストアンサーを適切に取り消すようにした#9155

Merged
komagata merged 3 commits intomainfrom
bug/not-properly-revoke-correct-answer
Oct 15, 2025
Merged

ベストアンサーを適切に取り消すようにした#9155
komagata merged 3 commits intomainfrom
bug/not-properly-revoke-correct-answer

Conversation

@ryufuta
Copy link
Copy Markdown
Contributor

@ryufuta ryufuta commented Sep 8, 2025

Issue

概要

  • ベストアンサーを取り消された回答のtypeの値が通常の回答と同様NULLになるようにした
  • その他軽微な修正をした
    • 動作に影響のないtypoを修正(AnswerCacheDestroyer#callに渡す引数を修正したが、この引数はメソッド内で元々使用されていない)
    • ベストアンサー取り消し成功時のレスポンスを明示

変更確認方法

  1. bug/not-properly-revoke-correct-answerをローカルに取り込む
  2. foreman start -f Procfile.devでローカルサーバを起動
  3. hajimeでログイン(任意のユーザーで良いが、hajimeが作成した解決済みのQ&Aが存在するため手順が楽)
  4. 解決済みの質問 にアクセス
  5. ベストアンサーの回答にある「ベストアンサーを取り消す」ボタンをクリック
  6. ベストアンサーが取り消されて通常の回答と同じ見た目になっていることを確認
  7. RailsコンソールでQuestion.find(782854533).answersを実行して上記の質問の回答を取得
  8. ベストアンサーを取り消した回答のtype属性がnilであることを確認

Screenshot

見た目の変更はなし。

本番環境での動作確認終了後にすること

本番環境のanswersテーブルにtypeの値が空文字列のレコードがあればNULLに置き換える。

Summary by CodeRabbit

  • バグ修正
    • 回答のタイプが空文字で保存される不整合を修正し、nilで統一しました。
    • 更新後に配信される通知に回答データが確実に含まれるようにして、関連画面での表示不具合を防止しました。
    • 更新処理の応答を明確化し、クライアント側での処理安定性を向上させました。

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Sep 8, 2025

Walkthrough

API::CorrectAnswersController#update で、更新時に answer.type を空文字列 ('') から nil に変更し、Newspaper.publish のペイロードでインスタンス変数 @answer ではなくローカル変数 answer をハッシュショートハンド { answer: } で渡すように修正。レスポンスを空の 204 No Content (head :no_content) に変更。その他の取得・更新フローは維持。

Changes

Cohort / File(s) Summary
API 正答更新ロジック
app/controllers/api/correct_answers_controller.rb
update 内で type: ''type: nil に変更。Newspaper.publish のペイロードを @answer からローカル変数 answer に短縮ハッシュ { answer: } で渡すよう修正。レスポンスを head :no_content(204)に変更。その他の取得・更新フローは維持。

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Client
  participant Controller as API::CorrectAnswersController
  participant Answer as Answer (モデル)
  participant Newspaper

  Client->>Controller: PATCH /api/correct_answers (params)
  Controller->>Answer: find(params[:answer_id])
  Answer-->>Controller: answer
  Note over Controller: answer.update(..., type: nil, ...)
  Controller->>Answer: answer.update(...)
  alt 更新成功
    Note over Controller,Newspaper: publish ペイロードをローカル変数で送信 `{ answer: }`
    Controller->>Newspaper: publish({ answer: })
    Newspaper-->>Controller: ok
    Controller-->>Client: 204 No Content
  else 更新失敗
    Controller-->>Client: 422 Unprocessable Entity
  end
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related issues

Suggested reviewers

  • komagata

Poem

ぴょんと跳ねて空文字は消えた
答えは軽くなり nil に還るよ
新聞うさぎがそっと届けるよ
小さな修正で庭は静かに
にんじん抱えて祝うよ 🐰📜

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed タイトルはプルリクエストの主要な変更点である「ベストアンサーの適切な取り消し」を簡潔かつ明確に要約しており、余計なノイズも含まれていないため問題ありません。
Description Check ✅ Passed プルリクテンプレートの必須セクションであるIssue番号、概要、変更確認方法、およびスクリーンショットの扱いについてすべて適切に記載されており、レビューに必要な情報が詳細かつ明確に提供されているため合格です。
✨ 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 bug/not-properly-revoke-correct-answer

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.

@ryufuta ryufuta marked this pull request as ready for review September 8, 2025 14:09
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 (4)
app/controllers/api/correct_answers_controller.rb (4)

20-21: STI解除後にベースクラスへreifyしてからpublishを行う提案

typenil にしても、その時点のRubyオブジェクトは依然として CorrectAnswer クラスのままです。イベント購読側がクラスに依存している場合のブレを避けるため、publish前に Answer に変換するのが安全です。

適用例:

-    answer.update!(type: nil)
-    Newspaper.publish(:answer_save, { answer: })
+    answer.update!(type: nil)
+    answer = answer.becomes(Answer)
+    Newspaper.publish(:answer_save, { answer: answer })

18-22: APIレスポンスを明示化(create と整合)

update はレスポンスを返していないため、明示的に head :ok(または適切なステータス)を返すと一貫します。

適用例:

-    Newspaper.publish(:answer_save, { answer: })
+    Newspaper.publish(:answer_save, { answer: })
+    head :ok

20-21: 例外を500にせずクライアントエラーで返すなら分岐に変更

update! は失敗時に500系になります。create 同様に :bad_request(または :unprocessable_entity)を返す方針なら分岐に揃えると良いです。

代替案:

-    answer.update!(type: nil)
-    Newspaper.publish(:answer_save, { answer: })
-    head :ok
+    if answer.update(type: nil)
+      answer = answer.becomes(Answer)
+      Newspaper.publish(:answer_save, { answer: answer })
+      head :ok
+    else
+      head :bad_request
+    end

20-21: 運用補足:空文字の backfill をコード化して安全に実施

PR概要どおり、本番の answers.type = ''NULL に置換する作業は one-off でなく migration/task にして監査可能にすると安全です。

例(データ修正専用migration):

class BackfillAnswersTypeEmptyToNull < ActiveRecord::Migration[7.0]
  disable_ddl_transaction!  # 大量件数ならバッチ化を検討

  def up
    say_with_time "Backfilling answers.type '' -> NULL" do
      Answer.unscoped.where(type: '').update_all(type: nil)
    end
  end

  def down
    # 意図的に不可逆
  end
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 929040f and 3f57e85.

📒 Files selected for processing (1)
  • app/controllers/api/correct_answers_controller.rb (1 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/api/correct_answers_controller.rb
🔇 Additional comments (1)
app/controllers/api/correct_answers_controller.rb (1)

21-21: 省略記法 { answer: } の互換性問題なし
プロジェクトの Ruby バージョンが .ruby-versionGemfile でいずれも 3.1.6 と確認できたため(Ruby 3.1+ 必須){ answer: } の省略記法はそのままで問題ありません。

@ryufuta ryufuta requested a review from kutimiti1234 September 8, 2025 14:22
@ryufuta
Copy link
Copy Markdown
Contributor Author

ryufuta commented Sep 8, 2025

@kutimiti1234
こちらのレビューをお願いできますか?

@kutimiti1234
Copy link
Copy Markdown
Contributor

@ryufuta
承知いたしました!
今週中には対応したいと思います!

@ryufuta
Copy link
Copy Markdown
Contributor Author

ryufuta commented Sep 9, 2025

@kutimiti1234
すみません。先に書いておくべきでしたが、レビューにあたっての補足情報です。

CodeRabbitの #9155 (review) の「20-21: 運用補足:空文字の backfill をコード化して安全に実施」の指摘に従うべきかどうかは本番環境を管理されている駒形さんにしか判断できないことなので、無視して大丈夫です。
kutimitiさんのレビュー終了後に私の方で駒形さんに相談します。

@kutimiti1234
Copy link
Copy Markdown
Contributor

kutimiti1234 commented Sep 10, 2025

@ryufuta
お疲れ様です。

特に動作確認で問題見られず、修正点も問題ないかと思います。ただ、3点ほど指摘というよりは疑問点があります。

  1. AnswerCacheDestroyerの定義でつかわないのに定義されているanswerは消さなくていいのか?
    下記の通り記述されており、payloadおよびanswerが使われていないのは納得できたのですが
    そもそもこちらの_answerは残しておく必然性があるものなのでしょうか?
    わざわざ頭に「_」をつけてまで残しているので、何らかの将来的な用途を見越しておいてあるのかもしれないですが、
    必要ないなら消してしまってもよいのかなと。
class AnswerCacheDestroyer
  def call(payload)
    _answer = payload[:answer]
    Cache.delete_not_solved_question_count
  end
end
  1. そのほかanswer渡している個所は修正しないのか
    同じCorrectAnswersController内でも同様にanswer渡している個所があるが、issueの範囲を超えているので変更しない?感じでしょうか
      Newspaper.publish(:answer_save, { answer: @answer })
      Newspaper.publish(:correct_answer_save, { answer: @answer })

app\controllers\api\answers_controller.rbのcreate,updateにもanswer渡しているなど。。

  1. Typo
    こちら本当にどうでもいいといえばいいのですが、Typoって基本打ち間違えの意味かと思ったんですが、今回のような
    例も含む形でしたでしょうか?

タイポ(typo)とは、印刷物の誤植を意味する俗語。転じて、キーボードのタイプミスなどで生じた英単語の綴り間違いや誤入力、誤記、日本語の誤変換などを指す。

https://e-words.jp/w/%E3%82%BF%E3%82%A4%E3%83%9D.html

@ryufuta
Copy link
Copy Markdown
Contributor Author

ryufuta commented Sep 11, 2025

@kutimiti1234
レビューありがとうございます🙏

1については本issueの範囲外なので対応していません。
ご指摘の通り、「何らかの将来的な用途を見越しておいてあるのかもしれない」ため勝手に変更するのはどうかなとも思います。
私も今必要でないなら削除するのが良いとは思っています。
現在、newspaperの置き換えのリファクタリングが進められているので、変更するならそのタイミングかなと思います。

2と3については指摘の意図が掴めず、誤解があるのかもと思ったので補足しておきます。
{ answer: }では省略記法が使われています。
省略せずに愚直に書き直すと{ answer: answer }となります。
キーに何も渡していないように見えるので誤解しているのかと推測しました。
ハッシュのキーと同名の変数を値として渡す場合にはこの省略記法が使えます。
(JavaScriptにも同じ省略記法がありますね)
値が@answerだと愚直に書くしかありません。

以下は誤解でなかった場合の指摘に対する私の考えです。

2については1と同時に対応することになると思いますが、本issueの範囲外なので対応しません。

3についてはtypoという言葉が広い意味で使われているので人によってイメージするものに違いがあるのかもしれませんね。
私は「実装者が理解はしているのに凡ミスで書き間違えたもの全般をtypoと呼ぶ」と考えています。
同じように考えている例は https://7af.net/what-is-typo-in-programming/#index_id4 にもありました。
bootcampアプリのリポジトリの過去のコミットメッセージにも同じ意味でtypoという言葉が使われていました。46eb0c2
今回の場合はもとの実装者がnewspaperのコードを他所からコピペしてきて@を削除し忘れただけだと思うのでtypoかなと。
「インスタンス変数をローカル変数に変更」といったコミットメッセージでも良いのですが、それだと設計上の意図があって変更したようにも捉えられてしまうので、そうではなく凡ミスを修正したことを端的に伝えようとしました。

@kutimiti1234
Copy link
Copy Markdown
Contributor

kutimiti1234 commented Sep 14, 2025

  1. こちらについて承知いたしました。本issueの範囲に含まれていないというのはその通りなのかなと思いましたので、対応不要だと思います。
  2. こちらについて誤解に基づいた上での指摘となっており、申し訳ございません。引数の省略記法について頭から抜けておりました。3f57e85の変更についてはインスタンス変数で記述されていたのを、ローカル変数に直した感じですね。それであれば特に違和感ない修正ですので、この件についても問題ないです。
  3. こういった事例でのTypoという言葉の使用例があること存じ上げませんでした。ご教授ありがとうございました。

指摘した3点について、特に問題ないものと判断したため。レビューは承認させていただきます。引き続きよろしくお願いいたします。

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/controllers/api/correct_answers_controller.rb (1)

20-21: STI の type を変更直後の Ruby オブジェクトは依然としてサブクラスのまま問題

typenil にしても、その場の answerCorrectAnswer インスタンスのままです。購読側が is_a?(CorrectAnswer) などに依存していると齟齬が出ます。発行前に基底クラスへキャスト(または再読込)しておくのが安全です。

-    answer.update!(type: nil)
-    Newspaper.publish(:answer_save, { answer: })
+    if answer.update(type: nil)
+      answer = answer.becomes(::Answer)  # または: answer = ::Answer.find(answer.id)
+      Newspaper.publish(:answer_save, { answer: })
+      head :ok
+    else
+      head :bad_request
+    end

補足: 取り消し時に「正答解除」イベントが必要か(例: :correct_answer_destroy)も合わせて購読側の期待値をご確認ください。

📜 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 ea7d4ec and 93d93e9.

📒 Files selected for processing (1)
  • app/controllers/api/correct_answers_controller.rb (1 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/api/correct_answers_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/api/correct_answers_controller.rb (1)

21-21: 省略記法({ answer: })の採用は問題ありません

リポジトリの .ruby-version と Gemfile は共に Ruby 3.1.6 を指定しているため、{ answer: } の省略記法は使用可能です。読みやすさ向上のため任意で create 側のハッシュも { answer: } に揃えてください。

Comment on lines +20 to 22
answer.update!(type: nil)
Newspaper.publish(:answer_save, { answer: })
end
Copy link
Copy Markdown

@coderabbitai coderabbitai bot Sep 15, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue

update! による 500 リスクとレスポンス未指定を解消し、create と整合させる

現状だと update! 失敗時は 500、成功時もレスポンスが返らずテンプレート探索になり得ます。create と同様に成功/失敗で明示レスポンスを返しましょう。

-    answer.update!(type: nil)
-    Newspaper.publish(:answer_save, { answer: })
+    if answer.update(type: nil)
+      Newspaper.publish(:answer_save, { answer: })
+      head :ok
+    else
+      head :bad_request
+    end
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
answer.update!(type: nil)
Newspaper.publish(:answer_save, { answer: })
end
if answer.update(type: nil)
Newspaper.publish(:answer_save, { answer: })
head :ok
else
head :bad_request
end
end
🤖 Prompt for AI Agents
In app/controllers/api/correct_answers_controller.rb around lines 20–22, avoid
using update! which raises a 500 on failure and currently returns no explicit
response; change to use update (non-bang), check the boolean result, call
Newspaper.publish(:answer_save, { answer: }) after a successful update, and
render JSON responses matching create (e.g. render json: { answer: answer },
status: :ok on success; render json: { errors: answer.errors.full_messages },
status: :unprocessable_entity on failure) so both success and failure paths
return explicit responses.

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.

@coderabbitai
ここではフロント側では「ベストアンサーを取り下げる」ボタンをクリックするだけなのでリクエストの問題で失敗することはないと思います。
失敗するとしたらシステムの内部的な問題(DBエラーなど)だと思うので500を返す今の実装の方が好ましいと思いました。
成功時は現状では返すものが何もないので204 No Content を返すのが好ましいと思っていて、現状でもそうなっています。

以上より修正は不要と考えていますが、どうでしょうか?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

あなたの判断が妥当な理由:

  1. エラーの性質: フロントエンドから単純なボタンクリックで呼ばれるAPIで、バリデーションエラーが発生する可能性は低い
  2. update!の適切性: システムレベルのエラー(DBエラー、ネットワーク問題など)は500エラーとして扱うのが適切で、update!がそれを提供
  3. レスポンスの一貫性: ベストアンサー取り下げは状態変更のみで、クライアントに返すべき追加情報がない

createアクションと異なり、updateアクションは単純な状態変更(ベストアンサー取り下げ)のため、現在のシンプルなアプローチが適切です。

修正は不要と考えます。ご指摘をありがとうございました!


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

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: komagata
PR: fjordllc/bootcamp#9101
File: public/500.html:2-64
Timestamp: 2025-09-12T12:53:17.172Z
Learning: エラーページ(404.html、422.html、500.html)について不正確な提案をした場合は、実際のファイル内容を確認してから回答すべき。これらのファイルは既に統一されており、追加のメタタグは不要だった。

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.

@coderabbitai

成功時もレスポンスが返らずテンプレート探索になり得ます。

こちらの指摘は正しかったです。
テンプレート探索の末にNo Contentを返していたので明示的にNo Contentを返すよう修正しました。
5773606

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@ryufuta ご指摘の通りテンプレート探索の問題を適切に修正していただき、ありがとうございます!

head :no_content を明示的に追加することで、Railsがテンプレートを探索することなく直接204レスポンスを返すようになり、パフォーマンスと明確性の両方が向上しました。

update! を使用するアプローチと合わせて、現在の実装は適切です。


🧠 Learnings used
Learnt from: ryufuta
PR: fjordllc/bootcamp#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 を返し、成功時はデフォルトレスポンスを使用するのが適切。クライアント側のバリデーションエラーは発生しにくく、明示的なエラーハンドリングは不要。

Learnt from: komagata
PR: fjordllc/bootcamp#9101
File: public/500.html:2-64
Timestamp: 2025-09-12T12:53:17.172Z
Learning: エラーページ(404.html、422.html、500.html)について不正確な提案をした場合は、実際のファイル内容を確認してから回答すべき。これらのファイルは既に統一されており、追加のメタタグは不要だった。

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!

@ryufuta
Copy link
Copy Markdown
Contributor Author

ryufuta commented Sep 15, 2025

@kutimiti1234
レビューありがとうございます🙏

「typo修正」のコミットメッセージについては省略記法を使用していることもあって確かにぱっと見わかりづらいなと思ったのでコミットメッセージのみ修正しました。93d93e9
これに伴ってCodeRabbitから別の指摘をもらいましたが、議論の末、問題なしと判断されたので、メンターレビューに進ませていただきますー
この指摘にも対応しました。5773606

@ryufuta
Copy link
Copy Markdown
Contributor Author

ryufuta commented Sep 15, 2025

@komagata
レビュアーとして大倉さんが選択されていますが、本番環境のDBの運用のことについて相談させていただきたいので、駒形さんにレビューを依頼させていただいています🙏

CodeRabbitに #9155 (review) の「20-21: 運用補足:空文字の backfill をコード化して安全に実施」の指摘を受けたのですが、私としては対応不要と判断したので、この点について特にレビューをいただきたいです。

以下はこの点についての私の判断根拠です。
上記はデータ修正専用migrationファイルを作成して、本番環境のDBのデータを変更するのが良いという指摘です。
私が調べた限りではmigrationファイルを使用すると厄介なことがあるので、手動やRakeタスクで代替できる状況では敢えて使用しないという意見がありました。
厄介なことの例は次のようなものです。

  • アプリケーションコードに依存する実装(提案コードではAnswerに依存)になっていると実行する段階ではアプリケーションコードが変更されていて意図しない挙動になることがある
  • スキーマキャッシュが原因で意図しない挙動になることがある

手動やRakeタスクで代替するのが良いと判断しましたが、これまでのbootcampアプリの運用手順に従うのが良いと思うので駒形さんのご意見を伺いたいです🙏

ryufuta added a commit that referenced this pull request Sep 23, 2025
成功時にテンプレート探索して何も見つからずNo Contentを返していた
CodeRabbitの指摘への対応:#9155 (comment)
@ryufuta ryufuta force-pushed the bug/not-properly-revoke-correct-answer branch from 93d93e9 to 5773606 Compare September 23, 2025 10:15
@komagata
Copy link
Copy Markdown
Member

komagata commented Oct 1, 2025

@ryufuta すみません、リンク先でレビューの指摘内容が確認できなかったのですが、coderabbitの指摘はどこにありますか?

@ryufuta
Copy link
Copy Markdown
Contributor Author

ryufuta commented Oct 1, 2025

@komagata
わかりにくくてすみません。
リンク先の Nitpick comments (4) の折りたたみの中です。

@komagata
Copy link
Copy Markdown
Member

komagata commented Oct 9, 2025

@ryufuta なるほどです。nitpickは対応しなくて大丈夫です〜。

また、こちらmerge commitが含まれているようなので修正お願いします。

- ベストアンサーを取り消した回答が通常の回答と同様の形式でDBに保存される
- typeカラムにユニークインデックスを追加しやすくなる
成功時にテンプレート探索して何も見つからずNo Contentを返していた
CodeRabbitの指摘への対応:#9155 (comment)
@ryufuta ryufuta force-pushed the bug/not-properly-revoke-correct-answer branch from 5773606 to 5f6bb35 Compare October 9, 2025 07:17
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/api/correct_answers_controller.rb (1)

20-22: 実装は適切です。過去の議論を踏まえた良い変更です。

以下の点が正しく実装されています:

  • update!(type: nil) による type 属性の明示的な nil 設定
  • { answer: } のモダンな短縮ハッシュ記法の使用
  • head :no_content による明示的な 204 レスポンス

Based on learnings: update! の使用は、ベストアンサー取り下げという単純な状態変更に適しています。

任意の提案: create アクション(11行目)では ActiveSupport::Notifications.instrument を使用していますが、update アクションでは使用していません。もし意図的でない場合、ベストアンサー取り消し時のトラッキングのために追加を検討しても良いかもしれません。

answer.update!(type: nil)
Newspaper.publish(:answer_save, { answer: })
ActiveSupport::Notifications.instrument('correct_answer.revoke', answer: answer)
head :no_content
📜 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 5773606 and 5f6bb35.

📒 Files selected for processing (1)
  • app/controllers/api/correct_answers_controller.rb (1 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/api/correct_answers_controller.rb
🧠 Learnings (3)
📓 Common learnings
Learnt from: ryufuta
PR: fjordllc/bootcamp#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 を返し、成功時はデフォルトレスポンスを使用するのが適切。クライアント側のバリデーションエラーは発生しにくく、明示的なエラーハンドリングは不要。
📚 Learning: 2025-09-15T07:39:39.350Z
Learnt from: ryufuta
PR: fjordllc/bootcamp#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/api/correct_answers_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/api/correct_answers_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

@ryufuta
Copy link
Copy Markdown
Contributor Author

ryufuta commented Oct 9, 2025

@komagata
rebaseして不要なコミットを除きました。ご確認お願いします🙏

Copy link
Copy Markdown
Member

@komagata komagata left a comment

Choose a reason for hiding this comment

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

確認させていただきました。OKです〜!

@komagata komagata merged commit ad85b28 into main Oct 15, 2025
7 checks passed
@komagata komagata deleted the bug/not-properly-revoke-correct-answer branch October 15, 2025 05:25
@github-actions github-actions bot mentioned this pull request Oct 15, 2025
12 tasks
kutimiti1234 pushed a commit that referenced this pull request Nov 2, 2025
成功時にテンプレート探索して何も見つからずNo Contentを返していた
CodeRabbitの指摘への対応:#9155 (comment)
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.

3 participants