Skip to content

定期イベント主催者ユーザーが退会/休会/研修終了した際の処理の見直し#9732

Merged
komagata merged 13 commits intomainfrom
feature/close-regular-event-when-no-organizers
Apr 8, 2026
Merged

定期イベント主催者ユーザーが退会/休会/研修終了した際の処理の見直し#9732
komagata merged 13 commits intomainfrom
feature/close-regular-event-when-no-organizers

Conversation

@yokomaru
Copy link
Copy Markdown
Contributor

@yokomaru yokomaru commented Mar 4, 2026

Issue

概要

  • 退会/休会/研修終了処理時の定期イベント関連の処理を変更
  • 退会/休会/研修終了画面で定期イベント主催者へのワーニング表示に「自分一人しか主催者がいない開催中イベントは終了になる」旨の記載を追加
    • 既存の細かい文言のズレなども修正
    • 以下もリファクタリングも併せて対応
      • ほぼ同じ文章が3箇所に記載されていたためラベル付きでパーシャルへ切り出し(Issueのコメント にて文言など確認済み)
      • viewでクエリを発行していた箇所をcontrollerへ移動
  • イベント作成時の既存バグを修正
    • イベント作成処理後のイベント作成者を主催者に登録する処理をcreateからfind_or_create_byに変更した
      • イベント登録画面にて、自分を主催者を自分1人してイベント作成すると主催者重複エラーとなる
      • 別途Issueは立てているので仕様の見直しや根本的な修正はそちらで行う
      • ローカルで都度エラーになるのは開発体験が悪いためこのPRで併せて対応する

変更確認方法

  1. feature/close-regular-event-when-no-organizersをローカルに取り込む
  2. bin/devでサーバーを起動
  3. 退会時のイベント離脱処理を確認(退会で詳細を確認し、休会・研修終了はざっと確認する)

事前準備

手順
  • hajime退会画面 へ遷移
  • 主催中イベント一覧の注意メッセージに以下が表示されている
    • 「退会後、ご自身のみが主催者である開催中のイベントは自動的に終了となります。」の一文が表示されていること
    • 事前準備で用意した開催中かつ主催者が1人のイベント が表示されていること
    • 開催中かつ主催者が複数人のイベント(チェリー本輪読会 )が表示されていること
  • 必須項目を入力して退会処理を実行
退会後の確認(komagata)
  • komagataでログイン
  • 以下を確認
    • 主催イベント
      • 開催中かつ主催者が1人のイベント
        • イベントが終了していること
      • 開催中かつ主催者が複数人のイベント(チェリー本輪読会 )
        • hajime が主催者から抜けていること
      • 終了済みイベント( 終了イベント)
        • 主催者はhajimeのままになっている
    • 参加イベント
  1. 休会

手順

  • hatsunoなど適当なアカウントでログイン
  • 主催者が1人の開催中イベントを作成
  • 休会画面 へ遷移
  • 「休会後、ご自身のみが主催者である開催中のイベントは自動的に終了となります。」が表示されている
  • 休会画面に主催中イベントが表示されている
  • 休会処理を実行

