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
Background
Groups were introduced in 2014 (commit e681a26) as a way to organise chapter membership into two categories:
StudentsandCoaches. Every chapter has conventionally required exactly these two groups. Issue #510 (2016) validated this by restrictingGroup::NAMESto only%w[Coaches Students].Problem
Creating a chapter via
/admin/chapters/newdoes not automatically create the required groups. An admin must then manually visit/admin/groups/newtwice — once for Students, once for Coaches — and pick the new chapter from a dropdown. If this step is missed:chapter/_subscriptions.html.hamliterates@chapter.groups)InvitationManager#chapter_students/#chapter_coachesquery empty relations)This is a foot-gun. The
chapter_with_groupsfabricator already auto-creates groups in tests/seeds (db/seeds.rbuses it for every chapter), so the codebase already assumes this pattern.Evidence
Production DB (
codebar_production_dump):→ Exactly 2 groups per chapter, confirming the invariant holds in practice.
Proposed solution
A service object in
app/services/chapter_creation_service.rbthat wraps chapter + groups in a single transaction:Controller change:
Admin::ChaptersController#createcalls the service:Why a service object:
Follow-up: remove manual group creation UI
Once this proposal is accepted, the groups
new/createflow 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 theadmin/groupsChosen select exists.Admin::GroupsController#newand#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.rband all its scopes — groups are still the core entity for subscriptions and invitations.chapter_with_groupsfabricator — can be folded into the basechapterfabricator, since every chapter now always has groups.Why this matters for the Chosen cleanup
The
admin/groups/newpage contains af.association :chapterthat 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