starscope 1.5.3 → 1.5.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9cc8ef281696106211f68e80221f662c49393bc0
4
- data.tar.gz: 489ce33b9f7a6db5c7206178e7bfdbbbc4d0f1ad
3
+ metadata.gz: fe59ab1f2e2cf5aaee13db7f085dc708323a3c50
4
+ data.tar.gz: 2d32eee21d9bc30996097deb13ac9c6fcb2363cb
5
5
  SHA512:
6
- metadata.gz: 1c1ebeefeb796053f86aeed6e774bdea8bc0848b6666ac0a26b37a06f39a0b2bdcaf0d90921685722dfcefbcf72feb8cc3de057f26ecf5fc34d6f1363eb4e146
7
- data.tar.gz: 896f6fd5750d5a41e4121e18e4f8b843bb8fae491b74408608f819202953df75e0d52919736e82a4d2442e642ffd76c2e5092a8e047cb79c47d5c6763c9ef96c
6
+ metadata.gz: 21d7d6d5d3cb0eb6b9f7903d54fdc90e1ac9abc34b1e0c6751e3814a664987bea98756a34c7f0d3b1fa08d78f61de9212b1714bfb38bccc48c3dc899ded71814
7
+ data.tar.gz: 5da04677144db25e80b40a96db6608f4ac32f1de8e2f7f36904726a7da0b6b1cff67df642df74a43b89419aa0bea9cb6ba731b98f5cd6244eac7917291ca41d5
@@ -1,5 +1,6 @@
1
- Lint/UnusedMethodArgument:
2
- Enabled: false
1
+ AllCops:
2
+ Exclude:
3
+ - 'test/fixtures/sample_ruby.rb'
3
4
 
4
5
  Metrics/AbcSize:
5
6
  Enabled: false
@@ -25,16 +26,9 @@ Metrics/ModuleLength:
25
26
  Metrics/PerceivedComplexity:
26
27
  Enabled: false
27
28
 
28
- Style/ClassAndModuleChildren:
29
- Enabled: false
30
-
31
29
  Style/Documentation:
32
30
  Enabled: false
33
31
 
34
- Style/Next:
35
- Exclude:
36
- - 'test/fixtures/sample_ruby.rb'
37
-
38
32
  Style/SpecialGlobalVars:
39
33
  Enabled: false
40
34
 
@@ -1,10 +1,10 @@
1
1
  language: ruby
2
2
  sudo: false
3
- cache: bundler
4
3
 
5
4
  rvm:
6
5
  - 1.9.3
7
6
  - 2.0
8
7
  - 2.1
9
8
  - 2.2
10
- - 2.3.0
9
+ - 2.3.3
10
+ - 2.4.0
@@ -1,7 +1,17 @@
1
1
  Changelog
2
2
  =========
3
3
 
