jumbotron 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 48040072e10a2363780b93b40c36c51e00683c247d616ae4cf8e0b245cca1815
4
- data.tar.gz: 56fdebbcc73ab653ebf1293d7bc718bece9c24e035552e10a402d3d0cf4a34a2
3
+ metadata.gz: 71a38ed750f1392e528a1039b2dd5cb33383581951da87f8261602d8fc20618c
4
+ data.tar.gz: b4d80cc8bbeeb0260f133838dd53d48f92315515be6f7e860e8c0ffb3d8526cd
5
5
  SHA512:
6
- metadata.gz: 22e5abd65bafd78af1bb38568088c5c7180321e28dc527cd8dec35ea61300d55f789a3e8723d35f3df00ddc3ef3ae7bddd8bbb7e6a4e1aac256eb0665bf24275
7
- data.tar.gz: 3deca6bd03bb5f95ddb485306ebd68fb33ced87215df67002a19f84f383ba748182d437d0581fa065043fe44bc60d605f2773af70a85884a596ab44c82a4d9ab
6
+ metadata.gz: 9b5cdcb6ff223603581417cb047916d47675843b2758305240539dd596be15b20be07bf2c8056d4c46bb683b69bc3554f7b28f29e98f3cf4affbab401eddc6b4
7
+ data.tar.gz: '09e5f5b8f0edd4eae5c12a6443841193ad669d87736ac360cc63c37e1c5f02b0aa40eeeda25a224f7c1b45884602f721464bfb7b312b65667700a00869c61939'
@@ -51,7 +51,7 @@ module Jumbotron
51
51
 
52
52
  policy :live,
53
53
  type: :live_game_update,
