basketball 0.0.8 → 0.0.9
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 +4 -4
- data/.rubocop.yml +9 -19
- data/CHANGELOG.md +1 -39
- data/README.md +72 -93
- data/basketball.gemspec +3 -6
- data/exe/{basketball-season-scheduling → basketball-coordinator} +1 -1
- data/exe/{basketball-draft → basketball-room} +1 -1
- data/lib/basketball/app/coordinator_cli.rb +243 -0
- data/lib/basketball/app/coordinator_repository.rb +191 -0
- data/lib/basketball/app/file_store.rb +22 -0
- data/lib/basketball/{draft/cli.rb → app/room_cli.rb} +53 -76
- data/lib/basketball/app/room_repository.rb +189 -0
- data/lib/basketball/app.rb +12 -0
- data/lib/basketball/draft/assessment.rb +31 -0
- data/lib/basketball/draft/event.rb +3 -2
- data/lib/basketball/draft/front_office.rb +35 -28
- data/lib/basketball/draft/{pick_event.rb → pick.rb} +13 -6
- data/lib/basketball/draft/room.rb +119 -119
- data/lib/basketball/draft/{player_search.rb → scout.rb} +4 -9
- data/lib/basketball/draft/skip.rb +12 -0
- data/lib/basketball/draft.rb +13 -6
- data/lib/basketball/entity.rb +10 -4
- data/lib/basketball/org/league.rb +73 -0
- data/lib/basketball/org/player.rb +26 -0
- data/lib/basketball/{draft → org}/position.rb +3 -2
- data/lib/basketball/org/team.rb +38 -0
- data/lib/basketball/org.rb +12 -0
- data/lib/basketball/season/arena.rb +112 -0
- data/lib/basketball/season/calendar.rb +41 -72
- data/lib/basketball/season/coordinator.rb +185 -126
- data/lib/basketball/season/{preseason_game.rb → exhibition.rb} +2 -1
- data/lib/basketball/season/game.rb +15 -10
- data/lib/basketball/season/matchup.rb +27 -0
- data/lib/basketball/season/opponent.rb +15 -0
- data/lib/basketball/season/{season_game.rb → regular.rb} +2 -1
- data/lib/basketball/season/result.rb +37 -0
- data/lib/basketball/season.rb +12 -13
- data/lib/basketball/value_object.rb +4 -1
- data/lib/basketball/version.rb +1 -1
- data/lib/basketball.rb +9 -4
- metadata +32 -44
- data/lib/basketball/draft/league.rb +0 -70
- data/lib/basketball/draft/player.rb +0 -43
- data/lib/basketball/draft/room_serializer.rb +0 -186
- data/lib/basketball/draft/roster.rb +0 -37
- data/lib/basketball/draft/sim_event.rb +0 -23
- data/lib/basketball/draft/skip_event.rb +0 -13
- data/lib/basketball/season/calendar_serializer.rb +0 -94
- data/lib/basketball/season/conference.rb +0 -57
- data/lib/basketball/season/division.rb +0 -43
- data/lib/basketball/season/league.rb +0 -114
- data/lib/basketball/season/league_serializer.rb +0 -99
- data/lib/basketball/season/scheduling_cli.rb +0 -198
- data/lib/basketball/season/team.rb +0 -21
metadata
CHANGED
@@ -1,29 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: basketball
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matthew Ruggio
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-05-
|
11
|
+
date: 2023-05-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: faker
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - "~>"
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '3.2'
|
20
|
-
type: :runtime
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - "~>"
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '3.2'
|
27
13
|
- !ruby/object:Gem::Dependency
|
28
14
|
name: slop
|
29
15
|
requirement: !ruby/object:Gem::Requirement
|
@@ -38,15 +24,15 @@ dependencies:
|
|
38
24
|
- - "~>"
|
39
25
|
- !ruby/object:Gem::Version
|
40
26
|
version: '4.10'
|
41
|
-
description:
|
42
|
-
|
43
|
-
|
44
|
-
|
27
|
+
description: " This library is meant to serve as the domain for a basketball league/season
|
28
|
+
simulator/turn-based game. It models core ideas such as: players, general managers,
|
29
|
+
draft strategy, drafting, season generation, season simultation, playoff generation,
|
30
|
+
playoff simulation, and more.\n"
|
45
31
|
email:
|
46
32
|
- mattruggio@icloud.com
|
47
33
|
executables:
|
48
|
-
- basketball-
|
49
|
-
- basketball-
|
34
|
+
- basketball-coordinator
|
35
|
+
- basketball-room
|
50
36
|
extensions: []
|
51
37
|
extra_rdoc_files: []
|
52
38
|
files:
|
@@ -62,37 +48,39 @@ files:
|
|
62
48
|
- README.md
|
63
49
|
- Rakefile
|
64
50
|
- basketball.gemspec
|
65
|
-
- exe/basketball-
|
66
|
-
- exe/basketball-
|
51
|
+
- exe/basketball-coordinator
|
52
|
+
- exe/basketball-room
|
67
53
|
- lib/basketball.rb
|
54
|
+
- lib/basketball/app.rb
|
55
|
+
- lib/basketball/app/coordinator_cli.rb
|
56
|
+
- lib/basketball/app/coordinator_repository.rb
|
57
|
+
- lib/basketball/app/file_store.rb
|
58
|
+
- lib/basketball/app/room_cli.rb
|
59
|
+
- lib/basketball/app/room_repository.rb
|
68
60
|
- lib/basketball/draft.rb
|
69
|
-
- lib/basketball/draft/
|
61
|
+
- lib/basketball/draft/assessment.rb
|
70
62
|
- lib/basketball/draft/event.rb
|
71
63
|
- lib/basketball/draft/front_office.rb
|
72
|
-
- lib/basketball/draft/
|
73
|
-
- lib/basketball/draft/pick_event.rb
|
74
|
-
- lib/basketball/draft/player.rb
|
75
|
-
- lib/basketball/draft/player_search.rb
|
76
|
-
- lib/basketball/draft/position.rb
|
64
|
+
- lib/basketball/draft/pick.rb
|
77
65
|
- lib/basketball/draft/room.rb
|
78
|
-
- lib/basketball/draft/
|
79
|
-
- lib/basketball/draft/
|
80
|
-
- lib/basketball/draft/sim_event.rb
|
81
|
-
- lib/basketball/draft/skip_event.rb
|
66
|
+
- lib/basketball/draft/scout.rb
|
67
|
+
- lib/basketball/draft/skip.rb
|
82
68
|
- lib/basketball/entity.rb
|
69
|
+
- lib/basketball/org.rb
|
70
|
+
- lib/basketball/org/league.rb
|
71
|
+
- lib/basketball/org/player.rb
|
72
|
+
- lib/basketball/org/position.rb
|
73
|
+
- lib/basketball/org/team.rb
|
83
74
|
- lib/basketball/season.rb
|
75
|
+
- lib/basketball/season/arena.rb
|
84
76
|
- lib/basketball/season/calendar.rb
|
85
|
-
- lib/basketball/season/calendar_serializer.rb
|
86
|
-
- lib/basketball/season/conference.rb
|
87
77
|
- lib/basketball/season/coordinator.rb
|
88
|
-
- lib/basketball/season/
|
78
|
+
- lib/basketball/season/exhibition.rb
|
89
79
|
- lib/basketball/season/game.rb
|
90
|
-
- lib/basketball/season/
|
91
|
-
- lib/basketball/season/
|
92
|
-
- lib/basketball/season/
|
93
|
-
- lib/basketball/season/
|
94
|
-
- lib/basketball/season/season_game.rb
|
95
|
-
- lib/basketball/season/team.rb
|
80
|
+
- lib/basketball/season/matchup.rb
|
81
|
+
- lib/basketball/season/opponent.rb
|
82
|
+
- lib/basketball/season/regular.rb
|
83
|
+
- lib/basketball/season/result.rb
|
96
84
|
- lib/basketball/value_object.rb
|
97
85
|
- lib/basketball/version.rb
|
98
86
|
homepage: https://github.com/mattruggio/basketball
|
@@ -123,5 +111,5 @@ requirements: []
|
|
123
111
|
rubygems_version: 3.4.6
|
124
112
|
signing_key:
|
125
113
|
specification_version: 4
|
126
|
-
summary: Basketball
|
114
|
+
summary: Basketball Simulation Domain Model
|
127
115
|
test_files: []
|
@@ -1,70 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'roster'
|
4
|
-
|
5
|
-
module Basketball
|
6
|
-
module Draft
|
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
|
@@ -1,43 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Basketball
|
4
|
-
module Draft
|
5
|
-
class Player < Entity
|
6
|
-
STAR_THRESHOLD = 75
|
7
|
-
OVERALL_STAR_INDICATOR = '⭐'
|
8
|
-
|
9
|
-
private_constant :STAR_THRESHOLD, :OVERALL_STAR_INDICATOR
|
10
|
-
|
11
|
-
attr_reader :first_name, :last_name, :position, :overall
|
12
|
-
|
13
|
-
def initialize(id:, position:, first_name: '', last_name: '', overall: 0)
|
14
|
-
super(id)
|
15
|
-
|
16
|
-
raise ArgumentError, 'position is required' unless position
|
17
|
-
|
18
|
-
@first_name = first_name.to_s
|
19
|
-
@last_name = last_name.to_s
|
20
|
-
@position = position
|
21
|
-
@overall = overall.to_i
|
22
|
-
|
23
|
-
freeze
|
24
|
-
end
|
25
|
-
|
26
|
-
def full_name
|
27
|
-
"#{first_name.strip} #{last_name.strip}".strip
|
28
|
-
end
|
29
|
-
|
30
|
-
def to_s
|
31
|
-
"[#{super}] #{full_name} (#{position}) #{overall} #{star_indicators.join(', ')}".strip
|
32
|
-
end
|
33
|
-
|
34
|
-
private
|
35
|
-
|
36
|
-
def star_indicators
|
37
|
-
[].tap do |indicators|
|
38
|
-
indicators << OVERALL_STAR_INDICATOR if overall >= STAR_THRESHOLD
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
@@ -1,186 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'front_office'
|
4
|
-
require_relative 'player'
|
5
|
-
require_relative 'pick_event'
|
6
|
-
require_relative 'sim_event'
|
7
|
-
require_relative 'skip_event'
|
8
|
-
|
9
|
-
module Basketball
|
10
|
-
module Draft
|
11
|
-
class RoomSerializer
|
12
|
-
EVENT_CLASSES = {
|
13
|
-
'PickEvent' => PickEvent,
|
14
|
-
'SimEvent' => SimEvent,
|
15
|
-
'SkipEvent' => SkipEvent
|
16
|
-
}.freeze
|
17
|
-
|
18
|
-
private_constant :EVENT_CLASSES
|
19
|
-
|
20
|
-
def to_hash(room)
|
21
|
-
{
|
22
|
-
'info' => serialize_info(room),
|
23
|
-
'room' => serialize_room(room),
|
24
|
-
'league' => serialize_league(room)
|
25
|
-
}
|
26
|
-
end
|
27
|
-
|
28
|
-
def from_hash(json)
|
29
|
-
front_offices = deserialize_front_offices(json)
|
30
|
-
players = deserialize_players(json)
|
31
|
-
events = deserialize_events(json, players, front_offices)
|
32
|
-
|
33
|
-
room_opts = {
|
34
|
-
players:,
|
35
|
-
front_offices:,
|
36
|
-
events:
|
37
|
-
}
|
38
|
-
|
39
|
-
room_opts[:rounds] = json.dig('room', 'rounds') if json.dig('room', 'rounds')
|
40
|
-
|
41
|
-
Room.new(**room_opts)
|
42
|
-
end
|
43
|
-
|
44
|
-
def deserialize(string)
|
45
|
-
json = JSON.parse(string)
|
46
|
-
|
47
|
-
from_hash(json)
|
48
|
-
end
|
49
|
-
|
50
|
-
def serialize(room)
|
51
|
-
to_hash(room).to_json
|
52
|
-
end
|
53
|
-
|
54
|
-
private
|
55
|
-
|
56
|
-
def serialize_room(room)
|
57
|
-
{
|
58
|
-
'rounds' => room.rounds,
|
59
|
-
'front_offices' => serialize_front_offices(room),
|
60
|
-
'players' => serialize_players(room),
|
61
|
-
'events' => serialize_events(room.events)
|
62
|
-
}
|
63
|
-
end
|
64
|
-
|
65
|
-
def serialize_info(room)
|
66
|
-
{
|
67
|
-
'total_picks' => room.total_picks,
|
68
|
-
'current_round' => room.current_round,
|
69
|
-
'current_round_pick' => room.current_round_pick,
|
70
|
-
'current_front_office' => room.current_front_office&.id,
|
71
|
-
'current_pick' => room.current_pick,
|
72
|
-
'remaining_picks' => room.remaining_picks,
|
73
|
-
'done' => room.done?
|
74
|
-
}
|
75
|
-
end
|
76
|
-
|
77
|
-
def serialize_league(room)
|
78
|
-
league = room.to_league
|
79
|
-
|
80
|
-
rosters = league.rosters.to_h do |roster|
|
81
|
-
[
|
82
|
-
roster.id,
|
83
|
-
{
|
84
|
-
'players' => roster.players.map(&:id)
|
85
|
-
}
|
86
|
-
]
|
87
|
-
end
|
88
|
-
|
89
|
-
{
|
90
|
-
'free_agents' => league.free_agents.map(&:id),
|
91
|
-
'rosters' => rosters
|
92
|
-
}
|
93
|
-
end
|
94
|
-
|
95
|
-
def serialize_front_offices(room)
|
96
|
-
room.front_offices.to_h do |front_office|
|
97
|
-
[
|
98
|
-
front_office.id,
|
99
|
-
{
|
100
|
-
'name' => front_office.name,
|
101
|
-
'fuzz' => front_office.fuzz,
|
102
|
-
'depth' => front_office.depth,
|
103
|
-
'prioritized_positions' => front_office.prioritized_positions.map(&:code)
|
104
|
-
}
|
105
|
-
]
|
106
|
-
end
|
107
|
-
end
|
108
|
-
|
109
|
-
def serialize_players(room)
|
110
|
-
room.players.to_h do |player|
|
111
|
-
[
|
112
|
-
player.id,
|
113
|
-
{
|
114
|
-
'first_name' => player.first_name,
|
115
|
-
'last_name' => player.last_name,
|
116
|
-
'overall' => player.overall,
|
117
|
-
'position' => player.position.code
|
118
|
-
}
|
119
|
-
]
|
120
|
-
end
|
121
|
-
end
|
122
|
-
|
123
|
-
def serialize_events(events)
|
124
|
-
events.map do |event|
|
125
|
-
{
|
126
|
-
'type' => event.class.name.split('::').last,
|
127
|
-
'front_office' => event.front_office.id,
|
128
|
-
'pick' => event.pick,
|
129
|
-
'round' => event.round,
|
130
|
-
'round_pick' => event.round_pick
|
131
|
-
}.tap do |hash|
|
132
|
-
hash['player'] = event.player.id if event.respond_to?(:player)
|
133
|
-
end
|
134
|
-
end
|
135
|
-
end
|
136
|
-
|
137
|
-
def deserialize_front_offices(json)
|
138
|
-
(json.dig('room', 'front_offices') || []).map do |id, front_office_hash|
|
139
|
-
prioritized_positions = (front_office_hash['prioritized_positions'] || []).map do |v|
|
140
|
-
Position.new(v)
|
141
|
-
end
|
142
|
-
|
143
|
-
front_office_opts = {
|
144
|
-
id:,
|
145
|
-
name: front_office_hash['name'],
|
146
|
-
prioritized_positions:,
|
147
|
-
fuzz: front_office_hash['fuzz'],
|
148
|
-
depth: front_office_hash['depth']
|
149
|
-
}
|
150
|
-
|
151
|
-
FrontOffice.new(**front_office_opts)
|
152
|
-
end
|
153
|
-
end
|
154
|
-
|
155
|
-
def deserialize_players(json)
|
156
|
-
(json.dig('room', 'players') || []).map do |id, player_hash|
|
157
|
-
player_opts = {
|
158
|
-
id:,
|
159
|
-
first_name: player_hash['first_name'],
|
160
|
-
last_name: player_hash['last_name'],
|
161
|
-
overall: player_hash['overall'],
|
162
|
-
position: Position.new(player_hash['position'])
|
163
|
-
}
|
164
|
-
|
165
|
-
Player.new(**player_opts)
|
166
|
-
end
|
167
|
-
end
|
168
|
-
|
169
|
-
def deserialize_events(json, players, front_offices)
|
170
|
-
(json.dig('room', 'events') || []).map do |event_hash|
|
171
|
-
event_opts = event_hash.slice('pick', 'round', 'round_pick').merge(
|
172
|
-
front_office: front_offices.find { |t| t.id == event_hash['front_office'] }
|
173
|
-
)
|
174
|
-
|
175
|
-
class_constant = EVENT_CLASSES.fetch(event_hash['type'])
|
176
|
-
|
177
|
-
if [PickEvent, SimEvent].include?(class_constant)
|
178
|
-
event_opts[:player] = players.find { |p| p.id == event_hash['player'] }
|
179
|
-
end
|
180
|
-
|
181
|
-
class_constant.new(**event_opts)
|
182
|
-
end
|
183
|
-
end
|
184
|
-
end
|
185
|
-
end
|
186
|
-
end
|
@@ -1,37 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Basketball
|
4
|
-
module Draft
|
5
|
-
class Roster < Entity
|
6
|
-
class PlayerRequiredError < StandardError; end
|
7
|
-
|
8
|
-
attr_reader :name, :players
|
9
|
-
|
10
|
-
def initialize(id:, name: '', players: [])
|
11
|
-
super(id)
|
12
|
-
|
13
|
-
@name = name.to_s
|
14
|
-
@players = players.each { |p| register!(p) }
|
15
|
-
|
16
|
-
freeze
|
17
|
-
end
|
18
|
-
|
19
|
-
def registered?(player)
|
20
|
-
players.include?(player)
|
21
|
-
end
|
22
|
-
|
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
|
30
|
-
end
|
31
|
-
|
32
|
-
def to_s
|
33
|
-
(["[#{super}] #{name} Roster"] + players.map(&:to_s)).join("\n")
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
@@ -1,23 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Basketball
|
4
|
-
module Draft
|
5
|
-
class SimEvent < Event
|
6
|
-
attr_reader_value :player
|
7
|
-
|
8
|
-
def initialize(front_office:, player:, pick:, round:, round_pick:)
|
9
|
-
super(front_office:, pick:, round:, round_pick:)
|
10
|
-
|
11
|
-
raise ArgumentError, 'player required' unless player
|
12
|
-
|
13
|
-
@player = player
|
14
|
-
|
15
|
-
freeze
|
16
|
-
end
|
17
|
-
|
18
|
-
def to_s
|
19
|
-
"#{player} auto-picked #{super}"
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
@@ -1,94 +0,0 @@
|
|
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 Season
|
9
|
-
class CalendarSerializer
|
10
|
-
GAME_CLASSES = {
|
11
|
-
'PreseasonGame' => PreseasonGame,
|
12
|
-
'SeasonGame' => SeasonGame
|
13
|
-
}.freeze
|
14
|
-
|
15
|
-
def to_hash(calendar)
|
16
|
-
teams = calendar.games.flat_map(&:teams).uniq
|
17
|
-
|
18
|
-
{
|
19
|
-
'year' => calendar.preseason_start_date.year,
|
20
|
-
'teams' => serialize_teams(teams),
|
21
|
-
'games' => serialize_games(calendar.games)
|
22
|
-
}
|
23
|
-
end
|
24
|
-
|
25
|
-
def from_hash(json)
|
26
|
-
Calendar.new(
|
27
|
-
year: json['year'].to_i,
|
28
|
-
games: deserialize_games(json)
|
29
|
-
)
|
30
|
-
end
|
31
|
-
|
32
|
-
def deserialize(string)
|
33
|
-
json = JSON.parse(string)
|
34
|
-
|
35
|
-
from_hash(json)
|
36
|
-
end
|
37
|
-
|
38
|
-
def serialize(calendar)
|
39
|
-
to_hash(calendar).to_json
|
40
|
-
end
|
41
|
-
|
42
|
-
private
|
43
|
-
|
44
|
-
## Deserialization
|
45
|
-
|
46
|
-
def deserialize_games(json)
|
47
|
-
teams = deserialize_teams(json['teams'])
|
48
|
-
|
49
|
-
(json['games'] || []).map do |game_hash|
|
50
|
-
GAME_CLASSES.fetch(game_hash['type']).new(
|
51
|
-
date: Date.parse(game_hash['date']),
|
52
|
-
home_team: teams.fetch(game_hash['home_team']),
|
53
|
-
away_team: teams.fetch(game_hash['away_team'])
|
54
|
-
)
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
def deserialize_teams(teams)
|
59
|
-
(teams || []).to_h do |id, team_hash|
|
60
|
-
team = Team.new(id:, name: team_hash['name'])
|
61
|
-
|
62
|
-
[
|
63
|
-
team.id,
|
64
|
-
team
|
65
|
-
]
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
## Serialization
|
70
|
-
|
71
|
-
def serialize_teams(teams)
|
72
|
-
teams.to_h do |team|
|
73
|
-
[
|
74
|
-
team.id,
|
75
|
-
{
|
76
|
-
'name' => team.name
|
77
|
-
}
|
78
|
-
]
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
def serialize_games(games)
|
83
|
-
games.sort_by(&:date).map do |game|
|
84
|
-
{
|
85
|
-
'type' => game.class.name.split('::').last,
|
86
|
-
'date' => game.date.to_s,
|
87
|
-
'home_team' => game.home_team.id,
|
88
|
-
'away_team' => game.away_team.id
|
89
|
-
}
|
90
|
-
end
|
91
|
-
end
|
92
|
-
end
|
93
|
-
end
|
94
|
-
end
|
@@ -1,57 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Basketball
|
4
|
-
module Season
|
5
|
-
class Conference < Entity
|
6
|
-
DIVISIONS_SIZE = 3
|
7
|
-
|
8
|
-
attr_reader :name, :divisions
|
9
|
-
|
10
|
-
def initialize(id:, name: '', divisions: [])
|
11
|
-
super(id)
|
12
|
-
|
13
|
-
@name = name.to_s
|
14
|
-
@divisions = []
|
15
|
-
|
16
|
-
divisions.each { |d| register_division!(d) }
|
17
|
-
|
18
|
-
if divisions.length != DIVISIONS_SIZE
|
19
|
-
raise BadDivisionsSizeError, "#{id} should have exactly #{DIVISIONS_SIZE} divisions"
|
20
|
-
end
|
21
|
-
|
22
|
-
freeze
|
23
|
-
end
|
24
|
-
|
25
|
-
def to_s
|
26
|
-
(["[#{super}] #{name}"] + divisions.map(&:to_s)).join("\n")
|
27
|
-
end
|
28
|
-
|
29
|
-
def division?(division)
|
30
|
-
divisions.include?(division)
|
31
|
-
end
|
32
|
-
|
33
|
-
def teams
|
34
|
-
divisions.flat_map(&:teams)
|
35
|
-
end
|
36
|
-
|
37
|
-
def team?(team)
|
38
|
-
teams.include?(team)
|
39
|
-
end
|
40
|
-
|
41
|
-
private
|
42
|
-
|
43
|
-
def register_division!(division)
|
44
|
-
raise ArgumentError, 'division is required' unless division
|
45
|
-
raise DivisionAlreadyRegisteredError, "#{division} already registered" if division?(division)
|
46
|
-
|
47
|
-
division.teams.each do |team|
|
48
|
-
raise TeamAlreadyRegisteredError, "#{team} already registered" if team?(team)
|
49
|
-
end
|
50
|
-
|
51
|
-
divisions << division
|
52
|
-
|
53
|
-
self
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
@@ -1,43 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Basketball
|
4
|
-
module Season
|
5
|
-
class Division < Entity
|
6
|
-
TEAMS_SIZE = 5
|
7
|
-
|
8
|
-
attr_reader :name, :teams
|
9
|
-
|
10
|
-
def initialize(id:, name: '', teams: [])
|
11
|
-
super(id)
|
12
|
-
|
13
|
-
@name = name.to_s
|
14
|
-
@teams = []
|
15
|
-
|
16
|
-
teams.each { |t| register_team!(t) }
|
17
|
-
|
18
|
-
raise BadTeamsSizeError, "#{id} should have exactly #{TEAMS_SIZE} teams" if teams.length != TEAMS_SIZE
|
19
|
-
|
20
|
-
freeze
|
21
|
-
end
|
22
|
-
|
23
|
-
def to_s
|
24
|
-
(["[#{super}] #{name}"] + teams.map(&:to_s)).join("\n")
|
25
|
-
end
|
26
|
-
|
27
|
-
def team?(team)
|
28
|
-
teams.include?(team)
|
29
|
-
end
|
30
|
-
|
31
|
-
private
|
32
|
-
|
33
|
-
def register_team!(team)
|
34
|
-
raise ArgumentError, 'team is required' unless team
|
35
|
-
raise TeamAlreadyRegisteredError, "#{team} already registered" if team?(team)
|
36
|
-
|
37
|
-
teams << team
|
38
|
-
|
39
|
-
self
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|