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