basketball 0.0.9 → 0.0.10

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