sportdb-quick 0.1.2 → 0.2.1
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 +0 -1
- data/bin/fbt +106 -57
- data/lib/sportdb/quick/match_parser.rb +21 -10
- data/lib/sportdb/quick/quick_league_outline_reader.rb +2 -1
- data/lib/sportdb/quick/quick_match_reader.rb +55 -16
- data/lib/sportdb/quick/version.rb +2 -2
- data/lib/sportdb/quick.rb +1 -1
- metadata +2 -3
- data/lib/sportdb/quick/opts.rb +0 -70
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 743ddb45d7379bdcb351178da359316544e663307b5d96c34f6ac90b9752fce3
|
4
|
+
data.tar.gz: 86960f084c612aec1e04f183ba1f3a89959518355e498527734b23a265090fa9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6068e848f99a756b9de1d4012cda3d5c6750dc99c329f773cbd692ffa9004f2cfa5835ffdb329aae055eaa30f1ffafeab42adc1a627d05ca040261d71d5476a7
|
7
|
+
data.tar.gz: d143f15ecca6cc4bdf3c72e0d8883c94622198e9fd721ce14e93ebc882c127ef4300d865bb05d7c129e82e2f27d81ea25ca4894e0d254a0b80e6600911cd6ac5
|
data/CHANGELOG.md
CHANGED
data/Manifest.txt
CHANGED
@@ -9,7 +9,6 @@ lib/sportdb/quick/csv/goal_parser_csv.rb
|
|
9
9
|
lib/sportdb/quick/csv/match_parser_csv.rb
|
10
10
|
lib/sportdb/quick/csv/match_status_parser.rb
|
11
11
|
lib/sportdb/quick/match_parser.rb
|
12
|
-
lib/sportdb/quick/opts.rb
|
13
12
|
lib/sportdb/quick/quick_league_outline_reader.rb
|
14
13
|
lib/sportdb/quick/quick_match_reader.rb
|
15
14
|
lib/sportdb/quick/version.rb
|
data/bin/fbt
CHANGED
@@ -26,8 +26,8 @@ require 'optparse'
|
|
26
26
|
|
27
27
|
args = ARGV
|
28
28
|
opts = { debug: false,
|
29
|
-
|
30
|
-
|
29
|
+
file: nil,
|
30
|
+
}
|
31
31
|
|
32
32
|
parser = OptionParser.new do |parser|
|
33
33
|
parser.banner = "Usage: #{$PROGRAM_NAME} [options]"
|
@@ -35,24 +35,21 @@ require 'optparse'
|
|
35
35
|
##
|
36
36
|
## check if git has a offline option?? (use same)
|
37
37
|
## check for other tools - why? why not?
|
38
|
-
|
39
|
-
|
40
|
-
|
38
|
+
# parser.on( "-q", "--quiet",
|
39
|
+
# "less debug output/messages - default is (#{!opts[:debug]})" ) do |debug|
|
40
|
+
# opts[:debug] = false
|
41
|
+
# end
|
42
|
+
parser.on( "--verbose", "--debug",
|
41
43
|
"turn on verbose / debug output (default: #{opts[:debug]})" ) do |debug|
|
42
|
-
|
43
|
-
|
44
|
+
opts[:debug] = true
|
45
|
+
end
|
46
|
+
|
47
|
+
parser.on( "-f FILE", "--file FILE",
|
48
|
+
"read datafiles (pathspecs) via .csv file") do |file|
|
49
|
+
opts[:file] = file
|
50
|
+
end
|
44
51
|
|
45
|
-
parser.on( "--metal",
|
46
|
-
"turn off typed parse tree; show to the metal tokens"+
|
47
|
-
" (default: #{opts[:metal]})" ) do |metal|
|
48
|
-
opts[:metal] = true
|
49
|
-
end
|
50
52
|
|
51
|
-
parser.on( "--quick",
|
52
|
-
"use quick match reader; output matches in json"+
|
53
|
-
" (default: #{opts[:quick]})" ) do |quick|
|
54
|
-
opts[:quick] = true
|
55
|
-
end
|
56
53
|
end
|
57
54
|
parser.parse!( args )
|
58
55
|
|
@@ -62,58 +59,110 @@ puts "ARGV:"
|
|
62
59
|
p args
|
63
60
|
|
64
61
|
|
62
|
+
## todo/check - use packs or projects or such
|
63
|
+
## instead of specs - why? why not?
|
64
|
+
specs = []
|
65
|
+
if opts[:file]
|
66
|
+
recs = read_csv( opts[:file] )
|
67
|
+
pp recs
|
68
|
+
## note - make pathspecs relative to passed in file arg!!!
|
69
|
+
basedir = File.dirname( opts[:file] )
|
70
|
+
recs.each do |rec|
|
71
|
+
paths = SportDb::Parser::Opts.find( rec['path'], dir: basedir )
|
72
|
+
specs << [paths, rec]
|
73
|
+
end
|
74
|
+
else
|
75
|
+
paths = if args.empty?
|
76
|
+
[
|
77
|
+
'../../../openfootball/euro/2021--europe/euro.txt',
|
78
|
+
'../../../openfootball/euro/2024--germany/euro.txt',
|
79
|
+
]
|
80
|
+
else
|
81
|
+
## check for directories
|
82
|
+
## and auto-expand
|
83
|
+
SportDb::Parser::Opts.expand_args( args )
|
84
|
+
end
|
85
|
+
specs << [paths, {}]
|
86
|
+
end
|
87
|
+
|
88
|
+
|
89
|
+
if opts[:debug]
|
90
|
+
SportDb::QuickMatchReader.debug = true
|
91
|
+
SportDb::MatchParser.debug = true
|
92
|
+
else
|
93
|
+
SportDb::QuickMatchReader.debug = false
|
94
|
+
SportDb::MatchParser.debug = false
|
95
|
+
LogUtils::Logger.root.level = :info
|
96
|
+
end
|
65
97
|
|
66
98
|
|
99
|
+
specs.each_with_index do |(paths, rec),i|
|
100
|
+
errors = []
|
101
|
+
paths.each_with_index do |path,j|
|
102
|
+
puts "==> [#{j+1}/#{paths.size}] reading >#{path}<..."
|
103
|
+
quick = SportDb::QuickMatchReader.new( read_text( path ) )
|
104
|
+
matches = quick.parse
|
105
|
+
|
106
|
+
if quick.errors?
|
107
|
+
puts "!! #{quick.errors.size} error(s):"
|
108
|
+
pp quick.errors
|
109
|
+
|
110
|
+
quick.errors.each do |err|
|
111
|
+
errors << [ path, *err ] # note: use splat (*) to add extra values (starting with msg)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
puts " #{matches.size} match(es)"
|
115
|
+
end
|
116
|
+
|
117
|
+
if errors.size > 0
|
118
|
+
puts
|
119
|
+
puts "!! #{errors.size} PARSE ERRORS in #{paths.size} datafile(s)"
|
120
|
+
pp errors
|
121
|
+
else
|
122
|
+
puts
|
123
|
+
puts " OK - no parse errors in #{paths.size} datafile(s)"
|
124
|
+
end
|
125
|
+
|
126
|
+
## add errors to rec via rec['errors'] to allow
|
127
|
+
## for further processing/reporting
|
128
|
+
rec['errors'] = errors
|
129
|
+
end
|
67
130
|
|
68
|
-
paths = if args.empty?
|
69
|
-
[
|
70
|
-
'../../../openfootball/euro/2021--europe/euro.txt',
|
71
|
-
'../../../openfootball/euro/2024--germany/euro.txt',
|
72
|
-
]
|
73
|
-
else
|
74
|
-
## check for directories
|
75
|
-
## and auto-expand
|
76
|
-
|
77
|
-
SportDb::Quick::Opts.expand_args( args )
|
78
|
-
end
|
79
131
|
|
80
132
|
|
81
|
-
|
133
|
+
###
|
134
|
+
## generate a report if --file option used
|
135
|
+
if opts[:file]
|
82
136
|
|
83
|
-
|
84
|
-
puts "==> [#{i+1}/#{paths.size}] reading >#{path}<..."
|
137
|
+
buf = String.new
|
85
138
|
|
86
|
-
|
87
|
-
## pp matches
|
88
|
-
## try json for matches
|
89
|
-
data = matches.map {|match| match.as_json }
|
90
|
-
pp data
|
91
|
-
puts
|
92
|
-
puts " #{data.size} match(es)"
|
93
|
-
end
|
94
|
-
else
|
95
|
-
SportDb::Parser::Linter.debug = true if opts[:debug]
|
139
|
+
buf << "# fbt summary report - #{specs.size} dataset(s)\n\n"
|
96
140
|
|
97
|
-
|
141
|
+
specs.each_with_index do |(paths, rec),i|
|
142
|
+
errors = rec['errors']
|
98
143
|
|
99
|
-
|
144
|
+
if errors.size > 0
|
145
|
+
buf << "!! #{errors.size} ERROR(S) "
|
146
|
+
else
|
147
|
+
buf << " OK "
|
148
|
+
end
|
149
|
+
buf << "%-20s" % rec['path']
|
150
|
+
buf << " - #{paths.size} datafile(s)"
|
151
|
+
buf << "\n"
|
100
152
|
|
101
|
-
|
102
|
-
|
103
|
-
|
153
|
+
if errors.size > 0
|
154
|
+
buf << errors.pretty_inspect
|
155
|
+
buf << "\n"
|
156
|
+
end
|
157
|
+
end
|
104
158
|
|
105
|
-
|
106
|
-
|
159
|
+
puts
|
160
|
+
puts "SUMMARY:"
|
161
|
+
puts buf
|
107
162
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
puts
|
112
|
-
puts "!! #{errors.size} parse error(s) in #{paths.size} datafiles(s)"
|
113
|
-
else
|
114
|
-
puts
|
115
|
-
puts "OK no parse errors found in #{paths.size} datafile(s)"
|
116
|
-
end
|
163
|
+
# maybe write out in the future?
|
164
|
+
# basedir = File.dirname( opts[:file] )
|
165
|
+
# basename = File.basename( opts[:file], File.extname( opts[:file] ))
|
117
166
|
end
|
118
167
|
|
119
168
|
|
@@ -3,6 +3,12 @@ module SportDb
|
|
3
3
|
|
4
4
|
class MatchParser ## simple match parser for team match schedules
|
5
5
|
|
6
|
+
def self.debug=(value) @@debug = value; end
|
7
|
+
def self.debug?() @@debug ||= false; end ## note: default is FALSE
|
8
|
+
def debug?() self.class.debug?; end
|
9
|
+
|
10
|
+
include Logging ## e.g. logger#debug, logger#info, etc.
|
11
|
+
|
6
12
|
|
7
13
|
def self.parse( lines, start: )
|
8
14
|
## todo/fix: add support for txt and lines
|
@@ -13,11 +19,7 @@ class MatchParser ## simple match parser for team match schedules
|
|
13
19
|
end
|
14
20
|
|
15
21
|
|
16
|
-
include Logging ## e.g. logger#debug, logger#info, etc.
|
17
22
|
|
18
|
-
def self.debug=(value) @@debug = value; end
|
19
|
-
def self.debug?() @@debug ||= false; end ## note: default is FALSE
|
20
|
-
def debug?() self.class.debug?; end
|
21
23
|
|
22
24
|
def _read_lines( txt ) ## todo/check: add alias preproc_lines or build_lines or prep_lines etc. - why? why not?
|
23
25
|
## returns an array of lines with comments and empty lines striped / removed
|
@@ -70,10 +72,17 @@ class MatchParser ## simple match parser for team match schedules
|
|
70
72
|
_read_lines( lines ) : lines
|
71
73
|
|
72
74
|
@start = start
|
75
|
+
@errors = []
|
73
76
|
end
|
74
77
|
|
75
78
|
|
79
|
+
attr_reader :errors
|
80
|
+
def errors?() @errors.size > 0; end
|
81
|
+
|
76
82
|
def parse
|
83
|
+
## note: every (new) read call - resets errors list to empty
|
84
|
+
@errors = []
|
85
|
+
|
77
86
|
@last_date = nil
|
78
87
|
@last_time = nil
|
79
88
|
@last_round = nil
|
@@ -92,8 +101,6 @@ class MatchParser ## simple match parser for team match schedules
|
|
92
101
|
|
93
102
|
|
94
103
|
@parser = Parser.new
|
95
|
-
|
96
|
-
@errors = []
|
97
104
|
@tree = []
|
98
105
|
|
99
106
|
attrib_found = false
|
@@ -509,7 +516,7 @@ class MatchParser ## simple match parser for team match schedules
|
|
509
516
|
end
|
510
517
|
end
|
511
518
|
|
512
|
-
pp [goals1,goals2]
|
519
|
+
pp [goals1,goals2] if debug?
|
513
520
|
|
514
521
|
## wrap in struct andd add/append to match
|
515
522
|
=begin
|
@@ -547,7 +554,7 @@ class GoalStruct
|
|
547
554
|
goals << goal
|
548
555
|
end
|
549
556
|
|
550
|
-
pp goals
|
557
|
+
pp goals if debug?
|
551
558
|
|
552
559
|
## quick & dirty - auto add goals to last match
|
553
560
|
## note - for hacky (quick& dirty) multi-line support
|
@@ -629,7 +636,9 @@ class GoalStruct
|
|
629
636
|
else
|
630
637
|
puts "!! PARSE ERROR - unexpected node type #{node_type} in match line; got:"
|
631
638
|
pp node
|
632
|
-
exit 1
|
639
|
+
## exit 1
|
640
|
+
@errors << ["PARSE ERROR - unexpected node type #{node_type} in match line; got: #{node.inspect}"]
|
641
|
+
return
|
633
642
|
end
|
634
643
|
end
|
635
644
|
|
@@ -637,7 +646,9 @@ class GoalStruct
|
|
637
646
|
if teams.size != 2
|
638
647
|
puts "!! PARSE ERROR - expected two teams; got #{teams.size}:"
|
639
648
|
pp teams
|
640
|
-
exit 1
|
649
|
+
## exit 1
|
650
|
+
@errors << ["PARSE ERROR - expected two teams; got #{teams.size}: #{teams.inspect}"]
|
651
|
+
return
|
641
652
|
end
|
642
653
|
|
643
654
|
team1 = teams[0]
|
@@ -86,7 +86,8 @@ class QuickLeagueOutlineReader
|
|
86
86
|
## => Österr. Bundesliga 2018/19, Regular Season
|
87
87
|
## => Österr. Bundesliga 2018/19, Championship Round
|
88
88
|
## etc.
|
89
|
-
|
89
|
+
## note limit split to two parts only!!!!
|
90
|
+
values = str.split( /[,<>‹›]/, 2 ) ## note: allow , > < or › ‹ for now
|
90
91
|
values = values.map { |value| value.strip } ## remove all whitespaces
|
91
92
|
values
|
92
93
|
end
|
@@ -4,6 +4,12 @@
|
|
4
4
|
module SportDb
|
5
5
|
class QuickMatchReader
|
6
6
|
|
7
|
+
def self.debug=(value) @@debug = value; end
|
8
|
+
def self.debug?() @@debug ||= false; end ## note: default is FALSE
|
9
|
+
def debug?() self.class.debug?; end
|
10
|
+
|
11
|
+
|
12
|
+
|
7
13
|
def self.read( path ) ## use - rename to read_file or from_file etc. - why? why not?
|
8
14
|
txt = File.open( path, 'r:utf-8' ) {|f| f.read }
|
9
15
|
parse( txt )
|
@@ -17,16 +23,43 @@ class QuickMatchReader
|
|
17
23
|
include Logging
|
18
24
|
|
19
25
|
def initialize( txt )
|
26
|
+
@errors = []
|
20
27
|
@txt = txt
|
21
28
|
end
|
22
29
|
|
30
|
+
attr_reader :errors
|
31
|
+
def errors?() @errors.size > 0; end
|
32
|
+
|
33
|
+
|
34
|
+
###
|
35
|
+
# helpers get matches & more after parse; all stored in data
|
36
|
+
#
|
37
|
+
## change/rename to event - why? why not?
|
38
|
+
def league_name
|
39
|
+
league = @data.keys[0]
|
40
|
+
season = @data[ league ].keys[0]
|
41
|
+
|
42
|
+
"#{league} #{season}"
|
43
|
+
end
|
44
|
+
|
45
|
+
def matches
|
46
|
+
league = @data.keys[0]
|
47
|
+
season = @data[ league ].keys[0]
|
48
|
+
@data[league][season]
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
|
23
53
|
def parse
|
24
|
-
|
54
|
+
## note: every (new) read call - resets errors list to empty
|
55
|
+
@errors = []
|
56
|
+
|
57
|
+
@data = {} # return data hash with leagues
|
25
58
|
# and seasons
|
26
59
|
# for now merge stage into matches
|
27
60
|
|
28
61
|
secs = QuickLeagueOutlineReader.parse( @txt )
|
29
|
-
pp secs
|
62
|
+
pp secs if debug?
|
30
63
|
|
31
64
|
secs.each do |sec| ## sec(tion)s
|
32
65
|
season = Season.parse( sec[:season] ) ## convert (str) to season obj!!!
|
@@ -46,14 +79,20 @@ class QuickMatchReader
|
|
46
79
|
|
47
80
|
auto_conf_teams, matches, rounds, groups = parser.parse
|
48
81
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
82
|
+
## auto-add "upstream" errors from parser
|
83
|
+
@errors += parser.errors if parser.errors?
|
84
|
+
|
85
|
+
|
86
|
+
if debug?
|
87
|
+
puts ">>> #{auto_conf_teams.size} teams:"
|
88
|
+
pp auto_conf_teams
|
89
|
+
puts ">>> #{matches.size} matches:"
|
90
|
+
## pp matches
|
91
|
+
puts ">>> #{rounds.size} rounds:"
|
92
|
+
pp rounds
|
93
|
+
puts ">>> #{groups.size} groups:"
|
94
|
+
pp groups
|
95
|
+
end
|
57
96
|
|
58
97
|
## note: pass along stage (if present): stage - optional from heading!!!!
|
59
98
|
if stage
|
@@ -62,10 +101,10 @@ class QuickMatchReader
|
|
62
101
|
end
|
63
102
|
end
|
64
103
|
|
65
|
-
data[ league ] ||= {}
|
66
|
-
data[ league ][ season.key ] ||= []
|
104
|
+
@data[ league ] ||= {}
|
105
|
+
@data[ league ][ season.key ] ||= []
|
67
106
|
|
68
|
-
data[ league ][ season.key ] += matches
|
107
|
+
@data[ league ][ season.key ] += matches
|
69
108
|
## note - skip teams, rounds, and groups for now
|
70
109
|
end
|
71
110
|
|
@@ -73,21 +112,21 @@ class QuickMatchReader
|
|
73
112
|
## allowed in quick style
|
74
113
|
|
75
114
|
|
76
|
-
leagues = data.keys
|
115
|
+
leagues = @data.keys
|
77
116
|
if leagues.size != 1
|
78
117
|
puts "!! (QUICK) PARSE ERROR - expected one league only; got #{leagues.size}:"
|
79
118
|
pp leagues
|
80
119
|
exit 1
|
81
120
|
end
|
82
121
|
|
83
|
-
seasons = data[ leagues[0] ].keys
|
122
|
+
seasons = @data[ leagues[0] ].keys
|
84
123
|
if seasons.size != 1
|
85
124
|
puts "!! (QUICK) PARSE ERROR - expected one #{leagues[0]} season only; got #{seasons.size}:"
|
86
125
|
pp seasons
|
87
126
|
exit 1
|
88
127
|
end
|
89
128
|
|
90
|
-
data[ leagues[0] ][ seasons[0] ]
|
129
|
+
@data[ leagues[0] ][ seasons[0] ]
|
91
130
|
end # method parse
|
92
131
|
|
93
132
|
end # class QuickMatchReader
|
data/lib/sportdb/quick.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sportdb-quick
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gerald Bauer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-09-
|
11
|
+
date: 2024-09-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sportdb-parser
|
@@ -107,7 +107,6 @@ files:
|
|
107
107
|
- lib/sportdb/quick/csv/match_parser_csv.rb
|
108
108
|
- lib/sportdb/quick/csv/match_status_parser.rb
|
109
109
|
- lib/sportdb/quick/match_parser.rb
|
110
|
-
- lib/sportdb/quick/opts.rb
|
111
110
|
- lib/sportdb/quick/quick_league_outline_reader.rb
|
112
111
|
- lib/sportdb/quick/quick_match_reader.rb
|
113
112
|
- lib/sportdb/quick/version.rb
|
data/lib/sportdb/quick/opts.rb
DELETED
@@ -1,70 +0,0 @@
|
|
1
|
-
|
2
|
-
module SportDb
|
3
|
-
module Quick
|
4
|
-
|
5
|
-
###
|
6
|
-
## note - Opts Helpers for now nested inside Parser - keep here? why? why not?
|
7
|
-
class Opts
|
8
|
-
|
9
|
-
SEASON_RE = %r{ (?:
|
10
|
-
\d{4}-\d{2}
|
11
|
-
| \d{4}(--[a-z0-9_-]+)?
|
12
|
-
)
|
13
|
-
}x
|
14
|
-
SEASON = SEASON_RE.source ## "inline" helper for embedding in other regexes - keep? why? why not?
|
15
|
-
|
16
|
-
|
17
|
-
## note: if pattern includes directory add here
|
18
|
-
## (otherwise move to more "generic" datafile) - why? why not?
|
19
|
-
MATCH_RE = %r{ (?: ^|/ ) # beginning (^) or beginning of path (/)
|
20
|
-
#{SEASON}
|
21
|
-
/[a-z0-9_-]+\.txt$ ## txt e.g /1-premierleague.txt
|
22
|
-
}x
|
23
|
-
|
24
|
-
|
25
|
-
def self.find( path )
|
26
|
-
datafiles = []
|
27
|
-
|
28
|
-
## note: normalize path - use File.expand_path ??
|
29
|
-
## change all backslash to slash for now
|
30
|
-
## path = path.gsub( "\\", '/' )
|
31
|
-
path = File.expand_path( path )
|
32
|
-
|
33
|
-
## check all txt files
|
34
|
-
## note: incl. files starting with dot (.)) as candidates
|
35
|
-
## (normally excluded with just *)
|
36
|
-
candidates = Dir.glob( "#{path}/**/{*,.*}.txt" )
|
37
|
-
## pp candidates
|
38
|
-
candidates.each do |candidate|
|
39
|
-
datafiles << candidate if MATCH_RE.match( candidate )
|
40
|
-
end
|
41
|
-
|
42
|
-
## pp datafiles
|
43
|
-
datafiles
|
44
|
-
end
|
45
|
-
|
46
|
-
|
47
|
-
def self.expand_args( args )
|
48
|
-
paths = []
|
49
|
-
|
50
|
-
args.each do |arg|
|
51
|
-
## check if directory
|
52
|
-
if Dir.exist?( arg )
|
53
|
-
datafiles = find( arg )
|
54
|
-
puts
|
55
|
-
puts " found #{datafiles.size} match txt datafiles in #{arg}"
|
56
|
-
pp datafiles
|
57
|
-
paths += datafiles
|
58
|
-
else
|
59
|
-
## assume it's a file
|
60
|
-
paths << arg
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
paths
|
65
|
-
end
|
66
|
-
end # class Opts
|
67
|
-
|
68
|
-
|
69
|
-
end # module Quick
|
70
|
-
end # module SportDb
|