scout-gear 7.2.0 → 7.3.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.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/.vimproject +37 -3
  3. data/VERSION +1 -1
  4. data/lib/scout/concurrent_stream.rb +9 -8
  5. data/lib/scout/exceptions.rb +1 -0
  6. data/lib/scout/log/color.rb +0 -1
  7. data/lib/scout/log/progress/util.rb +65 -0
  8. data/lib/scout/misc/helper.rb +31 -0
  9. data/lib/scout/misc/monitor.rb +1 -1
  10. data/lib/scout/misc.rb +1 -0
  11. data/lib/scout/open/stream.rb +21 -27
  12. data/lib/scout/persist.rb +42 -28
  13. data/lib/scout/semaphore.rb +8 -1
  14. data/lib/scout/tsv/dumper.rb +13 -8
  15. data/lib/scout/tsv/index.rb +127 -15
  16. data/lib/scout/tsv/open.rb +128 -0
  17. data/lib/scout/tsv/parser.rb +70 -43
  18. data/lib/scout/tsv/path.rb +4 -4
  19. data/lib/scout/tsv/persist/adapter.rb +52 -33
  20. data/lib/scout/tsv/persist/fix_width_table.rb +324 -0
  21. data/lib/scout/tsv/persist/serialize.rb +117 -0
  22. data/lib/scout/tsv/persist/tokyocabinet.rb +3 -3
  23. data/lib/scout/tsv/persist.rb +0 -2
  24. data/lib/scout/tsv/traverse.rb +130 -35
  25. data/lib/scout/tsv/util/filter.rb +303 -0
  26. data/lib/scout/tsv/util/process.rb +73 -0
  27. data/lib/scout/tsv/util/select.rb +220 -0
  28. data/lib/scout/tsv/util.rb +77 -19
  29. data/lib/scout/tsv.rb +2 -2
  30. data/lib/scout/work_queue/worker.rb +1 -1
  31. data/lib/scout/workflow/definition.rb +8 -0
  32. data/lib/scout/workflow/step/info.rb +4 -0
  33. data/lib/scout/workflow/step/progress.rb +14 -0
  34. data/lib/scout/workflow/step.rb +10 -5
  35. data/lib/scout/workflow/task.rb +8 -4
  36. data/lib/scout/workflow/usage.rb +2 -0
  37. data/scout-gear.gemspec +33 -10
  38. data/scout_commands/workflow/task +3 -2
  39. data/scout_commands/workflow/task_old +2 -2
  40. data/test/scout/open/test_stream.rb +1 -1
  41. data/test/scout/test_persist.rb +61 -0
  42. data/test/scout/test_tmpfile.rb +1 -1
  43. data/test/scout/test_tsv.rb +10 -1
  44. data/test/scout/test_work_queue.rb +1 -0
  45. data/test/scout/tsv/persist/test_adapter.rb +10 -0
  46. data/test/scout/tsv/persist/test_fix_width_table.rb +134 -0
  47. data/test/scout/tsv/test_index.rb +94 -2
  48. data/test/scout/tsv/test_open.rb +9 -0
  49. data/test/scout/tsv/test_parser.rb +28 -3
  50. data/test/scout/tsv/test_persist.rb +7 -0
  51. data/test/scout/tsv/test_traverse.rb +110 -3
  52. data/test/scout/tsv/test_util.rb +23 -0
  53. data/test/scout/tsv/util/test_filter.rb +188 -0
  54. data/test/scout/tsv/util/test_process.rb +47 -0
  55. data/test/scout/tsv/util/test_select.rb +44 -0
  56. data/test/scout/work_queue/test_worker.rb +63 -6
  57. data/test/scout/workflow/step/test_load.rb +3 -3
  58. data/test/scout/workflow/test_step.rb +10 -10
  59. data/test/test_helper.rb +3 -1
  60. metadata +19 -6
