Skip to content

Election voting#458

Open
leesheppard wants to merge 16 commits intomainfrom
election-voting
Open

Election voting#458
leesheppard wants to merge 16 commits intomainfrom
election-voting

Conversation

@leesheppard
Copy link
Copy Markdown
Member

To resolve issue #379

Continuing the work from @reentim

First iteration of an election system online

@leesheppard leesheppard requested a review from quintrino April 16, 2026 11:39
@leesheppard leesheppard self-assigned this Apr 16, 2026
Copilot AI review requested due to automatic review settings April 16, 2026 11:39
Comment thread spec/views/elections/index.html.erb_spec.rb Fixed
Comment thread app/controllers/ballots_controller.rb Fixed
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Implements a first-pass election voting system for ruby.org.au, including admin tooling to create elections and nominations.

Changes:

  • Adds core election domain models (Election, Nomination, Vote) and supporting DB migrations/schema updates.
  • Introduces public election pages (index/show) and a ballot submission endpoint.
  • Adds admin UI/controllers/views for managing elections and nominations, plus assorted dependency/Gemfile updates.

Reviewed changes

Copilot reviewed 52 out of 54 changed files in this pull request and generated 22 comments.

Show a summary per file
File Description
yarn.lock Updates frontend dependency lockfile versions.
spec/views/elections/show.html.erb_spec.rb Adds scaffold-generated view spec for elections show.
spec/views/elections/new.html.erb_spec.rb Adds scaffold-generated view spec for elections new.
spec/views/elections/index.html.erb_spec.rb Adds scaffold-generated view spec for elections index.
spec/views/elections/edit.html.erb_spec.rb Adds scaffold-generated view spec for elections edit.
spec/routing/elections_routing_spec.rb Adds routing spec for elections controller.
spec/requests/elections_spec.rb Adds scaffold-generated request spec for elections CRUD.
spec/requests/admin/elections_spec.rb Adds request spec coverage for admin election creation.
spec/models/vote_spec.rb Adds placeholder model spec for Vote.
spec/models/user_spec.rb Updates schema annotation for User.
spec/models/nomination_spec.rb Adds placeholder model spec for Nomination.
spec/models/election_spec.rb Adds model spec for Election#elected_users scoring behavior.
spec/helpers/elections_helper_spec.rb Adds placeholder helper spec for ElectionsHelper.
spec/factories/votes.rb Adds Vote factory and :for_nomination trait.
spec/factories/nominations.rb Adds Nomination factory.
spec/factories/elections.rb Adds Election factory with open/closed/pending traits.
db/schema.rb Reflects new elections/nominations/votes tables and new user columns.
db/migrate/20260416104148_fix_election_columns.rb Reworks election scoring columns to point_scale.
db/migrate/20251008023211_create_votes.rb Creates votes table for polymorphic scored voting.
db/migrate/20251007062829_create_nominations.rb Creates nominations table linking elections and users.
db/migrate/20251007055656_create_elections.rb Creates elections table (initial scoring columns).
config/routes.rb Adds public elections routes + ballot create; adds admin elections/nominations routes.
app/views/shared/_navigation.html.erb Adds Admin navigation link to Elections.
app/views/elections/show.html.erb Public election show page rendering election + ballot form.
app/views/elections/new.html.erb Adds scaffold-style public election new view.
app/views/elections/index.json.jbuilder Adds JSON index output for elections.
app/views/elections/index.html.erb Public elections list page (current/previous sections).
app/views/elections/edit.html.erb Adds scaffold-style public election edit view.
app/views/elections/_form.html.erb Implements ballot form UI for scoring nominees.
app/views/elections/_election.json.jbuilder Adds JSON partial for election.
app/views/elections/_election.html.erb Election partial rendering header + ballot form.
app/views/admin/posts/new.html.erb Admin post new page layout refactor.
app/views/admin/posts/edit.html.erb Admin post edit page layout refactor.
app/views/admin/posts/_form.html.erb Admin post form layout refactor.
app/views/admin/nominations/new.html.erb Adds admin nomination creation page.
app/views/admin/nominations/_form.html.erb Adds admin nomination form fields for member emails.
app/views/admin/elections/show.html.erb Adds admin election show page with action links.
app/views/admin/elections/new.html.erb Adds admin election new page.
app/views/admin/elections/index.html.erb Adds admin elections index table page.
app/views/admin/elections/edit.html.erb Adds admin election edit page.
app/views/admin/elections/_form.html.erb Adds admin election form fields.
app/views/admin/elections/_election.html.erb Adds admin election details + nominations listing partial.
app/models/vote.rb Adds Vote model and election-open validation.
app/models/user.rb Adds nomination associations on User.
app/models/nomination.rb Adds Nomination model linking election/nominee/nominator and votes.
app/models/election.rb Adds Election model, scopes, open?/closed?, and winner selection logic.
app/models/ballot.rb Adds Ballot ActiveModel wrapper for the voting form.
app/helpers/elections_helper.rb Adds ElectionsHelper module.
app/controllers/elections_controller.rb Adds elections controller (scaffold-style actions; index/show used publicly).
app/controllers/ballots_controller.rb Adds ballot submission endpoint creating votes.
app/controllers/admin/nominations_controller.rb Adds admin nomination creation flow.
app/controllers/admin/elections_controller.rb Adds admin election management controller actions.
Gemfile.lock Updates gem dependency versions.
Gemfile Reorders/adjusts gem declarations and groups.

