basketball 0.0.9 → 0.0.11
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/README.md +31 -21
 - data/basketball.gemspec +14 -8
 - data/exe/basketball +91 -0
 - data/lib/basketball/app/coordinator_cli.rb +56 -72
 - data/lib/basketball/app/coordinator_repository.rb +12 -88
 - data/lib/basketball/app/document_repository.rb +67 -0
 - data/lib/basketball/app/file_store.rb +16 -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 +99 -0
 - data/lib/basketball/app/room_cli.rb +30 -26
 - data/lib/basketball/app/room_repository.rb +1 -41
 - data/lib/basketball/app.rb +10 -2
 - data/lib/basketball/draft/pick.rb +0 -7
 - data/lib/basketball/draft/room.rb +11 -13
 - data/lib/basketball/entity.rb +9 -6
 - data/lib/basketball/org/conference.rb +47 -0
 - data/lib/basketball/org/division.rb +43 -0
 - data/lib/basketball/org/has_divisions.rb +25 -0
 - data/lib/basketball/org/has_players.rb +20 -0
 - data/lib/basketball/org/has_teams.rb +24 -0
 - data/lib/basketball/org/league.rb +59 -32
 - data/lib/basketball/org.rb +12 -1
 - data/lib/basketball/season/arena.rb +26 -25
 - data/lib/basketball/season/calendar.rb +52 -22
 - data/lib/basketball/season/coordinator.rb +25 -18
 - data/lib/basketball/season/detail.rb +47 -0
 - data/lib/basketball/season/exhibition.rb +1 -1
 - data/lib/basketball/season/opponent.rb +6 -0
 - data/lib/basketball/season/record.rb +92 -0
 - data/lib/basketball/season/scheduler.rb +223 -0
 - data/lib/basketball/season/standings.rb +56 -0
 - data/lib/basketball/season.rb +6 -0
 - data/lib/basketball/value_object.rb +6 -28
 - data/lib/basketball/value_object_dsl.rb +30 -0
 - data/lib/basketball/version.rb +1 -1
 - metadata +22 -6
 - /data/exe/{basketball-room → basketball-draft-room} +0 -0
 - /data/exe/{basketball-coordinator → basketball-season-coordinator} +0 -0
 
| 
         @@ -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,99 @@ 
     | 
|
| 
      
 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 
     | 
    
         
            +
                      id: league.id,
         
     | 
| 
      
 12 
     | 
    
         
            +
                      conferences: serialize_conferences(league.conferences)
         
     | 
| 
      
 13 
     | 
    
         
            +
                    }
         
     | 
| 
      
 14 
     | 
    
         
            +
                  end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                  def serialize_conferences(conferences)
         
     | 
| 
      
 17 
     | 
    
         
            +
                    conferences.map do |conference|
         
     | 
| 
      
 18 
     | 
    
         
            +
                      {
         
     | 
| 
      
 19 
     | 
    
         
            +
                        id: conference.id,
         
     | 
| 
      
 20 
     | 
    
         
            +
                        divisions: serialize_divisions(conference.divisions)
         
     | 
| 
      
 21 
     | 
    
         
            +
                      }
         
     | 
| 
      
 22 
     | 
    
         
            +
                    end
         
     | 
| 
      
 23 
     | 
    
         
            +
                  end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                  def serialize_divisions(divisions)
         
     | 
| 
      
 26 
     | 
    
         
            +
                    divisions.map do |division|
         
     | 
| 
      
 27 
     | 
    
         
            +
                      {
         
     | 
| 
      
 28 
     | 
    
         
            +
                        id: division.id,
         
     | 
| 
      
 29 
     | 
    
         
            +
                        teams: serialize_teams(division.teams)
         
     | 
| 
      
 30 
     | 
    
         
            +
                      }
         
     | 
| 
      
 31 
     | 
    
         
            +
                    end
         
     | 
| 
      
 32 
     | 
    
         
            +
                  end
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                  def serialize_teams(teams)
         
     | 
| 
      
 35 
     | 
    
         
            +
                    teams.map do |team|
         
     | 
