basketball 0.0.9 → 0.0.10

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ab187320b9735e8bc6ace40a171cbc9fecd27058e78e5ee18ab1b663b6ea3d54
4
- data.tar.gz: 6173d1bfdb1b531d54763d369f248944c532916e5fecac89698ac4f49a72e4d9
3
+ metadata.gz: dca22b3ec7251a012db2afed77f3bda87bd8b59bb59e6084fff80096efcb802a
4
+ data.tar.gz: 55973e68a7aece1571d926f21e5866e045322e1009b9a5139a959d5059e1b150
5
5
  SHA512:
6
- metadata.gz: df618bc45b196219e9f607a1842f5fda8513ea4dc7bce54756c98a327b87c9905bf41ded2c3dbf04cc70459d06ea4ab76864ff02713ecc2cf400e959cda081a6
7
- data.tar.gz: 536c2f5fff03419da03604302743b6ec0fccde9957238a0ae4c71c8b560b1709e3460afbc18e0fc377f239c20f4fb82fbb5e2d184b45f60c05d974d4f8615894
6
+ metadata.gz: af6b695d1ad8bd581822ffe6fe61421c5839a51e880f0d143060656a8e916e11ff8ae4ae5293e9babb2d8f45646b643d30b7753c03141f8d0c321b4010eca14d
7
+ data.tar.gz: d0addd9247f2a3b945874d628eec04e44cb826db2578d493bd99cc242beb8e78041084bf3fbffa53b6a00f2a2b788abb8540894e1caf9db5b0031a01357d5bc1
data/README.md CHANGED
@@ -20,9 +20,11 @@ Element | Description
20
20
  **Draft** | Bounded context (sub-module) dealing with running a round-robin player draft for teams.
21
21
  **Exhibition** | Pre-season game which has no impact to team record.
22
22
  **External Dependency** | Some outside system which this library or portions of this library are dependent on.
23
+ **File Store** | Implements a store that can interact with the underlying File System.
23
24
  **File System** | Local operating system that the CLI will use for persistence.
24
25
  **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.
25
26
  **Game** | Matches up a date with two teams (home and away) to represent a coordinatord match-up.
27
+ **League Repository** | Understands how to save and load League objects from JSON files on disk.
26
28
  **League** | Describes a league in terms of structure composed of teams and players.
27
29
  **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.
28
30
  **Org** | Bounded context (sub-module) dealing with overall organizational structure of a sports assocation.
@@ -36,6 +38,7 @@ Element | Description
36
38
  **Scout** | Knows how to stack rank lists of players.
37
39
  **Season** | Bounded context (sub-module) dealing with calendar and matchup generation.
38
40
  **Skip** | Result event emitted when a front office decides to skip a round.
41
+ **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.
39
42
  **Team Group** | Set of rosters that together form a cohesive league.
40
43
  **Team** | Member of a league and signs players. Has games assigned and played.
41
44
 
@@ -11,12 +11,19 @@ module Basketball
11
11
  #
12
12
  # exe/basketball-coordinator -o tmp/coordinator.json -ae
13
13
  class CoordinatorCLI
14
- attr_reader :opts, :io, :repository
14
+ attr_reader :opts, :io, :coordinator_repository
15
15
 
16
- def initialize(args:, io: $stdout)
17
- @io = io
18
- @opts = slop_parse(args)
19
- @repository = CoordinatorRepository.new
16
+ def initialize(
17
+ args:,
18
+ io: $stdout,
19
+ coordinator_repository: CoordinatorRepository.new(FileStore.new)
20
+ )
21
+ raise ArgumentError, 'coordinator_repository is required' unless coordinator_repository
22
+ raise ArgumentError, 'io is required' unless io
23
+
24
+ @io = io
25
+ @opts = slop_parse(args)
26
+ @coordinator_repository = coordinator_repository
20
27
 
21
28
  if no_input? && no_output?
22
29
  io.puts('Input and/or output paths are required.')
@@ -92,7 +99,7 @@ module Basketball
92
99
  def write(coordinator)
93
100
  path = output? ? output : input
94
101
 
95
- repository.save(path, coordinator)
102
+ coordinator_repository.save(path, coordinator)
96
103
 
97
104
  path
98
105
  end
@@ -173,7 +180,7 @@ module Basketball
173
180
  if input?
174
181
  io.puts("Coordinator loaded from: #{input}")
175
182
 
176
- repository.load(input)
183
+ coordinator_repository.load(input)
177
184
  else