4
- Trunk
4
+ v1.5.4 (unreleased)
5
+ --------------------
6
+
7
+ Improvements:
8
+ * When dumping file metadata, don't include the file contents.
9
+
10
+ Bug Fixes:
11
+ * Fix parsing ruby files with invalidly-encoded literals (#160).
12
+ * Fix exporting ctags files to different output directories (#163).
13
+
14
+ v1.5.3 (2016-03-02)
5
15
  --------------------
6
16
 
7
17
  Improvements:
@@ -172,6 +172,8 @@ def dump(db, table)
172
172
  db.tables.each { |t| dump_table(db, t) }
173
173
  when '_meta'
174
174
  puts db.metadata
175
+ when '_files'
176
+ puts db.metadata(:files).keys
175
177
  when /^_/
176
178
  puts db.metadata(table[1..-1].to_sym)
177
179
  else
@@ -8,331 +8,333 @@ require 'starscope/fragment_extractor'
8
8
  require 'starscope/queryable'
9
9
  require 'starscope/output'
10
10
 
11
- class Starscope::DB
12
- include Starscope::Exportable
13
- include Starscope::Queryable
14
-
15
- DB_FORMAT = 5
16
- FRAGMENT = :'!fragment'
17
-
18
- # dynamically load all our language extractors
19
- Dir.glob("#{File.dirname(__FILE__)}/langs/*.rb").each { |path| require path }
20
-
21
- langs = {}
22
- extractors = []
23
- Starscope::Lang.constants.each do |lang|
24
- extractor = Starscope::Lang.const_get(lang)
25
- extractors << extractor
26
- langs[lang.to_sym] = extractor.const_get(:VERSION)
27
- end
28
- LANGS = langs.freeze
29
- EXTRACTORS = extractors.freeze
30
-
31
- class NoTableError < StandardError; end
32
- class UnknownDBFormatError < StandardError; end
33
-
34
- def initialize(output, config = {})
35
- @output = output
36
- @meta = { paths: [], files: {}, excludes: [],
37
- langs: LANGS.dup, version: Starscope::VERSION }
38
- @tables = {}
39
- @config = config
40
- end
11
+ module Starscope
12
+ class DB
13
+ include Starscope::Exportable
14
+ include Starscope::Queryable
15
+
16
+ DB_FORMAT = 5
17
+ FRAGMENT = :'!fragment'
18
+
19
+ # dynamically load all our language extractors
20
+ Dir.glob("#{File.dirname(__FILE__)}/langs/*.rb").each { |path| require path }
21
+
22
+ langs = {}
23
+ extractors = []
24
+ Starscope::Lang.constants.each do |lang|
25
+ extractor = Starscope::Lang.const_get(lang)
26
+ extractors << extractor
27
+ langs[lang.to_sym] = extractor.const_get(:VERSION)
28
+ end
29
+ LANGS = langs.freeze
30
+ EXTRACTORS = extractors.freeze
31
+
32
+ class NoTableError < StandardError; end
33
+ class UnknownDBFormatError < StandardError; end
34
+
35
+ def initialize(output, config = {})
36
+ @output = output
37
+ @meta = { paths: [], files: {}, excludes: [],
38
+ langs: LANGS.dup, version: Starscope::VERSION }
39
+ @tables = {}
40
+ @config = config
41
+ end
41
42
 
42
- # returns true iff the database was already in the most recent format
43
- def load(filename)
44
- @output.extra("Reading database from `#{filename}`... ")
45
- current_fmt = open_db(filename)
46
- fixup if current_fmt
47
- current_fmt
48
- end
43
+ # returns true iff the database was already in the most recent format
44
+ def load(filename)
45
+ @output.extra("Reading database from `#{filename}`... ")
46
+ current_fmt = open_db(filename)
47
+ fixup if current_fmt
48
+ current_fmt
49
+ end
49
50
 
50
- def save(filename)
51
- @output.extra("Writing database to `#{filename}`...")
51
+ def save(filename)
52
+ @output.extra("Writing database to `#{filename}`...")
52
53
 
53
- # regardless of what the old version was, the new version is written by us
54
- @meta[:version] = Starscope::VERSION
54
+ # regardless of what the old version was, the new version is written by us
55
+ @meta[:version] = Starscope::VERSION
55
56
 
56
- @meta[:langs].merge!(LANGS)
57
+ @meta[:langs].merge!(LANGS)
57
58
 
58
- File.open(filename, 'w') do |file|
59
- Zlib::GzipWriter.wrap(file) do |stream|
60
- stream.puts DB_FORMAT
61
- stream.puts Oj.dump @meta
62
- stream.puts Oj.dump @tables
59
+ File.open(filename, 'w') do |file|
60
+ Zlib::GzipWriter.wrap(file) do |stream|
61
+ stream.puts DB_FORMAT
62
+ stream.puts Oj.dump @meta
63
+ stream.puts Oj.dump @tables
64
+ end
63
65
  end
64
66
  end
65
- end
66
67
 
67
- def add_excludes(paths)
68
- @output.extra("Excluding files in paths #{paths}...")
69
- @meta[:paths] -= paths.map { |p| self.class.normalize_glob(p) }
70
- paths = paths.map { |p| self.class.normalize_fnmatch(p) }
71
- @meta[:excludes] += paths
72
- @meta[:excludes].uniq!
73
- @all_excludes = nil # clear cache
74
-
75
- excluded = @meta[:files].keys.select { |name| matches_exclude?(name, paths) }
76
- remove_files(excluded)
77
- end
68
+ def add_excludes(paths)
69
+ @output.extra("Excluding files in paths #{paths}...")
70
+ @meta[:paths] -= paths.map { |p| self.class.normalize_glob(p) }
71
+ paths = paths.map { |p| self.class.normalize_fnmatch(p) }
72
+ @meta[:excludes] += paths
73
+ @meta[:excludes].uniq!
74
+ @all_excludes = nil # clear cache
78
75
 
79
- def add_paths(paths)
80
- @output.extra("Adding files in paths #{paths}...")
81
- @meta[:excludes] -= paths.map { |p| self.class.normalize_fnmatch(p) }
82
- @all_excludes = nil # clear cache
83
- paths = paths.map { |p| self.class.normalize_glob(p) }
84
- @meta[:paths] += paths
85
- @meta[:paths].uniq!
86
- files = Dir.glob(paths).select { |f| File.file? f }
87
- files.delete_if { |f| matches_exclude?(f) }
88
- return if files.empty?
89
- @output.new_pbar('Building', files.length)
90
- add_files(files)
91
- @output.finish_pbar
92
- end
76
+ excluded = @meta[:files].keys.select { |name| matches_exclude?(name, paths) }
77
+ remove_files(excluded)
78
+ end
93
79
 
94
- def update
95
- changes = @meta[:files].keys.group_by { |name| file_changed(name) }
96
- changes[:modified] ||= []
97
- changes[:deleted] ||= []
80
+ def add_paths(paths)
81
+ @output.extra("Adding files in paths #{paths}...")
82
+ @meta[:excludes] -= paths.map { |p| self.class.normalize_fnmatch(p) }
83
+ @all_excludes = nil # clear cache
84
+ paths = paths.map { |p| self.class.normalize_glob(p) }
85
+ @meta[:paths] += paths
86
+ @meta[:paths].uniq!
87
+ files = Dir.glob(paths).select { |f| File.file? f }
88
+ files.delete_if { |f| matches_exclude?(f) }
89
+ return if files.empty?
90
+ @output.new_pbar('Building', files.length)
91
+ add_files(files)
92
+ @output.finish_pbar
93
+ end
98
94
 
99
- new_files = (Dir.glob(@meta[:paths]).select { |f| File.file? f }) - @meta[:files].keys
100
- new_files.delete_if { |f| matches_exclude?(f) }
95
+ def update
96
+ changes = @meta[:files].keys.group_by { |name| file_changed(name) }
97
+ changes[:modified] ||= []
98
+ changes[:deleted] ||= []
101
99
 
102
- if changes[:deleted].empty? && changes[:modified].empty? && new_files.empty?
103
- @output.normal('No changes detected.')
104
- return false
105
- end
100
+ new_files = (Dir.glob(@meta[:paths]).select { |f| File.file? f }) - @meta[:files].keys
101
+ new_files.delete_if { |f| matches_exclude?(f) }
106
102
 
107
- @output.new_pbar('Updating', changes[:modified].length + new_files.length)
108
- remove_files(changes[:deleted])
109
- update_files(changes[:modified])
110
- add_files(new_files)
111
- @output.finish_pbar
103
+ if changes[:deleted].empty? && changes[:modified].empty? && new_files.empty?
104
+ @output.normal('No changes detected.')
105
+ return false
106
+ end
112
107
 
113
- true
114
- end
108
+ @output.new_pbar('Updating', changes[:modified].length + new_files.length)
109
+ remove_files(changes[:deleted])
110
+ update_files(changes[:modified])
111
+ add_files(new_files)
112
+ @output.finish_pbar
115
113
 
116
- def line_for_record(rec)
117
- return rec[:line] if rec[:line]
114
+ true
115
+ end
118
116
 
119
- file = @meta[:files][rec[:file]]
117
+ def line_for_record(rec)
118
+ return rec[:line] if rec[:line]
120
119
 
121
- return file[:lines][rec[:line_no] - 1] if file[:lines]
122
- end
120
+ file = @meta[:files][rec[:file]]
123
121
 
124
- def tables
125
- @tables.keys
126
- end
122
+ return file[:lines][rec[:line_no] - 1] if file[:lines]
123
+ end
127
124
 
128
- def records(table)
129
- raise NoTableError unless @tables[table]
125
+ def tables
126
+ @tables.keys
127
+ end
130
128
 
131
- @tables[table]
132
- end
129
+ def records(table)
130
+ raise NoTableError unless @tables[table]
133
131
 
134
- def metadata(key = nil)
135
- return @meta.keys if key.nil?
132
+ @tables[table]
133
+ end
136
134
 
137
- raise NoTableError unless @meta[key]
135
+ def metadata(key = nil)
136
+ return @meta.keys if key.nil?
138
137
 
139
- @meta[key]
140
- end
138
+ raise NoTableError unless @meta[key]
141
139
 
142
- def drop_all
143
- @meta[:files] = {}
144
- @tables = {}
145
- end
140
+ @meta[key]
141
+ end
146
142
 
147
- private
143
+ def drop_all
144
+ @meta[:files] = {}
145
+ @tables = {}
146
+ end
148
147
 
149
- def open_db(filename)
150
- File.open(filename, 'r') do |file|
151
- begin
152
- Zlib::GzipReader.wrap(file) do |stream|
153
- parse_db(stream)
148
+ private
149
+
150
+ def open_db(filename)
151
+ File.open(filename, 'r') do |file|
152
+ begin
153
+ Zlib::GzipReader.wrap(file) do |stream|
154
+ parse_db(stream)
155
+ end
156
+ rescue Zlib::GzipFile::Error
157
+ file.rewind
158
+ parse_db(file)
154
159
  end
155
- rescue Zlib::GzipFile::Error
156
- file.rewind
157
- parse_db(file)
158
160
  end
159
161
  end
160
- end
161
162
 
162
- # returns true iff the database is in the most recent format
163
- def parse_db(stream)
164
- case stream.gets.to_i
165
- when DB_FORMAT
166
- @meta = Oj.load(stream.gets)
167
- @tables = Oj.load(stream.gets)
163
+ # returns true iff the database is in the most recent format
164
+ def parse_db(stream)
165
+ case stream.gets.to_i
166
+ when DB_FORMAT
167
+ @meta = Oj.load(stream.gets)
168
+ @tables = Oj.load(stream.gets)
169
+ return true
170
+ when 3..4
171
+ # Old format, so read the directories segment then rebuild
172
+ add_paths(Oj.load(stream.gets))
173
+ return false
174
+ when 0..2
175
+ # Old format (pre-json), so read the directories segment then rebuild
176
+ len = stream.gets.to_i
177
+ add_paths(Marshal.load(stream.read(len)))
178
+ return false
179
+ else
180
+ raise UnknownDBFormatError
181
+ end
182
+ rescue Oj::ParseError
183
+ stream.rewind
184
+ raise unless stream.gets.to_i == DB_FORMAT
185
+ # try reading as formated json, which is much slower, but it is sometimes
186
+ # useful to be able to directly read your db
187
+ objects = []
188
+ Oj.load(stream) { |obj| objects << obj }
189
+ @meta, @tables = objects
168
190
  return true
169
- when 3..4
170
- # Old format, so read the directories segment then rebuild
171
- add_paths(Oj.load(stream.gets))
172
- return false
173
- when 0..2
174
- # Old format (pre-json), so read the directories segment then rebuild
175
- len = stream.gets.to_i
176
- add_paths(Marshal.load(stream.read(len)))
177
- return false
178
- else
179
- raise UnknownDBFormatError
180
191
  end
181
- rescue Oj::ParseError
182
- stream.rewind
183
- raise unless stream.gets.to_i == DB_FORMAT
184
- # try reading as formated json, which is much slower, but it is sometimes
185
- # useful to be able to directly read your db
186
- objects = []
187
- Oj.load(stream) { |obj| objects << obj }
188
- @meta, @tables = objects
189
- return true
190
- end
191
-
192
- def fixup
193
- # misc things that were't worth bumping the format for, but which might not be written by old versions
194
- @meta[:langs] ||= {}
195
- end
196
-
197
- def all_excludes
198
- @all_excludes ||= @meta[:excludes] + (@config[:excludes] || []).map { |x| self.class.normalize_fnmatch(x) }
199
- end
200
-
201
- def matches_exclude?(file, patterns = all_excludes)
202
- patterns.map { |p| File.fnmatch(p, file) }.any?
203
- end
204
192
 
205
- def add_files(files)
206
- files.each do |file|
207
- @output.extra("Adding `#{file}`")
208
- parse_file(file)
209
- @output.inc_pbar
193
+ def fixup
194
+ # misc things that were't worth bumping the format for, but which might not be written by old versions
195
+ @meta[:langs] ||= {}
210
196
  end
211
- end
212
197
 
213
- def remove_files(files)
214
- files.each do |file|
215
- @output.extra("Removing `#{file}`")
216
- @meta[:files].delete(file)
217
- end
218
- files = files.to_set
219
- @tables.each do |_, tbl|
220
- tbl.delete_if { |val| files.include?(val[:file]) }
198
+ def all_excludes
199
+ @all_excludes ||= @meta[:excludes] + (@config[:excludes] || []).map { |x| self.class.normalize_fnmatch(x) }
221
200
  end
222
- end
223
-
224
- def update_files(files)
225
- remove_files(files)
226
- add_files(files)
227
- end
228
201
 
229
- def parse_file(file)
230
- @meta[:files][file] = { last_updated: File.mtime(file).to_i }
202
+ def matches_exclude?(file, patterns = all_excludes)
203
+ patterns.map { |p| File.fnmatch(p, file) }.any?
204
+ end
231
205
 
232
- self.class.extractors.each do |extractor|
233
- begin
234
- next unless extractor.match_file file
235
- rescue => e
236
- @output.normal("#{extractor} raised \"#{e}\" while matching #{file}")
237
- next
206
+ def add_files(files)
207
+ files.each do |file|
208
+ @output.extra("Adding `#{file}`")
209
+ parse_file(file)
210
+ @output.inc_pbar
238
211
  end
239
-
240
- line_cache = File.readlines(file)
241
- lines = Array.new(line_cache.length)
242
- @meta[:files][file][:sublangs] = []
243
- extract_file(extractor, file, line_cache, lines)
244
-
245
- break
246
212
  end
247
- end
248
213
 
249
- def extract_file(extractor, file, line_cache, lines)
250
- fragment_cache = {}
251
-
252
- extractor_metadata = extractor.extract(file, File.read(file)) do |tbl, name, args|
253
- case tbl
254
- when FRAGMENT
255
- fragment_cache[name] ||= []
256
- fragment_cache[name] << args
257
- else
258
- @tables[tbl] ||= []
259
- @tables[tbl] << self.class.normalize_record(file, name, args)
260
-
261
- if args[:line_no]
262
- line_cache ||= File.readlines(file)
263
- lines ||= Array.new(line_cache.length)
264
- lines[args[:line_no] - 1] = line_cache[args[:line_no] - 1].chomp
265
- end
214
+ def remove_files(files)
215
+ files.each do |file|
216
+ @output.extra("Removing `#{file}`")
217
+ @meta[:files].delete(file)
218
+ end
219
+ files = files.to_set
220
+ @tables.each do |_, tbl|
221
+ tbl.delete_if { |val| files.include?(val[:file]) }
266
222
  end
267
223
  end
268
224
 
269
- fragment_cache.each do |lang, frags|
270
- extract_file(Starscope::FragmentExtractor.new(lang, frags), file, line_cache, lines)
271
- @meta[:files][file][:sublangs] << lang
225
+ def update_files(files)
226
+ remove_files(files)
227
+ add_files(files)
272
228
  end
273
229
 
274
- @meta[:files][file][:lang] = extractor.name.split('::').last.to_sym
275
- @meta[:files][file][:lines] = lines
230
+ def parse_file(file)
231
+ @meta[:files][file] = { last_updated: File.mtime(file).to_i }
276
232
 
277
- if extractor_metadata.is_a? Hash
278
- @meta[:files][file] = extractor_metadata.merge!(@meta[:files][file])
279
- end
233
+ self.class.extractors.each do |extractor|
234
+ begin
235
+ next unless extractor.match_file file
236
+ rescue => e
237
+ @output.normal("#{extractor} raised \"#{e}\" while matching #{file}")
238
+ next
239
+ end
280
240
 
281
- rescue => e
282
- @output.normal("#{extractor} raised \"#{e}\" while extracting #{file}")
283
- end
241
+ line_cache = File.readlines(file)
242
+ lines = Array.new(line_cache.length)
243
+ @meta[:files][file][:sublangs] = []
244
+ extract_file(extractor, file, line_cache, lines)
284
245
 
285
- def file_changed(name)
286
- file_meta = @meta[:files][name]
287
- if matches_exclude?(name) || !File.exist?(name) || !File.file?(name)
288
- :deleted
289
- elsif (file_meta[:last_updated] < File.mtime(name).to_i) ||
290
- language_out_of_date(file_meta[:lang]) ||
291
- (file_meta[:sublangs] || []).any? { |lang| language_out_of_date(lang) }
292
- :modified
293
- else
294
- :unchanged
246
+ break
247
+ end
295
248
  end
296
- end
297
249
 
298
- def language_out_of_date(lang)
299
- return false unless lang
300
- return true unless LANGS[lang]
301
- (@meta[:langs][lang] || 0) < LANGS[lang]
302
- end
250
+ def extract_file(extractor, file, line_cache, lines)
251
+ fragment_cache = {}
252
+
253
+ extractor_metadata = extractor.extract(file, File.read(file)) do |tbl, name, args|
254
+ case tbl
255
+ when FRAGMENT
256
+ fragment_cache[name] ||= []
257
+ fragment_cache[name] << args
258
+ else
259
+ @tables[tbl] ||= []
260
+ @tables[tbl] << self.class.normalize_record(file, name, args)
261
+
262
+ if args[:line_no]
263
+ line_cache ||= File.readlines(file)
264
+ lines ||= Array.new(line_cache.length)
265
+ lines[args[:line_no] - 1] = line_cache[args[:line_no] - 1].chomp
266
+ end
267
+ end
268
+ end
303
269
 
304
- class << self
305
- # File.fnmatch treats a "**" to match files and directories recursively
306
- def normalize_fnmatch(path)
307
- if path == '.'
308
- '**'
309
- elsif File.directory?(path)
310
- File.join(path, '**')
311
- else
312
- path
270
+ fragment_cache.each do |lang, frags|
271
+ extract_file(Starscope::FragmentExtractor.new(lang, frags), file, line_cache, lines)
272
+ @meta[:files][file][:sublangs] << lang
313
273
  end
274
+
275
+ @meta[:files][file][:lang] = extractor.name.split('::').last.to_sym
276
+ @meta[:files][file][:lines] = lines
277
+
278
+ if extractor_metadata.is_a? Hash
279
+ @meta[:files][file] = extractor_metadata.merge!(@meta[:files][file])
280
+ end
281
+
282
+ rescue => e
283
+ @output.normal("#{extractor} raised \"#{e}\" while extracting #{file}")
314
284
  end
315
285
 
316
- # Dir.glob treats a "**" to only match directories recursively; you need
317
- # "**/*" to match all files recursively
318
- def normalize_glob(path)
319
- if path == '.'
320
- File.join('**', '*')
321
- elsif File.directory?(path)
322
- File.join(path, '**', '*')
286
+ def file_changed(name)
287
+ file_meta = @meta[:files][name]
288
+ if matches_exclude?(name) || !File.exist?(name) || !File.file?(name)
289
+ :deleted
290
+ elsif (file_meta[:last_updated] < File.mtime(name).to_i) ||
291
+ language_out_of_date(file_meta[:lang]) ||
292
+ (file_meta[:sublangs] || []).any? { |lang| language_out_of_date(lang) }
293
+ :modified
323
294
  else
324
- path
295
+ :unchanged
325
296
  end
326
297
  end
327
298
 
328
- def normalize_record(file, name, args)
329
- args[:file] = file
330
- args[:name] = Array(name).map(&:to_sym)
331
- args
299
+ def language_out_of_date(lang)
300
+ return false unless lang
301
+ return true unless LANGS[lang]
302
+ (@meta[:langs][lang] || 0) < LANGS[lang]
332
303
  end
333
304
 
334
- def extractors # so we can stub it in tests
335
- EXTRACTORS
305
+ class << self
306
+ # File.fnmatch treats a "**" to match files and directories recursively
307
+ def normalize_fnmatch(path)
308
+ if path == '.'
309
+ '**'
310
+ elsif File.directory?(path)
311
+ File.join(path, '**')
312
+ else
313
+ path
314
+ end
315
+ end
316
+
317
+ # Dir.glob treats a "**" to only match directories recursively; you need
318
+ # "**/*" to match all files recursively
319
+ def normalize_glob(path)
320
+ if path == '.'
321
+ File.join('**', '*')
322
+ elsif File.directory?(path)
323
+ File.join(path, '**', '*')
324
+ else
325
+ path
326
+ end
327
+ end
328
+
329
+ def normalize_record(file, name, args)
330
+ args[:file] = file
331
+ args[:name] = Array(name).map(&:to_sym)
332
+ args
333
+ end
334
+
335
+ def extractors # so we can stub it in tests
336
+ EXTRACTORS
337
+ end
336
338
  end
337
339
  end
338
340
  end