| 
      
 36 
     | 
    
         
            +
                      {
         
     | 
| 
      
 37 
     | 
    
         
            +
                        id: team.id,
         
     | 
| 
      
 38 
     | 
    
         
            +
                        players: serialize_players(team.players)
         
     | 
| 
      
 39 
     | 
    
         
            +
                      }
         
     | 
| 
      
 40 
     | 
    
         
            +
                    end
         
     | 
| 
      
 41 
     | 
    
         
            +
                  end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                  def serialize_players(players)
         
     | 
| 
      
 44 
     | 
    
         
            +
                    players.map do |player|
         
     | 
| 
      
 45 
     | 
    
         
            +
                      {
         
     | 
| 
      
 46 
     | 
    
         
            +
                        id: player.id,
         
     | 
| 
      
 47 
     | 
    
         
            +
                        overall: player.overall,
         
     | 
| 
      
 48 
     | 
    
         
            +
                        position: player.position&.code
         
     | 
| 
      
 49 
     | 
    
         
            +
                      }
         
     | 
| 
      
 50 
     | 
    
         
            +
                    end
         
     | 
| 
      
 51 
     | 
    
         
            +
                  end
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                  # Deserialization
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                  def deserialize_league(league_hash)
         
     | 
| 
      
 56 
     | 
    
         
            +
                    Org::League.new(
         
     | 
| 
      
 57 
     | 
    
         
            +
                      conferences: deserialize_conferences(league_hash[:conferences])
         
     | 
| 
      
 58 
     | 
    
         
            +
                    )
         
     | 
| 
      
 59 
     | 
    
         
            +
                  end
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
                  def deserialize_conferences(conference_hashes)
         
     | 
| 
      
 62 
     | 
    
         
            +
                    (conference_hashes || []).map do |conference_hash|
         
     | 
| 
      
 63 
     | 
    
         
            +
                      Org::Conference.new(
         
     | 
| 
      
 64 
     | 
    
         
            +
                        id: conference_hash[:id],
         
     | 
| 
      
 65 
     | 
    
         
            +
                        divisions: deserialize_divisions(conference_hash[:divisions])
         
     | 
| 
      
 66 
     | 
    
         
            +
                      )
         
     | 
| 
      
 67 
     | 
    
         
            +
                    end
         
     | 
| 
      
 68 
     | 
    
         
            +
                  end
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
                  def deserialize_divisions(division_hashes)
         
     | 
| 
      
 71 
     | 
    
         
            +
                    (division_hashes || []).map do |division_hash|
         
     | 
| 
      
 72 
     | 
    
         
            +
                      Org::Division.new(
         
     | 
| 
      
 73 
     | 
    
         
            +
                        id: division_hash[:id],
         
     | 
| 
      
 74 
     | 
    
         
            +
                        teams: deserialize_teams(division_hash[:teams])
         
     | 
| 
      
 75 
     | 
    
         
            +
                      )
         
     | 
| 
      
 76 
     | 
    
         
            +
                    end
         
     | 
| 
      
 77 
     | 
    
         
            +
                  end
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
                  def deserialize_teams(team_hashes)
         
     | 
| 
      
 80 
     | 
    
         
            +
                    (team_hashes || []).map do |team_hash|
         
     | 
| 
      
 81 
     | 
    
         
            +
                      Org::Team.new(
         
     | 
| 
      
 82 
     | 
    
         
            +
                        id: team_hash[:id],
         
     | 
| 
      
 83 
     | 
    
         
            +
                        players: deserialize_players(team_hash[:players])
         
     | 
| 
      
 84 
     | 
    
         
            +
                      )
         
     | 
| 
      
 85 
     | 
    
         
            +
                    end
         
     | 
| 
      
 86 
     | 
    
         
            +
                  end
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
                  def deserialize_players(player_hashes)
         
     | 
| 
      
 89 
     | 
    
         
            +
                    (player_hashes || []).map do |player_hash|
         
     | 
| 
      
 90 
     | 
    
         
            +
                      Org::Player.new(
         
     | 
| 
      
 91 
     | 
    
         
            +
                        id: player_hash[:id],
         
     | 
| 
      
 92 
     | 
    
         
            +
                        overall: player_hash[:overall],
         
     | 
| 
      
 93 
     | 
    
         
            +
                        position: Org::Position.new(player_hash[:position])
         
     | 
| 
      
 94 
     | 
    
         
            +
                      )
         
     | 
| 
      
 95 
     | 
    
         
            +
                    end
         
     | 
| 
      
 96 
     | 
    
         
            +
                  end
         
     | 
| 
      
 97 
     | 
    
         
            +
                end
         
     | 
| 
      
 98 
     | 
    
         
            +
              end
         
     | 
| 
      
 99 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -3,23 +3,27 @@ 
     | 
|
| 
       3 
3 
     | 
    
         
             
            module Basketball
         
     | 
| 
       4 
4 
     | 
    
         
             
              module App
         
     | 
| 
       5 
5 
     | 
    
         
             
                # Examples:
         
     | 
| 
       6 
     | 
    
         
            -
                #   exe/basketball-room -o tmp/draft.json
         
     | 
| 
       7 
     | 
    
         
            -
                #   exe/basketball-room -i tmp/draft.json -o tmp/draft-wip.json -s 26 -p P-5,P-10 - 
     | 
| 
       8 
     | 
    
         
            -
                #   exe/basketball-room -i tmp/draft-wip.json -x 2
         
     | 
| 
       9 
     | 
    
         
            -
                #   exe/basketball-room -i tmp/draft-wip.json -g - 
     | 
| 
       10 
     | 
    
         
            -
                #   exe/basketball-room -i tmp/draft-wip.json -s 30 - 
     | 
| 
       11 
     | 
    
         
            -
                #   exe/basketball-room -i tmp/draft-wip.json - 
     | 
| 
       12 
     | 
    
         
            -
                #
         
     | 
| 
       13 
     | 
    
         
            -
                #   exe/basketball-room -o tmp/draft-wip.json -ale
         
     | 
| 
      
 6 
     | 
    
         
            +
                #   exe/basketball-draft-room -o tmp/draft.json
         
     | 
| 
      
 7 
     | 
    
         
            +
                #   exe/basketball-draft-room -i tmp/draft.json -o tmp/draft-wip.json -s 26 -p P-5,P-10 -l 10
         
     | 
| 
      
 8 
     | 
    
         
            +
                #   exe/basketball-draft-room -i tmp/draft-wip.json -x 2
         
     | 
| 
      
 9 
     | 
    
         
            +
                #   exe/basketball-draft-room -i tmp/draft-wip.json -g -l 10
         
     | 
| 
      
 10 
     | 
    
         
            +
                #   exe/basketball-draft-room -i tmp/draft-wip.json -s 30 -l 10
         
     | 
| 
      
 11 
     | 
    
         
            +
                #   exe/basketball-draft-room -i tmp/draft-wip.json -ate
         
     | 
| 
       14 
12 
     | 
    
         
             
                class RoomCLI
         
     | 
| 
       15 
13 
     | 
    
         
             
                  class PlayerNotFound < StandardError; end
         
     | 
| 
       16 
14 
     | 
    
         | 
| 
       17 
     | 
    
         
            -
                  attr_reader :opts, 
     | 
| 
      
 15 
     | 
    
         
            +
                  attr_reader :opts,
         
     | 
| 
      
 16 
     | 
    
         
            +
                              :io,
         
     | 
| 
      
 17 
     | 
    
         
            +
                              :room_repository
         
     | 
| 
       18 
18 
     | 
    
         | 
