Module: SpreeCmCommissioner::VotingSessionTimingConcern

Extended by:
ActiveSupport::Concern
Included in:
VotingSession
Defined in:
app/models/concerns/spree_cm_commissioner/voting_session_timing_concern.rb

Overview

How a voting session's vote window (vote_open_at/vote_close_at) gets its values, kept deliberately separate from opens_at/closes_at — the show's own time slot within its episode (an episode can span many voting sessions, e.g. one per day of a week-long run), which has nothing to do with when voting is actually open.

There is no mode flag: the presence of vote_open_at/vote_close_at is the signal.

Pre-set at creation  — auto-activates: once #activate flips status to enabled, can_vote?
                     just compares Time.current against the already-real timestamps.
Left blank           — #start_vote! rewrites them to now / now + vote_duration_seconds the
                     moment the organizer presses it, live during the broadcast.

Both paths start at status: archived and reach enabled through one of two routes:

archived --(check "Visible")--> on_air --#start_vote!--> enabled  (progressive reveal, then vote)
archived ------------------------------#start_vote!--> enabled     (reveal and vote in one click)

There is no separate "reveal" action: setting visible: true while archived (from the organizer dashboard's edit form) is the reveal — see #promote_from_archived_when_made_visible, which auto-transitions status to on_air in the same write rather than rejecting the attempt. on_air exists specifically so a session can show its contestants one by one — in sync with a live performance — before voting opens; it is visible but never votable (status isn't enabled, so can_vote? is false regardless of vote_open_at).

Constant Summary collapse

LIVE_STATE_COLUMNS =

Columns the show screen renders. display is included because hiding a session should disappear from the app as promptly as opening one appears.

%w[status opens_at closes_at vote_open_at vote_close_at display visible].freeze

Instance Method Summary collapse

Instance Method Details

#extend_vote!(seconds) ⇒ Object

Shifts vote_close_at by seconds (organizer-chosen at the moment, not necessarily vote_duration_seconds) without touching vote_open_at — distinct from #start_vote!, which would discard the time already elapsed by resetting the window to start now.

Extends from max(vote_close_at, Time.current), not from the raw stored value: the controller also offers this once a window has already lapsed (status still enabled, nothing auto-flipped it to closed yet) specifically to reopen it. Adding straight to a stale vote_close_at sitting hours in the past would land the new value in the past too — "+10 minutes" on a session that closed last night must mean 10 minutes from now, not 10 minutes from when it closed.

seconds may be negative to cut a currently-open window short without a full Stop Vote; going negative enough to land at or before vote_open_at raises via the closes-after-opens validation.



105
106
107
108
109
# File 'app/models/concerns/spree_cm_commissioner/voting_session_timing_concern.rb', line 105

def extend_vote!(seconds)
  update!(vote_close_at: [vote_close_at, Time.current].max + seconds.to_i.seconds)

  SpreeCmCommissioner::VotingSessionAutoCloseJob.set(wait_until: vote_close_at).perform_later(id)
end

#start_vote!Object

Opens the vote window now rather than at a pre-set time, overriding whatever vote_open_at already held (a live show running early is exactly when this gets pressed). visible: true is set explicitly so this also works when called directly from archived — revealing and opening voting in one click, without going through on_air first.



51
52
53
54
55
56
57
58
59
60
61
# File 'app/models/concerns/spree_cm_commissioner/voting_session_timing_concern.rb', line 51

def start_vote!
  now = Time.current
  update!(
    vote_open_at: now,
    vote_close_at: now + vote_duration_seconds.to_i.seconds,
    status: :enabled,
    visible: true
  )

  SpreeCmCommissioner::VotingSessionAutoCloseJob.set(wait_until: vote_close_at).perform_later(id)
end

#stop_vote!Object

Ends the vote window immediately and moves to closed. status: closed (not just a rewritten vote_close_at) matters for two reasons: votes are rejected the instant this runs, since can_vote? short-circuits on enabled? before ever comparing timestamps — no dependency on the inclusive Time#between? boundary at vote_close_at; and it's what triggers schedule_counter_reconciliation (only fires on saved_change_to_status?(enabled -> closed)), which would otherwise only run via the separate "Close" action, never on a manual stop.

vote_close_at clamped to vote_open_at + 1s: an organizer who starts and immediately stops would otherwise produce vote_close_at == vote_open_at, which the closes-after-opens validation rejects — a validation error on what is a legitimate "cancel that" action.



73
74
75
76
# File 'app/models/concerns/spree_cm_commissioner/voting_session_timing_concern.rb', line 73

def stop_vote!
  opened_at = vote_open_at || Time.current
  update!(status: :closed, vote_close_at: [Time.current, opened_at + 1.second].max)
end

#vote_window_pending?Boolean

True when voting hasn't opened yet — either vote_open_at was never set, it's set but its time hasn't arrived, or status is on_air. The on_air check matters on its own: on_air can be revisited after a prior round already closed (an organizer reverting status by hand to re-reveal), in which case vote_open_at/vote_close_at are still populated from that earlier round and both in the past — without this, the timestamp check alone would say the window isn't pending, and Firestore's state_for would wrongly report 'closed' for a session that's actually on_air right now. Covers "never started," "pre-scheduled but not live yet," and "on_air again after a previous round" identically, since none of them is votable nor has a real countdown to show.

Returns:

  • (Boolean)


87
88
89
# File 'app/models/concerns/spree_cm_commissioner/voting_session_timing_concern.rb', line 87

def vote_window_pending?
  on_air? || vote_open_at.blank? || Time.current < vote_open_at
end