54
- cadence: Cadence.new(every: 1, unit: :minute),
54
+ cadence: Cadence.new(every: 30, unit: :second),
55
55
  eligible: lambda { |game, now:|
56
56
  return false if TERMINAL_LIFECYCLES.include?(game.lifecycle)
57
57
  return false if INTERRUPTED_LIFECYCLES.include?(game.lifecycle)
@@ -48,7 +48,7 @@ module Jumbotron
48
48
 
49
49
  policy :live,
50
50
  type: :live_game_update,
51
- cadence: Cadence.new(every: 1, unit: :minute),
51
+ cadence: Cadence.new(every: 30, unit: :second),
52
52
  eligible: lambda { |game, now:|
53
53
  return false if TERMINAL_LIFECYCLES.include?(game.lifecycle)
54
54
  return false if INTERRUPTED_LIFECYCLES.include?(game.lifecycle)
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jumbotron
4
+ module Deserializers
5
+ module Public
6
+ class Games < CommandTower::Deserializers::ApplicationDeserializer
7
+ def call(params)
8
+ raw = unwrap(fetch_param(params, :game_ids))
9
+ return raw if deserializer_result?(raw)
10
+ return failure(errors: [{ code: "invalid_request", message: "game_ids is required" }]) if raw.nil?
11
+ unless raw.is_a?(Array)
12
+ return failure(errors: [{ code: "invalid_request", message: "game_ids must be an array" }])
13
+ end
14
+
15
+ if raw.size > Jumbotron::Public::MAX_GAMES_IDS
16
+ return failure(
17
+ errors: [{
18
+ code: "invalid_request",
19
+ message: "game_ids exceeds maximum of #{Jumbotron::Public::MAX_GAMES_IDS}"
20
+ }]
21
+ )
22
+ end
23
+
24
+ ids = []
25
+ raw.each do |item|
26
+ coerced = unwrap(require_integer(item, field: "game_ids", min: 1))
27
+ return coerced if deserializer_result?(coerced)
28
+
29
+ ids << coerced
30
+ end
31
+
32
+ include_raw = unwrap(fetch_param(params, :include))
33
+ return include_raw if deserializer_result?(include_raw)
34
+
35
+ includes = unwrap(Includes.parse(include_raw))
36
+ return includes if deserializer_result?(includes)
37
+
38
+ success(Jumbotron::Public::GamesRequest.new(game_ids: ids.uniq, includes: includes))
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -101,6 +101,10 @@ module Jumbotron
101
101
  )
102
102
  GameRequest = Data.define(:id, :includes)
103
103
  GameIdsRequest = Data.define(:game_ids)
104
+ # Bulk Game read — game_ids + optional includes (parity with #game / #schedule).
105
+ # Distinct from GameIdsRequest so consensus/current_lines stay include-free.
106
+ GamesRequest = Data.define(:game_ids, :includes)
107
+ MAX_GAMES_IDS = 64
104
108
  ScheduleRequest = Data.define(
105
109
  :league_id,
106
110
  :sport,
@@ -10,8 +10,9 @@ module Jumbotron
10
10
 
11
11
  def materialize(schedules, **)
12
12
  klass = job_class
13
- remove_stale(klass, schedules)
14
- upsert_desired(klass, schedules)
13
+ applicable = sidekiq_expressible(schedules)
14
+ remove_stale(klass, applicable)
15
+ upsert_desired(klass, applicable)
15
16
  end
16
17
 
17
18
  def cron_string(cadence)
@@ -20,6 +21,15 @@ module Jumbotron
20
21
 
21
22
  private
22
23
 
24
+ # Five-field cron cannot express sub-minute cadences (Solid Queue / fugit can).
25
+ def sidekiq_expressible(schedules)
26
+ schedules.reject { |definition| second_unit?(definition.cadence.unit) }
27
+ end
28
+
29
+ def second_unit?(unit)
30
+ Clock.canonical_unit(unit) == :second
31
+ end
32
+
23
33
  def remove_stale(klass, schedules)
24
34
  desired_ids = schedules.map(&:id)
25
35
  Array(klass.all).each do |job|
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jumbotron
4
+ module Workflows
5
+ class ReadGamesWorkflow < CommandTower::Workflows::ApplicationWorkflow
6
+ include PublicRead
7
+
8
+ retry_strategy :none
9
+
10
+ def call(**kwargs)
11
+ deserialized = Deserializers::Public::Games.call(kwargs)
12
+ return map_deserializer_failure(deserialized) unless deserialized.success?
13
+
14
+ request = deserialized.input
15
+ loaded = Services::Public::LoadGamesByIds.call(game_ids: request.game_ids)
16
+ return map_service_failure(loaded) unless loaded.success?
17
+
18
+ games = Array(loaded.data[:games])
19
+ included = Services::Public::LoadIncludedLines.call(games: games, includes: request.includes)
20
+ return map_service_failure(included) unless included.success?
21
+
22
+ public_games = games.map do |game|
23
+ assembled = Services::Public::AssemblePublicGame.call(
24
+ game: game,
25
+ includes: request.includes,
26
+ current_lines: included.data[:current_lines],
27
+ consensus_lines: included.data[:consensus_lines]
28
+ )
29
+ return map_service_failure(assembled) unless assembled.success?
30
+
31
+ assembled.data[:public_game]
32
+ end
33
+
34
+ success(payload: { games: public_games }, http_status: :ok)
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,31 @@
1
+ # Upgrade: Jumbotron 0.4.0
2
+
3
+ **From:** `0.3.0`
4
+ **To:** `0.4.0`
5
+
6
+ Minor release: adds a bulk public Game read, tightens live-game polling cadence to 30 seconds, and keeps the Sidekiq Cron scheduling backend safe for sub-minute cadences.
7
+
8
+ ## Host-facing change
9
+
10
+ | Change | Host impact |
11
+ |--------|-------------|
12
+ | `Jumbotron.client.games(game_ids:, include:)` | Bulk public Game read (parity with `#game` / `#schedule`). Accepts up to 64 `game_ids`, dedupes and preserves request order, silently omits unknown ids, and composes `current_lines` / `consensus_lines` through the same `include` contract as other public reads. Requesting more than 64 ids raises `Jumbotron::InvalidRequestError`. |
13
+ | ESPN and Sandbox `:live` adapter cadence | Live-game polling moved from every 1 minute to every 30 seconds. Hosts running the ESPN or Sandbox NFL adapters will see more frequent live-update scheduling. |
14
+ | Sidekiq Cron scheduling backend | Sub-minute (`:second`) cadences are now excluded from the five-field cron materialization (five-field cron cannot express sub-minute schedules). Hosts on the Sidekiq Cron backend for a 30-second-cadence adapter must run the Solid Queue backend (or another cadence-aware backend) for that schedule to actually fire; Sidekiq Cron silently skips it rather than erroring. |
15
+
16
+ ## Upgrade checklist
17
+
18
+ 1. Bump the gem to `0.4.0` (or `>= 0.4.0`).
19
+ 2. No new migration. Hosts already on `0.3.0` do not need to copy or run Jumbotron migrations for this release.
20
+ 3. Hosts using the Sidekiq Cron scheduling backend with the ESPN or Sandbox NFL adapter should confirm their live-game schedule is served by a backend that supports sub-minute cadence (e.g. Solid Queue), since Sidekiq Cron now omits it rather than materializing an incorrect once-per-minute job.
21
+ 4. Hosts may call the new bulk read directly:
22
+
23
+ ```ruby
24
+ games = Jumbotron.client.games(game_ids: [101, 102, 103], include: [:consensus])
25
+ ```
26
+
27
+ ## What is not in this release
28
+
29
+ - No sandbox projection or Reset persistence change.
30
+ - No ESPN public Schedule/CurrentLines contract change beyond the `:live` cadence value.
31
+ - No Pick'em administration UI or competitive-domain behavior.
@@ -44,6 +44,13 @@ module Jumbotron
44
44
  )
45
45
  end
46
46
 
47
+ def games(**)
48
+ Public::TranslateOutcome.call(
49
+ Workflows::ReadGamesWorkflow.call(**),
50
+ payload_key: :games
51
+ )
52
+ end
53
+
47
54
  def consensus(**)
48
55
  Public::TranslateOutcome.call(
49
56
  Workflows::ReadConsensusWorkflow.call(**),
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Jumbotron
4
- VERSION = "0.3.0"
4
+ VERSION = "0.4.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jumbotron
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - matt-taylor
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-09-09 00:00:00.000000000 Z
11
+ date: 2026-09-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: command_tower
@@ -130,6 +130,7 @@ files:
130
130
  - app/deserializers/jumbotron/deserializers/clients/espn/week.rb
131
131
  - app/deserializers/jumbotron/deserializers/public/game.rb
132
132
  - app/deserializers/jumbotron/deserializers/public/game_ids.rb
133
+ - app/deserializers/jumbotron/deserializers/public/games.rb
133
134
  - app/deserializers/jumbotron/deserializers/public/includes.rb
134
135
  - app/deserializers/jumbotron/deserializers/public/register_sandbox_projection.rb
135
136
  - app/deserializers/jumbotron/deserializers/public/reset_sandbox_projection.rb
@@ -249,6 +250,7 @@ files:
249
250
  - app/workflows/jumbotron/workflows/read_consensus_workflow.rb
250
251
  - app/workflows/jumbotron/workflows/read_current_lines_workflow.rb
251
252
  - app/workflows/jumbotron/workflows/read_game_workflow.rb
253
+ - app/workflows/jumbotron/workflows/read_games_workflow.rb
252
254
  - app/workflows/jumbotron/workflows/read_schedule_groups_workflow.rb
253
255
  - app/workflows/jumbotron/workflows/read_schedule_workflow.rb
254
256
  - app/workflows/jumbotron/workflows/sandbox/read_projection_workflow.rb
@@ -273,6 +275,7 @@ files:
273
275
  - docs/upgrades/0.1.4.md
274
276
  - docs/upgrades/0.2.0.md
275
277
  - docs/upgrades/0.3.0.md
278
+ - docs/upgrades/0.4.0.md
276
279
  - lib/jumbotron.rb
277
280
  - lib/jumbotron/client.rb
278
281
  - lib/jumbotron/engine.rb