@@ -0,0 +1,303 @@
1
+ require 'set'
2
+ module Filtered
3
+
4
+ class FilterArray
5
+ attr_accessor :filters
6
+
7
+ def ids
8
+ ids = filters.inject(nil){|list,filter| list.nil? ? filter.ids.dup : Misc.merge_sorted_arrays(list, filter.ids.dup)}
9
+ end
10
+
11
+ def method_missing(name, *args)
12
+ filters.each do |filter|
13
+ filter.send(name, *args)
14
+ end
15
+ end
16
+ end
17
+
18
+ #{{{ FILTER
19
+
20
+ class Filter
21
+ attr_accessor :data, :match, :fieldnum, :value, :list, :unsaved
22
+ attr_accessor :persistence
23
+
24
+ def initialize(data, match, value, persistence = nil)
25
+ @data = data
26
+ @match = match
27
+ @value = value
28
+ @unsaved = []
29
+
30
+ case
31
+ when Hash === persistence
32
+ @persistence = persistence
33
+ when String === persistence
34
+ @persistence = begin
35
+ file = ScoutCabinet.open(persistence, true, "HDB")
36
+ TSV.setup file, :type => :list
37
+ file.extend TSVAdapter
38
+ file
39
+ end
40
+ @persistence.read
41
+ end
42
+
43
+ @list = nil
44
+ case
45
+ when @match == :key
46
+ @value = Set.new(@value)
47
+ class << self
48
+ self
49
+ end.class_eval <<-EOC
50
+ def match_entry(key, entry)
51
+ key == @value or (Set === @value and @value.include? key)
52
+ end
53
+ EOC
54
+ when @match.match(/field:(.*)/)
55
+ @fieldnum = data.identify_field $1
56
+ class << self
57
+ self
58
+ end.class_eval <<-EOC
59
+ def match_entry(key, entry)
60
+ value = entry[@fieldnum]
61
+ value == @value or (Array === value and value.include? @value)
62
+ end
63
+ EOC
64
+ else
65
+ raise "Unknown match: #{ @match }"
66
+ end
67
+ end
68
+
69
+ def key
70
+ case
71
+ when String === value
72
+ value
73
+ else
74
+ Marshal.dump(value)
75
+ end
76
+ end
77
+
78
+ def save(ids)
79
+ if persistence
80
+ persistence.write_and_close do
81
+ persistence[self.key] = ids
82
+ end
83
+ else
84
+ if @list.nil?
85
+ @list = ids
86
+ else
87
+ @list.replace ids
88
+ end
89
+ end
90
+ end
91
+
92
+ def update
93
+ ids = []
94
+
95
+ data.with_unnamed do
96
+ data.unfiltered_each do |key, entry|
97
+ ids << key if match_entry(key, entry)
98
+ end
99
+ end
100
+
101
+ save(ids.sort)
102
+ end
103
+
104
+ def saved
105
+ if persistence.nil?
106
+ return nil if list.nil?
107
+ list
108
+ else
109
+ return nil if not persistence.include?(self.key)
110
+ persistence.write_and_close do
111
+ persistence[self.key]
112
+ end
113
+ end
114
+ end
115
+
116
+ def add_unsaved
117
+ save(Misc.merge_sorted_arrays(unsaved.sort, saved || [])) if unsaved.any?
118
+ unsaved.clear
119
+ end
120
+
121
+ def ids
122
+ add_unsaved
123
+
124
+ list = saved
125
+ if list.nil?
126
+ update
127
+ list = saved
128
+ end
129
+ list
130
+ end
131
+
132
+ def add(id)
133
+ unsaved.push id
134
+ end
135
+
136
+ def clean
137
+ add_unsaved
138
+ if persistence and persistence.include? self.key
139
+ persistence.write_and_close do
140
+ persistence.delete self.key
141
+ end
142
+ else
143
+ @list = nil
144
+ end
145
+ end
146
+
147
+ def reset
148
+ add_unsaved
149
+ if persistence
150
+ persistence.write_and_close do
151
+ persistence.clear
152
+ end
153
+ else
154
+ @list = nil
155
+ end
156
+ end
157
+ end
158
+
159
+ #}}} FILTER
160
+
161
+ def self.extended(base)
162
+ if not base.respond_to? :unfiltered_set
163
+ class << base
164
+ attr_accessor :filter_dir, :filters
165
+
166
+ alias unfiltered_set []=
167
+ alias []= filtered_set
168
+
169
+ alias unfiltered_filename filename
170
+ alias filename filtered_filename
171
+
172
+ alias unfiltered_keys keys
173
+ alias keys filtered_keys
174
+
175
+ alias unfiltered_values values
176
+ alias values filtered_values
177
+
178
+ alias unfiltered_each each
179
+ alias each filtered_each
180
+
181
+ alias unfiltered_collect collect
182
+ alias collect filtered_collect
183
+
184
+ alias unfiltered_delete delete
185
+ alias delete filtered_delete
186
+ end
187
+ end
188
+ base.filters = []
189
+ end
190
+
191
+ def filtered_filename
192
+ if filters.empty?
193
+ unfiltered_filename
194
+ else
195
+ unfiltered_filename + ":Filtered[#{filters.collect{|f| [f.match, Array === f.value ? Misc.digest(:values => f.value) : f.value] * "="} * ", "}]"
196
+ end
197
+ end
198
+
199
+ def filtered_set(key, value, clean = false)
200
+ if filters.empty?
201
+ self.send(:unfiltered_set, key, value, clean)
202
+ else
203
+ filters.each do |filter|
204
+ filter.add key if filter.match_entry key, value
205
+ end
206
+ self.send(:unfiltered_set, key, value, clean)
207
+ end
208
+ end
209
+
210
+ def filtered_keys
211
+ if filters.empty?
212
+ self.send(:unfiltered_keys)
213
+ else
214
+ filters.inject(nil){|list,filter| list.nil? ? filter.ids.dup : Misc.intersect_sorted_arrays(list, filter.ids.dup)}
215
+ end
216
+ end
217
+
218
+ def filtered_values
219
+ if filters.empty?
220
+ self.send(:unfiltered_values)
221
+ else
222
+ ids = filters.inject(nil){|list,filter| list.nil? ? filter.ids.dup : Misc.intersect_sorted_arrays(list, filter.ids.dup)}
223
+ self.send :values_at, *ids
224
+ end
225
+ end
226
+
227
+ def filtered_each(&block)
228
+ if filters.empty?
229
+ self.send(:unfiltered_each, &block)
230
+ else
231
+ ids = filters.inject(nil){|list,filter| list.nil? ? filter.ids.dup : Misc.intersect_sorted_arrays(list, filter.ids.dup)}
232
+
233
+ ids.each do |id|
234
+ value = self[id]
235
+ yield id, value if block_given?
236
+ [id, value]
237
+ end
238
+ end
239
+ end
240
+
241
+ def filtered_collect(&block)
242
+ if filters.empty?
243
+ self.send(:unfiltered_collect, &block)
244
+ else
245
+ ids = filters.inject(nil){|list,filter| list = (list.nil? ? filter.ids.dup : Misc.intersect_sorted_arrays(list, filter.ids.dup))}
246
+
247
+ new = self.annotate({})
248
+
249
+ ids.zip(self.send(:values_at, *ids)).each do |id, values|
250
+ new[id] = values
251
+ end
252
+ new.send :collect, &block
253
+ end
254
+ end
255
+
256
+ def filtered_delete(key)
257
+ if filters.empty?
258
+ self.send(:unfiltered_delete, key)
259
+ else
260
+ reset_filters
261
+ self.send :unfiltered_delete, key
262
+ end
263
+ end
264
+
265
+ def add_filter(match, value, persistence = nil)
266
+ if persistence.nil? and filter_dir
267
+ persistence = File.join(filter_dir, match.to_s + '.filter')
268
+ end
269
+
270
+ filter = Filter.new self, match, value, persistence
271
+ filters.push filter
272
+ end
273
+
274
+ def pop_filter
275
+ filters.pop.add_unsaved if filters.any?
276
+ end
277
+
278
+ def size
279
+ filters.empty? ? super : filters.collect{|f| f.ids.length }.min
280
+ end
281
+
282
+ end
283
+
284
+ module TSV
285
+ def filter(filter_dir = nil)
286
+ self.extend Filtered
287
+ self.filter_dir = filter_dir
288
+ self.filters = []
289
+ self
290
+ end
291
+
292
+ def reset_filters
293
+ if @filter_dir.nil? or @filter_dir.empty?
294
+ @filters.each do |filter| filter.reset end if Array === @filters
295
+ return
296
+ end
297
+
298
+ Dir.glob(File.join(@filter_dir, '*.filter')).each do |f|
299
+ FileUtils.rm f
300
+ end
301
+ end
302
+ end
303
+
@@ -0,0 +1,73 @@
1
+ module TSV
2
+ def process(field, &block)
3
+ field_pos = identify_field field
4
+
5
+ through do |key, values|
6
+ case
7
+ when type == :single
8
+ field_values = values
9
+ when type == :flat
10
+ field_values = values
11
+ else
12
+ next if values.nil?
13
+ field_values = values[field_pos]
14
+ end
15
+
16
+ new_values = case
17
+ when block.arity == 1
18
+ yield(field_values)
19
+ when block.arity == 2
20
+ yield(field_values, key)
21
+ when block.arity == 3
22
+ yield(field_values, key, values)
23
+ else
24
+ raise "Unexpected arity in block, must be 1, 2 or 3: #{block.arity}"
25
+ end
26
+
27
+ case
28
+ when type == :single
29
+ self[key] = new_values
30
+ when type == :flat
31
+ self[key] = new_values
32
+ else
33
+ if ! values[field_pos].frozen? && ((String === values[field_pos] && String === new_values) ||
34
+ (Array === values[field_pos] && Array === new_values))
35
+ values[field_pos].replace new_values
36
+ else
37
+ values[field_pos] = new_values
38
+ end
39
+ self[key] = values
40
+ end
41
+ end
42
+
43
+ self
44
+ end
45
+
46
+ def add_field(name = nil)
47
+ through do |key, values|
48
+ new_values = yield(key, values)
49
+ new_values = [new_values] if type == :double and not Array === new_values
50
+
51
+ case
52
+ when (values.nil? and (fields.nil? or fields.empty?))
53
+ values = [new_values]
54
+ when values.nil?
55
+ values = [nil] * fields.length + [new_values]
56
+ when Array === values
57
+ values += [new_values]
58
+ else
59
+ values << new_values
60
+ end
61
+
62
+ self[key] = values
63
+ end
64
+
65
+ if not fields.nil? and not name.nil?
66
+ new_fields = self.fields + [name]
67
+ self.fields = new_fields
68
+ end
69
+
70
+ self
71
+ end
72
+
73
+ end
@@ -0,0 +1,220 @@
1
+ module TSV
2
+ def select(method = nil, invert = false, &block)
3
+ new = TSV.setup({}, :key_field => key_field, :fields => fields, :type => type, :filename => filename, :identifiers => identifiers)
4
+
5
+ self.annotate(new)
6
+
7
+ case
8
+ when (method.nil? and block_given?)
9
+ through do |key, values|
10
+ new[key] = values if invert ^ (yield key, values)
11
+ end
12
+ when Array === method
13
+ method = Set.new method
14
+ with_unnamed do
15
+ case type
16
+ when :single
17
+ through do |key, value|
18
+ new[key] = value if invert ^ (method.include? key or method.include? value)
19
+ end
20
+ when :list, :flat
21
+ through do |key, values|
22
+ new[key] = values if invert ^ (method.include? key or (method & values).length > 0)
23
+ end
24
+ else
25
+ through do |key, values|
26
+ new[key] = values if invert ^ (method.include? key or (method & values.flatten).length > 0)
27
+ end
28
+ end
29
+ end
30
+ when Regexp === method
31
+ with_unnamed do
32
+ through do |key, values|
33
+ new[key] = values if invert ^ ([key,values].flatten.select{|v| v =~ method}.any?)
34
+ end
35
+ end
36
+ when (String === method || Symbol === method)
37
+ if block_given?
38
+ case
39
+ when block.arity == 1
40
+ with_unnamed do
41
+ case
42
+ when (method == key_field or method == :key)
43
+ through do |key, values|
44
+ new[key] = values if invert ^ (yield(key))
45
+ end
46
+ when (type == :single or type == :flat)
47
+ through do |key, value|
48
+ new[key] = value if invert ^ (yield(value))
49
+ end
50
+ else
51
+ pos = identify_field method
52
+ raise "Field #{ method } not identified. Available: #{ fields * ", " }" if pos.nil?
53
+
54
+ through do |key, values|
55
+ new[key] = values if invert ^ (yield(values[pos]))
56
+ end
57
+ end
58
+ end
59
+ when block.arity == 2
60
+ with_unnamed do
61
+ case
62
+ when (method == key_field or method == :key)
63
+ through do |key, values|
64
+ new[key] = values if invert ^ (yield(key, key))
65
+ end
66
+ when (type == :single or type == :flat)
67
+ through do |key, value|
68
+ new[key] = value if invert ^ (yield(key, value))
69
+ end
70
+ else
71
+ pos = identify_field method
72
+ through do |key, values|
73
+ new[key] = values if invert ^ (yield(key, values[pos]))
74
+ end
75
+ end
76
+
77
+ end
78
+ end
79
+
80
+ else
81
+ with_unnamed do
82
+ through do |key, values|
83
+ new[key] = values if invert ^ ([key,values].flatten.select{|v| v == method}.any?)
84
+ end
85
+ end
86
+ end
87
+ when Hash === method
88
+ key = method.keys.first
89
+ method = method.values.first
90
+ case
91
+ when (Array === method and (key == :key or key_field == key))
92
+ with_unnamed do
93
+ Annotated.purge(method).each{|key|
94
+ new[key] = self[key] if invert ^ (self.include? key)
95
+ }
96
+ end
97
+ when Array === method
98
+ with_unnamed do
99
+ method = Set.new method unless Set === method
100
+ case type
101
+ when :single
102
+ through :key, key do |key, value|
103
+ new[key] = self[key] if invert ^ (method.include? value)
104
+ end
105
+ when :list
106
+ through :key, key do |key, values|
107
+ new[key] = self[key] if invert ^ (method.include? values.first)
108
+ end
109
+ when :flat #untested
110
+ through :key, key do |key, values|
111
+ new[key] = self[key] if invert ^ ((method & values.flatten).any?)
112
+ end
113
+ else
114
+ through :key, key do |key, values|
115
+ new[key] = self[key] if invert ^ ((method & values.flatten).any?)
116
+ end
117
+ end
118
+ end
119
+
120
+ when Regexp === method
121
+ with_unnamed do
122
+ through :key, key do |key, values|
123
+ values = [values] if type == :single
124
+ new[key] = self[key] if invert ^ (values.flatten.select{|v| v =~ method}.any?)
125
+ end
126
+ end
127
+
128
+ when (String === method and method =~ /name:(.*)/)
129
+ name = $1
130
+ old_unnamed = self.unnamed
131
+ self.unnamed = false
132
+ if name.strip =~ /^\/(.*)\/$/
133
+ regexp = Regexp.new $1
134
+ through :key, key do |key, values|
135
+ case type
136
+ when :single
137
+ values = values.annotate([values])
138
+ when :double
139
+ values = values[0]
140
+ end
141
+ new[key] = self[key] if invert ^ (values.select{|v| v.name =~ regexp}.any?)
142
+ end
143
+ else
144
+ through :key, key do |key, values|
145
+ case type
146
+ when :single
147
+ values = values.annotate([values])
148
+ when :double
149
+ values = values[0]
150
+ end
151
+ new[key] = self[key] if invert ^ (values.select{|v| v.name == name}.any?)
152
+ end
153
+ end
154
+ self.unnamed = old_unnamed
155
+
156
+ when String === method
157
+ if method =~ /^([<>]=?)(.*)/
158
+ with_unnamed do
159
+ through :key, key do |key, values|
160
+ value = Array === values ? values.flatten.first : values
161
+ new[key] = self[key] if value.to_f.send($1, $2.to_f)
162
+ end
163
+ end
164
+ else
165
+ with_unnamed do
166
+ through :key, key do |key, values|
167
+ values = [values] if type == :single
168
+ new[key] = self[key] if invert ^ (values.flatten.select{|v| v == method}.length > 0)
169
+ end
170
+ end
171
+ end
172
+ when Numeric === method
173
+ with_unnamed do
174
+ through :key, key do |key, values|
175
+ new[key] = self[key] if invert ^ (values.flatten.length >= method)
176
+ end
177
+ end
178
+ when Proc === method
179
+ with_unnamed do
180
+ through :key, key do |key, values|
181
+ values = [values] if type == :single
182
+ new[key] = self[key] if invert ^ (values.flatten.select{|v| method.call(v)}.length > 0)
183
+ end
184
+ end
185
+ end
186
+ end
187
+ new
188
+ end
189
+
190
+ def reorder(key_field = nil, fields = nil, merge: true, one2one: :fill)
191
+ res = self.annotate({})
192
+ key_field_name, field_names = through key_field, fields, one2one: one2one do |k,v|
193
+ if @type == :double && merge && res.include?(k)
194
+ current = res[k]
195
+ if merge == :concat
196
+ v.each_with_index do |new,i|
197
+ next if new.empty?
198
+ current[i].concat(new)
199
+ end
200
+ else
201
+ merged = []
202
+ v.each_with_index do |new,i|
203
+ next if new.empty?
204
+ merged[i] = current[i] + new
205
+ end
206
+ res[k] = merged
207
+ end
208
+ else
209
+ res[k] = v
210
+ end
211
+ end
212
+ res.key_field = key_field_name
213
+ res.fields = field_names
214
+ res
215
+ end
216
+
217
+ def slice(fields)
218
+ reorder :key, fields
219
+ end
220
+ end
@@ -1,24 +1,82 @@
1
1
  #require_relative '../../../modules/rbbt-util/lib/rbbt/tsv/manipulate'
