basketball 0.0.8 → 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 +4 -4
- data/.rubocop.yml +9 -19
- data/CHANGELOG.md +1 -39
- data/README.md +75 -93
- data/basketball.gemspec +3 -6
- data/exe/{basketball-season-scheduling → basketball-coordinator} +1 -1
- data/exe/{basketball-draft → basketball-room} +1 -1
- data/lib/basketball/app/coordinator_cli.rb +250 -0
- data/lib/basketball/app/coordinator_repository.rb +114 -0
- data/lib/basketball/app/document_repository.rb +67 -0
- data/lib/basketball/app/file_store.rb +38 -0
- data/lib/basketball/app/in_memory_store.rb +42 -0
- data/lib/basketball/app/league_repository.rb +20 -0
- data/lib/basketball/app/league_serializable.rb +54 -0
- data/lib/basketball/{draft/cli.rb → app/room_cli.rb} +74 -80
- data/lib/basketball/app/room_repository.rb +149 -0
- data/lib/basketball/app.rb +20 -0
- data/lib/basketball/draft/assessment.rb +31 -0
- data/lib/basketball/draft/event.rb +3 -2
- data/lib/basketball/draft/front_office.rb +35 -28
- data/lib/basketball/draft/{pick_event.rb → pick.rb} +13 -6
- data/lib/basketball/draft/room.rb +119 -119
- data/lib/basketball/draft/{player_search.rb → scout.rb} +4 -9
- data/lib/basketball/draft/skip.rb +12 -0
- data/lib/basketball/draft.rb +13 -6
- data/lib/basketball/entity.rb +19 -10
- data/lib/basketball/org/league.rb +68 -0
- data/lib/basketball/org/player.rb +26 -0
- data/lib/basketball/{draft → org}/position.rb +3 -2
- data/lib/basketball/org/team.rb +38 -0
- data/lib/basketball/org.rb +12 -0
- data/lib/basketball/season/arena.rb +113 -0
- data/lib/basketball/season/calendar.rb +41 -72
- data/lib/basketball/season/coordinator.rb +186 -128
- data/lib/basketball/season/{preseason_game.rb → exhibition.rb} +2 -1
- data/lib/basketball/season/game.rb +15 -10
- data/lib/basketball/season/matchup.rb +27 -0
- data/lib/basketball/season/opponent.rb +15 -0
- data/lib/basketball/season/{season_game.rb → regular.rb} +2 -1
- data/lib/basketball/season/result.rb +37 -0
- data/lib/basketball/season.rb +12 -13
- data/lib/basketball/value_object.rb +8 -27
- data/lib/basketball/value_object_dsl.rb +30 -0
- data/lib/basketball/version.rb +1 -1
- data/lib/basketball.rb +9 -4
- metadata +37 -44
- data/lib/basketball/draft/league.rb +0 -70
- data/lib/basketball/draft/player.rb +0 -43
- data/lib/basketball/draft/room_serializer.rb +0 -186
- data/lib/basketball/draft/roster.rb +0 -37
- data/lib/basketball/draft/sim_event.rb +0 -23
- data/lib/basketball/draft/skip_event.rb +0 -13
- data/lib/basketball/season/calendar_serializer.rb +0 -94
- data/lib/basketball/season/conference.rb +0 -57
- data/lib/basketball/season/division.rb +0 -43
- data/lib/basketball/season/league.rb +0 -114
- data/lib/basketball/season/league_serializer.rb +0 -99
- data/lib/basketball/season/scheduling_cli.rb +0 -198
- data/lib/basketball/season/team.rb +0 -21
@@ -1,142 +1,148 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative 'league'
|
4
|
-
|
5
3
|
module Basketball
|
6
4
|
module Draft
|
7
|
-
|
5
|
+
# Main pick-by-pick iterator object which will round-robin rotate team selections.
|
6
|
+
class Room < Entity
|
8
7
|
class AlreadyPickedError < StandardError; end
|
9
|
-
class
|
8
|
+
class EndOfDraftError < StandardError; end
|
10
9
|
class EventOutOfOrderError < StandardError; end
|
11
|
-
class
|
10
|
+
class FrontOfficeAlreadyRegisteredError < StandardError; end
|
11
|
+
class PlayerAlreadyAddedError < StandardError; end
|
12
12
|
class UnknownFrontOfficeError < StandardError; end
|
13
|
-
class
|
14
|
-
|
15
|
-
DEFAULT_ROUNDS = 12
|
13
|
+
class UnknownPlayerError < StandardError; end
|
16
14
|
|
17
|
-
|
15
|
+
attr_reader :rounds, :players, :front_offices, :events
|
18
16
|
|
19
|
-
|
17
|
+
def initialize(front_offices:, rounds:, players: [], events: [])
|
18
|
+
super()
|
20
19
|
|
21
|
-
|
22
|
-
@players_by_id = players.to_h { |p| [p.id, p] }
|
23
|
-
@front_offices_by_id = front_offices.to_h { |fo| [fo.id, fo] }
|
24
|
-
@events = []
|
25
|
-
@rounds = rounds.to_i
|
20
|
+
raise InvalidRoundsError, "#{rounds} should be a positive number" unless rounds.positive?
|
26
21
|
|
27
|
-
|
28
|
-
|
22
|
+
@rounds = rounds
|
23
|
+
@players = []
|
24
|
+
@front_offices = []
|
25
|
+
@events = []
|
29
26
|
|
30
|
-
|
27
|
+
front_offices.each { |front_office| register!(front_office) }
|
28
|
+
players.each { |player| add_player!(player) }
|
29
|
+
events.each { |event| add_event!(event) }
|
31
30
|
end
|
32
31
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
32
|
+
# This method will return a materialized list of teams and their selections.
|
33
|
+
def league
|
34
|
+
Org::League.new.tap do |league|
|
35
|
+
front_offices.each do |front_office|
|
36
|
+
team = Org::Team.new(id: front_office.id)
|
38
37
|
|
39
|
-
|
40
|
-
|
38
|
+
league.register!(team)
|
39
|
+
|
40
|
+
drafted_players(front_office).each do |player|
|
41
|
+
league.sign!(player:, team:)
|
42
|
+
end
|
41
43
|
end
|
42
44
|
end
|
43
45
|
end
|
44
46
|
|
45
|
-
|
46
|
-
events.join("\n")
|
47
|
-
end
|
47
|
+
### Peek Methods
|
48
48
|
|
49
|
-
def
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
49
|
+
def assessment
|
50
|
+
Assessment.new(
|
51
|
+
drafted_players: drafted_players(front_office),
|
52
|
+
undrafted_players:,
|
53
|
+
pick:,
|
54
|
+
round:,
|
55
|
+
round_pick:
|
56
|
+
)
|
55
57
|
end
|
56
58
|
|
57
59
|
def total_picks
|
58
60
|
rounds * front_offices.length
|
59
61
|
end
|
60
62
|
|
61
|
-
def
|
63
|
+
def round
|
62
64
|
return if done?
|
63
65
|
|
64
|
-
(
|
66
|
+
(pick / front_offices.length.to_f).ceil
|
65
67
|
end
|
66
68
|
|
67
|
-
def
|
69
|
+
def round_pick
|
68
70
|
return if done?
|
69
71
|
|
70
|
-
mod =
|
72
|
+
mod = pick % front_offices.length
|
71
73
|
|
72
74
|
mod.positive? ? mod : front_offices.length
|
73
75
|
end
|
74
76
|
|
75
|
-
def
|
77
|
+
def front_office
|
76
78
|
return if done?
|
77
79
|
|
78
|
-
front_offices[
|
80
|
+
front_offices[round_pick - 1]
|
79
81
|
end
|
80
82
|
|
81
|
-
def
|
83
|
+
def pick
|
82
84
|
return if done?
|
83
85
|
|
84
|
-
|
86
|
+
internal_pick
|
85
87
|
end
|
86
88
|
|
87
89
|
def remaining_picks
|
88
|
-
total_picks -
|
90
|
+
total_picks - internal_pick + 1
|
89
91
|
end
|
90
92
|
|
91
93
|
def done?
|
92
|
-
|
94
|
+
internal_pick > total_picks
|
93
95
|
end
|
94
96
|
|
95
97
|
def not_done?
|
96
98
|
!done?
|
97
99
|
end
|
98
100
|
|
101
|
+
def drafted_players(front_office = nil)
|
102
|
+
raise UnknownFrontOfficeError, "#{front_office} doesnt exist" if front_office && !registered?(front_office)
|
103
|
+
|
104
|
+
player_events.each_with_object([]) do |e, memo|
|
105
|
+
next unless front_office.nil? || e.front_office == front_office
|
106
|
+
|
107
|
+
memo << e.player
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def undrafted_players
|
112
|
+
players - drafted_players
|
113
|
+
end
|
114
|
+
|
115
|
+
### Event Methods
|
116
|
+
|
99
117
|
def skip!
|
100
118
|
return if done?
|
101
119
|
|
102
|
-
event =
|
103
|
-
front_office: current_front_office,
|
104
|
-
pick: current_pick,
|
105
|
-
round: current_round,
|
106
|
-
round_pick: current_round_pick
|
107
|
-
)
|
120
|
+
event = Skip.new(front_office:, pick:, round:, round_pick:)
|
108
121
|
|
109
|
-
|
122
|
+
add_event!(event)
|
110
123
|
|
111
124
|
event
|
112
125
|
end
|
113
126
|
|
114
|
-
def sim!
|
115
|
-
|
116
|
-
|
127
|
+
def sim!
|
128
|
+
return if done?
|
129
|
+
|
130
|
+
player = front_office.pick(assessment)
|
131
|
+
event = Pick.new(front_office:, pick:, round:, round_pick:, player:, auto: true)
|
117
132
|
|
118
|
-
|
119
|
-
front_office = current_front_office
|
133
|
+
add_event!(event)
|
120
134
|
|
121
|
-
|
122
|
-
|
123
|
-
drafted_players: drafted_players(front_office),
|
124
|
-
round: current_round
|
125
|
-
)
|
135
|
+
event
|
136
|
+
end
|
126
137
|
|
127
|
-
|
128
|
-
|
129
|
-
player:,
|
130
|
-
pick: current_pick,
|
131
|
-
round: current_round,
|
132
|
-
round_pick: current_round_pick
|
133
|
-
)
|
138
|
+
def sim_rest!
|
139
|
+
events = []
|
134
140
|
|
135
|
-
|
141
|
+
while not_done?
|
142
|
+
event = sim!
|
136
143
|
|
137
|
-
yield
|
144
|
+
yield event if block_given?
|
138
145
|
|
139
|
-
counter += 1
|
140
146
|
events << event
|
141
147
|
end
|
142
148
|
|
@@ -146,76 +152,70 @@ module Basketball
|
|
146
152
|
def pick!(player)
|
147
153
|
return nil if done?
|
148
154
|
|
149
|
-
event =
|
150
|
-
front_office: current_front_office,
|
151
|
-
player:,
|
152
|
-
pick: current_pick,
|
153
|
-
round: current_round,
|
154
|
-
round_pick: current_round_pick
|
155
|
-
)
|
155
|
+
event = Pick.new(front_office:, pick:, round:, round_pick:, player:)
|
156
156
|
|
157
|
-
|
157
|
+
add_event!(event)
|
158
158
|
end
|
159
159
|
|
160
|
-
|
161
|
-
players - drafted_players
|
162
|
-
end
|
160
|
+
private
|
163
161
|
|
164
|
-
def
|
165
|
-
|
162
|
+
def player_events
|
163
|
+
events.select { |e| e.respond_to?(:player) }
|
166
164
|
end
|
167
165
|
|
168
|
-
|
166
|
+
# rubocop:disable Metrics/AbcSize
|
167
|
+
def add_event!(event)
|
168
|
+
raise EndOfDraftError, "#{total_picks} pick limit reached" if done?
|
169
|
+
raise UnknownFrontOfficeError, "#{front_office} doesnt exist" unless front_offices.include?(event.front_office)
|
170
|
+
raise EventOutOfOrderError, "#{event.front_office} cant pick right now" if event.front_office != front_office
|
171
|
+
raise EventOutOfOrderError, "#{event} has wrong pick" if event.pick != pick
|
172
|
+
raise EventOutOfOrderError, "#{event} has wrong round" if event.round != round
|
173
|
+
raise EventOutOfOrderError, "#{event} has wrong round_pick" if event.round_pick != round_pick
|
169
174
|
|
170
|
-
|
175
|
+
assert_player(event)
|
171
176
|
|
172
|
-
|
173
|
-
|
177
|
+
events << event
|
178
|
+
|
179
|
+
event
|
174
180
|
end
|
181
|
+
# rubocop:enable Metrics/AbcSize
|
175
182
|
|
176
|
-
def
|
177
|
-
|
183
|
+
def assert_player(event)
|
184
|
+
return unless event.respond_to?(:player)
|
185
|
+
|
186
|
+
raise AlreadyPickedError, "#{event.player} was already picked" if drafted_players.include?(event.player)
|
187
|
+
raise UnknownPlayerError, "#{event.player} doesnt exist" unless players.include?(event.player)
|
178
188
|
end
|
179
189
|
|
180
|
-
def
|
181
|
-
|
182
|
-
|
190
|
+
def internal_pick
|
191
|
+
events.length + 1
|
192
|
+
end
|
183
193
|
|
184
|
-
|
185
|
-
|
194
|
+
def registered?(front_office)
|
195
|
+
front_offices.include?(front_office)
|
186
196
|
end
|
187
197
|
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
def play!(event)
|
192
|
-
if event.respond_to?(:player) && drafted_players.include?(event.player)
|
193
|
-
raise AlreadyPickedError, "#{player} was already picked"
|
194
|
-
end
|
198
|
+
def register!(front_office)
|
199
|
+
raise ArgumentError, 'front_office required' unless front_office
|
200
|
+
raise FrontOfficeAlreadyRegisteredError, "#{front_office} already registered" if registered?(front_office)
|
195
201
|
|
196
|
-
|
197
|
-
raise UnknownPlayerError, "#{event.player} doesnt exist"
|
198
|
-
end
|
202
|
+
front_offices << front_office
|
199
203
|
|
200
|
-
|
201
|
-
|
202
|
-
end
|
204
|
+
self
|
205
|
+
end
|
203
206
|
|
204
|
-
|
207
|
+
def player?(player)
|
208
|
+
players.include?(player)
|
209
|
+
end
|
205
210
|
|
206
|
-
|
207
|
-
raise
|
208
|
-
raise
|
209
|
-
raise EventOutOfOrder, "#{event} has wrong round_pick" if event.round_pick != current_round_pick
|
210
|
-
raise EndOfDraftError, "#{total_picks} pick limit reached" if events.length > total_picks + 1
|
211
|
+
def add_player!(player)
|
212
|
+
raise ArgumentError, 'player required' unless player
|
213
|
+
raise PlayerAlreadyAddedError, "#{player} already added" if player?(player)
|
211
214
|
|
212
|
-
|
215
|
+
players << player
|
213
216
|
|
214
|
-
|
217
|
+
self
|
215
218
|
end
|
216
|
-
# rubocop:enable Metrics/AbcSize
|
217
|
-
# rubocop:enable Metrics/CyclomaticComplexity
|
218
|
-
# rubocop:enable Metrics/PerceivedComplexity
|
219
219
|
end
|
220
220
|
end
|
221
221
|
end
|
@@ -2,14 +2,9 @@
|
|
2
2
|
|
3
3
|
module Basketball
|
4
4
|
module Draft
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
def initialize(players = [])
|
9
|
-
@players = players
|
10
|
-
end
|
11
|
-
|
12
|
-
def query(position: nil, exclude_positions: [])
|
5
|
+
# A Scout knows how to process a set of players and figure out who the top prospects are.
|
6
|
+
class Scout
|
7
|
+
def top_for(players: [], position: nil, exclude_positions: [])
|
13
8
|
filtered_players = players
|
14
9
|
|
15
10
|
if position
|
@@ -24,7 +19,7 @@ module Basketball
|
|
24
19
|
end
|
25
20
|
end
|
26
21
|
|
27
|
-
filtered_players.sort_by
|
22
|
+
filtered_players.sort_by { |p| [p.overall, p.id] }.reverse
|
28
23
|
end
|
29
24
|
end
|
30
25
|
end
|
data/lib/basketball/draft.rb
CHANGED
@@ -1,9 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
# Services
|
4
|
+
require_relative 'draft/scout'
|
4
5
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
6
|
+
# Common
|
7
|
+
require_relative 'draft/assessment'
|
8
|
+
require_relative 'draft/event'
|
9
|
+
require_relative 'draft/front_office'
|
10
|
+
|
11
|
+
# Event Subclasses
|
12
|
+
require_relative 'draft/pick'
|
13
|
+
require_relative 'draft/skip'
|
14
|
+
|
15
|
+
# Specific
|
16
|
+
require_relative 'draft/room'
|
data/lib/basketball/entity.rb
CHANGED
@@ -1,31 +1,40 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Basketball
|
4
|
+
# Base class for uniquely identifiable classes. Subclasses are simply based on a string-based ID
|
5
|
+
# and comparison/sorting/equality will be done in a case-insensitive manner.
|
4
6
|
class Entity
|
5
|
-
extend Forwardable
|
6
7
|
include Comparable
|
7
8
|
|
8
9
|
attr_reader :id
|
9
10
|
|
10
|
-
|
11
|
-
|
12
|
-
def initialize(id)
|
13
|
-
raise ArgumentError, 'id is required' if id.to_s.empty?
|
14
|
-
|
15
|
-
@id = id.to_s.upcase
|
11
|
+
def initialize(id = nil)
|
12
|
+
@id = id
|
16
13
|
end
|
17
14
|
|
18
15
|
def <=>(other)
|
19
|
-
|
16
|
+
comparable_id <=> other.comparable_id
|
20
17
|
end
|
21
18
|
|
22
19
|
def ==(other)
|
23
|
-
|
20
|
+
comparable_id == other.comparable_id
|
24
21
|
end
|
25
22
|
alias eql? ==
|
26
23
|
|
27
24
|
def hash
|
28
|
-
|
25
|
+
comparable_id.hash
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_s
|
29
|
+
id.to_s
|
29
30
|
end
|
31
|
+
|
32
|
+
def comparable_id
|
33
|
+
id.to_s.upcase
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
attr_writer :id
|
30
39
|
end
|
31
40
|
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Basketball
|
4
|
+
module Org
|
5
|
+
# Describes a collection of teams and players. Holds the rules which support
|
6
|
+
# adding teams and players to ensure the all the teams are cohesive, such as:
|
7
|
+
# - preventing duplicate teams
|
8
|
+
# - preventing double-signing players across teams
|
9
|
+
class League < Entity
|
10
|
+
class TeamAlreadyRegisteredError < StandardError; end
|
11
|
+
class UnregisteredTeamError < StandardError; end
|
12
|
+
|
13
|
+
attr_reader :teams
|
14
|
+
|
15
|
+
def initialize(teams: [])
|
16
|
+
super()
|
17
|
+
|
18
|
+
@teams = []
|
19
|
+
|
20
|
+
teams.each { |team| register!(team) }
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_s
|
24
|
+
teams.map(&:to_s).join("\n")
|
25
|
+
end
|
26
|
+
|
27
|
+
def sign!(player:, team:)
|
28
|
+
raise ArgumentError, 'player is required' unless player
|
29
|
+
raise ArgumentError, 'team is required' unless team
|
30
|
+
raise UnregisteredTeamError, "#{team} is not registered" unless registered?(team)
|
31
|
+
raise PlayerAlreadySignedError, "#{player} is already signed" if signed?(player)
|
32
|
+
|
33
|
+
team.sign!(player)
|
34
|
+
|
35
|
+
self
|
36
|
+
end
|
37
|
+
|
38
|
+
def signed?(player)
|
39
|
+
players.include?(player)
|
40
|
+
end
|
41
|
+
|
42
|
+
def players
|
43
|
+
teams.flat_map(&:players)
|
44
|
+
end
|
45
|
+
|
46
|
+
def not_registered?(team)
|
47
|
+
!registered?(team)
|
48
|
+
end
|
49
|
+
|
50
|
+
def registered?(team)
|
51
|
+
teams.include?(team)
|
52
|
+
end
|
53
|
+
|
54
|
+
def register!(team)
|
55
|
+
raise ArgumentError, 'team is required' unless team
|
56
|
+
raise TeamAlreadyRegisteredError, "#{team} already registered" if registered?(team)
|
57
|
+
|
58
|
+
team.players.each do |player|
|
59
|
+
raise PlayerAlreadySignedError, "#{player} already signed" if signed?(player)
|
60
|
+
end
|
61
|
+
|
62
|
+
teams << team
|
63
|
+
|
64
|
+
self
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Basketball
|
4
|
+
module Org
|
5
|
+
# Base class describing a player.
|
6
|
+
# A consumer application should extend these specific to their specific sports traits.
|
7
|
+
class Player < Entity
|
8
|
+
attr_reader :overall, :position
|
9
|
+
|
10
|
+
def initialize(id:, overall: 0, position: nil)
|
11
|
+
super(id)
|
12
|
+
|
13
|
+
raise ArgumentError, 'position is required' unless position
|
14
|
+
|
15
|
+
@overall = overall
|
16
|
+
@position = position
|
17
|
+
|
18
|
+
freeze
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_s
|
22
|
+
"[#{super}] (#{position}) #{overall}".strip
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -1,7 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Basketball
|
4
|
-
module
|
4
|
+
module Org
|
5
|
+
# Describes a player position.
|
5
6
|
class Position < ValueObject
|
6
7
|
extend Forwardable
|
7
8
|
|
@@ -17,7 +18,7 @@ module Basketball
|
|
17
18
|
FRONT_COURT_VALUES = %w[PF C].to_set.freeze
|
18
19
|
ALL_VALUES = (BACK_COURT_VALUES.to_a + FRONT_COURT_VALUES.to_a).to_set.freeze
|
19
20
|
|
20
|
-
|
21
|
+
value_reader :code
|
21
22
|
|
22
23
|
def_delegators :code, :to_s
|
23
24
|
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Basketball
|
4
|
+
module Org
|
5
|
+
# Base class describing a team. A team here is bare metal and is just described by an ID
|
6
|
+
# and a collection of Player objects.
|
7
|
+
class Team < Entity
|
8
|
+
attr_reader :players
|
9
|
+
|
10
|
+
def initialize(id:, players: [])
|
11
|
+
super(id)
|
12
|
+
|
13
|
+
@players = []
|
14
|
+
|
15
|
+
players.each { |p| sign!(p) }
|
16
|
+
|
17
|
+
freeze
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_s
|
21
|
+
([super.to_s] + players.map(&:to_s)).join("\n")
|
22
|
+
end
|
23
|
+
|
24
|
+
def signed?(player)
|
25
|
+
players.include?(player)
|
26
|
+
end
|
27
|
+
|
28
|
+
def sign!(player)
|
29
|
+
raise ArgumentError, 'player is required' unless player
|
30
|
+
raise PlayerAlreadySignedError, "#{player} already signed by #{self}" if signed?(player)
|
31
|
+
|
32
|
+
players << player
|
33
|
+
|
34
|
+
self
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'org/player'
|
4
|
+
require_relative 'org/position'
|
5
|
+
require_relative 'org/team'
|
6
|
+
require_relative 'org/league'
|
7
|
+
|
8
|
+
module Basketball
|
9
|
+
module Org
|
10
|
+
class PlayerAlreadySignedError < StandardError; end
|
11
|
+
end
|
12
|
+
end
|