sportdb-formats 1.2.1 → 2.0.0
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/CHANGELOG.md +1 -1
- data/Manifest.txt +6 -7
- data/Rakefile +7 -3
- data/lib/sportdb/formats/csv/goal.rb +192 -0
- data/lib/sportdb/formats/csv/goal_parser_csv.rb +28 -0
- data/lib/sportdb/formats/csv/match_parser_csv.rb +490 -0
- data/lib/sportdb/formats/csv/match_status_parser.rb +90 -0
- data/lib/sportdb/formats/match/conf_parser.rb +14 -2
- data/lib/sportdb/formats/match/match_parser.rb +498 -489
- data/lib/sportdb/formats/name_helper.rb +87 -0
- data/lib/sportdb/formats/search/sport.rb +67 -52
- data/lib/sportdb/formats/search/structs.rb +116 -0
- data/lib/sportdb/formats/search/world.rb +40 -22
- data/lib/sportdb/formats/version.rb +3 -3
- data/lib/sportdb/formats.rb +82 -15
- metadata +69 -14
- data/lib/sportdb/formats/goals.rb +0 -321
- data/lib/sportdb/formats/lines_reader.rb +0 -47
- data/lib/sportdb/formats/match/mapper.rb +0 -319
- data/lib/sportdb/formats/match/mapper_teams.rb +0 -23
- data/lib/sportdb/formats/match/match_parser_auto_conf.rb +0 -270
- data/lib/sportdb/formats/outline_reader.rb +0 -90
- data/lib/sportdb/formats/parser_helper.rb +0 -90
| @@ -0,0 +1,87 @@ | |
| 1 | 
            +
             | 
| 2 | 
            +
            module SportDb
         | 
| 3 | 
            +
              module NameHelper
         | 
| 4 | 
            +
             | 
| 5 | 
            +
             | 
| 6 | 
            +
              ## note: allow placeholder years to e.g. (-___) or (-????)
         | 
| 7 | 
            +
              ##    for marking missing (to be filled in) years
         | 
| 8 | 
            +
              ##  e.g. (1887-1911), (-2013),
         | 
| 9 | 
            +
              ##      (1946-2001, 2013-) etc.
         | 
| 10 | 
            +
              ##  todo/check: make more strict  e.g. only accept 4-digit years? - why? why not?
         | 
| 11 | 
            +
              YEAR_RE =  %r{\(
         | 
| 12 | 
            +
                              [0-9, ?_-]+?     # note: non-greedy (minimum/first) match
         | 
| 13 | 
            +
                          \)}x
         | 
| 14 | 
            +
             | 
| 15 | 
            +
              def strip_year( name )
         | 
| 16 | 
            +
                ## check for year(s) e.g. (1887-1911), (-2013),
         | 
| 17 | 
            +
                ##                        (1946-2001, 2013-) etc.
         | 
| 18 | 
            +
                ##  todo/check: only sub once (not global) - why? why not?
         | 
| 19 | 
            +
                name.gsub( YEAR_RE, '' ).strip
         | 
| 20 | 
            +
              end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
              def has_year?( name ) name =~ YEAR_RE; end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
             | 
| 25 | 
            +
              LANG_RE =  %r{\[
         | 
| 26 | 
            +
                            [a-z]{1,2}    # note also allow single-letter [a] or [d] or [e] - why? why not?
         | 
| 27 | 
            +
                            \]}x
         | 
| 28 | 
            +
              def strip_lang( name )
         | 
| 29 | 
            +
                 name.gsub( LANG_RE, '' ).strip
         | 
| 30 | 
            +
              end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
              def has_lang?( name ) name =~ LANG_RE; end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
             | 
| 35 | 
            +
              def sanitize( name )
         | 
| 36 | 
            +
                ## check for year(s) e.g. (1887-1911), (-2013),
         | 
| 37 | 
            +
                ##                        (1946-2001,2013-) etc.
         | 
| 38 | 
            +
                name = strip_year( name )
         | 
| 39 | 
            +
                ## check lang codes e.g. [en], [fr], etc.
         | 
| 40 | 
            +
                name = strip_lang( name )
         | 
| 41 | 
            +
                name
         | 
| 42 | 
            +
              end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
             | 
| 45 | 
            +
              ## note: also add (),’,−  etc. e.g.
         | 
| 46 | 
            +
              ##   Estudiantes (LP) => Estudiantes LP
         | 
| 47 | 
            +
              ##   Saint Patrick’s Athletic FC => Saint Patricks Athletic FC
         | 
| 48 | 
            +
              ##   Myllykosken Pallo −47 => Myllykosken Pallo 47
         | 
| 49 | 
            +
              ##
         | 
| 50 | 
            +
              ##  add & too!!
         | 
| 51 | 
            +
              ##   e.g. Brighton & Hove Albion => Brighton Hove Albion  -- and others in England
         | 
| 52 | 
            +
             | 