確認(komagata

  • komagataでログイン
  • イベントが終了になっていることを確認
  1. 研修終了

手順

  • kensyu(研修生)でログイン
  • 主催者が1人の開催中イベントを用意
  • komagataでログインし、適当なイベントにkensyuを主催に追加する
  • 研修終了 へ遷移
  • 研修終了画面に主催中イベントが表示されている
    • 自分が作成したものと、komagataで追加したイベントが両方表示されていること
  • 「研修終了後、ご自身のみが主催者である開催中のイベントは自動的に終了となります。」が表示されている
  • 研修終了処理を実行

確認(komagata

  • komagataでログイン
  • 自分が作成したイベント
    • イベントが終了になっていることを確認
  • komagataで追加したイベントが両方表示されていること
    • kensyuが主催から外れていることを確認

Screenshot

各動線の主催イベント警告文

アクション 変更前 変更後
退会 スクリーンショット 2026-03-05 22 14 33 スクリーンショット 2026-03-05 18 27 28
休会 スクリーンショット 2026-03-05 22 14 24 スクリーンショット 2026-03-05 18 27 37
研修終了 スクリーンショット 2026-03-05 22 14 33 スクリーンショット 2026-03-05 18 29 27

Summary by CodeRabbit

リリースノート

  • リファクタリング
    • 定期イベントのクリーンアップ処理を統合し、オーガナイザー管理の効率を向上
    • 休止・退職・研修終了フローの表示を統一し、ユーザーへの警告メッセージを一貫性のあるデザインで表示
    • 複数箇所に散在していた定期イベント警告表示を共有コンポーネント化

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 4, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

定期イベントの後始末ロジックを整理し、cancel_participation_from_regular_eventsdelete_and_assign_new_organizerを統合したclean_up_regular_eventsメソッドで一元化。複数のコントローラーと関連モデルを更新し、自動再割り当て動作を削除。共有ビューパーシャルと対応テストも追加。

Changes

Cohort / File(s) Summary
Controllers
app/controllers/hibernation_controller.rb, app/controllers/retirement_controller.rb, app/controllers/training_completion_controller.rb, app/controllers/regular_events_controller.rb
各コントローラーの新規・作成アクションで@holding_regular_eventsを初期化し、ユーザー固有の呼び出しをcurrent_user.clean_up_regular_eventsに統合。regular_events_controllerではOrganizer.createregular_event_organizers.find_or_create_byに変更。
Models - User & Cleanup
app/models/user.rb, app/models/retirement.rb
cancel_participation_from_regular_eventsdelete_and_assign_new_organizerをUser から削除し、新しいclean_up_regular_eventsメソッドで統合。Retirement でも同様に2つのメソッドを削除し、新しいclean_up_regular_events委譲メソッドを追加。
Models - Regular Events
app/models/regular_event.rb, app/models/organizer.rb, app/models/regular_event_participation.rb
assign_admin_as_organizer_if_noneを削除、close_or_destroy_organizerメソッドを追加。Organizer からdelete_and_assign_newを削除。RegularEventParticipationfor_holding_eventsスコープを追加。
Views
app/views/hibernation/new.html.slim, app/views/retirement/new.html.slim, app/views/training_completion/new.html.slim, app/views/shared/_regular_events_warning.html.slim
各ビューで定期イベント警告を新しい共有パーシャルshared/regular_events_warningで統一。インライン警告ブロックを削除し、パーシャルへの委譲に変更。
Tests
test/models/organizer_test.rb, test/models/regular_event_test.rb, test/models/retirement_test.rb, test/models/user_test.rb, test/system/retirements/data_deletion_test.rb
削除されたメソッドのテストを削除、新しいclose_or_destroy_organizerテストを追加。cancel_participation_from_regular_eventsdelete_and_assign_new_organizerのテストをclean_up_regular_eventsテストに改名・再構成。
Fixtures
test/fixtures/regular_event_participations.yml
末尾の空白行を削除。

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~35 minutes

Possibly related issues

Possibly related PRs

Suggested reviewers

  • ryufuta
  • komagata

Poem

🐇 ✨ ほんの一つにまとめたら
クリーンアップの流れも澄み渡る
古い再割り当ては卒業して
新しいclose_or_destroyへ🎉
定期イベントも後始末も
スッキリ統一で幸せだ〜!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ 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%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed タイトルはPRの主要な変更内容(定期イベント主催者の退会/休会/研修終了時の処理見直し)を明確かつ簡潔に表現しており、変更セット全体とよく対応している。
Description check ✅ Passed 説明は必須セクション(Issue、概要、変更確認方法、Screenshot)をすべて含み、詳細な確認手順と期待される振る舞いが記載されている。

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/close-regular-event-when-no-organizers

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.

@yokomaru yokomaru force-pushed the feature/close-regular-event-when-no-organizers branch from c16e368 to f3492c9 Compare March 4, 2026 18:59
@yokomaru yokomaru self-assigned this Mar 5, 2026
@yokomaru yokomaru force-pushed the feature/close-regular-event-when-no-organizers branch 2 times, most recently from fec1e89 to cc8f8cb Compare March 5, 2026 02:15
@yokomaru yokomaru force-pushed the feature/close-regular-event-when-no-organizers branch 4 times, most recently from b0eb509 to ff11d9b Compare March 5, 2026 12:02
end

def set_holding_regular_events
@holding_regular_events = RegularEvent.organizer_event(current_user).holding
Copy link
Copy Markdown
Contributor Author

@yokomaru yokomaru Mar 5, 2026

Choose a reason for hiding this comment

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

元々slim側でクエリを発行していたロジックをそのままcontollerに移動しています。
ユーザーに紐づく開催中のイベントは、リレーションを使ってcurrent_user.organize_regular_events.holdingと取得できるのでorganizer_event(user)スコープは本来不要なのですが、他にも使っている箇所があったのでこちらがマージされた後別のPRで修正・削除対応しようと思っております。

- holding_regular_events = RegularEvent.organizer_event(current_user).holding
    - if holding_regular_events.any?

end

def set_holding_regular_events
@holding_regular_events = RegularEvent.organizer_event(current_user).holding
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.

end

def set_holding_regular_events
@holding_regular_events = RegularEvent.organizer_event(current_user).holding
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.

belongs_to :user
has_many :organizers, dependent: :destroy
# TODO: テーブル名を変更したら修正する
has_many :regular_event_organizers, class_name: 'Organizer', dependent: :destroy
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.

#9640 (comment) にあるように、一時的に変更した関連を使用して修正しています。テーブル名変更時に修正いたします。


class HibernationController < ApplicationController
skip_before_action :require_active_user_login, raise: false, only: %i[show]
before_action :set_holding_regular_events, only: %i[new create]
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.

@holding_regular_eventsはバリデーションエラー時にもセットしたいので、before_actionを使っています。(他のcontrollerも同様です)

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]
#set_holding_regular_events を実行するのが new と create のみなら、アクションに直接書いてもいいんじゃないのかな〜と思いました!(個人の感想です)
本当に個人の思想なのですが、基本的に Controller に限らずコールバックは避けたいです。
いつどのタイミングでどの処理が呼ばれるのか把握しづらいというのが一番の理由です。全体を把握するために視線の移動が発生するのはなるべく避けたい気持ちがあります。

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.

ありがとうございます!
なるべくDRYにした方が良いのかな?と考えていたのですが、
使いたいactionが2つであることと、全体の把握しづらさという点で納得したので
コールバックをやめて直接定義するように修正いたしました!

9484c07

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.

@yokomaru
DRY はいいよね〜
でも DRY に引っ張られて可読性が落ちると本末転倒だったりしちゃうね。
特にコールバックはね、取扱注意だからね、「ここぞ!」というときに使っていきたいね!

@yokomaru yokomaru force-pushed the feature/close-regular-event-when-no-organizers branch from ff11d9b to ce15c91 Compare March 5, 2026 12:28
if @regular_event.save
update_published_at
Organizer.create(user_id: current_user.id, regular_event_id: @regular_event.id)
@regular_event.regular_event_organizers.find_or_create_by(user: current_user)
Copy link
Copy Markdown
Contributor Author

@yokomaru yokomaru Mar 5, 2026

Choose a reason for hiding this comment

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

  • イベント登録画面にて、主催者を自分1人にしてイベント作成を行うと、毎回バリデーションエラー(主催者重複違反)となり検証や調査の妨げになるため、すでに登録済みの場合は作成処理を行わないようにfind_or_create_byを使って修正しました
  • 本来であればエラーハンドリングなども含めて処理の見直しが必要だと思われるので、別途立てたIssueで検討になると思います
  • 併せてよりRailsっぽい書き方にするために、外部キー指定ではなく、@regular_eventを起点にしてオブジェクト指定で更新するように修正しています

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.

[LGTM]
#find_or_create_by にしたのはよいですね!
オブジェクトを渡すのも Rails っぽくてよいですね〜

= form_with model: current_user, local: true, url: training_completion_path, method: :post, class: 'form' do |f|
.form__items
- if current_user.regular_events.any?
- if @holding_regular_events.any?
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.

current_user.regular_eventsだと主催イベントではなく作成したイベントのみの取得となっていたので修正しています。

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.

[LGTM]
オォン
こういうところ他にもありそうですね〜

@yokomaru yokomaru marked this pull request as ready for review March 5, 2026 13:20
@github-actions github-actions Bot requested a review from okuramasafumi March 5, 2026 13:21
@yokomaru yokomaru requested review from ryufuta and torinoko March 5, 2026 13:22
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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/controllers/regular_events_controller.rb`:
- Line 32: `@regular_event.regular_event_organizers.find_or_create_by`(user:
current_user) の戻り値を検証しておらず、unsaved record のまま後続処理を進めてしまう問題があるため、結果を変数(例:
organizer)に代入し organizer.persisted? をチェックして保存に失敗している場合は organizer.save
を試みるかエラーハンドリング(ログ出力と処理中断/レスポンス返却)を行ってください。対象は controller 内の
`@regular_event.regular_event_organizers.find_or_create_by`(user: current_user)
を置き換える箇所で、保存失敗時の分岐を追加して通知や後続処理を行わないようにしてください。

In `@app/models/regular_event.rb`:
- Around line 155-162: The current close_or_destroy_organizer has a race (count
then delete) and can leave an event with zero organizers; fix by performing the
check-and-delete inside a DB-backed lock/transaction to make it atomic: wrap
logic in self.with_lock (or transaction + lock on regular_event_organizers) then
re-check regular_event_organizers.reload.lock.count (or
regular_event_organizers.lock.count) and if count == 1 call update(finished:
true) else locate organizer = regular_event_organizers.find_by(user:) and only
call organizer.destroy if organizer.present? (do not raise if nil).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 77f6429b-06be-4a7f-accf-ea10b92cac8f

📥 Commits

Reviewing files that changed from the base of the PR and between 9f3db88 and ce15c91.

📒 Files selected for processing (26)
  • app/controllers/hibernation_controller.rb
  • app/controllers/regular_events_controller.rb
  • app/controllers/retirement_controller.rb
  • app/controllers/training_completion_controller.rb
  • app/models/organizer.rb
  • app/models/regular_event.rb
  • app/models/retirement.rb
  • app/models/user.rb
  • app/views/hibernation/new.html.slim
  • app/views/retirement/new.html.slim
  • app/views/shared/_regular_events_warning.html.slim
  • app/views/training_completion/new.html.slim
  • test/fixtures/discord_profiles.yml
  • test/fixtures/organizers.yml
  • test/fixtures/regular_event_participations.yml
  • test/fixtures/regular_event_repeat_rules.yml
  • test/fixtures/regular_events.yml
  • test/fixtures/talks.yml
  • test/fixtures/users.yml
  • test/models/organizer_test.rb
  • test/models/regular_event_test.rb
  • test/models/retirement_test.rb
  • test/models/user_test.rb
  • test/system/regular_events_test.rb
  • test/system/retirements/data_deletion_test.rb
  • test/system/user/courses_test.rb
💤 Files with no reviewable changes (2)
  • app/models/organizer.rb
  • test/models/organizer_test.rb

Comment thread app/controllers/regular_events_controller.rb
Comment thread app/models/regular_event.rb
@yokomaru yokomaru force-pushed the feature/close-regular-event-when-no-organizers branch from ce15c91 to fad4ea5 Compare March 6, 2026 04:24
Comment on lines +27 to +23
| 研修を修了される場合、イベントの進行に影響がないよう、任意で他の参加者に主催を引き継ぐことを推奨します。
| 退会手続きを完了する前に、
| 以下のリンク先でイベント設定変更を行なってください。
ul
- current_user.regular_events.each do |event|
li
= link_to edit_regular_event_path(event), target: '_blank', rel: 'noopener' do
| 定期イベント「#{event.title}」の設定変更
= render 'shared/regular_events_warning',
events: @holding_regular_events,
case_text: '研修を終了される',
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.

元の文章は研修を修了されるとなっていましたが、git grepしたところ基本的には研修に対しては終了を使っていたので文言を変更しています。

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.

[LGTM]
こういう用語や表現を揃える、とても大事! 👍

Comment thread app/models/user.rb Outdated
end

def clean_up_regular_events
regular_event_participations.joins(:regular_event).merge(RegularEvent.holding).destroy_all
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.

削除対象はregular_event_participationsなのですが、開催中イベントという条件を付けるためにjoins(:regular_event) して絞り込んでいます。

RegularEvent起点で削除するか悩んだのですが、最終的に削除するのはparticipationなので今回は regular_event_participations起点で実装しています。

もしより良い書き方があればご意見いただけると嬉しいです 🙇‍♀️

Copy link
Copy Markdown
Contributor

@torinoko torinoko Mar 10, 2026

Choose a reason for hiding this comment

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

[IMO]
コメント漏れしてた!
ユーザーのデータが変更されたことで発生するイベントなので、それでよいと思います。
あと「開催中のイベントの参加者」を取得することが目的であれば、それを RegularEventParticipation に任せちゃうという手もあるのかなと思いました(スコープ名は適当)
これは User から遠い情報をなるべく隠蔽したいという思惑です。
それは User の関心事じゃないからね。

# RegularEventParticipation
scope :currently, -> { joins(:regular_event).merge(RegularEvent.holding) }

そもそも「現在参加中のイベントだけ消す仕様ってどうなんですか?イベントの状態関係なく全部消したらだめなことって何かありますか?」という話もありますが……
そしたらシンプルな実装になるのにね……

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.

@yokomaru @torinoko
どこを起点にするかについては私の方でも後で検討しますが、取り急ぎ仕様について私の意見を書きます。

参加者から離脱するのを開催中のイベントに限定するという現状の仕様は良いと思います。
そもそも非アクティブなユーザーを定期イベントから離脱させたい理由は次のようなことかなと推測しています。

  • 既に活動していないユーザーがまだ活動しているかのように見えると紛らわしい
  • 退会の場合はネガティブな理由もあるので運営としても退会ユーザー自身としても見えやすいところに表示して欲しくない

終了した定期イベントであれば上記の懸念は該当しないので離脱させなくても良さそうです。
逆に終了した定期イベントからも離脱させるのは次の理由で避けた方が良いかなと思います。

  • 主催者からの離脱は開催中に限定することと一貫性がなく仕様がわかりにくい
  • 過去の参加履歴を削除するのはユーザーにとって都合が悪い場合がある
    • コミュニティでどういう活動をしていたかは他者からの評価につながるので残す方が良い(プロフィールから定期イベントの参加情報を確認できる機能を現在実装中)
    • 休会から復帰したときに終了済みイベントに参加していなかったことになるのは不自然
    • いなかったことになるのはかわいそうかも😢

あくまで私の意見なので気になるようならプロダクトオーナーと相談する方が良いとは思います。
(既に相談の上で今の仕様になったのかなと思いますが)

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.

@ryufuta
ありがとうございます!
開催中のイベントに限定する仕様はチーム開発MTG時に確認して決定しました!(参加の履歴のためなど概ねryufutaさんが書いてくださったことが理由となります!)
#9258 (comment)

@torinoko
上記仕様については問題ないでしょうか?🧐

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.

@yokomaru
そうですね、 @ryufuta の書いているとおり、終わっているものについては残す方がよいですね。
ミーティングでも方針が決まっているのであれば問題ないです!

@yokomaru
Copy link
Copy Markdown
Contributor Author

yokomaru commented Mar 6, 2026

@ryufuta (CC: @torinoko )

お疲れ様です!
こちらのPR、お手隙でレビューをお願いできますでしょうか。急ぎではありません。
どうぞよろしくお願いいたします。

Copy link
Copy Markdown
Contributor

@torinoko torinoko left a comment

Choose a reason for hiding this comment

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

@yokomaru
コメントしました!


class HibernationController < ApplicationController
skip_before_action :require_active_user_login, raise: false, only: %i[show]
before_action :set_holding_regular_events, only: %i[new create]
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]
#set_holding_regular_events を実行するのが new と create のみなら、アクションに直接書いてもいいんじゃないのかな〜と思いました!(個人の感想です)
本当に個人の思想なのですが、基本的に Controller に限らずコールバックは避けたいです。
いつどのタイミングでどの処理が呼ばれるのか把握しづらいというのが一番の理由です。全体を把握するために視線の移動が発生するのはなるべく避けたい気持ちがあります。

if @regular_event.save
update_published_at
Organizer.create(user_id: current_user.id, regular_event_id: @regular_event.id)
@regular_event.regular_event_organizers.find_or_create_by(user: current_user)
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.

[LGTM]
#find_or_create_by にしたのはよいですね!
オブジェクトを渡すのも Rails っぽくてよいですね〜

= form_with model: current_user, local: true, url: training_completion_path, method: :post, class: 'form' do |f|
.form__items
- if current_user.regular_events.any?
- if @holding_regular_events.any?
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.

[LGTM]
オォン
こういうところ他にもありそうですね〜

Comment on lines +27 to +23
| 研修を修了される場合、イベントの進行に影響がないよう、任意で他の参加者に主催を引き継ぐことを推奨します。
| 退会手続きを完了する前に、
| 以下のリンク先でイベント設定変更を行なってください。
ul
- current_user.regular_events.each do |event|
li
= link_to edit_regular_event_path(event), target: '_blank', rel: 'noopener' do
| 定期イベント「#{event.title}」の設定変更
= render 'shared/regular_events_warning',
events: @holding_regular_events,
case_text: '研修を終了される',
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.

[LGTM]
こういう用語や表現を揃える、とても大事! 👍

Comment thread test/fixtures/discord_profiles.yml Outdated
discord_profile_regular_event_organizer_user2:
user: regular_event_organizer_user2
account_name:
times_url: No newline at end of file
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.

[MUST]
最後に改行コード入れてほしい!

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.

コメントありがとうございます!talkの方もなかったので合わせて対応いたしました。

21aca3f

Comment on lines +29 to +39
regular_event_participation8:
user: regular_event_organizer_user1
regular_event: regular_event42

regular_event_participation9:
user: regular_event_organizer_user1
regular_event: regular_event43

regular_event_participation10:
user: regular_event_organizer_user1
regular_event: regular_event44
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.

[NITS]
今回の RegularEvent 周り fixture はテストのためにいろいろ条件を揃えるため作成されている感じですよね。
であれば regular_event_participation8 のような連番ではなく、目的を表すラベルにした方がよいのかなと思いました。それだと他の fixture との整合性が取れないにゃ〜とかはあるかなとも思いますが、この慣習がそもそもよいとは思えないので。

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.

コメントありがとうございます!

目的を表すラベルにした方がよい

こちら、自分も納得したのでできれば対応したいのですが、
どう命名したら管理しやすいのかわからなくなってしまったので、アドバイスいただけると幸いです!
以下のルールで検討しています。

  • (これは不要かもですが)何のデータかわかるようにprefixをつける
  • 中間テーブルなどは関連の命名に合わせる
  • 目的が同じだけど複数必要だから作ったデータは連番をつける

特に中間テーブルやテスト自体に必要はないけどデータを作るために必要なデータ(userでいうtalkやdiscord-profile、regular_eventでいうとregular_event_repeat_rules)の命名などが難しい&&長くなりがちで逆に読みづらくなっていないだろうか?と不安になりました。

管理のための良い命名アイデアがあればご意見いただきたいです🙏

  • test/fixtures/users.yml
user_regular_event_organizer_1:
user_regular_event_organizer_2:
  • test/fixtures/talks.yml
talk_regular_event_organizer_user_1:
  user: regular_event_organizer_user_1

talk_regular_event_organizer_user_2:
  user: regular_event_organizer_user_2
  • test/fixtures/discord_profiles.yml
discord_profile_regular_event_organizer_user_1:
  user: regular_event_organizer_user_1

discord_profile_regular_event_organizer_user_2:
  user: regular_event_organizer_user_2
  • test/fixtures/regular_events.yml
regular_event_holding_and_one_organizer:
  title: 開催中で主催者が1人の定期イベント
  user: regular_event_organizer_user_1

regular_event_holding_and_multiple_organizers:
  title: 開催中で主催者が複数人の定期イベント
  user: regular_event_organizer_user_1

regular_event_finished_and_one_organizer:
  title: 終了済みで主催者が1人の定期イベント
  user: regular_event_organizer_user_1
  • test/fixtures/regular_event_repeat_rules.yml
repeat_rule_holding_and_one_organizer:
  regular_event: holding_and_one_organizer

repeat_rule_holding_and_multiple_organizers:
  regular_event: holding_and_multiple_organizers

repeat_rule_finished_and_one_organizer:
  regular_event: finished_and_one_organizer
  • test/fixtures/regular_event_participations.yml
participation_holding_and_one_organizer:
  user: regular_event_organizer_user_1
  regular_event: holding_and_one_organizer

participation_holding_and_multiple_organizers:
  user: regular_event_organizer_user_1
  regular_event: holding_and_multiple_organizers

participation_finished_and_one_organizer:
  user: regular_event_organizer_user_1
  regular_event: finished_and_one_organizer
  • test/fixtures/organizers.yml
organizer_holding_and_one_organizer:
  user: regular_event_organizer_user_1
  regular_event: holding_and_one_organizer

organizer_holding_and_multiple_organizer_1:
  user: regular_event_organizer_user_1
  regular_event: holding_and_multiple_organizers

organizer_holding_and_multiple_organizers_2:
  user: regular_event_organizer_user_2
  regular_event: holding_and_multiple_organizers

organizer_finished_and_one_organizer:
  user: regular_event_organizer_user_1
  regular_event: finished_and_one_organizer

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.

以下、個人の見解です〜

  • test/fixtures/users.yml

    • user_regular_event_organizer_1 でよさそう
    • あとは regular_event_organize_user1 とか?
      • user を suffix にすることで用途を先に表す形になる
      • と思ったけどアソシエーション見たら元々 suffix にする案だったのかな?
  • test/fixtures/talks.yml

    • ちょっと把握しきれていないのだけど、これって必要なんですか?

ただこれ実はテストケースの分だけ fixture を用意するべきなのか?というお話もあるんですよね。
RSpec だったらやらないかな〜。
Minitest はあまり書いたことないのでわからないけど、たぶん普通だったらここまで fixture が膨らむことはないはず。テストの分だけ fixture を定義するというのがそもそも健全ではないんですよね。たぶんね。

と、思ってコード見直してて今気づいたんですけど、これ RegularEvent 以外のテストで行うことはちょっと違うかも。
RegularEvent の主催者がひとりかどうかをチェックして管理者削除なりイベント終了なり行っています。なのでそこの責務は RegularEvent が負っているので RegularEvent 側でテストする、User 側でテストするとしたら RegularEvent のイベントがちゃんと呼ばれていることな気がする。
Minitest::Mock とか使うのかな。

以下テストコードの方でもコメントしますね。

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.

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

29-29: 任意: 重複クエリの抽出を検討

Line 10 と Line 29 で RegularEvent.organizer_event(current_user).holding が重複しています。コミットメッセージにて「アクション内に直接書いた方が処理追跡がしやすい」という意図を理解していますが、before_action を使わずにプライベートメソッドへの抽出という選択肢もあります。

♻️ 提案(任意)
  def new
-   `@holding_regular_events` = RegularEvent.organizer_event(current_user).holding
+   `@holding_regular_events` = holding_regular_events_for_current_user
  end

  # ... in create failure path ...
-     `@holding_regular_events` = RegularEvent.organizer_event(current_user).holding
+     `@holding_regular_events` = holding_regular_events_for_current_user

  private

+ def holding_regular_events_for_current_user
+   RegularEvent.organizer_event(current_user).holding
+ end

現状でも可読性は十分なので、このまま進めても問題ありません。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/controllers/training_completion_controller.rb` at line 29, Duplicate
query RegularEvent.organizer_event(current_user).holding is used twice
(assigning `@holding_regular_events`); extract it into a single private helper
method (e.g., holding_regular_events_for(user)) and call that method from both
places instead of repeating the query, updating assignments to use
holding_regular_events_for(current_user) and leaving behavior unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@app/controllers/training_completion_controller.rb`:
- Line 29: Duplicate query RegularEvent.organizer_event(current_user).holding is
used twice (assigning `@holding_regular_events`); extract it into a single private
helper method (e.g., holding_regular_events_for(user)) and call that method from
both places instead of repeating the query, updating assignments to use
holding_regular_events_for(current_user) and leaving behavior unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: cc0ce7cb-23bd-44d7-83df-f6d2392f3879

📥 Commits

Reviewing files that changed from the base of the PR and between fad4ea5 and 9484c07.

📒 Files selected for processing (5)
  • app/controllers/hibernation_controller.rb
  • app/controllers/retirement_controller.rb
  • app/controllers/training_completion_controller.rb
  • test/fixtures/discord_profiles.yml
  • test/fixtures/talks.yml
🚧 Files skipped from review as they are similar to previous changes (3)
  • app/controllers/retirement_controller.rb
  • test/fixtures/discord_profiles.yml
  • app/controllers/hibernation_controller.rb

@yokomaru yokomaru force-pushed the feature/close-regular-event-when-no-organizers branch from 9484c07 to c4b7111 Compare March 10, 2026 11:34
- 参加・主催の離脱処理は現状どの動線からも必ず2つ一緒に呼ばれているため、`clean_up_regular_events`としてまとめた
- 参加・主催ともに離脱するのは開催中(holding)のイベントのみとした
- 離脱時に主催が1人の場合はkomagataに付け替えるではなくイベントを終了するように変更した
- 主催者が1人のイベントは自動的に終了となる旨を追記した
- 退会/休会/研修終了動線でほぼ同じ文言なためパーシャルへ切り出した
- slimファイル内で開催中のイベントを取得していたため、controllerで取得するように修正した
- 研修終了動線のバグと文言修正
- ローカル環境にて主催者にイベント作成者であるユーザーを登録してイベント作成をすると500エラーになり検証や調査の妨げになるため、すでに作成中の場合は作成処理を行わないように修正
- 併せてよりRailsっぽい書き方にするために、外部キー指定ではなく関連を起点にオブジェクト指定で更新するように修正した
- 実行するアクションがnewとcreate のみなのでアクションに直接書いた方が処理が追いやすいため
- Fixturesを新たに作成せず、テスト内で明示的にデータを作成してその結果を確認するようにした
- UserではなくParticipationの関心事として開催中のイベントに紐づく参加情報を取得できるようにした
@yokomaru yokomaru force-pushed the feature/close-regular-event-when-no-organizers branch from c0f92ca to 3cbee86 Compare March 18, 2026 06:24
@yokomaru yokomaru force-pushed the feature/close-regular-event-when-no-organizers branch from 3cbee86 to d5eed00 Compare March 18, 2026 07:06
@yokomaru
Copy link
Copy Markdown
Contributor Author

@ryufuta

RegularEvent起点での検討、ありがとうございます!
クラスを跨った処理をおく場所、本当に悩ましいですね・・!
退会/休会/研修終了を整理して定期イベントの後始末処理を見直す
で書いていただいたように
最終的には別のモジュールにするのが綺麗な気がしつつ、そこまでやるのか的な部分もあると思うのでどこに責務を置くのか本当に難しいなと改めて思いました。

定数についてもありがとうございます!!こちら念の為grepして削除いたしました。
丁寧にご確認いただきありがとうございましたー!

@yokomaru
Copy link
Copy Markdown
Contributor Author

@okuramasafumi
お疲れ様です。
チームレビューが終わったので、メンターレビューをお願いいたします!🙏

wants_announcement? && !wip?
end

def close_or_destroy_organizer(user)
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.

実装は問題ないと思うのですが、ここの意図としては、

  • 休会や退会にともなって、定期イベントから主催者を削除したい
  • しかし、主催者不在の定期イベントは存在できない
  • 従って、削除される主催者が唯一の主催者である場合、定期イベントを終了状態にする

という感じだと思います。このうち、「主催者不在の定期イベントは存在できない」要件は今回に限った話ではなさそうであり、モデルのバリデーションの一部になるのが本来の姿のように思います。
そうすると、主催者の削除と、イベントの状態確認・終了状態への遷移は異なるレイヤーの出来事として扱いたくなります。主催者を削除した結果、結果的に定期イベントが不正な状態になり、終了状態になる、という流れです。
しかし、不正な状態(バリデーションが通らない状態)なら自動的に終了状態になる、となると影響範囲が大きく、望んでいない挙動になってしまうでしょう。
なので、メソッドとしては、

# これは擬似コードです
event.remove_organizer
if event.invalid? # 主催者がいない!
  event.finished!
end
event.save

のような感じにして、バリデーションとして、

# これは擬似コードです
validate do
  errors.add if organizers.size.zero? && active # activeというのは、終了状態ではない、くらいの意味
end

みたいにしてあげると綺麗かな、という感じです。

ここまでやると少し大掛かりなので、今現状では、メソッドの先頭にコメントを書いておくだけでもよいと思います。

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.

@okuramasafumi
流れ的にはそれが自然ではありますね〜。
今回何でこういう状態になっているかというと、元々の仕様がもっとややこしくて。
その仕様をかなり整理して、今の状態になっています。
なので上記変更は別タスクで対応させていただければと思うのですがいかがでしょうか 🙏

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.

@okuramasafumi
ご指摘ありがとうございます!

本来はバリデーションで担保すべきという点や主催者の削除とイベントの状態確認・終了状態への遷移を異なるレイヤーの出来事として扱うという考え方につきまして、とても勉強になりました。
この2つが分離できると責務も明確になり設計としてもスッキリするなと感じました!

今回は影響範囲も考慮し、まずはコメントで意図を補足する形にしようと思います。

残すべきコメントとしては、メソッドの意図(主催者が0人にならないようにするための処理)+TODOとして将来的にバリデーションで担保することを記載するイメージで問題ないでしょうか?

# 定期イベントは主催者が1人以上必要なため
# 主催者が1人しかいない場合はイベントを終了状態にし
# それ以外の場合は主催者のみを削除する
#
# TODO: 本来は「主催者が0人のイベントは無効」という制約をバリデーションで担保する形にしたい
# https://github.com/fjordllc/bootcamp/pull/9732#discussion_r2969273381
def close_or_destroy_organizer(user)

@torinoko
たださんもコメントいただきありがとうございます!Issue化もしておきます。

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.

@yokomaru コメントそんな感じでよいと思います!

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.

@okuramasafumi
ありがとうございます!
こちらコメント追加いたしました。

5113131

@yokomaru yokomaru dismissed stale reviews from ryufuta and torinoko via d5eed00 March 27, 2026 16:53
@torinoko torinoko requested a review from okuramasafumi March 30, 2026 03:28
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 1, 2026

🚀 Review App

URL: https://bootcamp-pr-9732-fvlfu45apq-an.a.run.app

Basic認証: fjord / (ステージングと同じ)
PR更新時に自動で再デプロイされます。

@yokomaru
Copy link
Copy Markdown
Contributor Author

yokomaru commented Apr 1, 2026

@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!

@yokomaru
Copy link
Copy Markdown
Contributor Author

yokomaru commented Apr 6, 2026

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

@komagata
メンターレビューが通ったのでマージお願いします。

@komagata komagata merged commit fc33c4f into main Apr 8, 2026
8 checks passed
@komagata komagata deleted the feature/close-regular-event-when-no-organizers branch April 8, 2026 02:04
@komagata
Copy link
Copy Markdown
Member

komagata commented Apr 8, 2026

@yokomaru (CC: @okuramasafumi ) マージしました〜

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.

5 participants