basketball 0.0.17 → 0.0.18
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/README.md +0 -7
- data/lib/basketball/org/conference.rb +4 -3
- data/lib/basketball/org/division.rb +4 -3
- data/lib/basketball/org/player.rb +11 -5
- data/lib/basketball/org/team.rb +4 -3
- data/lib/basketball/season/coordinator.rb +25 -9
- data/lib/basketball/version.rb +1 -1
- data/lib/basketball.rb +0 -3
- metadata +2 -10
- data/lib/basketball/app/coordinator_repository.rb +0 -111
- data/lib/basketball/app/document_repository.rb +0 -71
- data/lib/basketball/app/file_store.rb +0 -38
- data/lib/basketball/app/in_memory_store.rb +0 -42
- data/lib/basketball/app/league_repository.rb +0 -109
- data/lib/basketball/app/room_repository.rb +0 -161
- data/lib/basketball/app/standings_repository.rb +0 -69
- data/lib/basketball/app.rb +0 -14
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: afad5b6023e9b0baf1dc11fe6874d4cc46430c1698aa4e97bf5dc2df8cbb314e
|
|
4
|
+
data.tar.gz: f98bb161b770d7148c4fb271ab1b58b2347cfe7b85f94acf1fb6332a4517e61b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 439ca00c393c39e8535ffc21b504b66aaf2a987f09e424ba55b8d4d33d740c6ece187e8e2821239b1d2fa7512aa3743e8a8750be9c41d71259a6093eb2bff57a
|
|
7
|
+
data.tar.gz: 433fbfeaf16e28312b165ff2b293b9a529ea57e95f0e03e4637b730f1dc36c22c095717825da54da6e0549eeccbeedbb999caf7db5afed80e8e439b25a15d2cd
|
data/README.md
CHANGED
|
@@ -14,18 +14,13 @@ Element | Description
|
|
|
14
14
|
**Assessment** | When the Room needs to know who a Front Office wants to select, the Room will send the Front Office an Assessment. The Assessment is a report of where the team currently stands: players picked, players available, and round information.
|
|
15
15
|
**Calendar** | Stores important boundary dates (exhibition start, exhibition end, season start, and season end).
|
|
16
16
|
**Conference** | A collection of Divisions.
|
|
17
|
-
**Coordinator Repository** | Understands how to save and load Coordinator objects.
|
|
18
17
|
**Coordinator** | Object which can take a League, Calendar, Games, and an Arena and provide an iterable interface to enumerate through days and simulate games as results.
|
|
19
18
|
**Detail** | Re-representation of a Result object but from a specific team's perspective.
|
|
20
19
|
**Division** | A collection of teams.
|
|
21
20
|
**Draft** | Bounded context (sub-module) dealing with running a round-robin player draft for teams.
|
|
22
21
|
**Exhibition** | Pre-season game which has no impact to team record.
|
|
23
|
-
**External Dependency** | Some outside system which this library or portions of this library are dependent on.
|
|
24
|
-
**File Store** | Implements a store that can interact with the underlying File System.
|
|
25
|
-
**File System** | Local operating system that repositories can use as their underlying persistence layer.
|
|
26
22
|
**Front Office** | Identifiable as a team, contains logic for how to auto-pick draft selections. Meant to be subclassed and extended to include more intricate player selection logic as the base will simply randomly select a player.
|
|
27
23
|
**Game** | Matches up a date with two teams (home and away) to represent a coordinatord match-up.
|
|
28
|
-
**League Repository** | Understands how to save and load League objects from JSON files on disk.
|
|
29
24
|
**League** | Describes a league in terms of structure composed of conferences, divisions, teams, and players.
|
|
30
25
|
**Match** | When the Coordinator needs an Arena instance to select a game winner, it will send the Arena a Match. A match is Game but also includes the active roster (players) for both teams that will participate in the game.
|
|
31
26
|
**Org** | Bounded context (sub-module) dealing with overall organizational structure of a sports assocation.
|
|
@@ -34,14 +29,12 @@ Element | Description
|
|
|
34
29
|
**Record** | Represents a team's overall record.
|
|
35
30
|
**Regular** | Game that counts towards regular season record.
|
|
36
31
|
**Result** | The outcome of a game (typically with a home and away score).
|
|
37
|
-
**Room Repository** | Understands how to save and load Room objects.
|
|
38
32
|
**Room** | Main object responsible for providing an iterable interface capable of executing a draft, pick by pick.
|
|
39
33
|
**Scheduler** | Knows how to take a League and a year and generate a game-populated calendar.
|
|
40
34
|
**Scout** | Knows how to stack rank lists of players.
|
|
41
35
|
**Season** | Bounded context (sub-module) dealing with calendar and matchup generation.
|
|
42
36
|
**Skip** | Result event emitted when a front office decides to skip a round.
|
|
43
37
|
**Standings** | Synthesizes teams and results into team standings with win/loss records and more.
|
|
44
|
-
**Standings Repository** | Understands how to save and load Standings objects.
|
|
45
38
|
**Store** | Interface for the underlying Repository persistence layer. While a Document Repository is mainly responsible for serialization/de-serialization, the store actually knows how to read/write the data.
|
|
46
39
|
**Team Group** | Set of rosters that together form a cohesive league.
|
|
47
40
|
**Team** | Member of a league and signs players. Has games assigned and played.
|
|
@@ -6,12 +6,13 @@ module Basketball
|
|
|
6
6
|
class Conference < Entity
|
|
7
7
|
include HasDivisions
|
|
8
8
|
|
|
9
|
-
attr_reader :divisions
|
|
9
|
+
attr_reader :divisions, :name
|
|
10
10
|
|
|
11
|
-
def initialize(id:, divisions: [])
|
|
11
|
+
def initialize(id:, name: '', divisions: [])
|
|
12
12
|
super(id)
|
|
13
13
|
|
|
14
14
|
@divisions = []
|
|
15
|
+
@name = name.to_s
|
|
15
16
|
|
|
16
17
|
divisions.each { |d| register_division!(d) }
|
|
17
18
|
|
|
@@ -19,7 +20,7 @@ module Basketball
|
|
|
19
20
|
end
|
|
20
21
|
|
|
21
22
|
def to_s
|
|
22
|
-
([super] + divisions.map(&:to_s)).join("\n")
|
|
23
|
+
(["[#{super}] #{name}"] + divisions.map(&:to_s)).join("\n")
|
|
23
24
|
end
|
|
24
25
|
|
|
25
26
|
def teams
|
|
@@ -6,12 +6,13 @@ module Basketball
|
|
|
6
6
|
class Division < Entity
|
|
7
7
|
include HasTeams
|
|
8
8
|
|
|
9
|
-
attr_reader :teams
|
|
9
|
+
attr_reader :teams, :name
|
|
10
10
|
|
|
11
|
-
def initialize(id:, teams: [])
|
|
11
|
+
def initialize(id:, name: '', teams: [])
|
|
12
12
|
super(id)
|
|
13
13
|
|
|
14
14
|
@teams = []
|
|
15
|
+
@name = name.to_s
|
|
15
16
|
|
|
16
17
|
teams.each { |t| register_team!(t) }
|
|
17
18
|
|
|
@@ -19,7 +20,7 @@ module Basketball
|
|
|
19
20
|
end
|
|
20
21
|
|
|
21
22
|
def to_s
|
|
22
|
-
([super] + teams.map(&:to_s)).join("\n")
|
|
23
|
+
(["[#{super}] #{name}"] + teams.map(&:to_s)).join("\n")
|
|
23
24
|
end
|
|
24
25
|
|
|
25
26
|
def players
|
|
@@ -5,21 +5,27 @@ module Basketball
|
|
|
5
5
|
# Base class describing a player.
|
|
6
6
|
# A consumer application should extend these specific to their specific sports traits.
|
|
7
7
|
class Player < Entity
|
|
8
|
-
attr_reader :overall, :position
|
|
8
|
+
attr_reader :overall, :position, :first_name, :last_name
|
|
9
9
|
|
|
10
|
-
def initialize(id:, overall: 0, position: nil)
|
|
10
|
+
def initialize(id:, overall: 0, position: nil, first_name: '', last_name: '')
|
|
11
11
|
super(id)
|
|
12
12
|
|
|
13
13
|
raise ArgumentError, 'position is required' unless position
|
|
14
14
|
|
|
15
|
-
@overall
|
|
16
|
-
@position
|
|
15
|
+
@overall = overall
|
|
16
|
+
@position = position
|
|
17
|
+
@first_name = first_name
|
|
18
|
+
@last_name = last_name
|
|
17
19
|
|
|
18
20
|
freeze
|
|
19
21
|
end
|
|
20
22
|
|
|
23
|
+
def full_name
|
|
24
|
+
"#{first_name} #{last_name}".strip
|
|
25
|
+
end
|
|
26
|
+
|
|
21
27
|
def to_s
|
|
22
|
-
"[#{super}] (#{position}) #{overall}".strip
|
|
28
|
+
"[#{super}] #{full_name} (#{position}) #{overall}".strip
|
|
23
29
|
end
|
|
24
30
|
end
|
|
25
31
|
end
|
data/lib/basketball/org/team.rb
CHANGED
|
@@ -5,12 +5,13 @@ module Basketball
|
|
|
5
5
|
# Base class describing a team. A team here is bare metal and is just described by an ID
|
|
6
6
|
# and a collection of Player objects.
|
|
7
7
|
class Team < Entity
|
|
8
|
-
attr_reader :players
|
|
8
|
+
attr_reader :players, :name
|
|
9
9
|
|
|
10
|
-
def initialize(id:, players: [])
|
|
10
|
+
def initialize(id:, name: '', players: [])
|
|
11
11
|
super(id)
|
|
12
12
|
|
|
13
13
|
@players = []
|
|
14
|
+
@name = name.to_s
|
|
14
15
|
|
|
15
16
|
players.each { |p| sign!(p) }
|
|
16
17
|
|
|
@@ -18,7 +19,7 @@ module Basketball
|
|
|
18
19
|
end
|
|
19
20
|
|
|
20
21
|
def to_s
|
|
21
|
-
([super
|
|
22
|
+
(["[#{super}] #{name}"] + players.map(&:to_s)).join("\n")
|
|
22
23
|
end
|
|
23
24
|
|
|
24
25
|
def signed?(player)
|
|
@@ -16,7 +16,8 @@ module Basketball
|
|
|
16
16
|
attr_reader :calendar,
|
|
17
17
|
:current_date,
|
|
18
18
|
:arena,
|
|
19
|
-
:results
|
|
19
|
+
:results,
|
|
20
|
+
:league
|
|
20
21
|
|
|
21
22
|
def_delegators :calendar,
|
|
22
23
|
:exhibition_start_date,
|
|
@@ -28,29 +29,32 @@ module Basketball
|
|
|
28
29
|
:regulars_for,
|
|
29
30
|
:games_for
|
|
30
31
|
|
|
31
|
-
def initialize(calendar:, current_date:, results: [])
|
|
32
|
+
def initialize(calendar:, league:, current_date:, results: [])
|
|
32
33
|
super()
|
|
33
34
|
|
|
34
|
-
raise ArgumentError, 'calendar is required'
|
|
35
|
+
raise ArgumentError, 'calendar is required' unless calendar
|
|
35
36
|
raise ArgumentError, 'current_date is required' if current_date.to_s.empty?
|
|
37
|
+
raise ArgumentError, 'league is required' unless league
|
|
36
38
|
|
|
37
39
|
@calendar = calendar
|
|
38
40
|
@current_date = current_date
|
|
39
41
|
@arena = Arena.new
|
|
40
42
|
@results = []
|
|
43
|
+
@league = league
|
|
41
44
|
|
|
42
45
|
results.each { |result| replay!(result) }
|
|
43
46
|
|
|
44
47
|
assert_current_date
|
|
45
48
|
assert_all_past_dates_are_played
|
|
46
49
|
assert_all_future_dates_arent_played
|
|
50
|
+
assert_all_known_teams
|
|
47
51
|
end
|
|
48
52
|
|
|
49
|
-
def sim_rest!(
|
|
53
|
+
def sim_rest!(&)
|
|
50
54
|
events = []
|
|
51
55
|
|
|
52
56
|
while not_done?
|
|
53
|
-
new_events = sim!(
|
|
57
|
+
new_events = sim!(&)
|
|
54
58
|
|
|
55
59
|
events += new_events
|
|
56
60
|
end
|
|
@@ -68,7 +72,7 @@ module Basketball
|
|
|
68
72
|
raise OutOfBoundsError, "current date #{current_date} should be on or after #{exhibition_start_date}"
|
|
69
73
|
end
|
|
70
74
|
|
|
71
|
-
def sim!
|
|
75
|
+
def sim!
|
|
72
76
|
raise ArgumentError, 'league is required' unless league
|
|
73
77
|
|
|
74
78
|
return [] if done?
|
|
@@ -77,8 +81,8 @@ module Basketball
|
|
|
77
81
|
games = games_for(date: current_date)
|
|
78
82
|
|
|
79
83
|
games.each do |game|
|
|
80
|
-
home_players = opponent_team(
|
|
81
|
-
away_players = opponent_team(
|
|
84
|
+
home_players = opponent_team(game.home_opponent).players
|
|
85
|
+
away_players = opponent_team(game.away_opponent).players
|
|
82
86
|
matchup = Matchup.new(game:, home_players:, away_players:)
|
|
83
87
|
event = arena.play(matchup)
|
|
84
88
|
|
|
@@ -156,7 +160,7 @@ module Basketball
|
|
|
156
160
|
|
|
157
161
|
attr_writer :arena
|
|
158
162
|
|
|
159
|
-
def opponent_team(
|
|
163
|
+
def opponent_team(opponent)
|
|
160
164
|
league.teams.find { |t| t == opponent }
|
|
161
165
|
end
|
|
162
166
|
|
|
@@ -220,6 +224,18 @@ module Basketball
|
|
|
220
224
|
|
|
221
225
|
result
|
|
222
226
|
end
|
|
227
|
+
|
|
228
|
+
def assert_known_teams(game)
|
|
229
|
+
raise UnknownTeamError, "unknown opponent: #{game.home_opponent}" unless league.team?(game.home_opponent)
|
|
230
|
+
|
|
231
|
+
return if league.team?(game.away_opponent)
|
|
232
|
+
|
|
233
|
+
raise UnknownTeamError, "unknown opponent: #{game.away_opponent}"
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
def assert_all_known_teams
|
|
237
|
+
calendar.games.each { |game| assert_known_teams(game) }
|
|
238
|
+
end
|
|
223
239
|
end
|
|
224
240
|
end
|
|
225
241
|
end
|
data/lib/basketball/version.rb
CHANGED
data/lib/basketball.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
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.18
|
|
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-06-
|
|
11
|
+
date: 2023-06-14 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: " This library is meant to serve as the domain for a basketball league/season
|
|
14
14
|
simulator/turn-based game. It models core ideas such as: players, general managers,
|
|
@@ -33,14 +33,6 @@ files:
|
|
|
33
33
|
- Rakefile
|
|
34
34
|
- basketball.gemspec
|
|
35
35
|
- lib/basketball.rb
|
|
36
|
-
- lib/basketball/app.rb
|
|
37
|
-
- lib/basketball/app/coordinator_repository.rb
|
|
38
|
-
- lib/basketball/app/document_repository.rb
|
|
39
|
-
- lib/basketball/app/file_store.rb
|
|
40
|
-
- lib/basketball/app/in_memory_store.rb
|
|
41
|
-
- lib/basketball/app/league_repository.rb
|
|
42
|
-
- lib/basketball/app/room_repository.rb
|
|
43
|
-
- lib/basketball/app/standings_repository.rb
|
|
44
36
|
- lib/basketball/draft.rb
|
|
45
37
|
- lib/basketball/draft/assessment.rb
|
|
46
38
|
- lib/basketball/draft/event.rb
|
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Basketball
|
|
4
|
-
module App
|
|
5
|
-
# Knows how to flatten a Coordinator instance and rehydrate one from JSON and/or a Ruby hash.
|
|
6
|
-
class CoordinatorRepository < DocumentRepository
|
|
7
|
-
GAME_CLASSES = {
|
|
8
|
-
'Exhibition' => Season::Exhibition,
|
|
9
|
-
'Regular' => Season::Regular
|
|
10
|
-
}.freeze
|
|
11
|
-
|
|
12
|
-
private_constant :GAME_CLASSES
|
|
13
|
-
|
|
14
|
-
private
|
|
15
|
-
|
|
16
|
-
def from_h(hash)
|
|
17
|
-
Season::Coordinator.new(
|
|
18
|
-
calendar: deserialize_calendar(hash[:calendar]),
|
|
19
|
-
current_date: Date.parse(hash[:current_date]),
|
|
20
|
-
results: deserialize_results(hash[:results])
|
|
21
|
-
)
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def to_h(coordinator)
|
|
25
|
-
{
|
|
26
|
-
id: coordinator.id,
|
|
27
|
-
calendar: serialize_calendar(coordinator.calendar),
|
|
28
|
-
current_date: coordinator.current_date.to_s,
|
|
29
|
-
results: serialize_results(coordinator.results)
|
|
30
|
-
}
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
# Serialization
|
|
34
|
-
|
|
35
|
-
def serialize_games(games)
|
|
36
|
-
games.map { |game| serialize_game(game) }
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
def serialize_calendar(calendar)
|
|
40
|
-
{
|
|
41
|
-
exhibition_start_date: calendar.exhibition_start_date.to_s,
|
|
42
|
-
exhibition_end_date: calendar.exhibition_end_date.to_s,
|
|
43
|
-
regular_start_date: calendar.regular_start_date.to_s,
|
|
44
|
-
regular_end_date: calendar.regular_end_date.to_s,
|
|
45
|
-
games: serialize_games(calendar.games)
|
|
46
|
-
}
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
def serialize_game(game)
|
|
50
|
-
{
|
|
51
|
-
type: game.class.name.split('::').last,
|
|
52
|
-
date: game.date.to_s,
|
|
53
|
-
home_opponent: game.home_opponent.id,
|
|
54
|
-
away_opponent: game.away_opponent.id
|
|
55
|
-
}
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
def serialize_result(result)
|
|
59
|
-
{
|
|
60
|
-
game: serialize_game(result.game),
|
|
61
|
-
home_score: result.home_score,
|
|
62
|
-
away_score: result.away_score
|
|
63
|
-
}
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
def serialize_results(results)
|
|
67
|
-
results.map do |result|
|
|
68
|
-
serialize_result(result)
|
|
69
|
-
end
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
# Deserialization
|
|
73
|
-
|
|
74
|
-
def deserialize_calendar(calendar_hash)
|
|
75
|
-
Season::Calendar.new(
|
|
76
|
-
exhibition_start_date: Date.parse(calendar_hash[:exhibition_start_date]),
|
|
77
|
-
exhibition_end_date: Date.parse(calendar_hash[:exhibition_end_date]),
|
|
78
|
-
regular_start_date: Date.parse(calendar_hash[:regular_start_date]),
|
|
79
|
-
regular_end_date: Date.parse(calendar_hash[:regular_end_date]),
|
|
80
|
-
games: deserialize_games(calendar_hash[:games])
|
|
81
|
-
)
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
def deserialize_games(game_hashes)
|
|
85
|
-
(game_hashes || []).map { |game_hash| deserialize_game(game_hash) }
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
def deserialize_game(game_hash)
|
|
89
|
-
GAME_CLASSES.fetch(game_hash[:type]).new(
|
|
90
|
-
date: Date.parse(game_hash[:date]),
|
|
91
|
-
home_opponent: Season::Opponent.new(id: game_hash[:home_opponent]),
|
|
92
|
-
away_opponent: Season::Opponent.new(id: game_hash[:away_opponent])
|
|
93
|
-
)
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
def deserialize_results(result_hashes)
|
|
97
|
-
(result_hashes || []).map do |result_hash|
|
|
98
|
-
deserialize_result(result_hash)
|
|
99
|
-
end
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
def deserialize_result(result_hash)
|
|
103
|
-
Season::Result.new(
|
|
104
|
-
game: deserialize_game(result_hash[:game]),
|
|
105
|
-
home_score: result_hash[:home_score],
|
|
106
|
-
away_score: result_hash[:away_score]
|
|
107
|
-
)
|
|
108
|
-
end
|
|
109
|
-
end
|
|
110
|
-
end
|
|
111
|
-
end
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Basketball
|
|
4
|
-
module App
|
|
5
|
-
# Base class for all repositories which are based on a flat document.
|
|
6
|
-
# At the very minimum sub-classes should implement #to_h(object) and #from_h(hash).
|
|
7
|
-
class DocumentRepository
|
|
8
|
-
attr_reader :store
|
|
9
|
-
|
|
10
|
-
def initialize(store = InMemoryStore.new)
|
|
11
|
-
super()
|
|
12
|
-
|
|
13
|
-
@store = store
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def load(id)
|
|
17
|
-
contents = store.read(id)
|
|
18
|
-
|
|
19
|
-
deserialize(contents).tap do |object|
|
|
20
|
-
object.send('id=', id)
|
|
21
|
-
end
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def save(id, object)
|
|
25
|
-
object.send('id=', id)
|
|
26
|
-
|
|
27
|
-
contents = serialize(object)
|
|
28
|
-
|
|
29
|
-
store.write(id, contents)
|
|
30
|
-
|
|
31
|
-
object
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
def delete(object)
|
|
35
|
-
return false unless object.id
|
|
36
|
-
|
|
37
|
-
store.delete(object.id)
|
|
38
|
-
|
|
39
|
-
object.send('id=', nil)
|
|
40
|
-
|
|
41
|
-
true
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
def exist?(id)
|
|
45
|
-
store.exist?(id)
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
protected
|
|
49
|
-
|
|
50
|
-
def from_h(hash)
|
|
51
|
-
Entity.new(hash[:id])
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
def to_h(entity)
|
|
55
|
-
{ id: entity.id }
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
private
|
|
59
|
-
|
|
60
|
-
def deserialize(string)
|
|
61
|
-
hash = JSON.parse(string, symbolize_names: true)
|
|
62
|
-
|
|
63
|
-
from_h(hash)
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
def serialize(object)
|
|
67
|
-
to_h(object).to_json
|
|
68
|
-
end
|
|
69
|
-
end
|
|
70
|
-
end
|
|
71
|
-
end
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Basketball
|
|
4
|
-
module App
|
|
5
|
-
# Knows how to read and write documents to disk.
|
|
6
|
-
class FileStore
|
|
7
|
-
class PathNotFoundError < StandardError; end
|
|
8
|
-
|
|
9
|
-
def exist?(path)
|
|
10
|
-
File.exist?(path)
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
def read(path)
|
|
14
|
-
raise PathNotFoundError, "'#{path}' not found" unless exist?(path)
|
|
15
|
-
|
|
16
|
-
File.read(path)
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def write(path, contents)
|
|
20
|
-
dir = File.dirname(path)
|
|
21
|
-
|
|
22
|
-
FileUtils.mkdir_p(dir)
|
|
23
|
-
|
|
24
|
-
File.write(path, contents)
|
|
25
|
-
|
|
26
|
-
nil
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
def delete(path)
|
|
30
|
-
raise PathNotFoundError, "'#{path}' not found" unless exist?(path)
|
|
31
|
-
|
|
32
|
-
File.delete(path)
|
|
33
|
-
|
|
34
|
-
nil
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
end
|
|
38
|
-
end
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Basketball
|
|
4
|
-
module App
|
|
5
|
-
# Knows how to read and write documents to a Ruby Hash.
|
|
6
|
-
class InMemoryStore
|
|
7
|
-
class KeyNotFoundError < StandardError; end
|
|
8
|
-
|
|
9
|
-
attr_reader :data
|
|
10
|
-
|
|
11
|
-
def initialize(data = {})
|
|
12
|
-
@data = data
|
|
13
|
-
|
|
14
|
-
freeze
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
def exist?(key)
|
|
18
|
-
data.key?(key.to_s)
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
def read(key)
|
|
22
|
-
raise KeyNotFoundError, "'#{key}' not found" unless exist?(key)
|
|
23
|
-
|
|
24
|
-
data[key.to_s]
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
def write(key, contents)
|
|
28
|
-
data[key.to_s] = contents
|
|
29
|
-
|
|
30
|
-
nil
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
def delete(key)
|
|
34
|
-
raise KeyNotFoundError, "'#{key}' not found" unless exist?(key)
|
|
35
|
-
|
|
36
|
-
data.delete(key)
|
|
37
|
-
|
|
38
|
-
nil
|
|
39
|
-
end
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
end
|
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Basketball
|
|
4
|
-
module App
|
|
5
|
-
# Knows how to flatten a League instances and rehydrate one from JSON and/or a Ruby hash.
|
|
6
|
-
class LeagueRepository < DocumentRepository
|
|
7
|
-
private
|
|
8
|
-
|
|
9
|
-
def from_h(hash)
|
|
10
|
-
deserialize_league(hash)
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
def to_h(league)
|
|
14
|
-
serialize_league(league)
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
# Serialization
|
|
18
|
-
|
|
19
|
-
def serialize_league(league)
|
|
20
|
-
{
|
|
21
|
-
id: league.id,
|
|
22
|
-
conferences: serialize_conferences(league.conferences)
|
|
23
|
-
}
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
def serialize_conferences(conferences)
|
|
27
|
-
conferences.map do |conference|
|
|
28
|
-
{
|
|
29
|
-
id: conference.id,
|
|
30
|
-
divisions: serialize_divisions(conference.divisions)
|
|
31
|
-
}
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
def serialize_divisions(divisions)
|
|
36
|
-
divisions.map do |division|
|
|
37
|
-
{
|
|
38
|
-
id: division.id,
|
|
39
|
-
teams: serialize_teams(division.teams)
|
|
40
|
-
}
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
def serialize_teams(teams)
|
|
45
|
-
teams.map do |team|
|
|
46
|
-
{
|
|
47
|
-
id: team.id,
|
|
48
|
-
players: serialize_players(team.players)
|
|
49
|
-
}
|
|
50
|
-
end
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
def serialize_players(players)
|
|
54
|
-
players.map do |player|
|
|
55
|
-
{
|
|
56
|
-
id: player.id,
|
|
57
|
-
overall: player.overall,
|
|
58
|
-
position: player.position&.code
|
|
59
|
-
}
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
# Deserialization
|
|
64
|
-
|
|
65
|
-
def deserialize_league(league_hash)
|
|
66
|
-
Org::League.new(
|
|
67
|
-
conferences: deserialize_conferences(league_hash[:conferences])
|
|
68
|
-
)
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
def deserialize_conferences(conference_hashes)
|
|
72
|
-
(conference_hashes || []).map do |conference_hash|
|
|
73
|
-
Org::Conference.new(
|
|
74
|
-
id: conference_hash[:id],
|
|
75
|
-
divisions: deserialize_divisions(conference_hash[:divisions])
|
|
76
|
-
)
|
|
77
|
-
end
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
def deserialize_divisions(division_hashes)
|
|
81
|
-
(division_hashes || []).map do |division_hash|
|
|
82
|
-
Org::Division.new(
|
|
83
|
-
id: division_hash[:id],
|
|
84
|
-
teams: deserialize_teams(division_hash[:teams])
|
|
85
|
-
)
|
|
86
|
-
end
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
def deserialize_teams(team_hashes)
|
|
90
|
-
(team_hashes || []).map do |team_hash|
|
|
91
|
-
Org::Team.new(
|
|
92
|
-
id: team_hash[:id],
|
|
93
|
-
players: deserialize_players(team_hash[:players])
|
|
94
|
-
)
|
|
95
|
-
end
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
def deserialize_players(player_hashes)
|
|
99
|
-
(player_hashes || []).map do |player_hash|
|
|
100
|
-
Org::Player.new(
|
|
101
|
-
id: player_hash[:id],
|
|
102
|
-
overall: player_hash[:overall],
|
|
103
|
-
position: Org::Position.new(player_hash[:position])
|
|
104
|
-
)
|
|
105
|
-
end
|
|
106
|
-
end
|
|
107
|
-
end
|
|
108
|
-
end
|
|
109
|
-
end
|
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Basketball
|
|
4
|
-
module App
|
|
5
|
-
# Can load and save Room objects as JSON Documents.
|
|
6
|
-
class RoomRepository < DocumentRepository
|
|
7
|
-
PICK_EVENT = 'Pick'
|
|
8
|
-
SKIP_EVENT = 'Skip'
|
|
9
|
-
|
|
10
|
-
private_constant :PICK_EVENT, :SKIP_EVENT
|
|
11
|
-
|
|
12
|
-
private
|
|
13
|
-
|
|
14
|
-
def from_h(hash)
|
|
15
|
-
front_offices = deserialize_front_offices(hash[:front_offices])
|
|
16
|
-
players = deserialize_players(hash[:players])
|
|
17
|
-
events = deserialize_events(hash[:events], players:, front_offices:)
|
|
18
|
-
|
|
19
|
-
Draft::Room.new(
|
|
20
|
-
rounds: hash[:rounds],
|
|
21
|
-
front_offices:,
|
|
22
|
-
players:,
|
|
23
|
-
events:
|
|
24
|
-
)
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
def to_h(room)
|
|
28
|
-
{
|
|
29
|
-
id: room.id,
|
|
30
|
-
rounds: room.rounds,
|
|
31
|
-
front_offices: room.front_offices.map { |fo| serialize_front_office(fo) },
|
|
32
|
-
players: room.players.map { |p| serialize_player(p) },
|
|
33
|
-
events: serialize_events(room.events)
|
|
34
|
-
}
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
# Serialization
|
|
38
|
-
|
|
39
|
-
def serialize_player(player)
|
|
40
|
-
{
|
|
41
|
-
id: player.id,
|
|
42
|
-
overall: player.overall,
|
|
43
|
-
position: player.position&.code
|
|
44
|
-
}
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
def serialize_front_office(front_office)
|
|
48
|
-
{
|
|
49
|
-
id: front_office.id,
|
|
50
|
-
risk_level: front_office.risk_level,
|
|
51
|
-
prioritized_positions: front_office.prioritized_positions.map(&:code),
|
|
52
|
-
star_level: front_office.star_level
|
|
53
|
-
}
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
def serialize_events(events)
|
|
57
|
-
events.map do |event|
|
|
58
|
-
case event
|
|
59
|
-
when Draft::Pick
|
|
60
|
-
serialize_pick(event)
|
|
61
|
-
when Draft::Skip
|
|
62
|
-
serialize_skip(event)
|
|
63
|
-
end
|
|
64
|
-
end
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
def serialize_pick(event)
|
|
68
|
-
{
|
|
69
|
-
id: event.id || event.pick,
|
|
70
|
-
type: PICK_EVENT,
|
|
71
|
-
front_office: event.front_office.id,
|
|
72
|
-
pick: event.pick,
|
|
73
|
-
round: event.round,
|
|
74
|
-
round_pick: event.round_pick,
|
|
75
|
-
auto: event.auto,
|
|
76
|
-
player: event.player.id
|
|
77
|
-
}
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
def serialize_skip(event)
|
|
81
|
-
{
|
|
82
|
-
id: event.id || event.pick,
|
|
83
|
-
type: SKIP_EVENT,
|
|
84
|
-
front_office: event.front_office.id,
|
|
85
|
-
pick: event.pick,
|
|
86
|
-
round: event.round,
|
|
87
|
-
round_pick: event.round_pick
|
|
88
|
-
}
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
# Deserialization
|
|
92
|
-
|
|
93
|
-
def deserialize_player(player_hash)
|
|
94
|
-
Org::Player.new(
|
|
95
|
-
id: player_hash[:id],
|
|
96
|
-
overall: player_hash[:overall],
|
|
97
|
-
position: Org::Position.new(player_hash[:position])
|
|
98
|
-
)
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
def deserialize_front_office(hash)
|
|
102
|
-
Draft::FrontOffice.new(
|
|
103
|
-
id: hash[:id],
|
|
104
|
-
risk_level: hash[:risk_level].to_i,
|
|
105
|
-
prioritized_positions: (hash[:prioritized_positions] || []).map { |c| Org::Position.new(c) },
|
|
106
|
-
star_level: hash[:star_level].to_i
|
|
107
|
-
)
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
def deserialize_front_offices(hashes)
|
|
111
|
-
(hashes || []).map { |fo| deserialize_front_office(fo) }
|
|
112
|
-
end
|
|
113
|
-
|
|
114
|
-
def deserialize_players(hashes)
|
|
115
|
-
(hashes || []).map { |hash| deserialize_player(hash) }
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
def deserialize_pick(hash, players:, front_office:)
|
|
119
|
-
player_id = hash[:player]
|
|
120
|
-
player = players.find { |p| p.id == player_id }
|
|
121
|
-
|
|
122
|
-
Draft::Pick.new(
|
|
123
|
-
front_office:,
|
|
124
|
-
pick: hash[:pick],
|
|
125
|
-
round: hash[:round],
|
|
126
|
-
round_pick: hash[:round_pick],
|
|
127
|
-
player:,
|
|
128
|
-
auto: hash[:auto]
|
|
129
|
-
)
|
|
130
|
-
end
|
|
131
|
-
|
|
132
|
-
def deserialize_skip(hash, front_office:)
|
|
133
|
-
Draft::Skip.new(
|
|
134
|
-
front_office:,
|
|
135
|
-
pick: hash[:pick],
|
|
136
|
-
round: hash[:round],
|
|
137
|
-
round_pick: hash[:round_pick]
|
|
138
|
-
)
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
def deserialize_events(hashes, players:, front_offices:)
|
|
142
|
-
(hashes || []).map do |hash|
|
|
143
|
-
front_office_id = hash[:front_office]
|
|
144
|
-
front_office = front_offices.find { |fo| fo.id == front_office_id }
|
|
145
|
-
|
|
146
|
-
event =
|
|
147
|
-
case hash[:type]
|
|
148
|
-
when PICK_EVENT
|
|
149
|
-
deserialize_pick(hash, players:, front_office:)
|
|
150
|
-
when SKIP_EVENT
|
|
151
|
-
deserialize_skip(hash, front_office:)
|
|
152
|
-
end
|
|
153
|
-
|
|
154
|
-
event.tap do |e|
|
|
155
|
-
e.send('id=', hash[:id])
|
|
156
|
-
end
|
|
157
|
-
end
|
|
158
|
-
end
|
|
159
|
-
end
|
|
160
|
-
end
|
|
161
|
-
end
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Basketball
|
|
4
|
-
module App
|
|
5
|
-
# Can load and save Standings objects as JSON Documents.
|
|
6
|
-
class StandingsRepository < DocumentRepository
|
|
7
|
-
private
|
|
8
|
-
|
|
9
|
-
def from_h(hash)
|
|
10
|
-
Season::Standings.new(
|
|
11
|
-
records: deserialize_records(hash[:records])
|
|
12
|
-
)
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def to_h(standings)
|
|
16
|
-
{
|
|
17
|
-
id: standings.id,
|
|
18
|
-
records: serialize_records(standings.records)
|
|
19
|
-
}
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
# Serialization
|
|
23
|
-
|
|
24
|
-
def serialize_records(records)
|
|
25
|
-
(records || []).map do |record|
|
|
26
|
-
{
|
|
27
|
-
id: record.id,
|
|
28
|
-
details: serialize_details(record.details)
|
|
29
|
-
}
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
def serialize_details(details)
|
|
34
|
-
(details || []).map do |detail|
|
|
35
|
-
{
|
|
36
|
-
date: detail.date.to_s,
|
|
37
|
-
home: detail.home,
|
|
38
|
-
opponent: detail.opponent.id,
|
|
39
|
-
opponent_score: detail.opponent_score,
|
|
40
|
-
score: detail.score
|
|
41
|
-
}
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
# Deserialization
|
|
46
|
-
|
|
47
|
-
def deserialize_records(record_hashes)
|
|
48
|
-
(record_hashes || []).map do |record_hash|
|
|
49
|
-
Season::Record.new(
|
|
50
|
-
id: record_hash[:id],
|
|
51
|
-
details: deserialize_details(record_hash[:details])
|
|
52
|
-
)
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
def deserialize_details(detail_hashes)
|
|
57
|
-
(detail_hashes || []).map do |detail_hash|
|
|
58
|
-
Season::Detail.new(
|
|
59
|
-
date: Date.parse(detail_hash[:date]),
|
|
60
|
-
home: detail_hash[:home],
|
|
61
|
-
opponent: Season::Opponent.new(id: detail_hash[:opponent]),
|
|
62
|
-
opponent_score: detail_hash[:opponent_score].to_i,
|
|
63
|
-
score: detail_hash[:score].to_i
|
|
64
|
-
)
|
|
65
|
-
end
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
end
|
|
69
|
-
end
|
data/lib/basketball/app.rb
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
# Stores
|
|
4
|
-
require_relative 'app/file_store'
|
|
5
|
-
require_relative 'app/in_memory_store'
|
|
6
|
-
|
|
7
|
-
# Repositories / Common
|
|
8
|
-
require_relative 'app/document_repository'
|
|
9
|
-
|
|
10
|
-
# Repositories / Implementations
|
|
11
|
-
require_relative 'app/coordinator_repository'
|
|
12
|
-
require_relative 'app/league_repository'
|
|
13
|
-
require_relative 'app/room_repository'
|
|
14
|
-
require_relative 'app/standings_repository'
|