178
185
  io.puts('Input path was not provided, generating fresh coordinator')
179
186
 
@@ -3,7 +3,9 @@
3
3
  module Basketball
4
4
  module App
5
5
  # Knows how to flatten a Coordinator instance and rehydrate one from JSON and/or a Ruby hash.
6
- class CoordinatorRepository
6
+ class CoordinatorRepository < DocumentRepository
7
+ include LeagueSerializable
8
+
7
9
  GAME_CLASSES = {
8
10
  'Exhibition' => Season::Exhibition,
9
11
  'Regular' => Season::Regular
@@ -11,46 +13,8 @@ module Basketball
11
13
 
12
14
  private_constant :GAME_CLASSES
13
15
 
14
- attr_reader :store
15
-
16
- def initialize(store: FileStore.new)
17
- super()
18
-
19
- @store = store
20
-
21
- freeze
22
- end
23
-
24
- def load(path)
25
- contents = store.read(path)
26
-
27
- deserialize(contents).tap do |coordinator|
28
- coordinator.send('id=', path)
29
- end
30
- end
31
-
32
- def save(path, coordinator)
33
- contents = serialize(coordinator)
34
-
35
- store.write(path, contents)
36
-
37
- coordinator.send('id=', path)
38
-
39
- coordinator
40
- end
41
-
42
16
  private
43
17
 
44
- def deserialize(string)
45
- hash = JSON.parse(string, symbolize_names: true)
46
-
47
- from_h(hash)
48
- end
49
-
50
- def serialize(object)
51
- to_h(object).to_json
52
- end
53
-
54
18
  def from_h(hash)
55
19
  Season::Coordinator.new(
56
20
  calendar: deserialize_calendar(hash[:calendar]),
@@ -71,14 +35,6 @@ module Basketball
71
35
 
72
36
  # Serialization
73
37
 
74
- def serialize_player(player)
75
- {
76
- id: player.id,
77
- overall: player.overall,
78
- position: player.position&.code
79
- }
80
- end
81
-
82
38
  def serialize_games(games)
83
39
  games.map { |game| serialize_game(game) }
84
40
  end
@@ -110,19 +66,6 @@ module Basketball
110
66
  }
111
67
  end
112
68
 
113
- def serialize_league(league)
114
- {
115
- teams: league.teams.map { |team| serialize_team(team) }
116
- }
117
- end
118
-
119
- def serialize_team(team)
120
- {
121
- id: team.id,
122
- players: team.players.map { |player| serialize_player(player) }
123
- }
124
- end
125
-
126
69
  def serialize_results(results)
127
70
  results.map do |result|
128
71
  serialize_result(result)
@@ -131,26 +74,6 @@ module Basketball
131
74
 
132
75
  # Deserialization
133
76
 
134
- def deserialize_player(player_hash)
135
- Org::Player.new(
136
- id: player_hash[:id],
137
- overall: player_hash[:overall],
138
- position: Org::Position.new(player_hash[:position])
139
- )
140
- end
141
-
142
- def deserialize_league(league_hash)
143
- team_hashes = league_hash[:teams] || []
144
-
145
- teams = team_hashes.map do |team_hash|
146
- players = (team_hash[:players] || []).map { |player_hash| deserialize_player(player_hash) }
147
-
148
- Org::Team.new(id: team_hash[:id], players:)
149
- end
150
-
151
- Org::League.new(teams:)
152
- end
153
-
154
77
  def deserialize_calendar(calendar_hash)
155
78
  Season::Calendar.new(
156
79
  preseason_start_date: Date.parse(calendar_hash[:preseason_start_date]),
@@ -0,0 +1,67 @@
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
+ protected
45
+
46
+ def from_h(hash)
47
+ Entity.new(hash[:id])
48
+ end
49
+
50
+ def to_h(entity)
51
+ { id: entity.id }
52
+ end
53
+
54
+ private
55
+
56
+ def deserialize(string)
57
+ hash = JSON.parse(string, symbolize_names: true)
58
+
59
+ from_h(hash)
60
+ end
61
+
62
+ def serialize(object)
63
+ to_h(object).to_json
64
+ end
65
+ end
66
+ end
67
+ end
@@ -4,7 +4,15 @@ module Basketball
4
4
  module App
5
5
  # Knows how to read and write documents to disk.
6
6
  class FileStore
7
+ class PathNotFoundError < StandardError; end
8
+
9
+ def exist?(path)
10
+ File.exist?(path)
11
+ end
12
+
7
13
  def read(path)
14
+ raise PathNotFoundError, "'#{path}' not found" unless exist?(path)
15
+
8
16
  File.read(path)
9
17
  end
10
18
 
@@ -17,6 +25,14 @@ module Basketball
17
25
 
18
26
  nil
19
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
20
36
  end
21
37
  end
22
38
  end
@@ -0,0 +1,42 @@
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
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Basketball
4
+ module App
5
+ # Knows how to flatten a League instance and rehydrate one from JSON and/or a Ruby hash.
6
+ class LeagueRepository < DocumentRepository
7
+ include LeagueSerializable
8
+
9
+ private
10
+
11
+ def from_h(hash)
12
+ deserialize_league(hash)
13
+ end
14
+
15
+ def to_h(league)
16
+ serialize_league(league)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Basketball
4
+ module App
5
+ # Provides methods to serialize/deserialize a League object
6
+ module LeagueSerializable
7
+ # Serialization
8
+
9
+ def serialize_league(league)
10
+ {
11
+ teams: league.teams.map { |team| serialize_team(team) }
12
+ }
13
+ end
14
+
15
+ def serialize_team(team)
16
+ {
17
+ id: team.id,
18
+ players: team.players.map { |player| serialize_player(player) }
19
+ }
20
+ end
21
+
22
+ def serialize_player(player)
23
+ {
24
+ id: player.id,
25
+ overall: player.overall,
26
+ position: player.position&.code
27
+ }
28
+ end
29
+
30
+ # Deserialization
31
+
32
+ def deserialize_league(league_hash)
33
+ team_hashes = league_hash[:teams] || []
34
+ teams = team_hashes.map { |team_hash| deserialize_team(team_hash) }
35
+
36
+ Org::League.new(teams:)
37
+ end
38
+
39
+ def deserialize_team(team_hash)
40
+ players = (team_hash[:players] || []).map { |player_hash| deserialize_player(player_hash) }
41
+
42
+ Org::Team.new(id: team_hash[:id], players:)
43
+ end
44
+
45
+ def deserialize_player(player_hash)
46
+ Org::Player.new(
47
+ id: player_hash[:id],
48
+ overall: player_hash[:overall],
49
+ position: Org::Position.new(player_hash[:position])
50
+ )
51
+ end
52
+ end
53
+ end
54
+ end
@@ -10,16 +10,25 @@ module Basketball
10
10
  # exe/basketball-room -i tmp/draft-wip.json -s 30 -t 10
11
11
  # exe/basketball-room -i tmp/draft-wip.json -ale
12
12
  #
13
- # exe/basketball-room -o tmp/draft-wip.json -ale
13
+ # exe/basketball-room -o tmp/draft-wip.json -ale -r tmp/draft-league.json
14
14
  class RoomCLI
15
15
  class PlayerNotFound < StandardError; end
16
16
 
17
- attr_reader :opts, :io, :repository
18
-
19
- def initialize(args:, io: $stdout)
20
- @io = io
21
- @opts = slop_parse(args)
22
- @repository = RoomRepository.new
17
+ attr_reader :opts,
18
+ :io,
19
+ :room_repository,
20
+ :league_repository
21
+
22
+ def initialize(
23
+ args:,
24
+ io: $stdout,
25
+ room_repository: RoomRepository.new(FileStore.new),
26
+ league_repository: LeagueRepository.new(FileStore.new)
27
+ )
28
+ @io = io
29
+ @opts = slop_parse(args)
30
+ @room_repository = room_repository
31
+ @league_repository = league_repository
23
32
 
24
33
  if opts[:input].to_s.empty? && opts[:output].to_s.empty?
25
34
  io.puts('Input and/or output paths are required.')
@@ -39,12 +48,19 @@ module Basketball
39
48
  events(room)
40
49
  league(room)
41
50
  query(room)
51
+ rosters(room)
42
52
 
43
53
  self
44
54
  end
45
55
 
46
56
  private
47
57
 
58
+ def rosters(room)
59
+ return if opts[:rosters].to_s.empty?
60
+
61
+ league_repository.save(opts[:rosters], room.league)
62
+ end
63
+
48
64
  def status(room)
49
65
  io.puts
50
66
  io.puts('Status')
@@ -74,6 +90,7 @@ module Basketball
74
90
  o.bool '-l', '--league', 'Output all teams and their picks', default: false
75
91
  o.integer '-x', '--skip', 'Number of picks to skip (default is 0).', default: 0
76
92
  o.bool '-e', '--events', 'Output event log.', default: false
93
+ o.string '-r', '--rosters', 'Path to write the resulting rosters (league) to.'
77
94
 
78
95
  o.on '-h', '--help', 'Print out help, like this is doing right now.' do
79
96
  io.puts(o)
@@ -141,7 +158,7 @@ module Basketball
141
158
  end
142
159
 
143
160
  def read
144
- repository.load(opts[:input])
161
+ room_repository.load(opts[:input])
145
162
  end
146
163
 
147
164
  # rubocop:disable Metrics/AbcSize
@@ -200,7 +217,7 @@ module Basketball
200
217
  def write(room)
201
218
  output = output_default_to_input
202
219
 
203
- repository.save(output, room)
220
+ room_repository.save(output, room)
204
221
 
205
222
  io.puts
206
223
  io.puts("Draft written to: #{output}")
@@ -3,54 +3,14 @@
3
3
  module Basketball
4
4
  module App
5
5
  # Can load and save Room objects to JSON files.
6
- class RoomRepository
6
+ class RoomRepository < DocumentRepository
7
7
  PICK_EVENT = 'Pick'
8
8
  SKIP_EVENT = 'Skip'
9
9
 
10
10
  private_constant :PICK_EVENT, :SKIP_EVENT
11
11
 
12
- attr_reader :store
13
-
14
- def initialize(store: FileStore.new)
15
- super()
16
-
17
- @store = store
18
-
19
- freeze
20
- end
21
-
22
- def load(path)
23
- contents = store.read(path)
24
-
25
- room = deserialize(contents)
26
-
27
- room.send('id=', path)
28
-
29
- room
30
- end
31
-
32
- def save(path, room)
33
- contents = serialize(room)
34
-
35
- store.write(path, contents)
36
-
37
- room.send('id=', path)
38
-
39
- room
40
- end
41
-
42
12
  private
43
13
 
44
- def deserialize(string)
45
- hash = JSON.parse(string, symbolize_names: true)
46
-
47
- from_h(hash)
48
- end
49
-
50
- def serialize(object)
51
- to_h(object).to_json
52
- end
53
-
54
14
  def from_h(hash)
55
15
  front_offices = deserialize_front_offices(hash[:front_offices])
56
16
  players = deserialize_players(hash[:players])
@@ -1,10 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Services
3
+ # Stores
4
4
  require_relative 'app/file_store'
5
+ require_relative 'app/in_memory_store'
5
6
 
6
- # Repositories
7
+ # Serialization
8
+ require_relative 'app/league_serializable'
9
+
10
+ # Repositories / Common
11
+ require_relative 'app/document_repository'
12
+
13
+ # Repositories / Implementations
7
14
  require_relative 'app/coordinator_repository'
15
+ require_relative 'app/league_repository'
8
16
  require_relative 'app/room_repository'
9
17
 
10
18
  # Controllers
@@ -3,7 +3,7 @@
3
3
  module Basketball
4
4
  module Draft
5
5
  # Main pick-by-pick iterator object which will round-robin rotate team selections.
6
- class Room
6
+ class Room < Entity
7
7
  class AlreadyPickedError < StandardError; end
8
8
  class EndOfDraftError < StandardError; end
9
9
  class EventOutOfOrderError < StandardError; end
@@ -12,9 +12,11 @@ module Basketball
12
12
  class UnknownFrontOfficeError < StandardError; end
13
13
  class UnknownPlayerError < StandardError; end
14
14
 
15
- attr_reader :rounds, :players, :front_offices, :events, :id
15
+ attr_reader :rounds, :players, :front_offices, :events
16
16
 
17
17
  def initialize(front_offices:, rounds:, players: [], events: [])
18
+ super()
19
+
18
20
  raise InvalidRoundsError, "#{rounds} should be a positive number" unless rounds.positive?
19
21
 
20
22
  @rounds = rounds
@@ -157,8 +159,6 @@ module Basketball
157
159
 
158
160
  private
159
161
 
160
- attr_writer :id
161
-
162
162
  def player_events
163
163
  events.select { |e| e.respond_to?(:player) }
164
164
  end
@@ -4,16 +4,11 @@ module Basketball
4
4
  # Base class for uniquely identifiable classes. Subclasses are simply based on a string-based ID
5
5
  # and comparison/sorting/equality will be done in a case-insensitive manner.
6
6
  class Entity
7
- extend Forwardable
8
7
  include Comparable
9
8
 
10
9
  attr_reader :id
11
10
 
12
- def_delegators :id, :to_s
13
-
14
- def initialize(id)
15
- raise ArgumentError, 'id is required' if id.to_s.empty?
16
-
11
+ def initialize(id = nil)
17
12
  @id = id
18
13
  end
19
14
 
@@ -30,8 +25,16 @@ module Basketball
30
25
  comparable_id.hash
31
26
  end
32
27
 
28
+ def to_s
29
+ id.to_s
30
+ end
31
+
33
32
  def comparable_id
34
33
  id.to_s.upcase
35
34
  end
35
+
36
+ private
37
+
38
+ attr_writer :id
36
39
  end
37
40
  end
@@ -6,18 +6,18 @@ module Basketball
6
6
  # adding teams and players to ensure the all the teams are cohesive, such as:
7
7
  # - preventing duplicate teams
8
8
  # - preventing double-signing players across teams
9
- class League
9
+ class League < Entity
10
10
  class TeamAlreadyRegisteredError < StandardError; end
11
11
  class UnregisteredTeamError < StandardError; end
12
12
 
13
13
  attr_reader :teams
14
14
 
15
15
  def initialize(teams: [])
16
+ super()
17
+
16
18
  @teams = []
17
19
 
18
20
  teams.each { |team| register!(team) }
19
-
20
- freeze
21
21
  end
22
22
 
23
23
  def to_s
@@ -63,11 +63,6 @@ module Basketball
63
63
 
64
64
  self
65
65
  end
66
-
67
- def ==(other)
68
- teams == other.teams
69
- end
70
- alias eql? ==
71
66
  end
72
67
  end
73
68
  end
@@ -4,14 +4,14 @@ module Basketball
4
4
  module Season
5
5
  # A very, very, very basic starting point for a "semi-randomized" game simulator.
6
6
  class Arena
7
- RANDOM = :random
8
- TOP_ONE = :top_one
9
- TOP_TWO = :top_two
10
- TOP_THREE = :top_three
11
- TOP_SIX = :top_six
12
- MAX_HOME_ADVANTAGE = 5
13
-
14
- STRATEGY_FREQUENCIES = {
7
+ RANDOM = :random
8
+ TOP_ONE = :top_one
9
+ TOP_TWO = :top_two
10
+ TOP_THREE = :top_three
11
+ TOP_SIX = :top_six
12
+ DEFAULT_MAX_HOME_ADVANTAGE = 5
13
+
14
+ DEFAULT_STRATEGY_FREQUENCIES = {
15
15
  RANDOM => 10,
16
16
  TOP_ONE => 5,
17
17
  TOP_TWO => 10,
@@ -19,17 +19,14 @@ module Basketball
19
19
  TOP_SIX => 30
20
20
  }.freeze
21
21
 
22
- private_constant :STRATEGY_FREQUENCIES,
23
- :RANDOM,
24
- :TOP_ONE,
25
- :TOP_TWO,
26
- :TOP_SIX,
27
- :MAX_HOME_ADVANTAGE
22
+ attr_reader :lotto, :max_home_advantage
28
23
 
29
- def initialize
30
- @lotto = STRATEGY_FREQUENCIES.inject([]) do |memo, (name, frequency)|
31
- memo + ([name] * frequency)
32
- end.shuffle
24
+ def initialize(
25
+ strategy_frquencies: DEFAULT_STRATEGY_FREQUENCIES,
26
+ max_home_advantage: DEFAULT_MAX_HOME_ADVANTAGE
27
+ )
28
+ @max_home_advantage = max_home_advantage
29
+ @lotto = make_lotto(strategy_frquencies)
33
30
 
34
31
  freeze
35
32
  end
@@ -57,7 +54,11 @@ module Basketball
57
54
 
58
55
  private
59
56
 
60
- attr_reader :lotto
57
+ def make_lotto(strategy_frquencies)
58
+ strategy_frquencies.inject([]) do |memo, (name, frequency)|
59
+ memo + ([name] * frequency)
60
+ end.shuffle
61
+ end
61
62
 
62
63
  def pick_strategy
63
64
  lotto.sample
@@ -89,7 +90,7 @@ module Basketball
89
90
  end
90
91
 
91
92
  def random_home_advantage
92
- rand(0..MAX_HOME_ADVANTAGE)
93
+ rand(0..max_home_advantage)
93
94
  end
94
95
 
95
96
  def top_one_strategy(matchup)
@@ -3,7 +3,7 @@
3
3
  module Basketball
4
4
  module Season
5
5
  # Main iterator-based object that knows how to manage a calendar and simulate games per day.
6
- class Coordinator
6
+ class Coordinator < Entity
7
7
  extend Forwardable
8
8
 
9
9
  class AlreadyPlayedGameError < StandardError; end
@@ -18,8 +18,7 @@ module Basketball
18
18
  :current_date,
19
19
  :arena,
20
20
  :results,
21
- :league,
22
- :id
21
+ :league
23
22
 
24
23
  def_delegators :calendar,
25
24
  :preseason_start_date,
@@ -156,7 +155,7 @@ module Basketball
156
155
 
157
156
  private
158
157
 
159
- attr_writer :id, :arena
158
+ attr_writer :arena
160
159
 
161
160
  def opponent_team(opponent)
162
161
  league.teams.find { |t| t == opponent }
@@ -1,37 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'value_object_dsl'
4
+
3
5
  module Basketball
4
6
  # A Value Object is something that has no specific identity, instead its identity is the sum of
5
7
  # the attribute values. Changing one will change the entire object's identity.
6
- # Comes with a very simple DSL for specifying properties along with base equality and sorting methods.
8
+ # Comes with a very simple DSL provided by ValueObjectDSL for specifying properties along with
9
+ # base equality and sorting methods.
7
10
  class ValueObject
8
11
  include Comparable
9
-
10
- class << self
11
- def all_value_keys
12
- @all_value_keys ||= ancestors.flat_map do |ancestor|
13
- if ancestor < Basketball::ValueObject
14
- ancestor.value_keys
15
- else
16
- []
17
- end
18
- end.uniq.sort
19
- end
20
-
21
- def all_sorted_value_keys
22
- all_value_keys.sort
23
- end
24
-
25
- def value_keys
26
- @value_keys ||= []
27
- end
28
-
29
- def value_reader(*keys)
30
- keys.each { |k| value_keys << k.to_sym }
31
-
32
- attr_reader(*keys)
33
- end
34
- end
12
+ extend ValueObjectDSL
35
13
 
36
14
  def to_s
37
15
  to_h.map { |k, v| "#{k}: #{v}" }.join(', ')
@@ -55,7 +33,7 @@ module Basketball
55
33
  alias eql? ==
56
34
 
57
35
  def hash
58
- all_sorted_values.map(&:hash).hash
36
+ all_sorted_values.hash
59
37
  end
60
38
 
61
39
  def all_sorted_values
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Basketball
4
+ # Class-level methods that extend core Ruby attr_* methods.
5
+ module ValueObjectDSL
6
+ def all_value_keys
7
+ @all_value_keys ||= ancestors.flat_map do |ancestor|
8
+ if ancestor < Basketball::ValueObject
9
+ ancestor.value_keys
10
+ else
11
+ []
12
+ end
13
+ end.uniq.sort
14
+ end
15
+
16
+ def all_sorted_value_keys
17
+ all_value_keys.sort
18
+ end
19
+
20
+ def value_keys
21
+ @value_keys ||= []
22
+ end
23
+
24
+ def value_reader(*keys)
25
+ keys.each { |k| value_keys << k.to_sym }
26
+
27
+ attr_reader(*keys)
28
+ end
29
+ end
30
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Basketball
4
- VERSION = '0.0.9'
4
+ VERSION = '0.0.10'
5
5
  end
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.9
4
+ version: 0.0.10
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-30 00:00:00.000000000 Z
11
+ date: 2023-06-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: slop
@@ -54,7 +54,11 @@ files:
54
54
  - lib/basketball/app.rb
55
55
  - lib/basketball/app/coordinator_cli.rb
56
56
  - lib/basketball/app/coordinator_repository.rb
57
+ - lib/basketball/app/document_repository.rb
57
58
  - lib/basketball/app/file_store.rb
59
+ - lib/basketball/app/in_memory_store.rb
60
+ - lib/basketball/app/league_repository.rb
61
+ - lib/basketball/app/league_serializable.rb
58
62
  - lib/basketball/app/room_cli.rb
59
63
  - lib/basketball/app/room_repository.rb
60
64
  - lib/basketball/draft.rb
@@ -82,6 +86,7 @@ files:
82
86
  - lib/basketball/season/regular.rb
83
87
  - lib/basketball/season/result.rb
84
88
  - lib/basketball/value_object.rb
89
+ - lib/basketball/value_object_dsl.rb
85
90
  - lib/basketball/version.rb
86
91
  homepage: https://github.com/mattruggio/basketball
87
92
  licenses: