fbtok 0.4.2 → 0.5.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0bd9a1ff623cc7049c87db9b8344799d9ee49294bf1cd8f2a48cd57886fd0b89
4
- data.tar.gz: 35acfef6ccd67319d218e8dd849e3772042d8190afe786f73ba98fa19ca2418b
3
+ metadata.gz: 6bb0384d86033771326222bc9284264b9bfc44c55210261e6b598fffb3ddf925
4
+ data.tar.gz: a7692e7b0f394373c364234ec7b7c57933d6bd1344b58d4139beb41381a59921
5
5
  SHA512:
6
- metadata.gz: 20054a913668ac69f58bc17ebbc4bdc6de6528639f64a52b60925e87129f05fbd9bfe4a87c9ea8be55bf0d5010280e0fdf2f56b8b1b892b9f4744d99aa66239b
7
- data.tar.gz: c7980b69a62273c6fffd4c7025ccb086a96070ee8dff51248fab1ddf08ef2625de467e646c4496570f163519d88985e06698c1452e639419c629521320bc26c5
6
+ metadata.gz: 854430fe426e69eb27651930db928cb24bf7f4b489503a222f7ff67178fb087b465718e71cc4a9421a591ab87c784e35f2a26514c99de9151ddb9a1a13c4ec53
7
+ data.tar.gz: e602fe075cfbd20eeed1e744f4bdfa35314f2e42db69537cb8ff14ccc87b8fed50400d5f3aa53974e342a0b09c00b7110e0614e0d06ede5595c19e6e652de63d
data/CHANGELOG.md CHANGED
@@ -1,4 +1,4 @@
1
- ### 0.4.2
1
+ ### 0.5.1
2
2
  ### 0.0.1 / 2025-01-02
3
3
 
4
4
  * Everything is new. First release.
data/Manifest.txt CHANGED
@@ -12,4 +12,6 @@ lib/fbtok/fbquick.rb
12
12
  lib/fbtok/fbtok.rb
13
13
  lib/fbtok/fbtree.rb
14
14
  lib/fbtok/fbx.rb
15
+ lib/fbtok/filepack.rb
15
16
  lib/fbtok/pathspec.rb
17
+ lib/fbtok/pathspec_report.rb
data/Rakefile CHANGED
@@ -2,7 +2,7 @@ require 'hoe'
2
2
 
3
3
 
4
4
  Hoe.spec 'fbtok' do
5
- self.version = '0.4.2'
5
+ self.version = '0.5.1'
6
6
 
7
7
  self.summary = "fbtok - football.txt lint tools incl. tokenizer, parser & more"
8
8
  self.description = summary
@@ -19,7 +19,7 @@ Hoe.spec 'fbtok' do
19
19
  self.licenses = ['Public Domain']
20
20
 
21
21
  self.extra_deps = [
22
- ['sportdb-parser', '>= 0.7.1'],
22
+ ['sportdb-parser', '>= 0.7.2'],
23
23
  ['sportdb-quick', '>= 0.7.0'],
24
24
  ]
25
25
 
data/lib/fbtok/fbquick.rb CHANGED
@@ -54,10 +54,27 @@ end
54
54
  specs = if opts[:file]
55
55
  read_pathspecs( opts[:file] )
56
56
  else
57
- args = ['/sports/openfootball/euro/2021--europe/euro.txt',
58
- '/sports/openfootball/euro/2024--germany/euro.txt'] if args.empty?
59
-
60
- build_pathspecs( args )
57
+ ## check for filepack
58
+ ##
59
+ ## fix-fix-fix
60
+ ## add tool specific filepack
61
+ ## e.g. filepack.quik|quick.txt or such
62
+ ## to match first??
63
+ ## check a list of names!!
64
+ filepack = if File.file?( './filepack.txt')
65
+ read_filepack( './filepack.txt' )
66
+ else
67
+ nil
68
+ end
69
+
70
+ path = SportDb::Pathspec.path(
71
+ ['/sports/sportdb/sport.db.v2/parser/fbtxt-specs',
72
+ '/sports/sportdb/sport.db.v2/parser/fbtxt-samples',
73
+ '/sports/openfootball'])
74
+
75
+
76
+ build_pathspecs( args, path: path,
77
+ filepack: filepack )
61
78
  end
62
79
 
63
80
  pp specs
@@ -91,6 +108,7 @@ pp specs
91
108
  pp errors
92
109
  else
93
110
  puts
111
+ pp datafiles
94
112
  puts " OK - no parse errors in #{datafiles.size} datafile(s)"
95
113
  end
96
114
 
data/lib/fbtok/fbtok.rb CHANGED
@@ -6,8 +6,8 @@ def self.main( args=ARGV )
6
6
 
7
7
  opts = {
8
8
  debug: true,
9
- warn: false,
10
9
  file: nil,
10
+ # warn: false,
11
11
  }
12
12
 
13
13
 
@@ -23,10 +23,10 @@ parser = OptionParser.new do |parser|
23
23
  "turn on verbose / debug output (default: #{opts[:debug]})" ) do |debug|
24
24
  opts[:debug] = true
25
25
  end
26
- parser.on( "-w", "--warn",
27
- "turn warnings into errors (default: #{opts[:warn]})" ) do |warn|
28
- opts[:warn] = true
29
- end
26
+ # parser.on( "-w", "--warn",
27
+ # "turn warnings into errors (default: #{opts[:warn]})" ) do |warn|
28
+ # opts[:warn] = true
29
+ # end
30
30
 
31
31
 
32
32
  parser.on( "-f FILE", "--file FILE",
@@ -58,10 +58,20 @@ end
58
58
  specs = if opts[:file]
59
59
  read_pathspecs( opts[:file] )
60
60
  else
61
- args = ['/sports/openfootball/euro/2021--europe/euro.txt',
62
- '/sports/openfootball/euro/2024--germany/euro.txt'] if args.empty?
63
-
64
- build_pathspecs( args )
61
+ ## check for filepack
62
+ filepack = if File.file?( './filepack.txt')
63
+ read_filepack( './filepack.txt' )
64
+ else
65
+ nil
66
+ end
67
+
68
+ path = SportDb::Pathspec.path(
69
+ ['/sports/sportdb/sport.db.v2/parser/fbtxt-specs',
70
+ '/sports/sportdb/sport.db.v2/parser/fbtxt-samples',
71
+ '/sports/openfootball'])
72
+
73
+ build_pathspecs( args, path: path,
74
+ filepack: filepack )
65
75
  end
66
76
 
67
77
 
@@ -82,11 +92,20 @@ specs.each_with_index do |rec,i|
82
92
  tokens, more_errors = lexer.tokenize_with_errors
83
93
 
84
94
  ####
85
- ## todo - report error on empty file (no tokens!!!)
95
+ ## todo - report error on empty file (no tokens!!!) - why? why not?
86
96
 
87
97
  puts " #{tokens.size} token(s)"
88
98
 
89
- errors += more_errors if more_errors.size > 0
99
+ if more_errors.size > 0
100
+ ## note - auto-add filename to errors
101
+ more_errors.each do |msg|
102
+ ###
103
+ ### errors << [ path, *msg ] # note: use splat (*) to add extra values (starting with msg)
104
+ errors << [path, msg]
105
+ end
106
+ end
107
+
108
+
90
109
  end
91
110
 
92
111
  if errors.size > 0
@@ -96,6 +115,7 @@ specs.each_with_index do |rec,i|
96
115
  puts "!! #{errors.size} tokenize error(s) in #{datafiles.size} datafiles(s)"
97
116
  else
98
117
  puts
118
+ pp datafiles
99
119
  puts "OK no tokenize errors found in #{datafiles.size} datafile(s)"
100
120
  end
101
121
 
data/lib/fbtok/fbtree.rb CHANGED
@@ -6,8 +6,8 @@ def self.main( args=ARGV )
6
6
 
7
7
  opts = {
8
8
  debug: true,
9
- warn: false,
10
9
  file: nil,
10
+ ## warn: false,
11
11
  }
12
12
 
13
13
  parser = OptionParser.new do |parser|
@@ -22,10 +22,10 @@ parser = OptionParser.new do |parser|
22
22
  "turn on verbose / debug output (default: #{opts[:debug]})" ) do |debug|