| 
       19 
     | 
    
         
            -
                  def initialize( 
     | 
| 
       20 
     | 
    
         
            -
                     
     | 
| 
       21 
     | 
    
         
            -
                     
     | 
| 
       22 
     | 
    
         
            -
                     
     | 
| 
      
 19 
     | 
    
         
            +
                  def initialize(
         
     | 
| 
      
 20 
     | 
    
         
            +
                    args:,
         
     | 
| 
      
 21 
     | 
    
         
            +
                    io: $stdout,
         
     | 
| 
      
 22 
     | 
    
         
            +
                    room_repository: RoomRepository.new(FileStore.new)
         
     | 
| 
      
 23 
     | 
    
         
            +
                  )
         
     | 
| 
      
 24 
     | 
    
         
            +
                    @io                = io
         
     | 
| 
      
 25 
     | 
    
         
            +
                    @opts              = slop_parse(args)
         
     | 
| 
      
 26 
     | 
    
         
            +
                    @room_repository   = room_repository
         
     | 
| 
       23 
27 
     | 
    
         | 
| 
       24 
28 
     | 
    
         
             
                    if opts[:input].to_s.empty? && opts[:output].to_s.empty?
         
     | 
| 
       25 
29 
     | 
    
         
             
                      io.puts('Input and/or output paths are required.')
         
     | 
| 
         @@ -37,7 +41,7 @@ module Basketball 
     | 
|
| 
       37 
41 
     | 
    
         
             
                    status(room)
         
     | 
| 
       38 
42 
     | 
    
         
             
                    write(room)
         
     | 
| 
       39 
43 
     | 
    
         
             
                    events(room)
         
     | 
| 
       40 
     | 
    
         
            -
                     
     | 
| 
      
 44 
     | 
    
         
            +
                    teams(room)
         
     | 
| 
       41 
45 
     | 
    
         
             
                    query(room)
         
     | 
| 
       42 
46 
     | 
    
         | 
| 
       43 
47 
     | 
    
         
             
                    self
         
     | 
| 
         @@ -63,15 +67,15 @@ module Basketball 
     | 
|
| 
       63 
67 
     | 
    
         | 
| 
       64 
68 
     | 
    
         
             
                  def slop_parse(args)
         
     | 
| 
       65 
69 
     | 
    
         
             
                    Slop.parse(args) do |o|
         
     | 
| 
       66 
     | 
    
         
            -
                      o.banner = 'Usage: basketball-room [options] ...'
         
     | 
| 
      
 70 
     | 
    
         
            +
                      o.banner = 'Usage: basketball-draft-room [options] ...'
         
     | 
| 
       67 
71 
     | 
    
         | 
| 
       68 
72 
     | 
    
         
             
                      o.string  '-i', '--input',        'Path to load the Room from. If omitted then a new draft will be generated.'
         
     | 
| 
       69 
73 
     | 
    
         
             
                      o.string  '-o', '--output',       'Path to write the room to (if omitted then input path will be used)'
         
     | 
| 
       70 
74 
     | 
    
         
             
                      o.integer '-s', '--simulate',     'Number of picks to simulate (default is 0).', default: 0
         
     | 
| 
       71 
75 
     | 
    
         
             
                      o.bool    '-a', '--simulate-all', 'Simulate the rest of the draft', default: false
         
     | 
| 
       72 
76 
     | 
    
         
             
                      o.array   '-p', '--picks',        'Comma-separated list of ordered player IDs to pick.', delimiter: ','
         
     | 
| 
       73 
     | 
    
         
            -
                      o.integer '- 
     | 
| 
       74 
     | 
    
         
            -
                      o.bool    '- 
     | 
| 
      
 77 
     | 
    
         
            +
                      o.integer '-l', '--list',         'List the top rated available players (default is 0).', default: 0
         
     | 
| 
      
 78 
     | 
    
         
            +
                      o.bool    '-t', '--teams',        'Output all teams and their picks', default: false
         
     | 
| 
       75 
79 
     | 
    
         
             
                      o.integer '-x', '--skip',         'Number of picks to skip (default is 0).', default: 0
         
     | 
| 
       76 
80 
     | 
    
         
             
                      o.bool    '-e', '--events',       'Output event log.', default: false
         
     | 
| 
       77 
81 
     | 
    
         | 
| 
         @@ -112,11 +116,11 @@ module Basketball 
     | 
|
| 
       112 
116 
     | 
    
         
             
                    Draft::Room.new(rounds: 12, players:, front_offices:)
         
     | 
| 
       113 
117 
     | 
    
         
             
                  end
         
     | 
| 
       114 
118 
     | 
    
         | 
| 
       115 
     | 
    
         
            -
                  def  
     | 
| 
       116 
     | 
    
         
            -
                    return unless opts[: 
     | 
| 
      
 119 
     | 
    
         
            +
                  def teams(room)
         
     | 
| 
      
 120 
     | 
    
         
            +
                    return unless opts[:teams]
         
     | 
| 
       117 
121 
     | 
    
         | 
| 
       118 
122 
     | 
    
         
             
                    io.puts
         
     | 
| 
       119 
     | 
    
         
            -
                    io.puts(room. 
     | 
| 
      
 123 
     | 
    
         
            +
                    io.puts(room.teams)
         
     | 
| 
       120 
124 
     | 
    
         
             
                  end
         
     | 
| 
       121 
125 
     | 
    
         | 
| 
       122 
126 
     | 
    
         
             
                  def events(room)
         
     | 
| 
         @@ -129,19 +133,19 @@ module Basketball 
     | 
|
| 
       129 
133 
     | 
    
         
             
                  end
         
     | 
| 
       130 
134 
     | 
    
         | 
| 
       131 
135 
     | 
    
         
             
                  def query(room)
         
     | 
| 
       132 
     | 
    
         
            -
                     
     | 
| 
      
 136 
     | 
    
         
            +
                    list = opts[:list]
         
     | 
| 
       133 
137 
     | 
    
         | 
| 
       134 
     | 
    
         
            -
                    return if  
     | 
| 
      
 138 
     | 
    
         
            +
                    return if list <= 0
         
     | 
| 
       135 
139 
     | 
    
         | 
| 
       136 
     | 
    
         
            -
                    players = room.undrafted_players.sort_by(&:overall).reverse.take(opts[: 
     | 
| 
      
 140 
     | 
    
         
            +
                    players = room.undrafted_players.sort_by(&:overall).reverse.take(opts[:list])
         
     | 
| 
       137 
141 
     | 
    
         | 
| 
       138 
142 
     | 
    
         
             
                    io.puts
         
     | 
| 
       139 
     | 
    
         
            -
                    io.puts("Top #{ 
     | 
| 
      
 143 
     | 
    
         
            +
                    io.puts("Top #{list} available players")
         
     | 
| 
       140 
144 
     | 
    
         
             
                    io.puts(players)
         
     | 
| 
       141 
145 
     | 
    
         
             
                  end
         
     | 
| 
       142 
146 
     | 
    
         | 
| 
       143 
147 
     | 
    
         
             
                  def read
         
     | 
| 
       144 
     | 
    
         
            -
                     
     | 
| 
      
 148 
     | 
    
         
            +
                    room_repository.load(opts[:input])
         
     | 
| 
       145 
149 
     | 
    
         
             
                  end
         
     | 
| 
       146 
150 
     | 
    
         | 
| 
       147 
151 
     | 
    
         
             
                  # rubocop:disable Metrics/AbcSize
         
     | 
| 
         @@ -200,7 +204,7 @@ module Basketball 
     | 
|
| 
       200 
204 
     | 
    
         
             
                  def write(room)
         
     | 
| 
       201 
205 
     | 
    
         
             
                    output = output_default_to_input
         
     | 
| 
       202 
206 
     | 
    
         | 
| 
       203 
     | 
    
         
            -
                     
     | 
| 
      
 207 
     | 
    
         
            +
                    room_repository.save(output, room)
         
     | 
| 
       204 
208 
     | 
    
         | 
| 
       205 
209 
     | 
    
         
             
                    io.puts
         
     | 
| 
       206 
210 
     | 
    
         
             
                    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])
         
     | 
    
        data/lib/basketball/app.rb
    CHANGED
    
    | 
         @@ -1,10 +1,18 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            # frozen_string_literal: true
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
     | 
    
         
            -
            #  
     | 
| 
      
 3 
     | 
    
         
            +
            # Stores
         
     | 
| 
       4 
4 
     | 
    
         
             
            require_relative 'app/file_store'
         
     | 
| 
      
 5 
     | 
    
         
            +
            require_relative 'app/in_memory_store'
         
     | 
| 
       5 
6 
     | 
    
         | 
| 
       6 
     | 
    
         
            -
            #  
     | 
| 
      
 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 
     | 
| 
      
 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
         
     | 
| 
         @@ -28,17 +30,15 @@ module Basketball 
     | 
|
| 
       28 
30 
     | 
    
         
             
                  end
         
     | 
| 
       29 
31 
     | 
    
         | 
| 
       30 
32 
     | 
    
         
             
                  # This method will return a materialized list of teams and their selections.
         
     | 
| 
       31 
     | 
    
         
            -
                  def  
     | 
| 
       32 
     | 
    
         
            -
                     
     | 
| 
       33 
     | 
    
         
            -
                       
     | 
| 
       34 
     | 
    
         
            -
                        team = Org::Team.new(id: front_office.id)
         
     | 
| 
       35 
     | 
    
         
            -
             
     | 
| 
       36 
     | 
    
         
            -
                        league.register!(team)
         
     | 
| 
      
 33 
     | 
    
         
            +
                  def teams
         
     | 
| 
      
 34 
     | 
    
         
            +
                    front_offices.each_with_object([]) do |front_office, memo|
         
     | 
| 
      
 35 
     | 
    
         
            +
                      team = Org::Team.new(id: front_office.id)
         
     | 
| 
       37 
36 
     | 
    
         | 
| 
       38 
     | 
    
         
            -
             
     | 
| 
       39 
     | 
    
         
            -
             
     | 
| 
       40 
     | 
    
         
            -
                        end
         
     | 
| 
      
 37 
     | 
    
         
            +
                      drafted_players(front_office).each do |player|
         
     | 
| 
      
 38 
     | 
    
         
            +
                        team.sign!(player)
         
     | 
| 
       41 
39 
     | 
    
         
             
                      end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                      memo << team
         
     | 
| 
       42 
42 
     | 
    
         
             
                    end
         
     | 
| 
       43 
43 
     | 
    
         
             
                  end
         
     | 
| 
       44 
44 
     | 
    
         | 
| 
         @@ -157,8 +157,6 @@ module Basketball 
     | 
|
| 
       157 
157 
     | 
    
         | 
| 
       158 
158 
     | 
    
         
             
                  private
         
     | 
| 
       159 
159 
     | 
    
         | 
| 
       160 
     | 
    
         
            -
                  attr_writer :id
         
     | 
| 
       161 
     | 
    
         
            -
             
     | 
| 
       162 
160 
     | 
    
         
             
                  def player_events
         
     | 
| 
       163 
161 
     | 
    
         
             
                    events.select { |e| e.respond_to?(:player) }
         
     | 
| 
       164 
162 
     | 
    
         
             
                  end
         
     | 
    
        data/lib/basketball/entity.rb
    CHANGED
    
    | 
         @@ -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 
     | 
    
         
            -
                 
     | 
| 
       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
         
     | 
| 
         @@ -0,0 +1,47 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Basketball
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Org
         
     | 
| 
      
 5 
     | 
    
         
            +
                # A collection of divisions, teams, and players.
         
     | 
| 
      
 6 
     | 
    
         
            +
                class Conference < Entity
         
     | 
| 
      
 7 
     | 
    
         
            +
                  include HasDivisions
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                  attr_reader :divisions
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                  def initialize(id:, divisions: [])
         
     | 
| 
      
 12 
     | 
    
         
            +
                    super(id)
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                    @divisions = []
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                    divisions.each { |d| register_division!(d) }
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                    freeze
         
     | 
| 
      
 19 
     | 
    
         
            +
                  end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                  def to_s
         
     | 
| 
      
 22 
     | 
    
         
            +
                    ([super] + divisions.map(&:to_s)).join("\n")
         
     | 
| 
      
 23 
     | 
    
         
            +
                  end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                  def teams
         
     | 
| 
      
 26 
     | 
    
         
            +
                    divisions.flat_map(&:teams)
         
     | 
| 
      
 27 
     | 
    
         
            +
                  end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                  def players
         
     | 
| 
      
 30 
     | 
    
         
            +
                    divisions.flat_map(&:players)
         
     | 
| 
      
 31 
     | 
    
         
            +
                  end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                  private
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                  def register_division!(division)
         
     | 
| 
      
 36 
     | 
    
         
            +
                    raise ArgumentError, 'division is required' unless division
         
     | 
| 
      
 37 
     | 
    
         
            +
                    raise DivisionAlreadyRegisteredError, "#{division} already registered" if division?(division)
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                    assert_teams_are_not_already_registered(division.teams)
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                    divisions << division
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                    self
         
     | 
| 
      
 44 
     | 
    
         
            +
                  end
         
     | 
| 
      
 45 
     | 
    
         
            +
                end
         
     | 
| 
      
 46 
     | 
    
         
            +
              end
         
     | 
| 
      
 47 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,43 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Basketball
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Org
         
     | 
| 
      
 5 
     | 
    
         
            +
                # A collection of teams and players.
         
     | 
| 
      
 6 
     | 
    
         
            +
                class Division < Entity
         
     | 
| 
      
 7 
     | 
    
         
            +
                  include HasTeams
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                  attr_reader :teams
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                  def initialize(id:, teams: [])
         
     | 
| 
      
 12 
     | 
    
         
            +
                    super(id)
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                    @teams = []
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                    teams.each { |t| register_team!(t) }
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                    freeze
         
     | 
| 
      
 19 
     | 
    
         
            +
                  end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                  def to_s
         
     | 
| 
      
 22 
     | 
    
         
            +
                    ([super] + teams.map(&:to_s)).join("\n")
         
     | 
| 
      
 23 
     | 
    
         
            +
                  end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                  def players
         
     | 
| 
      
 26 
     | 
    
         
            +
                    teams.flat_map(&:players)
         
     | 
| 
      
 27 
     | 
    
         
            +
                  end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                  private
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                  def register_team!(team)
         
     | 
| 
      
 32 
     | 
    
         
            +
                    raise ArgumentError, 'team is required' unless team
         
     | 
| 
      
 33 
     | 
    
         
            +
                    raise TeamAlreadyRegisteredError, "#{team} already registered" if team?(team)
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                    assert_players_are_not_already_signed(team.players)
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                    teams << team
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                    self
         
     | 
| 
      
 40 
     | 
    
         
            +
                  end
         
     | 
| 
      
 41 
     | 
    
         
            +
                end
         
     | 
| 
      
 42 
     | 
    
         
            +
              end
         
     | 
| 
      
 43 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,25 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Basketball
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Org
         
     | 
| 
      
 5 
     | 
    
         
            +
                # Helper methods for objects that can be composed of divisions which are also composed of teams
         
     | 
| 
      
 6 
     | 
    
         
            +
                # and players.
         
     | 
| 
      
 7 
     | 
    
         
            +
                module HasDivisions
         
     | 
| 
      
 8 
     | 
    
         
            +
                  include HasTeams
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                  def division?(division)
         
     | 
| 
      
 11 
     | 
    
         
            +
                    divisions.include?(division)
         
     | 
| 
      
 12 
     | 
    
         
            +
                  end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                  private
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                  def assert_divisions_are_not_already_registered(divisions)
         
     | 
| 
      
 17 
     | 
    
         
            +
                    divisions.each do |division|
         
     | 
| 
      
 18 
     | 
    
         
            +
                      raise DivisionAlreadyRegisteredError, "#{division} already registered" if division?(division)
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                      assert_teams_are_not_already_registered(division.teams)
         
     | 
| 
      
 21 
     | 
    
         
            +
                    end
         
     | 
| 
      
 22 
     | 
    
         
            +
                  end
         
     | 
| 
      
 23 
     | 
    
         
            +
                end
         
     | 
| 
      
 24 
     | 
    
         
            +
              end
         
     | 
| 
      
 25 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,20 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Basketball
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Org
         
     | 
| 
      
 5 
     | 
    
         
            +
                # Helper methods for objects that can be composed of players.
         
     | 
| 
      
 6 
     | 
    
         
            +
                module HasPlayers
         
     | 
| 
      
 7 
     | 
    
         
            +
                  def player?(player)
         
     | 
| 
      
 8 
     | 
    
         
            +
                    players.include?(player)
         
     | 
| 
      
 9 
     | 
    
         
            +
                  end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                  private
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                  def assert_players_are_not_already_signed(players)
         
     | 
| 
      
 14 
     | 
    
         
            +
                    players.each do |player|
         
     | 
| 
      
 15 
     | 
    
         
            +
                      raise PlayerAlreadySignedError, "#{player} already registered" if player?(player)
         
     | 
| 
      
 16 
     | 
    
         
            +
                    end
         
     | 
| 
      
 17 
     | 
    
         
            +
                  end
         
     | 
| 
      
 18 
     | 
    
         
            +
                end
         
     | 
| 
      
 19 
     | 
    
         
            +
              end
         
     | 
| 
      
 20 
     | 
    
         
            +
            end
         
     |