Comment thread spec/requests/elections_spec.rb Outdated
Comment thread spec/views/elections/index.html.erb_spec.rb Outdated
Comment thread app/controllers/ballots_controller.rb
Comment thread spec/views/elections/edit.html.erb_spec.rb Outdated
Comment on lines +50 to +57
<% unless @post.published_at %>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 sm:gap-6 items-start">
<%= form.label :publish_scheduled_at, class: "block text-sm font-medium text-gray-700 md:text-right md:mt-2" %>
<div class="md:col-span-2">
<%= form.datetime_field :publish_scheduled_at,
disabled: @post.publish_scheduled_at?,
class: "w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-ruby-red focus:border-ruby-red #{'opacity-60 cursor-not-allowed' if @post.publish_scheduled_at?}" %>
<% if @post.publish_scheduled_at? %>
Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

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

This partial receives post as a local, but it uses the instance variable @post for publish scheduling logic. That will break if the partial is ever rendered with a different local name or without @post set. Use the post local consistently (post.published_at, post.publish_scheduled_at?).

Suggested change
<% unless @post.published_at %>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 sm:gap-6 items-start">
<%= form.label :publish_scheduled_at, class: "block text-sm font-medium text-gray-700 md:text-right md:mt-2" %>
<div class="md:col-span-2">
<%= form.datetime_field :publish_scheduled_at,
disabled: @post.publish_scheduled_at?,
class: "w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-ruby-red focus:border-ruby-red #{'opacity-60 cursor-not-allowed' if @post.publish_scheduled_at?}" %>
<% if @post.publish_scheduled_at? %>
<% unless post.published_at %>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 sm:gap-6 items-start">
<%= form.label :publish_scheduled_at, class: "block text-sm font-medium text-gray-700 md:text-right md:mt-2" %>
<div class="md:col-span-2">
<%= form.datetime_field :publish_scheduled_at,
disabled: post.publish_scheduled_at?,
class: "w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-ruby-red focus:border-ruby-red #{'opacity-60 cursor-not-allowed' if post.publish_scheduled_at?}" %>
<% if post.publish_scheduled_at? %>

Copilot uses AI. Check for mistakes.
Comment on lines +38 to +39
<%= form.range_field :nomination_id_scores,
name: "ballot[nomination_id_scores][#{nomination.id}]",
Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

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

This ballot form uses form.range_field :nomination_id_scores, but Ballot doesn’t define a nomination_id_scores attribute (no attr_accessor, no ActiveModel attributes). This will typically raise when rendering the field. Either use range_field_tag (since you’re manually setting name:) or add an accessor/attribute on Ballot for nomination_id_scores.

Suggested change
<%= form.range_field :nomination_id_scores,
name: "ballot[nomination_id_scores][#{nomination.id}]",
<%= range_field_tag "ballot[nomination_id_scores][#{nomination.id}]",
nil,

Copilot uses AI. Check for mistakes.
Comment thread app/views/elections/edit.html.erb Outdated
Comment thread app/views/elections/index.html.erb Outdated
@@ -0,0 +1,30 @@
<%= form_with(model: [:admin, :election, nomination], class: "space-y-6 sm:space-y-8") do |form| %>
Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

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

The form model array is [:admin, :election, nomination], which doesn’t include the actual @election record. This will generate an incorrect action URL for the nested /admin/elections/:election_id/nominations routes. Pass the election instance (e.g., [:admin, @election, nomination]) and ensure the partial is rendered with election: available.

Suggested change
<%= form_with(model: [:admin, :election, nomination], class: "space-y-6 sm:space-y-8") do |form| %>
<% election = local_assigns.fetch(:election, @election) %>
<%= form_with(model: [:admin, election, nomination], class: "space-y-6 sm:space-y-8") do |form| %>

Copilot uses AI. Check for mistakes.
Comment thread spec/routing/elections_routing_spec.rb Outdated
Replace direct Election.create! calls with FactoryBot.create in elections index and show view specs.
Refactor controller logic and tests
Comment thread app/controllers/ballots_controller.rb Fixed
@ruby-au ruby-au temporarily deployed to ruby-org-au-election-vo-yzq1xy April 16, 2026 13:03 Inactive
Improve BallotsController#create by loading all referenced Nomination records in a single query and indexing them by id to avoid N+1 queries
@leesheppard leesheppard temporarily deployed to ruby-org-au-election-vo-yzq1xy April 16, 2026 13:13 Inactive
@leesheppard leesheppard temporarily deployed to ruby-org-au-election-vo-yzq1xy April 16, 2026 13:44 Inactive
Comment on lines +7 to +10
vote = Vote.find_or_initialize_by(
voter: current_user,
votable: nomination,
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Todo

Development

Successfully merging this pull request may close these issues.

4 participants