2
2
  #Log.warn "USING OLD RBBT CODE: #{__FILE__}"
3
+ require_relative 'traverse'
4
+ require_relative 'util/process'
5
+ require_relative 'util/select'
3
6
  module TSV
4
- #[:each, :collect, :map].each do |method|
5
- # define_method(method) do |*args,&block|
6
- # super(*args) do |k,v|
7
- # NamedArray.setup(v, @fields) unless @unnamed
8
- # block.call k, v
9
- # end
10
- # end
11
- #end
12
-
13
- #[:select, :reject].each do |method|
14
- # define_method(method) do |*args,&block|
15
- # res = super(*args) do |k,v|
16
- # NamedArray.setup(v, @fields) unless @unnamed
17
- # block.call k, v
18
- # end
19
- # self.annotate(res)
20
- # res
21
- # end
22
- #end
7
+ def [](*args)
8
+ v = super(*args)
9
+ NamedArray.setup(v, @fields) unless @unnamed || ! (Array === v)
10
+ v
11
+ end
23
12
 
13
+ def each(*args, &block)
14
+ if block_given?
15
+ super(*args) do |k,v|
16
+ NamedArray.setup(v, @fields) unless @unnamed || ! (Array === v)
17
+ block.call(k, v)
18
+ end
19
+ else
20
+ super(*args)
21
+ end
22
+ end
23
+
24
+ def collect(*args, &block)
25
+ if block_given?
26
+ res = []
27
+ each do |k,v|
28
+ res << yield(k, v)
29
+ end
30
+ res
31
+ else
32
+ super(*args)
33
+ end
34
+ end
35
+
36
+ def with_unnamed
37
+ begin
38
+ old_unnamed = unnamed
39
+ unnamed = true
40
+ yield
41
+ ensure
42
+ unnamed = old_unnamed
43
+ end
44
+ end
45
+
46
+ def summary
47
+ key = nil
48
+ values = nil
49
+ self.each do |k, v|
50
+ key = k
51
+ values = v
52
+ break
53
+ end
54
+
55
+ filename = @filename
56
+ filename = "No filename" if filename.nil? || filename.empty?
57
+ filename.find if Path === filename
58
+ filename = File.basename(filename) + " [" + File.basename(persistence_path) + "]" if respond_to?(:persistence_path) and persistence_path
59
+
60
+ with_unnamed do
61
+ <<-EOF
62
+ Filename = #{filename}
63
+ Key field = #{key_field || "*No key field*"}
64
+ Fields = #{fields ? Log.fingerprint(fields) : "*No field info*"}
65
+ Type = #{type}
66
+ Size = #{size}
67
+ namespace = #{Log.fingerprint namespace}
68
+ identifiers = #{Log.fingerprint identifiers}
69
+ Example:
70
+ - #{key} -- #{Log.fingerprint values }
71
+ EOF
72
+ end
73
+ end
74
+
75
+ def all_fields
76
+ [@key_field] + @fields
77
+ end
78
+
79
+ def fingerprint
80
+ "TSV:{"<< Log.fingerprint(self.all_fields|| []) << ";" << Log.fingerprint(self.keys) << "}"
81
+ end
24
82
  end
data/lib/scout/tsv.rb CHANGED
@@ -6,10 +6,11 @@ require_relative 'tsv/persist'
6
6
  require_relative 'tsv/index'
7
7
  require_relative 'tsv/path'
8
8
  require_relative 'tsv/traverse'
9
+ require_relative 'tsv/open'
9
10
 
10
11
  module TSV
11
12
  extend MetaExtension
12
- extension_attr :key_field, :fields, :type, :filename, :namespace, :unnamed
13
+ extension_attr :key_field, :fields, :type, :filename, :namespace, :unnamed, :identifiers
13
14
 
14
15
  def self.open(file, options = {})
15
16
  persist, type = IndiferentHash.process_options options, :persist, :persist_type, :persist => false, :persist_type => "HDB"
@@ -22,6 +23,5 @@ module TSV
22
23
  end
23
24
  end
24
25
  end
25
-
26
26
  end
27
27