23
23
  opts[:debug] = true
24
24
  end
25
- parser.on( "-w", "--warn",
26
- "turn warnings into errors (default: #{opts[:warn]})" ) do |warn|
27
- opts[:warn] = true
28
- end
25
+ # parser.on( "-w", "--warn",
26
+ # "turn warnings into errors (default: #{opts[:warn]})" ) do |warn|
27
+ # opts[:warn] = true
28
+ # end
29
29
 
30
30
 
31
31
  parser.on( "-f FILE", "--file FILE",
@@ -38,7 +38,6 @@ parser = OptionParser.new do |parser|
38
38
  end
39
39
  parser.parse!( args )
40
40
 
41
-
42
41
  if opts[:debug]
43
42
  puts "OPTS:"
44
43
  p opts
@@ -47,8 +46,6 @@ if opts[:debug]
47
46
  end
48
47
 
49
48
 
50
- # SportDb::Parser::Linter.debug = opts[:debug]
51
- # SportDb::Parser::Linter.warn = opts[:warn]
52
49
 
53
50
 
54
51
 
@@ -58,13 +55,37 @@ end
58
55
  specs = if opts[:file]
59
56
  read_pathspecs( opts[:file] )
60
57
  else
61
- args = ['/sports/openfootball/euro/2021--europe/euro.txt',
62
- '/sports/openfootball/euro/2024--germany/euro.txt'] if args.empty?
63
-
64
- build_pathspecs( args )
58
+ ## check for filepack
59
+ filepack = if File.file?( './filepack.txt')
60
+ read_filepack( './filepack.txt' )
61
+ else
62
+ nil
63
+ end
64
+
65
+ path = SportDb::Pathspec.path(
66
+ ['/sports/sportdb/sport.db.v2/parser/fbtxt-specs',
67
+ '/sports/sportdb/sport.db.v2/parser/fbtxt-samples',
68
+ '/sports/openfootball'])
69
+
70
+ build_pathspecs( args, path: path,
71
+ filepack: filepack )
65
72
  end
66
73
 
67
74
 
75
+ pp specs
76
+
77
+ ## if more than single datafile
78
+ ## auto-switch into quite mode for now
79
+ ## if specs.size > 0 && specs[0]['datafiles'].size > 1
80
+ ## opts[:debug] = false
81
+ ## end
82
+
83
+ # SportDb::Parser::Linter.debug = opts[:debug]
84
+ # SportDb::Parser::Linter.warn = opts[:warn]
85
+
86
+
87
+
88
+
68
89
 
69
90
 
70
91
  specs.each_with_index do |rec,i|
@@ -81,7 +102,14 @@ specs.each_with_index do |rec,i|
81
102
 
82
103
  dump_tree_stats( tree )
83
104
 
84
- errors += parser.errors if parser.errors?
105
+ if parser.errors?
106
+ ## note - auto-add filename to errors
107
+ parser.errors.each do |msg|
108
+ ###
109
+ ### errors << [ path, *msg ] # note: use splat (*) to add extra values (starting with msg)
110
+ errors << [path, msg]
111
+ end
112
+ end
85
113
  end
86
114
 
87
115
 
@@ -92,6 +120,7 @@ specs.each_with_index do |rec,i|
92
120
  puts "!! #{errors.size} parse error(s) in #{datafiles.size} datafiles(s)"
93
121
  else
94
122
  puts
123
+ pp datafiles
95
124
  puts "OK no parse errors found in #{datafiles.size} datafile(s)"
96
125
  end
97
126
 
data/lib/fbtok/fbx.rb CHANGED
@@ -54,8 +54,8 @@ end
54
54
 
55
55
 
56
56
  args = [
57
- '../../../openfootball/euro/2021--europe/euro.txt',
58
- '../../../openfootball/euro/2024--germany/euro.txt',
57
+ '/sports/openfootball/euro/2021--europe/euro.txt',
58
+ '/sports/openfootball/euro/2024--germany/euro.txt',
59
59
  ] if args.empty?
60
60
 
61
61
  pp args
@@ -0,0 +1,63 @@
1
+ ###################
2
+ ##
3
+ ## quick and dirty format for filepacks
4
+ ## that is, named list of files
5
+ ##
6
+ ## note - space indent(ation) does NOT matter
7
+ ##
8
+
9
+
10
+
11
+ ## PACK eng|en
12
+ ## add NAME eng|en - why? why not?
13
+ ##
14
+ ## change to FILEPACK_NAME - why? why not?
15
+ FILEPACK_HEADER_RE = %r{\A
16
+ (?: PACK ) [ ]+
17
+ (?<header> .+?) ## note - use non-greedy
18
+ \z}x
19
+
20
+ ## DIR /sports/openfootball
21
+ ## CD /sports/openfootball -- keep - why? why not?
22
+ FILEPACK_DIR_RE = %r{\A
23
+ (?: DIR|CD ) [ ]+
24
+ (?<dir> .+? ) ## note - use non-greedy
25
+
26
+ \z}x
27
+
28
+
29
+
30
+ def parse_filepack( txt )
31
+ h={}
32
+ recs = nil
33
+ basedir = nil
34
+
35
+ txt.each_line do |line|
36
+ line = line.strip
37
+
38
+ next if line.start_with?('#') || line.empty?
39
+
40
+ break if line == '__END__'
41
+
42
+
43
+ if m=FILEPACK_HEADER_RE.match(line)
44
+ keys = m[:header].strip.split( /[ ]*\|[ ]*/ )
45
+ basedir = nil
46
+ recs = []
47
+ ## note - normalize keys for now (always downcase)
48
+ keys.each {|key| h[key.downcase] = recs }
49
+ elsif m=FILEPACK_DIR_RE.match(line)
50
+ basedir = m[:dir].strip
51
+ else
52
+ file = basedir ? File.join( basedir, line ) : line
53
+ recs << file
54
+ end
55
+ end
56
+
57
+ h
58
+ end
59
+
60
+
61
+ def read_filepack( path )
62
+ parse_filepack( read_text( path ))
63
+ end
@@ -1,13 +1,8 @@
1
1
 
2
2
 
3
3
  module SportDb
4
-
5
4
  class Pathspec
6
5
 
7
- def self.debug=(value) @@debug = value; end
8
- def self.debug?() @@debug ||= false; end ## note: default is FALSE
9
-
10
-
11
6
  SEASON_RE = %r{ (?:
12
7
  (?<season>\d{4}-\d{2})
13
8
  | (?<season>\d{4})
@@ -62,9 +57,6 @@ class Pathspec
62
57
  ## but starting filename e.g. 2024_friendlies.txt or 2024-25_bundesliga.txt
63
58
 
64
59
 
65
- def self._norm_seasons( seasons )
66
- seasons.map {|season| Season(season) }
67
- end
68
60
 
69
61
 
70
62
  ## todo/check - rename to glob or expand or such - why? why not?
@@ -74,23 +66,31 @@ end
74
66
  ## that will use generic **/*.txt and only use ignore filter!!!
75
67
 
76
68
 
77
- def self._find( path, seasons: nil )
69
+ def self.find( path, seasons: nil )
70
+ ##
71
+ ## note - only if seasons filter is turn on
72
+ ## MATCH_RE gets used!!!
73
+ ## otherwise generic **/*.txt
74
+ ##
75
+ ## note - the ignore/exlude filter always gets used/applied for now
76
+
77
+
78
78
  ## check - rename dir
79
79
  ## use root_dir or work_dir or cd or such - why? why not?
80
80
 
81
-
82
81
  ## note: normalize path - use File.expand_path ??
83
82
  ## change all backslash to slash for now
84
- ## path = path.gsub( "\\", '/' )
85
- path = File.expand_path( path )
83
+ fullpath = File.expand_path( path )
86
84
 
87
85
  ####
88
86
  ## note - make sure path exists; raise error if not
89
- raise Errno::ENOENT, "No such directory - #{path})" unless Dir.exist?( path )
87
+ ## ENOENT => Error No Entity
88
+ raise Errno::ENOENT, "No such directory - #{path})" unless Dir.exist?( fullpath )
90
89
 
91
90
 
92
91
  if seasons && seasons.size > 0
93
- seasons = _norm_seasons( seasons ) ## norm seasons (string, integer => Season obj)
92
+ ## norm seasons (string, integer => Season obj)
93
+ seasons = seasons.map {|season| Season(season) }
94
94
  end
95
95
 
96
96
 
@@ -99,41 +99,45 @@ def self._find( path, seasons: nil )
99
99
  ## (normally excluded with just *)
100
100
  ## was: Dir.glob( "#{path}/**/{*,.*}.txt" )
101
101
 
102
- candidates = Dir.glob( "#{path}/**/*.txt" )
102
+ candidates = Dir.glob( "#{fullpath}/**/*.txt" )
103
103
  ## pp candidates
104
104
 
105
105
 
106
106
  datafiles = []
107
107
  candidates.each do |candidate|
108
- if m = MATCH_RE.match( candidate )
109
108
 
110
- ## check for seasons filter
111
- next if seasons && seasons.size > 0 &&
112
- !seasons.include?( Season.parse( m[:season] ))
109
+ ## (i) check for (optional) seasons filter
110
+ if seasons && seasons.size > 0
111
+ if m=MATCH_RE.match( candidate )
112
+ next unless seasons.include?( Season.parse( m[:season] ))
113
+ else
114
+ next ## note - no season found in filename; skip too
115
+ end
116
+ end
117
+
113
118
 
119
+ ## (ii) check for (default/built-in) ignore/excludes
120
+ basename = File.basename( candidate, File.extname( candidate ))
121
+ dirname = File.dirname( candidate )
114
122
 
115
- #########
116
- ### exclude squad
117
- ## and .v2 or .v2603
123
+ ### exclude basenames with:
124
+ ## - squad
125
+ ## - .v2 or .v2603
118
126
  ##
119
127
  ## - worldcup/more/1930_squads.txt => squads
120
- ## - 2014--brazil/cup.v2.txt",
121
- ## - 2014--brazil/cup.v260318_164934.txt",
122
-
123
- basename = File.basename( candidate, File.extname( candidate ))
128
+ ## - 2014--brazil/cup.v2.txt
129
+ ## - 2014--brazil/cup.v260318_164934.txt
124
130
 
125
- next if /squad/i.match( basename )
126
- next if /\.v[0-9][0-9_]*/i.match( basename )
131
+ next if /squad/i.match?( basename )
132
+ next if /\.v[0-9][0-9_]*/i.match?( basename )
127
133
 
128
134
  #####
129
- ### exclude dirs w/ squad or wiki
130
- dirname = File.dirname( candidate )
131
-
132
- next if /squad|wiki/i.match( dirname )
135
+ ### exclude dirs with:
136
+ ## - squad or wiki
137
+ next if /squad|wiki/i.match?( dirname )
133
138
 
134
139
 
135
140
  datafiles << candidate
136
- end
137
141
  end
138
142
 
139
143
  ## pp datafiles
@@ -141,22 +145,48 @@ def self._find( path, seasons: nil )
141
145
  end
142
146
 
143
147
 
148
+ def self.path( default=[])
149
+ ## check for FBPATH
150
+ ## or FBTXT_PATH
151
+ path = ENV['FBPATH'] || ENV['FBTXT_PATH']
152
+ if path
153
+ path.split( /[:;]/)
154
+ else
155
+ default
156
+ end
157
+ end
158
+ end # class Pathspec
159
+
160
+
144
161
 
145
162
 
146
163
 
164
+ class Pathspecs ## change to a module - why? why not?
165
+
166
+
167
+ def self.debug=(value) @@debug = value; end
168
+ def self.debug?() @@debug ||= false; end ## note: default is FALSE
169
+
170
+
147
171
  def self.read( src )
148
- recs = read_csv( src )
172
+ ## note: normalize src - use File.expand_path ??
173
+ ## change all backslash to slash for now (\ to /)
174
+ fullsrc = File.expand_path( src )
175
+
176
+ recs = read_csv( fullsrc )
149
177
  pp recs if debug?
150
178
 
151
179
  ## note - make pathspecs relative to passed in file arg!!!
152
- basedir = File.dirname( src )
180
+ basedir = File.dirname( fullsrc )
153
181
 
154
182
  recs.each do |rec|
183
+ ##
184
+ ## todo/check - check for season/seasons column - why? why not?
155
185
  path = rec['path']
156
186
  fullpath = File.expand_path( path, basedir )
157
- datafiles = _find( fullpath )
187
+ datafiles = Pathspec.find( fullpath )
158
188
 
159
- ## add (new) datafiles column (from expanded pathspec)
189
+ ## auto-add (new) datafiles column (from expanded pathspec)
160
190
  rec['datafiles'] = datafiles
161
191
  end
162
192
 
@@ -165,90 +195,82 @@ end
165
195
 
166
196
 
167
197
 
198
+ def self.build( args, path: [],
199
+ filepack: nil )
200
+ recs = []
168
201
 
169
- end # class Pathspec
170
-
171
-
172
-
173
- ##
174
- # PathspecReport (aka/formerly BatchReport)
175
-
176
- class PathspecReport
177
- def initialize( specs, title: )
178
- @specs = specs
179
- @title = title
180
- end
181
-
182
- def build
183
- buf = String.new
184
- buf << "# #{@title} - #{@specs.size} dataset(s)\n\n"
185
-
186
- @specs.each_with_index do |rec,i|
187
- datafiles = rec['datafiles']
188
- errors = rec['errors']
189
-
190
- if errors.size > 0
191
- buf << "!! #{errors.size} ERROR(S) "
192
- else
193
- buf << " OK "
194
- end
195
- buf << "%-20s" % rec['path']
196
- buf << " - #{datafiles.size} datafile(s)"
197
- buf << "\n"
198
-
199
- if errors.size > 0
200
- buf << errors.pretty_inspect
201
- buf << "\n"
202
+ ## check fo no args case (and filepack present with default)
203
+ if args.empty?
204
+ if filepack && filepack.has_key?('default')
205
+ recs << { 'path' => '<default>',
206
+ 'datafiles' => filepack['default'] }
207
+ end
208
+ else
209
+
210
+ ## note - collect all "loose/standalone" files (NOT directories)
211
+ ## in single default pathspec node
212
+ more = []
213
+
214
+ args.each do |arg|
215
+ ## note - ALWAYS give priority to existing directory matches (over filepack)
216
+ ## e.g. euro or such - why? why not?
217
+ ##
218
+ ## todo/check if euro/ works too?
219
+ ## check if directory
220
+ if Dir.exist?( arg )
221
+ recs << { 'path' => arg,
222
+ 'datafiles' => Pathspec.find( arg ) }
223
+ elsif filepack && filepack.has_key?( arg.downcase )
224
+ recs << { 'path' => "<#{arg.downcase}>",
225
+ 'datafiles' => filepack[arg.downcase] }
226
+ else ## assume it's a file
227
+ file = find_file( arg, path: path )
228
+ ## check if file (exists) in any path lookup
229
+ if file
230
+ ## todo/fix:
231
+ ## add File.expand_path upstream in find_file !!!
232
+ ## and remove later here
233
+ ## also fix upstream Errorno::ENOENT to Errno::ENOENT !!!
234
+ ##
235
+ ## make sure path exists; raise error if not
236
+ ## (auto-)expand path to normalize - yes why? why not?
237
+ more << File.expand_path(file)
238
+ else
239
+ raise Errno::ENOENT, "No such file or directory - #{arg}"
240
+ end
202
241
  end
203
- end
242
+ end
204
243
 
205
- buf
206
- end # method build
207
- end # class BatchReport
244
+ if more.size > 0
245
+ recs << { 'path' => '<input>',
246
+ 'datafiles' => more }
247
+ end
248
+ end
208
249
 
209
- BatchReport = PathspecReport
250
+ recs
251
+ end
210
252
 
253
+ end # class Pathspecs
211
254
  end # module Sportdb
212
255
 
213
256
 
214
257
 
258
+
215
259
  ##
216
260
  ## keep helpers as global functions - why? why not?
217
261
 
218
262
 
219
-
220
- def build_pathspecs( args ) ### note: was expand_args
221
- specs = []
222
-
223
- ## note - collect all "loose/standalone" files (NOT directories)
224
- ## in single default pathspec node
225
- more = []
226
-
227
- args.each do |arg|
228
- ## check if directory
229
- if Dir.exist?( arg )
230
- datafiles = SportDb::Pathspec._find( arg )
231
- specs << { 'path' => arg,
232
- 'datafiles' => datafiles }
233
- elsif File.file?( arg ) ## assume it's a file
234
- ## make sure path exists; raise error if not
235
- ## (auto-)expand path to normalize - why? why not?
236
- more << arg
237
- else
238
- raise Errno::ENOENT, "No such file or directory - #{arg}"
239
- end
240
- end
241
-
242
- if more.size > 0
243
- specs << { 'path' => '<input>',
244
- 'datafiles' => more }
245
- end
246
-
247
- specs
263
+ ## build pathspecs via arguments
264
+ ## - (i) every dir is a pathspec entry/record
265
+ ## - (ii) all files get bundled together into <input> pathspec entry/record
266
+ ##
267
+ ## note: was formerly known as expand_args
268
+ def build_pathspecs( args, path: [], filepack: nil )
269
+ SportDb::Pathspecs.build( args, path: path, filepack: filepack )
248
270
  end
249
271
 
250
-
251
-
272
+ ####
273
+ ## read pathspecs via csv file (using path column)
252
274
  def read_pathspecs( src )
253
- SportDb::Pathspec.read( src )
275
+ SportDb::Pathspecs.read( src )
254
276
  end
@@ -0,0 +1,42 @@
1
+ module SportDb
2
+
3
+ ##
4
+ # PathspecReport (aka/formerly BatchReport)
5
+
6
+ class PathspecReport
7
+ def initialize( specs, title: )
8
+ @specs = specs
9
+ @title = title
10
+ end
11
+
12
+ def build
13
+ buf = String.new
14
+ buf << "# #{@title} - #{@specs.size} dataset(s)\n\n"
15
+
16
+ @specs.each_with_index do |rec,i|
17
+ datafiles = rec['datafiles']
18
+ errors = rec['errors']
19
+
20
+ if errors.size > 0
21
+ buf << "!! #{errors.size} ERROR(S) "
22
+ else
23
+ buf << " OK "
24
+ end
25
+ buf << "%-20s" % rec['path']
26
+ buf << " - #{datafiles.size} datafile(s)"
27
+ buf << "\n"
28
+
29
+ if errors.size > 0
30
+ buf << errors.pretty_inspect
31
+ buf << "\n"
32
+ end
33
+ end
34
+
35
+ buf
36
+ end # method build
37
+ end # class BatchReport
38
+
39
+ BatchReport = PathspecReport
40
+
41
+
42
+ end # module Sportdb
data/lib/fbtok.rb CHANGED
@@ -3,8 +3,9 @@ require 'sportdb/quick'
3
3
 
4
4
 
5
5
  ## our own code
6
+ require_relative 'fbtok/filepack'
6
7
  require_relative 'fbtok/pathspec'
7
-
8
+ require_relative 'fbtok/pathspec_report'
8
9
 
9
10
  require_relative 'fbtok/fbtok'
10
11
  require_relative 'fbtok/fbtree'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fbtok
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.2
4
+ version: 0.5.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: 2026-05-31 00:00:00.000000000 Z
11
+ date: 2026-06-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sportdb-parser
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 0.7.1
19
+ version: 0.7.2
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: 0.7.1
26
+ version: 0.7.2
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: sportdb-quick
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -100,7 +100,9 @@ files:
100
100
  - lib/fbtok/fbtok.rb
101
101
  - lib/fbtok/fbtree.rb
102
102
  - lib/fbtok/fbx.rb
103
+ - lib/fbtok/filepack.rb
103
104
  - lib/fbtok/pathspec.rb
105
+ - lib/fbtok/pathspec_report.rb
104
106
  homepage: https://github.com/sportdb/footty
105
107
  licenses:
106
108
  - Public Domain