Skip to content

Auto-create Students and Coaches groups when a new chapter is created #2823

Description

@mroderick

Background

Groups were introduced in 2014 (commit e681a26) as a way to organise chapter membership into two categories: Students and Coaches. Every chapter has conventionally required exactly these two groups. Issue #510 (2016) validated this by restricting Group::NAMES to only %w[Coaches Students].

Problem

Creating a chapter via /admin/chapters/new does not automatically create the required groups. An admin must then manually visit /admin/groups/new twice — once for Students, once for Coaches — and pick the new chapter from a dropdown. If this step is missed:

  • Members visiting the chapter page see no subscription buttons (chapter/_subscriptions.html.haml iterates @chapter.groups)
  • Workshop invitations silently send to zero members (InvitationManager#chapter_students / #chapter_coaches query empty relations)
  • The admin chapter show page shows no group stats

This is a foot-gun. The chapter_with_groups fabricator already auto-creates groups in tests/seeds (db/seeds.rb uses it for every chapter), so the codebase already assumes this pattern.

Evidence

Production DB (codebar_production_dump):

  • 54 chapters
  • 108 groups
    → Exactly 2 groups per chapter, confirming the invariant holds in practice.

Proposed solution

A service object in app/services/chapter_creation_service.rb that wraps chapter + groups in a single transaction:

class ChapterCreationService
  Result = Struct.new(:chapter, :success, :errors, keyword_init: true)

  def self.call(params)
    chapter = Chapter.new(params)

    ActiveRecord::Base.transaction do
      chapter.save!
      chapter.groups.create!(name: 'Students')
      chapter.groups.create!(name: 'Coaches')
    end

    Result.new(chapter: chapter, success: true)
  rescue ActiveRecord::RecordInvalid => e
    Result.new(chapter: chapter, success: false, errors: e.message)
  end
end

Controller change: Admin::ChaptersController#create calls the service:

def create
  result = ChapterCreationService.call(chapter_params)
  @chapter = result.chapter
  authorize(@chapter)

  if result.success
    flash[:notice] = "Chapter #{@chapter.name} has been successfully created"
    redirect_to [:admin, @chapter]
  else
    flash[:notice] = @chapter.errors.full_messages
    render 'new'
  end
end

Why a service object:

  • Single SQL transaction — if groups fail, chapter is rolled back
  • Controller stays thin (coordinator, not business logic)
  • Testable in isolation with unit tests
  • Reusable if other entry points need chapter creation (seeds, API, console)
  • No hidden model callbacks — the side effect is explicit at the call site

Follow-up: remove manual group creation UI

Once this proposal is accepted, the groups new/create flow becomes dead code and can be removed as a follow-up PR:

What becomes unnecessary

  • app/views/admin/groups/new.html.haml — the form for manually creating a group and picking its chapter from a dropdown. This is the last place the admin/groups Chosen select exists.
  • Admin::GroupsController#new and #create — no admin should ever need to create a group by hand.
  • app/views/layouts/_admin_menu.html.haml:28 — the "New group" link in the admin menu.

What stays

  • Admin::GroupsController#show — viewing group members, eligible counts, and subscriptions is still useful.
  • app/models/group.rb and all its scopes — groups are still the core entity for subscriptions and invitations.
  • chapter_with_groups fabricator — can be folded into the base chapter fabricator, since every chapter now always has groups.

Why this matters for the Chosen cleanup

The admin/groups/new page contains a f.association :chapter that gets Chosen-enhanced by the global init. Removing this page eliminates one more <select> from the site and brings us closer to dropping Chosen entirely.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions