basketball 0.0.4 → 0.0.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -2
- data/CODE_OF_CONDUCT.md +1 -1
- data/README.md +120 -18
- data/basketball.gemspec +1 -1
- data/exe/basketball-schedule +7 -0
- data/lib/basketball/drafting/cli.rb +15 -15
- data/lib/basketball/drafting/engine.rb +43 -42
- data/lib/basketball/drafting/engine_serializer.rb +41 -49
- data/lib/basketball/drafting/event.rb +10 -10
- data/lib/basketball/drafting/front_office.rb +9 -4
- data/lib/basketball/drafting/league.rb +70 -0
- data/lib/basketball/drafting/pick_event.rb +3 -3
- data/lib/basketball/drafting/roster.rb +18 -24
- data/lib/basketball/drafting/sim_event.rb +3 -3
- data/lib/basketball/drafting.rb +6 -0
- data/lib/basketball/scheduling/calendar.rb +121 -0
- data/lib/basketball/scheduling/calendar_serializer.rb +84 -0
- data/lib/basketball/scheduling/cli.rb +198 -0
- data/lib/basketball/scheduling/conference.rb +57 -0
- data/lib/basketball/scheduling/coordinator.rb +180 -0
- data/lib/basketball/scheduling/division.rb +43 -0
- data/lib/basketball/scheduling/game.rb +32 -0
- data/lib/basketball/scheduling/league.rb +114 -0
- data/lib/basketball/scheduling/league_serializer.rb +90 -0
- data/lib/basketball/scheduling/preseason_game.rb +11 -0
- data/lib/basketball/scheduling/season_game.rb +8 -0
- data/lib/basketball/scheduling/team.rb +21 -0
- data/lib/basketball/scheduling.rb +17 -0
- data/lib/basketball/value_object.rb +16 -7
- data/lib/basketball/version.rb +1 -1
- data/lib/basketball.rb +1 -0
- metadata +18 -3
- data/lib/basketball/drafting/team.rb +0 -28
@@ -2,7 +2,6 @@
|
|
2
2
|
|
3
3
|
require_relative 'front_office'
|
4
4
|
require_relative 'player'
|
5
|
-
require_relative 'team'
|
6
5
|
require_relative 'pick_event'
|
7
6
|
require_relative 'sim_event'
|
8
7
|
require_relative 'skip_event'
|
@@ -19,14 +18,14 @@ module Basketball
|
|
19
18
|
private_constant :EVENT_CLASSES
|
20
19
|
|
21
20
|
def deserialize(string)
|
22
|
-
json
|
23
|
-
|
24
|
-
players
|
25
|
-
events
|
21
|
+
json = JSON.parse(string, symbolize_names: true)
|
22
|
+
front_offices = deserialize_front_offices(json)
|
23
|
+
players = deserialize_players(json)
|
24
|
+
events = deserialize_events(json, players, front_offices)
|
26
25
|
|
27
26
|
engine_opts = {
|
28
27
|
players:,
|
29
|
-
|
28
|
+
front_offices:,
|
30
29
|
events:
|
31
30
|
}
|
32
31
|
|
@@ -39,7 +38,7 @@ module Basketball
|
|
39
38
|
{
|
40
39
|
info: serialize_info(engine),
|
41
40
|
engine: serialize_engine(engine),
|
42
|
-
|
41
|
+
league: serialize_league(engine)
|
43
42
|
}.to_json
|
44
43
|
end
|
45
44
|
|
@@ -48,7 +47,7 @@ module Basketball
|
|
48
47
|
def serialize_engine(engine)
|
49
48
|
{
|
50
49
|
rounds: engine.rounds,
|
51
|
-
|
50
|
+
front_offices: serialize_front_offices(engine),
|
52
51
|
players: serialize_players(engine),
|
53
52
|
events: serialize_events(engine.events)
|
54
53
|
}
|
@@ -59,37 +58,40 @@ module Basketball
|
|
59
58
|
total_picks: engine.total_picks,
|
60
59
|
current_round: engine.current_round,
|
61
60
|
current_round_pick: engine.current_round_pick,
|
62
|
-
|
61
|
+
current_front_office: engine.current_front_office&.id,
|
63
62
|
current_pick: engine.current_pick,
|
64
63
|
remaining_picks: engine.remaining_picks,
|
65
|
-
done: engine.done
|
66
|
-
undrafted_players: engine.undrafted_players.map(&:id)
|
64
|
+
done: engine.done?
|
67
65
|
}
|
68
66
|
end
|
69
67
|
|
70
|
-
def
|
71
|
-
engine.
|
68
|
+
def serialize_league(engine)
|
69
|
+
league = engine.to_league
|
70
|
+
|
71
|
+
rosters = league.rosters.to_h do |roster|
|
72
72
|
[
|
73
73
|
roster.id,
|
74
74
|
{
|
75
|
-
events: roster.events.map(&:id),
|
76
75
|
players: roster.players.map(&:id)
|
77
76
|
}
|
78
77
|
]
|
79
78
|
end
|
79
|
+
|
80
|
+
{
|
81
|
+
free_agents: league.free_agents.map(&:id),
|
82
|
+
rosters:
|
83
|
+
}
|
80
84
|
end
|
81
85
|
|
82
|
-
def
|
83
|
-
engine.
|
86
|
+
def serialize_front_offices(engine)
|
87
|
+
engine.front_offices.to_h do |front_office|
|
84
88
|
[
|
85
|
-
|
89
|
+
front_office.id,
|
86
90
|
{
|
87
|
-
name:
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
prioritized_positions: team.front_office.prioritized_positions
|
92
|
-
}
|
91
|
+
name: front_office.name,
|
92
|
+
fuzz: front_office.fuzz,
|
93
|
+
depth: front_office.depth,
|
94
|
+
prioritized_positions: front_office.prioritized_positions.map(&:code)
|
93
95
|
}
|
94
96
|
]
|
95
97
|
end
|
@@ -113,8 +115,7 @@ module Basketball
|
|
113
115
|
events.map do |event|
|
114
116
|
{
|
115
117
|
type: event.class.name.split('::').last,
|
116
|
-
|
117
|
-
team: event.team.id,
|
118
|
+
front_office: event.front_office.id,
|
118
119
|
pick: event.pick,
|
119
120
|
round: event.round,
|
120
121
|
round_pick: event.round_pick
|
@@ -124,30 +125,21 @@ module Basketball
|
|
124
125
|
end
|
125
126
|
end
|
126
127
|
|
127
|
-
def
|
128
|
-
(json.dig(:engine, :
|
129
|
-
|
128
|
+
def deserialize_front_offices(json)
|
129
|
+
(json.dig(:engine, :front_offices) || []).map do |id, front_office_hash|
|
130
|
+
prioritized_positions = (front_office_hash[:prioritized_positions] || []).map do |v|
|
131
|
+
Position.new(v)
|
132
|
+
end
|
133
|
+
|
134
|
+
front_office_opts = {
|
130
135
|
id:,
|
131
|
-
name:
|
136
|
+
name: front_office_hash[:name],
|
137
|
+
prioritized_positions:,
|
138
|
+
fuzz: front_office_hash[:fuzz],
|
139
|
+
depth: front_office_hash[:depth]
|
132
140
|
}
|
133
141
|
|
134
|
-
|
135
|
-
front_office_hash = team_hash[:front_office] || {}
|
136
|
-
|
137
|
-
prioritized_positions = (front_office_hash[:prioritized_positions] || []).map do |v|
|
138
|
-
Position.new(v)
|
139
|
-
end
|
140
|
-
|
141
|
-
front_office_opts = {
|
142
|
-
prioritized_positions:,
|
143
|
-
fuzz: front_office_hash[:fuzz],
|
144
|
-
depth: front_office_hash[:depth]
|
145
|
-
}
|
146
|
-
|
147
|
-
team_opts[:front_office] = FrontOffice.new(**front_office_opts)
|
148
|
-
end
|
149
|
-
|
150
|
-
Team.new(**team_opts)
|
142
|
+
FrontOffice.new(**front_office_opts)
|
151
143
|
end
|
152
144
|
end
|
153
145
|
|
@@ -162,10 +154,10 @@ module Basketball
|
|
162
154
|
end
|
163
155
|
end
|
164
156
|
|
165
|
-
def deserialize_events(json, players,
|
157
|
+
def deserialize_events(json, players, front_offices)
|
166
158
|
(json.dig(:engine, :events) || []).map do |event_hash|
|
167
|
-
event_opts = event_hash.slice(:
|
168
|
-
|
159
|
+
event_opts = event_hash.slice(:pick, :round, :round_pick).merge(
|
160
|
+
front_office: front_offices.find { |t| t.id == event_hash[:front_office] }
|
169
161
|
)
|
170
162
|
|
171
163
|
class_constant = EVENT_CLASSES.fetch(event_hash[:type])
|
@@ -2,22 +2,22 @@
|
|
2
2
|
|
3
3
|
module Basketball
|
4
4
|
module Drafting
|
5
|
-
class Event <
|
6
|
-
|
5
|
+
class Event < ValueObject
|
6
|
+
attr_reader_value :pick, :round, :round_pick, :front_office
|
7
7
|
|
8
|
-
def initialize(
|
9
|
-
super(
|
8
|
+
def initialize(front_office:, pick:, round:, round_pick:)
|
9
|
+
super()
|
10
10
|
|
11
|
-
raise ArgumentError, '
|
11
|
+
raise ArgumentError, 'front_office required' unless front_office
|
12
12
|
|
13
|
-
@
|
14
|
-
@pick
|
15
|
-
@round
|
16
|
-
@round_pick
|
13
|
+
@front_office = front_office
|
14
|
+
@pick = pick.to_i
|
15
|
+
@round = round.to_i
|
16
|
+
@round_pick = round_pick.to_i
|
17
17
|
end
|
18
18
|
|
19
19
|
def to_s
|
20
|
-
"##{pick} overall (R#{round}:P#{round_pick}) by #{
|
20
|
+
"##{pick} overall (R#{round}:P#{round_pick}) by #{front_office}"
|
21
21
|
end
|
22
22
|
end
|
23
23
|
end
|
@@ -2,18 +2,19 @@
|
|
2
2
|
|
3
3
|
module Basketball
|
4
4
|
module Drafting
|
5
|
-
class FrontOffice <
|
5
|
+
class FrontOffice < Entity
|
6
6
|
MAX_DEPTH = 3
|
7
7
|
MAX_FUZZ = 2
|
8
8
|
MAX_POSITIONS = 12
|
9
9
|
|
10
10
|
private_constant :MAX_DEPTH, :MAX_FUZZ, :MAX_POSITIONS
|
11
11
|
|
12
|
-
|
12
|
+
attr_reader :prioritized_positions, :fuzz, :depth, :name
|
13
13
|
|
14
|
-
def initialize(prioritized_positions: [], fuzz: rand(0..MAX_FUZZ), depth: rand(0..MAX_DEPTH))
|
15
|
-
super()
|
14
|
+
def initialize(id:, name: '', prioritized_positions: [], fuzz: rand(0..MAX_FUZZ), depth: rand(0..MAX_DEPTH))
|
15
|
+
super(id)
|
16
16
|
|
17
|
+
@name = name
|
17
18
|
@fuzz = fuzz.to_i
|
18
19
|
@depth = depth.to_i
|
19
20
|
@prioritized_positions = prioritized_positions
|
@@ -36,6 +37,10 @@ module Basketball
|
|
36
37
|
players[0..fuzz].sample
|
37
38
|
end
|
38
39
|
|
40
|
+
def to_s
|
41
|
+
"[#{super}] #{name}"
|
42
|
+
end
|
43
|
+
|
39
44
|
private
|
40
45
|
|
41
46
|
def adaptive_search(undrafted_player_search:, drafted_players:)
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'roster'
|
4
|
+
|
5
|
+
module Basketball
|
6
|
+
module Drafting
|
7
|
+
class League
|
8
|
+
class RosterNotFoundError < StandardError; end
|
9
|
+
class RosterAlreadyAddedError < StandardError; end
|
10
|
+
|
11
|
+
attr_reader :free_agents, :rosters
|
12
|
+
|
13
|
+
def initialize(free_agents: [], front_offices: [])
|
14
|
+
@rosters = []
|
15
|
+
@free_agents = []
|
16
|
+
|
17
|
+
front_offices.each { |front_office| add_roster(front_office) }
|
18
|
+
free_agents.each { |p| register!(player: p) }
|
19
|
+
|
20
|
+
freeze
|
21
|
+
end
|
22
|
+
|
23
|
+
def roster(front_office)
|
24
|
+
rosters.find { |r| r == front_office }
|
25
|
+
end
|
26
|
+
|
27
|
+
def register!(player:, front_office: nil)
|
28
|
+
raise PlayerRequiredError, 'player is required' unless player
|
29
|
+
|
30
|
+
rosters.each do |roster|
|
31
|
+
if roster.registered?(player)
|
32
|
+
raise PlayerAlreadyRegisteredError,
|
33
|
+
"#{player} already registered to: #{roster.id}"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
if free_agents.include?(player)
|
38
|
+
raise PlayerAlreadyRegisteredError,
|
39
|
+
"#{player} already registered as a free agent"
|
40
|
+
end
|
41
|
+
|
42
|
+
if front_office
|
43
|
+
roster = roster(front_office)
|
44
|
+
|
45
|
+
raise RosterNotFoundError, "Roster not found for: #{front_office}" unless roster
|
46
|
+
|
47
|
+
roster.sign!(player)
|
48
|
+
else
|
49
|
+
free_agents << player
|
50
|
+
end
|
51
|
+
|
52
|
+
self
|
53
|
+
end
|
54
|
+
|
55
|
+
def to_s
|
56
|
+
(['League'] + rosters.map(&:to_s)).join("\n")
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def add_roster(front_office)
|
62
|
+
raise RosterAlreadyAddedError, "#{front_office} already added" if rosters.include?(front_office)
|
63
|
+
|
64
|
+
rosters << Roster.new(id: front_office.id, name: front_office.name)
|
65
|
+
|
66
|
+
self
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -5,10 +5,10 @@ require_relative 'event'
|
|
5
5
|
module Basketball
|
6
6
|
module Drafting
|
7
7
|
class PickEvent < Event
|
8
|
-
|
8
|
+
attr_reader_value :player
|
9
9
|
|
10
|
-
def initialize(
|
11
|
-
super(
|
10
|
+
def initialize(front_office:, player:, pick:, round:, round_pick:)
|
11
|
+
super(front_office:, pick:, round:, round_pick:)
|
12
12
|
|
13
13
|
raise ArgumentError, 'player required' unless player
|
14
14
|
|
@@ -2,41 +2,35 @@
|
|
2
2
|
|
3
3
|
module Basketball
|
4
4
|
module Drafting
|
5
|
-
class Roster <
|
6
|
-
|
5
|
+
class Roster < Entity
|
6
|
+
class PlayerRequiredError < StandardError; end
|
7
7
|
|
8
|
-
|
8
|
+
attr_reader :name, :players
|
9
9
|
|
10
|
-
|
10
|
+
def initialize(id:, name: '', players: [])
|
11
|
+
super(id)
|
11
12
|
|
12
|
-
|
13
|
+
@name = name.to_s
|
14
|
+
@players = players.each { |p| register!(p) }
|
13
15
|
|
14
|
-
|
15
|
-
super()
|
16
|
-
|
17
|
-
raise ArgumentError, 'team is required' unless team
|
18
|
-
|
19
|
-
other_teams_pick_event_ids = events.reject { |e| e.team == team }.map(&:id)
|
20
|
-
|
21
|
-
if other_teams_pick_event_ids.any?
|
22
|
-
raise WrongTeamEventError,
|
23
|
-
"Event(s): #{other_teams_pick_event_ids.join(',')} has wrong team"
|
24
|
-
end
|
25
|
-
|
26
|
-
@team = team
|
27
|
-
@events = events
|
16
|
+
freeze
|
28
17
|
end
|
29
18
|
|
30
|
-
def
|
31
|
-
|
19
|
+
def registered?(player)
|
20
|
+
players.include?(player)
|
32
21
|
end
|
33
22
|
|
34
|
-
def
|
35
|
-
|
23
|
+
def sign!(player)
|
24
|
+
raise PlayerRequiredError, 'player is required' unless player
|
25
|
+
raise PlayerAlreadyRegisteredError, "#{player} already registered for #{id}" if registered?(player)
|
26
|
+
|
27
|
+
players << player
|
28
|
+
|
29
|
+
self
|
36
30
|
end
|
37
31
|
|
38
32
|
def to_s
|
39
|
-
([
|
33
|
+
(["[#{super}] #{name} Roster"] + players.map(&:to_s)).join("\n")
|
40
34
|
end
|
41
35
|
end
|
42
36
|
end
|
@@ -3,10 +3,10 @@
|
|
3
3
|
module Basketball
|
4
4
|
module Drafting
|
5
5
|
class SimEvent < Event
|
6
|
-
|
6
|
+
attr_reader_value :player
|
7
7
|
|
8
|
-
def initialize(
|
9
|
-
super(
|
8
|
+
def initialize(front_office:, player:, pick:, round:, round_pick:)
|
9
|
+
super(front_office:, pick:, round:, round_pick:)
|
10
10
|
|
11
11
|
raise ArgumentError, 'player required' unless player
|
12
12
|
|
data/lib/basketball/drafting.rb
CHANGED
@@ -0,0 +1,121 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Basketball
|
4
|
+
module Scheduling
|
5
|
+
class Calendar < ValueObject
|
6
|
+
class TeamAlreadyBookedError < StandardError; end
|
7
|
+
class InvalidGameOrderError < StandardError; end
|
8
|
+
class OutOfBoundsError < StandardError; end
|
9
|
+
|
10
|
+
attr_reader :preseason_start_date,
|
11
|
+
:preseason_end_date,
|
12
|
+
:season_start_date,
|
13
|
+
:season_end_date
|
14
|
+
|
15
|
+
attr_reader_value :year, :games
|
16
|
+
|
17
|
+
def initialize(year:, games: [])
|
18
|
+
super()
|
19
|
+
|
20
|
+
raise ArgumentError, 'year is required' unless year
|
21
|
+
|
22
|
+
@year = year
|
23
|
+
@preseason_start_date = Date.new(year, 9, 30)
|
24
|
+
@preseason_end_date = Date.new(year, 10, 14)
|
25
|
+
@season_start_date = Date.new(year, 10, 18)
|
26
|
+
@season_end_date = Date.new(year + 1, 4, 29)
|
27
|
+
@games = []
|
28
|
+
|
29
|
+
games.each { |game| add!(game) }
|
30
|
+
|
31
|
+
freeze
|
32
|
+
end
|
33
|
+
|
34
|
+
def add!(game)
|
35
|
+
assert_in_bounds(game)
|
36
|
+
assert_free_date(game)
|
37
|
+
|
38
|
+
@games << game
|
39
|
+
|
40
|
+
self
|
41
|
+
end
|
42
|
+
|
43
|
+
def preseason_games_for(date: nil, team: nil)
|
44
|
+
games_for(date:, team:).select { |game| game.is_a?(PreseasonGame) }
|
45
|
+
end
|
46
|
+
|
47
|
+
def season_games_for(date: nil, team: nil)
|
48
|
+
games_for(date:, team:).select { |game| game.is_a?(SeasonGame) }
|
49
|
+
end
|
50
|
+
|
51
|
+
def games_for(date: nil, team: nil)
|
52
|
+
games.select do |game|
|
53
|
+
(date.nil? || game.date == date) &&
|
54
|
+
(team.nil? || (game.home_team == team || game.away_team == team))
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def available_preseason_dates_for(team)
|
59
|
+
all_preseason_dates - preseason_games_for(team:).map(&:date)
|
60
|
+
end
|
61
|
+
|
62
|
+
def available_season_dates_for(team)
|
63
|
+
all_season_dates - season_games_for(team:).map(&:date)
|
64
|
+
end
|
65
|
+
|
66
|
+
def available_preseason_matchup_dates(team1, team2)
|
67
|
+
available_team_dates = available_preseason_dates_for(team1)
|
68
|
+
available_other_team_dates = available_preseason_dates_for(team2)
|
69
|
+
|
70
|
+
available_team_dates & available_other_team_dates
|
71
|
+
end
|
72
|
+
|
73
|
+
def available_season_matchup_dates(team1, team2)
|
74
|
+
available_team_dates = available_season_dates_for(team1)
|
75
|
+
available_other_team_dates = available_season_dates_for(team2)
|
76
|
+
|
77
|
+
available_team_dates & available_other_team_dates
|
78
|
+
end
|
79
|
+
|
80
|
+
def teams
|
81
|
+
games.flat_map(&:teams)
|
82
|
+
end
|
83
|
+
|
84
|
+
def team(id)
|
85
|
+
teams.find { |t| t == Team.new(id:) }
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
def all_preseason_dates
|
91
|
+
(preseason_start_date..preseason_end_date).to_a
|
92
|
+
end
|
93
|
+
|
94
|
+
def all_season_dates
|
95
|
+
(season_start_date..season_end_date).to_a
|
96
|
+
end
|
97
|
+
|
98
|
+
def assert_free_date(game)
|
99
|
+
if games_for(date: game.date, team: game.home_team).any?
|
100
|
+
raise TeamAlreadyBookedError, "#{game.home_team} already playing on #{game.date}"
|
101
|
+
end
|
102
|
+
|
103
|
+
return unless games_for(date: game.date, team: game.away_team).any?
|
104
|
+
|
105
|
+
raise TeamAlreadyBookedError, "#{game.away_team} already playing on #{game.date}"
|
106
|
+
end
|
107
|
+
|
108
|
+
def assert_in_bounds(game)
|
109
|
+
if game.is_a?(PreseasonGame)
|
110
|
+
raise OutOfBoundsError, "#{game.date} is before preseason begins" if game.date < preseason_start_date
|
111
|
+
raise OutOfBoundsError, "#{game.date} is after preseason ends" if game.date > preseason_end_date
|
112
|
+
elsif game.is_a?(SeasonGame)
|
113
|
+
raise OutOfBoundsError, "#{game.date} is before season begins" if game.date < season_start_date
|
114
|
+
raise OutOfBoundsError, "#{game.date} is after season ends" if game.date > season_end_date
|
115
|
+
else
|
116
|
+
raise ArgumentError, "Dont know what this game type is: #{game.class.name}"
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'game'
|
4
|
+
require_relative 'preseason_game'
|
5
|
+
require_relative 'season_game'
|
6
|
+
|
7
|
+
module Basketball
|
8
|
+
module Scheduling
|
9
|
+
class CalendarSerializer
|
10
|
+
GAME_CLASSES = {
|
11
|
+
'PreseasonGame' => PreseasonGame,
|
12
|
+
'SeasonGame' => SeasonGame
|
13
|
+
}.freeze
|
14
|
+
|
15
|
+
def deserialize(string)
|
16
|
+
json = JSON.parse(string, symbolize_names: true)
|
17
|
+
|
18
|
+
Calendar.new(
|
19
|
+
year: json[:year].to_i,
|
20
|
+
games: deserialize_games(json)
|
21
|
+
)
|
22
|
+
end
|
23
|
+
|
24
|
+
def serialize(calendar)
|
25
|
+
{
|
26
|
+
year: calendar.preseason_start_date.year,
|
27
|
+
teams: serialize_teams(calendar.games.flat_map(&:teams).uniq),
|
28
|
+
games: serialize_games(calendar.games)
|
29
|
+
}.to_json
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
## Deserialization
|
35
|
+
|
36
|
+
def deserialize_games(json)
|
37
|
+
teams = deserialize_teams(json[:teams])
|
38
|
+
|
39
|
+
(json[:games] || []).map do |game_hash|
|
40
|
+
GAME_CLASSES.fetch(game_hash[:type]).new(
|
41
|
+
date: Date.parse(game_hash[:date]),
|
42
|
+
home_team: teams.fetch(game_hash[:home_team]),
|
43
|
+
away_team: teams.fetch(game_hash[:away_team])
|
44
|
+
)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def deserialize_teams(teams)
|
49
|
+
(teams || []).to_h do |id, team_hash|
|
50
|
+
team = Team.new(id:, name: team_hash[:name])
|
51
|
+
|
52
|
+
[
|
53
|
+
team.id,
|
54
|
+
team
|
55
|
+
]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
## Serialization
|
60
|
+
|
61
|
+
def serialize_teams(teams)
|
62
|
+
teams.to_h do |team|
|
63
|
+
[
|
64
|
+
team.id,
|
65
|
+
{
|
66
|
+
name: team.name
|
67
|
+
}
|
68
|
+
]
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def serialize_games(games)
|
73
|
+
games.sort_by(&:date).map do |game|
|
74
|
+
{
|
75
|
+
type: game.class.name.split('::').last,
|
76
|
+
date: game.date,
|
77
|
+
home_team: game.home_team.id,
|
78
|
+
away_team: game.away_team.id
|
79
|
+
}
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|