basketball 0.0.8 → 0.0.9
Sign up to get free protection for your applications and to get access to all the features.
- 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
|