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