| 53 | 
            +
              NORM_RE =  %r{
         | 
| 54 | 
            +
                                [.'’º/()&_−-]
         | 
| 55 | 
            +
                              }x   # note: in [] dash (-) if last doesn't need to get escaped
         | 
| 56 | 
            +
              ## note: remove all dots (.), dash (-), ', º, /, etc.
         | 
| 57 | 
            +
              #   .  U+002E (46) - FULL STOP
         | 
| 58 | 
            +
              #   '  U+0027 (39) - APOSTROPHE
         | 
| 59 | 
            +
              #   ’  U+2019 (8217) - RIGHT SINGLE QUOTATION MARK
         | 
| 60 | 
            +
              #   º  U+00BA (186) - MASCULINE ORDINAL INDICATOR
         | 
| 61 | 
            +
              #   /  U+002F (47) - SOLIDUS
         | 
| 62 | 
            +
              #   (  U+0028 (40) - LEFT PARENTHESIS
         | 
| 63 | 
            +
              #   )  U+0029 (41) - RIGHT PARENTHESIS
         | 
| 64 | 
            +
              #   −  U+2212 (8722) - MINUS SIGN
         | 
| 65 | 
            +
              #   -  U+002D (45) - HYPHEN-MINUS
         | 
| 66 | 
            +
             | 
| 67 | 
            +
              ##         for norm(alizing) names
         | 
| 68 | 
            +
              def strip_norm( name )
         | 
| 69 | 
            +
                name.gsub( NORM_RE, '' )
         | 
| 70 | 
            +
              end
         | 
| 71 | 
            +
             | 
| 72 | 
            +
              def normalize( name )
         | 
| 73 | 
            +
                # note: do NOT call sanitize here (keep normalize "atomic" for reuse)
         | 
| 74 | 
            +
                name = strip_norm( name )
         | 
| 75 | 
            +
                name = name.gsub( ' ', '' )  # note: also remove all spaces!!!
         | 
| 76 | 
            +
             | 
| 77 | 
            +
                ## todo/check: use our own downcase - why? why not?
         | 
| 78 | 
            +
                name = downcase_i18n( name )     ## do NOT care about upper and lowercase for now
         | 
| 79 | 
            +
                name
         | 
| 80 | 
            +
              end
         | 
| 81 | 
            +
             | 
| 82 | 
            +
             | 
| 83 | 
            +
              def variants( name )  Variant.find( name ); end
         | 
| 84 | 
            +
             | 
| 85 | 
            +
              end  # module NameHelper
         | 
| 86 | 
            +
            end   # module SportDb
         | 
| 87 | 
            +
             | 
| @@ -6,7 +6,7 @@ | |
| 6 6 |  | 
| 7 7 | 
             
            class SportSearch
         | 
| 8 8 |  | 
| 9 | 
            -
            class Search    ## base search service - use/keep - why? why not? | 
| 9 | 
            +
            class Search    ## base search service - use/keep - why? why not?
         | 
| 10 10 | 
             
              def initialize( service ) @service = service; end
         | 
| 11 11 | 
             
            end  # class Search
         | 
| 12 12 |  | 
| @@ -15,48 +15,62 @@ class PlayerSearch < Search | |
| 15 15 | 
             
              ###################
         | 
| 16 16 | 
             
              ## core required delegates  - use delegate generator - why? why not?
         | 
| 17 17 | 
             
              def match_by( name:, country: nil, year: nil )
         | 
| 18 | 
            -
                @service.match_by( name:    name, | 
| 18 | 
            +
                @service.match_by( name:    name,
         | 
| 19 19 | 
             
                                   country: country,
         | 
| 20 | 
            -
                                   year:    year ) | 
| 20 | 
            +
                                   year:    year )
         | 
| 21 21 | 
             
              end
         | 
| 22 22 |  | 
| 23 23 | 
             
              ###############
         | 
| 24 24 | 
             
              ### more deriv support functions / helpers
         | 
| 25 25 | 
             
              def match( name ) match_by( name: name ); end
         | 
| 26 | 
            -
              ## add more here - why? why not? | 
| 26 | 
            +
              ## add more here - why? why not?
         | 
| 27 27 | 
             
            end   # class PlayerSearch
         | 
| 28 28 |  | 
| 29 29 |  | 
| 30 30 | 
             
            class LeagueSearch < Search
         | 
| 31 | 
            +
             | 
| 31 32 | 
             
              ###################
         | 
| 32 33 | 
             
              ## core required delegates  - use delegate generator - why? why not?
         | 
| 33 | 
            -
              def match_by( name | 
| 34 | 
            -
             | 
| 35 | 
            -
             | 
| 34 | 
            +
              def match_by( name: nil, code: nil, country: nil )
         | 
| 35 | 
            +
                 ## todo/fix upstream - remove "generic" match_by() - why? why not?
         | 
| 36 | 
            +
                 ###
         | 
| 37 | 
            +
                 if code && name.nil?
         | 
| 38 | 
            +
                  @service.match_by_code( code, country: country )
         | 
| 39 | 
            +
                elsif name && code.nil?
         | 
| 40 | 
            +
                  @service.match_by_name( name, country: country )
         | 
| 41 | 
            +
                else
         | 
| 42 | 
            +
                  raise ArgumentError, "LeagueSearch#match_by - one (and only one arg) required - code: or name:"
         | 
| 43 | 
            +
                end
         | 
| 44 | 
            +
                ## @service.match_by( name: name,
         | 
| 45 | 
            +
                ##                   country: country )
         | 
| 36 46 | 
             
              end
         | 
| 37 47 |  | 
| 48 | 
            +
              ## all-in-one query (name or code)
         | 
| 49 | 
            +
              def match( q, country: nil )
         | 
| 50 | 
            +
                 @service.match_by_name_or_code( q, country: country )
         | 
| 51 | 
            +
              end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
             | 
| 38 54 | 
             
              ###############
         | 
| 39 55 | 
             
              ### more deriv support functions / helpers
         | 
| 40 | 
            -
              def  | 
| 41 | 
            -
             | 
| 42 | 
            -
              def find!( name )
         | 
| 43 | 
            -
                league = find( name )
         | 
| 56 | 
            +
              def find!( q )
         | 
| 57 | 
            +
                league = find( q )
         | 
| 44 58 | 
             
                if league.nil?
         | 
| 45 | 
            -
                  puts "** !!! ERROR - no league match found for >#{ | 
| 59 | 
            +
                  puts "** !!! ERROR - no league match found for >#{q}<, add to leagues table; sorry"
         | 
| 46 60 | 
             
                  exit 1
         | 
| 47 61 | 
             
                end
         | 
| 48 62 | 
             
                league
         | 
| 49 63 | 
             
              end
         | 
| 50 64 |  | 
| 51 | 
            -
              def find(  | 
| 65 | 
            +
              def find( q )
         | 
| 52 66 | 
             
                league = nil
         | 
| 53 | 
            -
                recs = match(  | 
| 67 | 
            +
                recs = match( q )
         | 
| 54 68 | 
             
                # pp m
         | 
| 55 69 |  | 
| 56 70 | 
             
                if recs.empty?
         | 
| 57 71 | 
             
                  ## fall through/do nothing
         | 
| 58 72 | 
             
                elsif recs.size > 1
         | 
| 59 | 
            -
                  puts "** !!! ERROR - ambigious league  | 
| 73 | 
            +
                  puts "** !!! ERROR - ambigious league query; too many leagues (#{recs.size}) found:"
         | 
| 60 74 | 
             
                  pp recs
         | 
| 61 75 | 
             
                  exit 1
         | 
| 62 76 | 
             
                else
         | 
| @@ -68,19 +82,20 @@ class LeagueSearch < Search | |
| 68 82 | 
             
            end # class LeagueSearch
         | 
| 69 83 |  | 
| 70 84 |  | 
| 85 | 
            +
             | 
| 71 86 | 
             
            class GroundSearch  < Search
         | 
| 72 87 | 
             
              ###################
         | 
| 73 88 | 
             
              ## core required delegates  - use delegate generator - why? why not?
         | 
| 74 89 | 
             
              def match_by( name:, country: nil, city: nil )
         | 
| 75 | 
            -
                @service.match_by( name:    name, | 
| 90 | 
            +
                @service.match_by( name:    name,
         | 
| 76 91 | 
             
                                   country: country,
         | 
| 77 | 
            -
                                   city:    city ) | 
| 92 | 
            +
                                   city:    city )
         | 
| 78 93 | 
             
              end
         | 
| 79 94 |  | 
| 80 95 | 
             
              ###############
         | 
| 81 96 | 
             
              ### more deriv support functions / helpers
         | 
| 82 97 | 
             
              def match( name ) match_by( name: name ); end
         | 
| 83 | 
            -
              ## add more here - why? why not? | 
| 98 | 
            +
              ## add more here - why? why not?
         | 
| 84 99 | 
             
            end  # class GroundSearch
         | 
| 85 100 |  | 
| 86 101 |  | 
| @@ -106,31 +121,31 @@ class ClubSearch  < Search | |
| 106 121 | 
             
                                   league:  nil,
         | 
| 107 122 | 
             
                                   mods:    nil )
         | 
| 108 123 | 
             
                ## for now assume "global" mods  - checks only for name
         | 
| 109 | 
            -
                ## | 
| 124 | 
            +
                ##
         | 
| 110 125 | 
             
                if mods && mods[ name ]
         | 
| 111 126 | 
             
                  club = mods[ name ]
         | 
| 112 127 | 
             
                  return [club]   # note: wrap (single record) in array
         | 
| 113 | 
            -
                end | 
| 128 | 
            +
                end
         | 
| 114 129 |  | 
| 115 130 | 
             
                ## note: add "auto-magic" country calculation via league record
         | 
| 116 131 | 
             
                ##            if league is a national league for football clubs
         | 
| 117 132 | 
             
                if league
         | 
| 118 133 | 
             
                    raise ArgumentError, "match_by - league AND country NOT supported; sorry"  if country
         | 
| 119 | 
            -
            ### find countries via league | 
| 134 | 
            +
            ### find countries via league
         | 
| 120 135 | 
             
            ###     support league.intl? too - why? why not?
         | 
| 121 136 | 
             
            ###       or only nationa league
         | 
| 122 137 | 
             
                    raise ArgumentError, "match_by - league - only national club leagues supported (not int'l or national teams for now); sorry"   unless league.national? && league.clubs?
         | 
| 123 | 
            -
             | 
| 138 | 
            +
             | 
| 124 139 | 
             
                    ### calc countries
         | 
| 125 140 | 
             
                    ### uses "global" func in sports-catalogs for now
         | 
| 126 | 
            -
                    ##   move code here - why? why not? | 
| 141 | 
            +
                    ##   move code here - why? why not?
         | 
| 127 142 | 
             
                    country = find_countries_for_league( league )
         | 
| 128 | 
            -
                    @service.match_by( name:    name, | 
| 143 | 
            +
                    @service.match_by( name:    name,
         | 
| 129 144 | 
             
                                       country: country )
         | 
| 130 145 | 
             
                else
         | 
| 131 | 
            -
                    @service.match_by( name:    name, | 
| 146 | 
            +
                    @service.match_by( name:    name,
         | 
| 132 147 | 
             
                                       country: country )
         | 
| 133 | 
            -
                end | 
| 148 | 
            +
                end
         | 
| 134 149 | 
             
              end
         | 
| 135 150 |  | 
| 136 151 |  | 
| @@ -145,10 +160,10 @@ class ClubSearch  < Search | |
| 145 160 | 
             
              ###############
         | 
| 146 161 | 
             
              ### more deriv support functions / helpers
         | 
| 147 162 | 
             
              def match( name ) match_by( name: name ); end
         | 
| 148 | 
            -
             | 
| 163 | 
            +
             | 
| 149 164 | 
             
              ##########
         | 
| 150 | 
            -
              #  "legacy" finders - return zero or one club | 
| 151 | 
            -
              ##    (if more than one match, exit/raise error/exception) | 
| 165 | 
            +
              #  "legacy" finders - return zero or one club
         | 
| 166 | 
            +
              ##    (if more than one match, exit/raise error/exception)
         | 
| 152 167 | 
             
              def find( name )   find_by( name: name ); end
         | 
| 153 168 | 
             
              def find!( name )  find_by!( name: name ); end
         | 
| 154 169 |  | 
| @@ -156,7 +171,7 @@ class ClubSearch  < Search | |
| 156 171 | 
             
              ##   if there is more than one match than find aborts / fails
         | 
| 157 172 | 
             
              def find_by!( name:, country: nil,
         | 
| 158 173 | 
             
                                   league:  nil )    ## todo/fix: add international or league flag?
         | 
| 159 | 
            -
                club = find_by( name:    name, | 
| 174 | 
            +
                club = find_by( name:    name,
         | 
| 160 175 | 
             
                                country: country,
         | 
| 161 176 | 
             
                                league:  league )
         | 
| 162 177 |  | 
| @@ -174,8 +189,8 @@ class ClubSearch  < Search | |
| 174 189 | 
             
                ## note: allow passing in of country key too (auto-counvert)
         | 
| 175 190 | 
             
                ##       and country struct too
         | 
| 176 191 | 
             
                ##     - country assumes / allows the country key or fifa code for now
         | 
| 177 | 
            -
                recs = match_by( name:    name, | 
| 178 | 
            -
                                 country: country, | 
| 192 | 
            +
                recs = match_by( name:    name,
         | 
| 193 | 
            +
                                 country: country,
         | 
| 179 194 | 
             
                                 league:  league )
         | 
| 180 195 |  | 
| 181 196 | 
             
                club = nil
         | 
| @@ -243,12 +258,12 @@ end # class EventSearch | |
| 243 258 |  | 
| 244 259 | 
             
            ####
         | 
| 245 260 | 
             
            ## virtual table for season lookup
         | 
| 246 | 
            -
            ##   note - use EventSeaon  to avoid name conflict with (global) Season class | 
| 247 | 
            -
            ##      find a better name SeasonInfo or SeasonFinder or SeasonStore | 
| 261 | 
            +
            ##   note - use EventSeaon  to avoid name conflict with (global) Season class
         | 
| 262 | 
            +
            ##      find a better name SeasonInfo or SeasonFinder or SeasonStore
         | 
| 248 263 | 
             
            ##                       or SeasonQ or ??
         | 
| 249 264 | 
             
            class EventSeasonSearch
         | 
| 250 | 
            -
                def initialize( events: ) | 
| 251 | 
            -
                    @events = events | 
| 265 | 
            +
                def initialize( events: )
         | 
| 266 | 
            +
                    @events = events
         | 
| 252 267 | 
             
                end
         | 
| 253 268 |  | 
| 254 269 | 
             
              ###############
         | 
| @@ -256,7 +271,7 @@ class EventSeasonSearch | |
| 256 271 | 
             
              ##
         | 
| 257 272 | 
             
              def find_by( date:, league: )
         | 
| 258 273 | 
             
                date = Date.strptime( date, '%Y-%m-%d' )   if date.is_a?( String )
         | 
| 259 | 
            -
             | 
| 274 | 
            +
             | 
| 260 275 | 
             
                infos = @events.seasons( league )
         | 
| 261 276 |  | 
| 262 277 | 
             
                infos.each do |info|
         | 
| @@ -265,17 +280,17 @@ class EventSeasonSearch | |
| 265 280 | 
             
                nil
         | 
| 266 281 | 
             
              end
         | 
| 267 282 | 
             
            end # class EventSeasonSearch
         | 
| 268 | 
            -
             | 
| 283 | 
            +
             | 
| 269 284 |  | 
| 270 285 | 
             
            ######
         | 
| 271 286 | 
             
            ### add virtual team search ( clubs + national teams)
         | 
| 272 287 | 
             
            ##   note: no record base!!!!!
         | 
| 273 288 | 
             
            class TeamSearch
         | 
| 274 289 | 
             
                ## note: "virtual" index lets you search clubs and/or national_teams (don't care)
         | 
| 275 | 
            -
             | 
| 276 | 
            -
              def initialize( clubs:, national_teams: ) | 
| 290 | 
            +
             | 
| 291 | 
            +
              def initialize( clubs:, national_teams: )
         | 
| 277 292 | 
             
                @clubs          = clubs
         | 
| 278 | 
            -
                @national_teams = national_teams | 
| 293 | 
            +
                @national_teams = national_teams
         | 
| 279 294 | 
             
              end
         | 
| 280 295 |  | 
| 281 296 | 
             
                ## todo/check: rename to/use map_by! for array version - why? why not?
         | 
| @@ -290,8 +305,8 @@ class TeamSearch | |
| 290 305 | 
             
                    _find_by!( name: name, league: league, mods: mods )
         | 
| 291 306 | 
             
                  end
         | 
| 292 307 | 
             
                end
         | 
| 293 | 
            -
             | 
| 294 | 
            -
             | 
| 308 | 
            +
             | 
| 309 | 
            +
             | 
| 295 310 | 
             
                def _find_by!( name:, league:, mods: nil )
         | 
| 296 311 | 
             
                  if mods && mods[ league.key ] && mods[ league.key ][ name ]
         | 
| 297 312 | 
             
                    mods[ league.key ][ name ]
         | 
| @@ -300,7 +315,7 @@ class TeamSearch | |
| 300 315 | 
             
                      if league.intl?    ## todo/fix: add intl? to ActiveRecord league!!!
         | 
| 301 316 | 
             
                        @clubs.find!( name )
         | 
| 302 317 | 
             
                      else  ## assume clubs in domestic/national league tournament
         | 
| 303 | 
            -
                         ## note - search by league countries (may incl. more than one country | 
| 318 | 
            +
                         ## note - search by league countries (may incl. more than one country
         | 
| 304 319 | 
             
                         ##             e.g. us incl. ca, fr incl. mc, ch incl. li, etc.
         | 
| 305 320 | 
             
                        @clubs.find_by!( name: name, league: league )
         | 
| 306 321 | 
             
                      end
         | 
| @@ -310,8 +325,8 @@ class TeamSearch | |
| 310 325 | 
             
                  end
         | 
| 311 326 | 
             
                end # method _find_by!
         | 
| 312 327 | 
             
              end  # class TeamSearch
         | 
| 313 | 
            -
             | 
| 314 | 
            -
             | 
| 328 | 
            +
             | 
| 329 | 
            +
             | 
| 315 330 |  | 
| 316 331 | 
             
               def initialize( leagues:,
         | 
| 317 332 | 
             
                               national_teams:,
         | 
| @@ -320,11 +335,11 @@ class TeamSearch | |
| 320 335 | 
             
                               events:,
         | 
| 321 336 | 
             
                               players:
         | 
| 322 337 | 
             
                               )
         | 
| 323 | 
            -
                   @leagues        = LeagueSearch.new( leagues ) | 
| 338 | 
            +
                   @leagues        = LeagueSearch.new( leagues )
         | 
| 324 339 | 
             
                   @national_teams = NationalTeamSearch.new( national_teams )
         | 
| 325 340 | 
             
                   @clubs          = ClubSearch.new( clubs )
         | 
| 326 | 
            -
                   @events         = EventSearch.new( events ) | 
| 327 | 
            -
             | 
| 341 | 
            +
                   @events         = EventSearch.new( events )
         | 
| 342 | 
            +
             | 
| 328 343 | 
             
                   @grounds        = GroundSearch.new( grounds )
         | 
| 329 344 |  | 
| 330 345 | 
             
                   @players        = PlayerSearch.new( players )
         | 
| @@ -332,8 +347,8 @@ class TeamSearch | |
| 332 347 | 
             
                   ## virtual deriv ("composite") search services
         | 
| 333 348 | 
             
                   @teams          = TeamSearch.new( clubs:          @clubs,
         | 
| 334 349 | 
             
                                                     national_teams: @national_teams )
         | 
| 335 | 
            -
                   @event_seasons  = EventSeasonSearch.new( events: @events ) | 
| 336 | 
            -
             | 
| 350 | 
            +
                   @event_seasons  = EventSeasonSearch.new( events: @events )
         | 
| 351 | 
            +
             | 
| 337 352 | 
             
               end
         | 
| 338 353 |  | 
| 339 354 | 
             
                def countries
         | 
| @@ -351,7 +366,7 @@ class TeamSearch | |
| 351 366 | 
             
             def grounds()         @grounds; end
         | 
| 352 367 |  | 
| 353 368 | 
             
             def players()        @players; end
         | 
| 354 | 
            -
             | 
| 369 | 
            +
             | 
| 355 370 | 
             
             def teams()          @teams; end         ## note - virtual table
         | 
| 356 371 | 
             
             def seasons()        @event_seasons; end ## note - virtual table
         | 
| 357 372 | 
             
            end  # class SportSearch
         | 
| @@ -0,0 +1,116 @@ | |
| 1 | 
            +
            ###
         | 
| 2 | 
            +
            #  note - extend all structs for with search api
         | 
| 3 | 
            +
            #
         | 
| 4 | 
            +
            #  todo - add more helpers!!!!
         | 
| 5 | 
            +
            #
         | 
| 6 | 
            +
            #
         | 
| 7 | 
            +
            #  todo - fix - move all non-core search functionality/machinery
         | 
| 8 | 
            +
            #                  over here - why? why not?
         | 
| 9 | 
            +
             | 
| 10 | 
            +
             | 
| 11 | 
            +
             | 
| 12 | 
            +
            module Sports
         | 
| 13 | 
            +
             | 
| 14 | 
            +
            class Country
         | 
| 15 | 
            +
                def self._search #### use service/api or such - why? why not?
         | 
| 16 | 
            +
                    SportDb::Import.world.countries
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
               def self.find_by( code: nil, name: nil )
         | 
| 19 | 
            +
                    _search.find_by( code: code, name: name )
         | 
| 20 | 
            +
               end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
               def self.find( q )   ## find by code (first) or name (second)
         | 
| 23 | 
            +
                   _search.find( q )
         | 
| 24 | 
            +
               end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
               def self.parse_heading( line )
         | 
| 27 | 
            +
                  ## fix - move parse code here from search - why? why not?
         | 
| 28 | 
            +
                  _search.parse( line )
         | 
| 29 | 
            +
               end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
               ## add alternate names/aliases
         | 
| 32 | 
            +
               class << self
         | 
| 33 | 
            +
                alias_method :[],      :find    ### keep shortcut - why? why not?
         | 
| 34 | 
            +
                alias_method :heading, :parse_heading
         | 
| 35 | 
            +
               end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
             | 
| 38 | 
            +
            # open question - what name to use build or  parse_line or ?
         | 
| 39 | 
            +
            #                              or   parse_recs for CountryReader?
         | 
| 40 | 
            +
            #          remove CountryReader helper methods - why? why not?
         | 
| 41 | 
            +
            #   use parse_heading/heading for now !!!
         | 
| 42 | 
            +
            #
         | 
| 43 | 
            +
            #   def self.parse( line )  or build( line ) ??
         | 
| 44 | 
            +
            #      SportDb::Import.world.countries.parse( line )
         | 
| 45 | 
            +
            #   end
         | 
| 46 | 
            +
            #
         | 
| 47 | 
            +
            # !!!! note - conflict with
         | 
| 48 | 
            +
            #     def self.read( path )  CountryReader.read( path ); end
         | 
| 49 | 
            +
            #     def self.parse( txt )  CountryReader.parse( txt ); end
         | 
| 50 | 
            +
            #
         | 
| 51 | 
            +
            end # class Country
         | 
| 52 | 
            +
             | 
| 53 | 
            +
             | 
| 54 | 
            +
            ###
         | 
| 55 | 
            +
            ## todo/fix - add find_by( code: ), find_by( name: )
         | 
| 56 | 
            +
            ##                   split - why? why not?
         | 
| 57 | 
            +
             | 
| 58 | 
            +
             | 
| 59 | 
            +
            class League
         | 
| 60 | 
            +
                def self._search #### use service/api or such - why? why not?
         | 
| 61 | 
            +
                    SportDb::Import.catalog.leagues
         | 
| 62 | 
            +
                end
         | 
| 63 | 
            +
                def self.match_by( name: nil, code: nil,
         | 
| 64 | 
            +
                                    country: nil )
         | 
| 65 | 
            +
                   _search.match_by( name: name, code: code,
         | 
| 66 | 
            +
                                     country: country )
         | 
| 67 | 
            +
                end
         | 
| 68 | 
            +
                def self.match( q ) _search.match( q ); end
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                def self.find!( q ) _search.find!( q ); end
         | 
| 71 | 
            +
                def self.find( q )  _search_find( q ); end
         | 
| 72 | 
            +
            end # class League
         | 
| 73 | 
            +
             | 
| 74 | 
            +
             | 
| 75 | 
            +
            class NationalTeam
         | 
| 76 | 
            +
                def self._search #### use service/api or such - why? why not?
         | 
| 77 | 
            +
                    SportDb::Import.catalog.national_teams
         | 
| 78 | 
            +
                end
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                def self.find( q )   _search.find( q ); end
         | 
| 81 | 
            +
                def self.find!( q )  _search_find!( q ); end
         | 
| 82 | 
            +
            end # class NationalTeam
         | 
| 83 | 
            +
             | 
| 84 | 
            +
             | 
| 85 | 
            +
            class Club
         | 
| 86 | 
            +
                def self._search #### use service/api or such - why? why not?
         | 
| 87 | 
            +
                    SportDb::Import.catalog.clubs
         | 
| 88 | 
            +
                end
         | 
| 89 | 
            +
             | 
| 90 | 
            +
                def self.match_by( name:, country: nil,
         | 
| 91 | 
            +
                                     league:  nil,
         | 
| 92 | 
            +
                                     mods:    nil )
         | 
| 93 | 
            +
                    _search.match_by( name: name, country: country,
         | 
| 94 | 
            +
                                      league: league, mods: mods )
         | 
| 95 | 
            +
                end
         | 
| 96 | 
            +
                def self.match( name ) match_by( name: name ); end
         | 
| 97 | 
            +
             | 
| 98 | 
            +
                def self.find( name )   _search.find_by( name: name ); end
         | 
| 99 | 
            +
                def self.find!( name )  _search.find_by!( name: name ); end
         | 
| 100 | 
            +
             | 
| 101 | 
            +
                def self.find_by!( name:, country: nil,
         | 
| 102 | 
            +
                                          league:  nil )
         | 
| 103 | 
            +
                   _search.find_by!( name: name, country: country,
         | 
| 104 | 
            +
                                                 league: league )
         | 
| 105 | 
            +
                end
         | 
| 106 | 
            +
                def self.find_by( name:, country: nil,
         | 
| 107 | 
            +
                                         league:  nil )
         | 
| 108 | 
            +
                   _search.find_by( name: name, country: country,
         | 
| 109 | 
            +
                                                league: league )
         | 
| 110 | 
            +
                end
         | 
| 111 | 
            +
             | 
| 112 | 
            +
               def self.build_mods( mods )
         | 
| 113 | 
            +
                   _search_build_mods( mods )
         | 
| 114 | 
            +
                end
         | 
| 115 | 
            +
              end # class Club
         | 
| 116 | 
            +
            end   # module Sports
         | 
| @@ -6,7 +6,7 @@ | |
| 6 6 | 
             
            # -                 .find_by_name
         | 
| 7 7 |  | 
| 8 8 |  | 
| 9 | 
            -
            class WorldSearch | 
| 9 | 
            +
            class WorldSearch
         | 
| 10 10 |  | 
| 11 11 | 
             
            class CitySearch
         | 
| 12 12 | 
             
              def initialize( service ) @service = service; end
         | 
| @@ -14,29 +14,45 @@ class CitySearch | |
| 14 14 | 
             
              ###################
         | 
| 15 15 | 
             
              ## core required delegates  - use delegate generator - why? why not?
         | 
| 16 16 | 
             
              def match_by( name: )
         | 
| 17 | 
            -
                @service.match_by( name: name ) | 
| 17 | 
            +
                @service.match_by( name: name )
         | 
| 18 18 | 
             
              end
         | 
| 19 19 | 
             
            end  # class CitySearch
         | 
| 20 20 |  | 
| 21 21 |  | 
| 22 22 | 
             
            class CountrySearch
         | 
| 23 23 | 
             
              def initialize( service ) @service = service; end
         | 
| 24 | 
            -
             | 
| 24 | 
            +
             | 
| 25 25 | 
             
                ###################
         | 
| 26 26 | 
             
                ## core required delegates  - use delegate generator - why? why not?
         | 
| 27 | 
            -
                def find_by_code( code ) | 
| 28 | 
            -
             | 
| 27 | 
            +
                def find_by_code( code )
         | 
| 28 | 
            +
                  puts "!! DEPRECATED - use CountrySearch#find_by( code: )"
         | 
| 29 | 
            +
                  @service.find_by_code( code )
         | 
| 30 | 
            +
                end
         | 
| 31 | 
            +
                def find_by_name( name )
         | 
| 32 | 
            +
                  puts "!! DEPRECATED - use CountrySearch#find_by( name: )"
         | 
| 33 | 
            +
                  @service.find_by_name( name )
         | 
| 34 | 
            +
                end
         | 
| 29 35 |  | 
| 36 | 
            +
                def find_by( code: nil, name: nil )
         | 
| 37 | 
            +
                  ## todo/fix upstream - change to find_by( code:, name:, ) too
         | 
| 38 | 
            +
                  if code && name.nil?
         | 
| 39 | 
            +
                    @service.find_by_code( code )
         | 
| 40 | 
            +
                  elsif name && code.nil?
         | 
| 41 | 
            +
                    @service.find_by_name( name )
         | 
| 42 | 
            +
                  else
         | 
| 43 | 
            +
                    raise ArgumentError, "CountrySearch#find_by - one (and only one arg) required - code: or name:"
         | 
| 44 | 
            +
                  end
         | 
| 45 | 
            +
                end
         | 
| 30 46 |  | 
| 31 | 
            -
                ###############
         | 
| 32 | 
            -
                ### more deriv support functions / helpers
         | 
| 33 47 | 
             
                def find( q )
         | 
| 34 | 
            -
             | 
| 35 | 
            -
                   country = find_by_name( q )  if country.nil?     ## try lookup / find by (normalized) name
         | 
| 36 | 
            -
                   country
         | 
| 48 | 
            +
                  @service.find_by_name_or_code( q )
         | 
| 37 49 | 
             
                end
         | 
| 38 50 | 
             
                alias_method :[], :find    ### keep shortcut - why? why not?
         | 
| 39 | 
            -
             | 
| 51 | 
            +
             | 
| 52 | 
            +
             | 
| 53 | 
            +
                ###############
         | 
| 54 | 
            +
                ### more deriv support functions / helpers
         | 
| 55 | 
            +
             | 
| 40 56 | 
             
             ###
         | 
| 41 57 | 
             
             ##   split/parse country line
         | 
| 42 58 | 
             
             ##
         | 
| @@ -56,29 +72,31 @@ class CountrySearch | |
| 56 72 | 
             
               country = nil
         | 
| 57 73 | 
             
               values.each do |value|
         | 
| 58 74 | 
             
                  value = value.strip
         | 
| 59 | 
            -
                  ## check for trailing country code e.g. (at), (eng), etc | 
| 60 | 
            -
                   | 
| 75 | 
            +
                  ## check for trailing country code e.g. (at), (eng), etc
         | 
| 76 | 
            +
                  ##   allow code 1 to 5 for now - northern cyprus(fifa) with 5 letters?.
         | 
| 77 | 
            +
                  ##     add/allow  gb-eng, gb-wal (official iso2!!), in the future too - why? why not?
         | 
| 78 | 
            +
                  if value =~ /[ ]+\((?<code>[A-Za-z]{1,5})\)$/  ## e.g. Austria (at)
         | 
| 61 79 | 
             
                    code =  $~[:code]
         | 
| 62 80 | 
             
                    name = value[0...(value.size-code.size-2)].strip  ## note: add -2 for brackets
         | 
| 63 | 
            -
                    candidates = [  | 
| 81 | 
            +
                    candidates = [ find_by( code: code ), find_by( name: name ) ]
         | 
| 64 82 | 
             
                    if candidates[0].nil?
         | 
| 65 | 
            -
                      puts "** !!! ERROR  | 
| 83 | 
            +
                      puts "** !!! ERROR Country.parse_heading - unknown code >#{code}< in line: #{line}"
         | 
| 66 84 | 
             
                      pp line
         | 
| 67 85 | 
             
                      exit 1
         | 
| 68 86 | 
             
                    end
         | 
| 69 87 | 
             
                    if candidates[1].nil?
         | 
| 70 | 
            -
                      puts "** !!! ERROR  | 
| 88 | 
            +
                      puts "** !!! ERROR Country.parse_heading - unknown name >#{code}< in line: #{line}"
         | 
| 71 89 | 
             
                      pp line
         | 
| 72 90 | 
             
                      exit 1
         | 
| 73 91 | 
             
                    end
         | 
| 74 92 | 
             
                    if candidates[0] != candidates[1]
         | 
| 75 | 
            -
                      puts "** !!! ERROR  | 
| 93 | 
            +
                      puts "** !!! ERROR Country.parse_heading - name and code do NOT match the same country:"
         | 
| 76 94 | 
             
                      pp line
         | 
| 77 95 | 
             
                      pp candidates
         | 
| 78 96 | 
             
                      exit 1
         | 
| 79 97 | 
             
                    end
         | 
| 80 98 | 
             
                    if country && country != candidates[0]
         | 
| 81 | 
            -
                      puts "** !!! ERROR  | 
| 99 | 
            +
                      puts "** !!! ERROR Country.parse_heading - names do NOT match the same country:"
         | 
| 82 100 | 
             
                      pp line
         | 
| 83 101 | 
             
                      pp country
         | 
| 84 102 | 
             
                      pp candidates
         | 
| @@ -89,12 +107,12 @@ class CountrySearch | |
| 89 107 | 
             
                    ## just assume value is name or code
         | 
| 90 108 | 
             
                    candidate = find( value )
         | 
| 91 109 | 
             
                    if candidate.nil?
         | 
| 92 | 
            -
                      puts "** !!! ERROR  | 
| 110 | 
            +
                      puts "** !!! ERROR Country.parse_heading - unknown name or code >#{value}< in line: #{line}"
         | 
| 93 111 | 
             
                      pp line
         | 
| 94 112 | 
             
                      exit 1
         | 
| 95 113 | 
             
                    end
         | 
| 96 114 | 
             
                    if country && country != candidate
         | 
| 97 | 
            -
                      puts "** !!! ERROR  | 
| 115 | 
            +
                      puts "** !!! ERROR Country.parse_heading - names do NOT match the same country:"
         | 
| 98 116 | 
             
                      pp line
         | 
| 99 117 | 
             
                      pp country
         | 
| 100 118 | 
             
                      pp candidate
         | 
| @@ -126,9 +144,9 @@ end  # class WorldSearch | |
| 126 144 |  | 
| 127 145 |  | 
| 128 146 | 
             
            class DummyCountrySearch
         | 
| 129 | 
            -
                def find_by_code( code ) | 
| 147 | 
            +
                def find_by_code( code )
         | 
| 130 148 | 
             
                    puts "[WARN] no world search configured; cannot find country by code"
         | 
| 131 | 
            -
                    nil | 
| 149 | 
            +
                    nil
         | 
| 132 150 | 
             
                end
         | 
| 133 151 | 
             
                def find_by_name( name )
         | 
| 134 152 | 
             
                    puts "[WARN] no world search configured; cannot find country by name"
         | 
| @@ -2,9 +2,9 @@ module SportDb | |
| 2 2 | 
             
            module Module
         | 
| 3 3 | 
             
            module Formats
         | 
| 4 4 |  | 
| 5 | 
            -
              MAJOR =  | 
| 6 | 
            -
              MINOR =  | 
| 7 | 
            -
              PATCH =  | 
| 5 | 
            +
              MAJOR = 2    ## todo: namespace inside version or something - why? why not??
         | 
| 6 | 
            +
              MINOR = 0
         | 
| 7 | 
            +
              PATCH = 0
         | 
| 8 8 | 
             
              VERSION = [MAJOR,MINOR,PATCH].join('.')
         | 
| 9 9 |  | 
| 10 10 | 
             
              def self.version
         |