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.
displayis 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
-
#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. -
#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).
-
#stop_vote! ⇒ Object
Ends the vote window immediately and moves to closed.
-
#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.
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.
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 |