scout-gear 7.1.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.
- checksums.yaml +4 -4
- data/.vimproject +65 -2
- data/VERSION +1 -1
- data/bin/scout +5 -1
- data/lib/rbbt-scout.rb +5 -0
- data/lib/scout/concurrent_stream.rb +13 -8
- data/lib/scout/config.rb +168 -0
- data/lib/scout/exceptions.rb +5 -3
- data/lib/scout/indiferent_hash/options.rb +1 -0
- data/lib/scout/indiferent_hash.rb +4 -2
- data/lib/scout/log/color.rb +3 -2
- data/lib/scout/log/progress/report.rb +1 -0
- data/lib/scout/log/progress/util.rb +66 -1
- data/lib/scout/log/progress.rb +5 -3
- data/lib/scout/log.rb +3 -2
- data/lib/scout/misc/helper.rb +31 -0
- data/lib/scout/misc/monitor.rb +4 -1
- data/lib/scout/misc/system.rb +15 -0
- data/lib/scout/misc.rb +2 -0
- data/lib/scout/named_array.rb +68 -0
- data/lib/scout/open/stream.rb +58 -33
- data/lib/scout/path/find.rb +27 -3
- data/lib/scout/path/util.rb +7 -4
- data/lib/scout/persist/serialize.rb +7 -14
- data/lib/scout/persist.rb +46 -12
- data/lib/scout/resource/produce.rb +7 -94
- data/lib/scout/resource/software.rb +176 -0
- data/lib/scout/semaphore.rb +8 -1
- data/lib/scout/tsv/dumper.rb +112 -0
- data/lib/scout/tsv/index.rb +161 -0
- data/lib/scout/tsv/open.rb +128 -0
- data/lib/scout/tsv/parser.rb +230 -30
- data/lib/scout/tsv/path.rb +13 -0
- data/lib/scout/tsv/persist/adapter.rb +367 -0
- data/lib/scout/tsv/persist/fix_width_table.rb +324 -0
- data/lib/scout/tsv/persist/serialize.rb +117 -0
- data/lib/scout/tsv/persist/tokyocabinet.rb +113 -0
- data/lib/scout/tsv/persist.rb +13 -0
- data/lib/scout/tsv/traverse.rb +143 -0
- data/lib/scout/tsv/util/filter.rb +303 -0
- data/lib/scout/tsv/util/process.rb +73 -0
- data/lib/scout/tsv/util/select.rb +220 -0
- data/lib/scout/tsv/util.rb +82 -0
- data/lib/scout/tsv.rb +16 -3
- data/lib/scout/work_queue/worker.rb +4 -4
- data/lib/scout/work_queue.rb +22 -7
- data/lib/scout/workflow/definition.rb +101 -4
- data/lib/scout/workflow/step/config.rb +18 -0
- data/lib/scout/workflow/step/dependencies.rb +40 -0
- data/lib/scout/workflow/step/file.rb +15 -0
- data/lib/scout/workflow/step/info.rb +35 -4
- data/lib/scout/workflow/step/progress.rb +14 -0
- data/lib/scout/workflow/step/provenance.rb +148 -0
- data/lib/scout/workflow/step.rb +71 -17
- data/lib/scout/workflow/task.rb +10 -5
- data/lib/scout/workflow/usage.rb +3 -1
- data/lib/scout/workflow.rb +11 -3
- data/lib/scout-gear.rb +1 -0
- data/lib/scout.rb +1 -0
- data/scout-gear.gemspec +64 -10
- data/scout_commands/find +1 -1
- data/scout_commands/workflow/task +16 -9
- data/scout_commands/workflow/task_old +2 -2
- data/share/software/install_helpers +523 -0
- data/test/scout/log/test_progress.rb +0 -2
- data/test/scout/misc/test_system.rb +21 -0
- data/test/scout/open/test_stream.rb +160 -1
- data/test/scout/path/test_find.rb +14 -7
- data/test/scout/resource/test_software.rb +24 -0
- data/test/scout/test_config.rb +66 -0
- data/test/scout/test_meta_extension.rb +10 -0
- data/test/scout/test_named_array.rb +19 -0
- data/test/scout/test_persist.rb +96 -0
- data/test/scout/test_tmpfile.rb +1 -1
- data/test/scout/test_tsv.rb +50 -1
- data/test/scout/test_work_queue.rb +41 -13
- data/test/scout/tsv/persist/test_adapter.rb +44 -0
- data/test/scout/tsv/persist/test_fix_width_table.rb +134 -0
- data/test/scout/tsv/persist/test_tokyocabinet.rb +92 -0
- data/test/scout/tsv/test_dumper.rb +44 -0
- data/test/scout/tsv/test_index.rb +156 -0
- data/test/scout/tsv/test_open.rb +9 -0
- data/test/scout/tsv/test_parser.rb +114 -3
- data/test/scout/tsv/test_persist.rb +43 -0
- data/test/scout/tsv/test_traverse.rb +116 -0
- data/test/scout/tsv/test_util.rb +23 -0
- data/test/scout/tsv/util/test_filter.rb +188 -0
- data/test/scout/tsv/util/test_process.rb +47 -0
- data/test/scout/tsv/util/test_select.rb +44 -0
- data/test/scout/work_queue/test_worker.rb +66 -9
- data/test/scout/workflow/step/test_dependencies.rb +25 -0
- data/test/scout/workflow/step/test_info.rb +15 -17
- data/test/scout/workflow/step/test_load.rb +19 -21
- data/test/scout/workflow/step/test_provenance.rb +25 -0
- data/test/scout/workflow/test_step.rb +206 -10
- data/test/scout/workflow/test_task.rb +0 -3
- data/test/test_helper.rb +9 -1
- metadata +50 -6
@@ -0,0 +1,324 @@
|
|
1
|
+
class FixWidthTable
|
2
|
+
|
3
|
+
attr_accessor :filename, :file, :value_size, :record_size, :range, :size, :mask, :write
|
4
|
+
def initialize(filename, value_size = nil, range = nil, update = false, in_memory = true)
|
5
|
+
@filename = filename
|
6
|
+
|
7
|
+
if update || %w(memory stringio).include?(filename.to_s.downcase) || ! File.exist?(filename)
|
8
|
+
Log.debug "FixWidthTable create: #{ filename }"
|
9
|
+
@value_size = value_size
|
10
|
+
@range = range
|
11
|
+
@record_size = @value_size + (@range ? 16 : 8)
|
12
|
+
@write = true
|
13
|
+
|
14
|
+
if %w(memory stringio).include?(filename.to_s.downcase)
|
15
|
+
@filename = :memory
|
16
|
+
@file = StringIO.new
|
17
|
+
else
|
18
|
+
FileUtils.rm @filename if File.exist? @filename
|
19
|
+
FileUtils.mkdir_p File.dirname(@filename) unless File.exist? @filename
|
20
|
+
@file = File.open(@filename, 'w:ASCII-8BIT')
|
21
|
+
end
|
22
|
+
|
23
|
+
@file.write [value_size].pack("L")
|
24
|
+
@file.write [@range ? 1 : 0 ].pack("C")
|
25
|
+
|
26
|
+
@size = 0
|
27
|
+
else
|
28
|
+
Log.debug "FixWidthTable up-to-date: #{ filename } - (in_memory:#{in_memory})"
|
29
|
+
if in_memory
|
30
|
+
@file = Open.open(@filename, :mode => 'r:ASCII-8BIT'){|f| StringIO.new f.read}
|
31
|
+
else
|
32
|
+
@file = File.open(@filename, 'r:ASCII-8BIT')
|
33
|
+
end
|
34
|
+
@value_size = @file.read(4).unpack("L").first
|
35
|
+
@range = @file.read(1).unpack("C").first == 1
|
36
|
+
@record_size = @value_size + (@range ? 16 : 8)
|
37
|
+
@write = false
|
38
|
+
|
39
|
+
@size = (File.size(@filename) - 5) / (@record_size)
|
40
|
+
end
|
41
|
+
|
42
|
+
@mask = "a#{@value_size}"
|
43
|
+
end
|
44
|
+
|
45
|
+
def write?
|
46
|
+
@write
|
47
|
+
end
|
48
|
+
|
49
|
+
def persistence_path
|
50
|
+
@filename
|
51
|
+
end
|
52
|
+
|
53
|
+
def persistence_path=(value)
|
54
|
+
@filename=value
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.get(filename, value_size = nil, range = nil, update = false)
|
58
|
+
return self.new(filename, value_size, range, update) if filename == :memory
|
59
|
+
case
|
60
|
+
when (!File.exist?(filename) or update or not Persist::CONNECTIONS.include?(filename))
|
61
|
+
Persist::CONNECTIONS[filename] = self.new(filename, value_size, range, update)
|
62
|
+
end
|
63
|
+
|
64
|
+
Persist::CONNECTIONS[filename]
|
65
|
+
end
|
66
|
+
|
67
|
+
def format(pos, value)
|
68
|
+
padding = value_size - value.length
|
69
|
+
if @range
|
70
|
+
(pos + [padding, value + ("\0" * padding)]).pack("llll#{mask}")
|
71
|
+
else
|
72
|
+
[pos, padding, value + ("\0" * padding)].pack("ll#{mask}")
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def add(pos, value)
|
77
|
+
format = format(pos, value)
|
78
|
+
@file.write format
|
79
|
+
|
80
|
+
@size += 1
|
81
|
+
end
|
82
|
+
|
83
|
+
def last_pos
|
84
|
+
pos(size - 1)
|
85
|
+
end
|
86
|
+
|
87
|
+
def idx_pos(index)
|
88
|
+
return nil if index < 0 or index >= size
|
89
|
+
@file.seek(5 + (record_size) * index, IO::SEEK_SET)
|
90
|
+
@file.read(4).unpack("l").first
|
91
|
+
end
|
92
|
+
|
93
|
+
def idx_pos_end(index)
|
94
|
+
return nil if index < 0 or index >= size
|
95
|
+
@file.seek(9 + (record_size) * index, IO::SEEK_SET)
|
96
|
+
@file.read(4).unpack("l").first
|
97
|
+
end
|
98
|
+
|
99
|
+
def idx_overlap(index)
|
100
|
+
return nil if index < 0 or index >= size
|
101
|
+
@file.seek(13 + (record_size) * index, IO::SEEK_SET)
|
102
|
+
@file.read(4).unpack("l").first
|
103
|
+
end
|
104
|
+
|
105
|
+
def idx_value(index)
|
106
|
+
return nil if index < 0 or index >= size
|
107
|
+
@file.seek((@range ? 17 : 9 ) + (record_size) * index, IO::SEEK_SET)
|
108
|
+
padding = @file.read(4).unpack("l").first+1
|
109
|
+
txt = @file.read(value_size)
|
110
|
+
str = txt.unpack(mask).first
|
111
|
+
padding > 1 ? str[0..-padding] : str
|
112
|
+
end
|
113
|
+
|
114
|
+
def read(force = false)
|
115
|
+
return if @filename == :memory
|
116
|
+
@write = false
|
117
|
+
@file.close unless @file.closed?
|
118
|
+
@file = File.open(filename, 'r:ASCII-8BIT')
|
119
|
+
end
|
120
|
+
|
121
|
+
def close
|
122
|
+
@write = false
|
123
|
+
@file.close
|
124
|
+
end
|
125
|
+
|
126
|
+
def dump
|
127
|
+
read
|
128
|
+
@file.rewind
|
129
|
+
@file.read
|
130
|
+
end
|
131
|
+
|
132
|
+
#{{{ Adding data
|
133
|
+
|
134
|
+
def add_point(data)
|
135
|
+
data.sort_by{|value,pos| pos }.each do |value, pos|
|
136
|
+
add pos, value
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def add_range_point(pos, value)
|
141
|
+
@latest ||= []
|
142
|
+
while @latest.any? and @latest[0] < pos[0]
|
143
|
+
@latest.shift
|
144
|
+
end
|
145
|
+
overlap = @latest.length
|
146
|
+
add pos + [overlap], value
|
147
|
+
@latest << pos[1]
|
148
|
+
end
|
149
|
+
|
150
|
+
def add_range(data)
|
151
|
+
@latest = []
|
152
|
+
data.sort_by{|value, pos| pos[0] }.each do |value, pos|
|
153
|
+
add_range_point(pos, value)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
#{{{ Searching
|
158
|
+
|
159
|
+
def closest(pos)
|
160
|
+
upper = size - 1
|
161
|
+
lower = 0
|
162
|
+
|
163
|
+
return -1 if upper < lower
|
164
|
+
|
165
|
+
while(upper >= lower) do
|
166
|
+
idx = lower + (upper - lower) / 2
|
167
|
+
pos_idx = idx_pos(idx)
|
168
|
+
|
169
|
+
case pos <=> pos_idx
|
170
|
+
when 0
|
171
|
+
break
|
172
|
+
when -1
|
173
|
+
upper = idx - 1
|
174
|
+
when 1
|
175
|
+
lower = idx + 1
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
if pos_idx > pos
|
180
|
+
idx = idx - 1
|
181
|
+
end
|
182
|
+
|
183
|
+
idx.to_i
|
184
|
+
end
|
185
|
+
|
186
|
+
def get_range(pos, return_idx = false)
|
187
|
+
case pos
|
188
|
+
when Range
|
189
|
+
r_start = pos.begin
|
190
|
+
r_end = pos.end
|
191
|
+
when Array
|
192
|
+
r_start, r_end = pos
|
193
|
+
else
|
194
|
+
r_start, r_end = pos, pos
|
195
|
+
end
|
196
|
+
|
197
|
+
idx = closest(r_start)
|
198
|
+
|
199
|
+
return [] if idx >= size
|
200
|
+
return [] if idx < 0 and r_start == r_end
|
201
|
+
|
202
|
+
idx = 0 if idx < 0
|
203
|
+
|
204
|
+
overlap = idx_overlap(idx)
|
205
|
+
|
206
|
+
idx -= overlap unless overlap.nil?
|
207
|
+
|
208
|
+
values = []
|
209
|
+
l_start = idx_pos(idx)
|
210
|
+
l_end = idx_pos_end(idx)
|
211
|
+
|
212
|
+
if return_idx
|
213
|
+
while l_start <= r_end
|
214
|
+
values << idx if l_end >= r_start
|
215
|
+
idx += 1
|
216
|
+
break if idx >= size
|
217
|
+
l_start = idx_pos(idx)
|
218
|
+
l_end = idx_pos_end(idx)
|
219
|
+
end
|
220
|
+
else
|
221
|
+
while l_start <= r_end
|
222
|
+
values << idx_value(idx) if l_end >= r_start
|
223
|
+
idx += 1
|
224
|
+
break if idx >= size
|
225
|
+
l_start = idx_pos(idx)
|
226
|
+
l_end = idx_pos_end(idx)
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
values
|
231
|
+
end
|
232
|
+
|
233
|
+
def get_point(pos, return_idx = false)
|
234
|
+
if Range === pos
|
235
|
+
r_start = pos.begin
|
236
|
+
r_end = pos.end
|
237
|
+
else
|
238
|
+
r_start = pos.to_i
|
239
|
+
r_end = pos.to_i
|
240
|
+
end
|
241
|
+
|
242
|
+
idx = closest(r_start)
|
243
|
+
|
244
|
+
return [] if idx >= size
|
245
|
+
return [] if idx < 0 and r_start == r_end
|
246
|
+
|
247
|
+
idx = 0 if idx < 0
|
248
|
+
|
249
|
+
idx += 1 unless idx_pos(idx) >= r_start
|
250
|
+
|
251
|
+
return [] if idx >= size
|
252
|
+
|
253
|
+
values = []
|
254
|
+
l_start = idx_pos(idx)
|
255
|
+
l_end = idx_pos_end(idx)
|
256
|
+
if return_idx
|
257
|
+
while l_start <= r_end
|
258
|
+
values << idx
|
259
|
+
idx += 1
|
260
|
+
break if idx >= size
|
261
|
+
l_start = idx_pos(idx)
|
262
|
+
l_end = idx_pos_end(idx)
|
263
|
+
end
|
264
|
+
else
|
265
|
+
while l_start <= r_end
|
266
|
+
values << idx_value(idx)
|
267
|
+
idx += 1
|
268
|
+
break if idx >= size
|
269
|
+
l_start = idx_pos(idx)
|
270
|
+
l_end = idx_pos_end(idx)
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
values
|
275
|
+
end
|
276
|
+
|
277
|
+
def [](pos)
|
278
|
+
return [] if size == 0
|
279
|
+
self.read
|
280
|
+
if @range
|
281
|
+
get_range(pos)
|
282
|
+
else
|
283
|
+
get_point(pos)
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
def overlaps(pos, value = false)
|
288
|
+
return [] if size == 0
|
289
|
+
idxs = if @range
|
290
|
+
get_range(pos, true)
|
291
|
+
else
|
292
|
+
get_point(pos, true)
|
293
|
+
end
|
294
|
+
if value
|
295
|
+
idxs.collect{|idx| [idx_pos(idx), idx_pos_end(idx), idx_value(idx)] * ":"}
|
296
|
+
else
|
297
|
+
idxs.collect{|idx| [idx_pos(idx), idx_pos_end(idx)] * ":"}
|
298
|
+
end
|
299
|
+
end
|
300
|
+
|
301
|
+
|
302
|
+
def values_at(*list)
|
303
|
+
list.collect{|pos|
|
304
|
+
self[pos]
|
305
|
+
}
|
306
|
+
end
|
307
|
+
|
308
|
+
def chunked_values_at(keys, max = 5000)
|
309
|
+
Misc.ordered_divide(keys, max).inject([]) do |acc,c|
|
310
|
+
new = self.values_at(*c)
|
311
|
+
new.annotate acc if new.respond_to? :annotate and acc.empty?
|
312
|
+
acc.concat(new)
|
313
|
+
end
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
Persist.save_drivers[:fwt] = proc do |file, content|
|
318
|
+
content.file.seek 0
|
319
|
+
Misc.sensiblewrite(file, content.file.read)
|
320
|
+
end
|
321
|
+
|
322
|
+
Persist.load_drivers[:fwt] = proc do |file|
|
323
|
+
FixWidthTable.new file
|
324
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
require 'base64'
|
2
|
+
module TSVAdapter
|
3
|
+
|
4
|
+
class CleanSerializer
|
5
|
+
def self.dump(o); o end
|
6
|
+
def self.load(o); o end
|
7
|
+
end
|
8
|
+
|
9
|
+
class BinarySerializer
|
10
|
+
def self.dump(o); [o].pack('m'); end
|
11
|
+
def self.load(str); str.unpack('m').first; end
|
12
|
+
end
|
13
|
+
|
14
|
+
class IntegerSerializer
|
15
|
+
def self.dump(i); [i].pack("l"); end
|
16
|
+
def self.load(str); str.unpack("l").first; end
|
17
|
+
end
|
18
|
+
|
19
|
+
class FloatSerializer
|
20
|
+
def self.dump(i); [i].pack("d"); end
|
21
|
+
def self.load(str); str.unpack("d").first; end
|
22
|
+
end
|
23
|
+
|
24
|
+
class StrictIntegerArraySerializer
|
25
|
+
def self.dump(a); a.pack("l*"); end
|
26
|
+
def self.load(str); a = str.unpack("l*"); end
|
27
|
+
end
|
28
|
+
|
29
|
+
class StrictFloatArraySerializer
|
30
|
+
def self.dump(a); a.pack("d*"); end
|
31
|
+
def self.load(str); a = str.unpack("d*"); end
|
32
|
+
end
|
33
|
+
|
34
|
+
class IntegerArraySerializer
|
35
|
+
NIL_INT = -999
|
36
|
+
def self.dump(a); a.collect{|v| v || NIL_INT}.pack("l*"); end
|
37
|
+
def self.load(str); a = str.unpack("l*"); a.collect{|v| v == NIL_INT ? nil : v}; end
|
38
|
+
end
|
39
|
+
|
40
|
+
class FloatArraySerializer
|
41
|
+
NIL_FLOAT = -999.999
|
42
|
+
def self.dump(a); a.collect{|v| v || NIL_FLOAT}.pack("d*"); end
|
43
|
+
def self.load(str); a = str.unpack("d*"); a.collect{|v| v == NIL_FLOAT ? nil : v}; end
|
44
|
+
end
|
45
|
+
|
46
|
+
class StringSerializer
|
47
|
+
def self.dump(str); str.to_s; end
|
48
|
+
def self.load(str); str.dup; end
|
49
|
+
end
|
50
|
+
|
51
|
+
class StringArraySerializer
|
52
|
+
def self.dump(array)
|
53
|
+
array.collect{|a| a.to_s} * "\t"
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.load(string)
|
57
|
+
return nil if string.nil? or string == 'nil'
|
58
|
+
return [] if string.empty?
|
59
|
+
string.split("\t", -1)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
class StringDoubleArraySerializer
|
64
|
+
def self.dump(array)
|
65
|
+
begin
|
66
|
+
array.collect{|a| a.collect{|a| a.to_s } * "|"} * "\t"
|
67
|
+
rescue Encoding::CompatibilityError
|
68
|
+
array.collect{|a| a.collect{|a| a.to_s.force_encoding('UTF-8')} * "|"} * "\t"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def self.load(string)
|
73
|
+
return [] if string.nil?
|
74
|
+
string.split("\t", -1).collect{|l| l.split("|", -1)}
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
class TSVMarshalSerializer
|
79
|
+
def self.dump(tsv)
|
80
|
+
Marshal.dump(tsv.dup)
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.load(string)
|
84
|
+
TSV.setup Marshal.load(string)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
class TSVSerializer
|
89
|
+
def self.dump(tsv)
|
90
|
+
tsv.to_s
|
91
|
+
end
|
92
|
+
|
93
|
+
def self.load(string)
|
94
|
+
TSV.open StringIO.new(string)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
SERIALIZER_ALIAS = {
|
99
|
+
:single => StringSerializer,
|
100
|
+
:list => StringArraySerializer,
|
101
|
+
:flat => StringArraySerializer,
|
102
|
+
:double => StringDoubleArraySerializer,
|
103
|
+
:clean => CleanSerializer,
|
104
|
+
:integer => IntegerSerializer,
|
105
|
+
:float => FloatSerializer,
|
106
|
+
:integer_array => IntegerArraySerializer,
|
107
|
+
:float_array => FloatArraySerializer,
|
108
|
+
:strict_integer_array => StrictIntegerArraySerializer,
|
109
|
+
:strict_float_array => StrictFloatArraySerializer,
|
110
|
+
:marshal => Marshal,
|
111
|
+
:string => StringSerializer,
|
112
|
+
:binary => BinarySerializer,
|
113
|
+
:tsv => TSVSerializer,
|
114
|
+
:marshal_tsv => TSVMarshalSerializer
|
115
|
+
}
|
116
|
+
|
117
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
require 'tokyocabinet'
|
2
|
+
require_relative 'adapter'
|
3
|
+
|
4
|
+
module ScoutCabinet
|
5
|
+
attr_accessor :persistence_path, :persistence_class
|
6
|
+
|
7
|
+
def self.open(path, write, tokyocabinet_class = TokyoCabinet::HDB)
|
8
|
+
path = path.find if Path === path
|
9
|
+
if String === tokyocabinet_class && tokyocabinet_class.include?(":big")
|
10
|
+
big = true
|
11
|
+
tokyocabinet_class = tokyocabinet_class.split(":").first
|
12
|
+
else
|
13
|
+
big = false
|
14
|
+
end
|
15
|
+
|
16
|
+
dir = File.dirname(File.expand_path(path))
|
17
|
+
Open.mkdir(dir) unless File.exist?(dir)
|
18
|
+
|
19
|
+
tokyocabinet_class = TokyoCabinet::HDB if tokyocabinet_class == "HDB" or tokyocabinet_class.nil?
|
20
|
+
tokyocabinet_class = TokyoCabinet::BDB if tokyocabinet_class == "BDB"
|
21
|
+
|
22
|
+
database = Persist::CONNECTIONS[path] ||= tokyocabinet_class.new
|
23
|
+
|
24
|
+
if big and not Open.exists?(path)
|
25
|
+
database.tune(nil,nil,nil,tokyocabinet_class::TLARGE | tokyocabinet_class::TDEFLATE)
|
26
|
+
end
|
27
|
+
|
28
|
+
flags = (write ? tokyocabinet_class::OWRITER | tokyocabinet_class::OCREAT : tokyocabinet_class::OREADER)
|
29
|
+
database.close
|
30
|
+
|
31
|
+
if !database.open(path, flags)
|
32
|
+
ecode = database.ecode
|
33
|
+
raise "Open error: #{database.errmsg(ecode)}. Trying to open file #{path}"
|
34
|
+
end
|
35
|
+
|
36
|
+
database.extend ScoutCabinet
|
37
|
+
database.persistence_path ||= path
|
38
|
+
database.persistence_class = tokyocabinet_class
|
39
|
+
|
40
|
+
database.open(path, tokyocabinet_class::OREADER)
|
41
|
+
|
42
|
+
Persist::CONNECTIONS[path] = database
|
43
|
+
|
44
|
+
database
|
45
|
+
end
|
46
|
+
|
47
|
+
def close
|
48
|
+
@closed = true
|
49
|
+
@writable = false
|
50
|
+
super
|
51
|
+
end
|
52
|
+
|
53
|
+
def read(force = false)
|
54
|
+
return if ! write? && ! closed && ! force
|
55
|
+
self.close
|
56
|
+
if !self.open(@persistence_path, persistence_class::OREADER)
|
57
|
+
ecode = self.ecode
|
58
|
+
raise "Open error: #{self.errmsg(ecode)}. Trying to open file #{@persistence_path}"
|
59
|
+
end
|
60
|
+
|
61
|
+
@writable = false
|
62
|
+
@closed = false
|
63
|
+
|
64
|
+
self
|
65
|
+
end
|
66
|
+
|
67
|
+
def write(force = true)
|
68
|
+
return if write? && ! closed && ! force
|
69
|
+
self.close
|
70
|
+
|
71
|
+
if !self.open(@persistence_path, persistence_class::OWRITER)
|
72
|
+
ecode = self.ecode
|
73
|
+
raise "Open error: #{self.errmsg(ecode)}. Trying to open file #{@persistence_path}"
|
74
|
+
end
|
75
|
+
|
76
|
+
@writable = true
|
77
|
+
@closed = false
|
78
|
+
|
79
|
+
self
|
80
|
+
end
|
81
|
+
|
82
|
+
#def self.open_tokyocabinet(path, write, serializer = nil, tokyocabinet_class = TokyoCabinet::HDB)
|
83
|
+
# raise
|
84
|
+
# write = true unless File.exist? path
|
85
|
+
|
86
|
+
# FileUtils.mkdir_p File.dirname(path) unless File.exist?(File.dirname(path))
|
87
|
+
|
88
|
+
# database = Persist::TCAdapter.open(path, write, tokyocabinet_class)
|
89
|
+
|
90
|
+
# unless serializer == :clean
|
91
|
+
# TSV.setup database
|
92
|
+
# database.write_and_read do
|
93
|
+
# database.serializer = serializer
|
94
|
+
# end if serializer && database.serializer != serializer
|
95
|
+
# end
|
96
|
+
|
97
|
+
# database
|
98
|
+
#end
|
99
|
+
end
|
100
|
+
|
101
|
+
Persist.save_drivers[:HDB] = proc do |file, content|
|
102
|
+
data = ScoutCabinet.open(file, true, "HDB")
|
103
|
+
content.annotate(data)
|
104
|
+
data.extend TSVAdapter
|
105
|
+
data.merge!(content)
|
106
|
+
data
|
107
|
+
end
|
108
|
+
|
109
|
+
Persist.load_drivers[:HDB] = proc do |file|
|
110
|
+
data = ScoutCabinet.open(file, false, "HDB")
|
111
|
+
data.extend TSVAdapter unless TSVAdapter === data
|
112
|
+
data
|
113
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require_relative 'persist/adapter'
|
2
|
+
require_relative 'persist/tokyocabinet'
|
3
|
+
|
4
|
+
Persist.save_drivers[:tsv] = proc do |file,content|
|
5
|
+
stream = if IO === content
|
6
|
+
content
|
7
|
+
elsif content.respond_to?(:stream)
|
8
|
+
content.stream
|
9
|
+
end
|
10
|
+
Open.sensible_write(file, stream)
|
11
|
+
end
|
12
|
+
|
13
|
+
Persist.load_drivers[:tsv] = proc do |file| TSV.open file end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
require_relative 'parser'
|
2
|
+
module TSV
|
3
|
+
def self.identify_field(key_field, fields, name)
|
4
|
+
return :key if name == :key || key_field.start_with?(name.to_s)
|
5
|
+
name.collect!{|n| key_field == n ? :key : n } if Array === name
|
6
|
+
NamedArray.identify_name(fields, name)
|
7
|
+
end
|
8
|
+
|
9
|
+
def identify_field(name)
|
10
|
+
TSV.identify_field(@key_field, @fields, name)
|
11
|
+
end
|
12
|
+
|
13
|
+
def traverse(key_field_pos = :key, fields_pos = nil, type: nil, one2one: false, unnamed: false, key_field: nil, fields: nil, &block)
|
14
|
+
key_field = key_field_pos if key_field.nil?
|
15
|
+
fields = fields_pos if fields.nil?
|
16
|
+
type = @type if type.nil?
|
17
|
+
key_pos = self.identify_field(key_field)
|
18
|
+
fields = [fields] unless fields.nil? || Array === fields
|
19
|
+
positions = fields.nil? ? nil : self.identify_field(fields)
|
20
|
+
|
21
|
+
if key_pos == :key
|
22
|
+
key_name = @key_field
|
23
|
+
else
|
24
|
+
key_name = @fields[key_pos]
|
25
|
+
if positions.nil?
|
26
|
+
positions = (0..@fields.length-1).to_a
|
27
|
+
positions.delete_at key_pos
|
28
|
+
positions.unshift :key
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
if positions.nil? && key_pos == :key
|
33
|
+
field_names = @fields
|
34
|
+
elsif positions.nil? && key_pos != :key
|
35
|
+
field_names = @fields.dup
|
36
|
+
field_names.delete_at key_pos
|
37
|
+
elsif positions.include?(:key)
|
38
|
+
field_names = positions.collect{|p| p == :key ? @key_field : @fields[p] }
|
39
|
+
else
|
40
|
+
field_names = @fields.values_at *positions
|
41
|
+
end
|
42
|
+
|
43
|
+
key_index = positions.index :key if positions
|
44
|
+
positions.delete :key if positions
|
45
|
+
|
46
|
+
each do |key,values|
|
47
|
+
values = [values] if @type == :single
|
48
|
+
if positions.nil?
|
49
|
+
if key_pos != :key
|
50
|
+
values = values.dup
|
51
|
+
key = values.delete_at(key_pos)
|
52
|
+
end
|
53
|
+
else
|
54
|
+
orig_key = key
|
55
|
+
key = values[key_pos] if key_pos != :key
|
56
|
+
|
57
|
+
values = values.values_at(*positions)
|
58
|
+
if key_index
|
59
|
+
if @type == :double
|
60
|
+
values.insert key_index, [orig_key]
|
61
|
+
else
|
62
|
+
values.insert key_index, orig_key
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
if Array === key
|
68
|
+
if @type == :double && one2one
|
69
|
+
if one2one == :fill
|
70
|
+
key.each_with_index do |key_i,i|
|
71
|
+
if type == :double
|
72
|
+
v_i = values.collect{|v| [v[i] || v.first] }
|
73
|
+
else
|
74
|
+
v_i = values.collect{|v| v[i] || v.first }
|
75
|
+
end
|
76
|
+
yield key_i, v_i
|
77
|
+
end
|
78
|
+
else
|
79
|
+
key.each_with_index do |key_i,i|
|
80
|
+
if type == :double
|
81
|
+
v_i = values.collect{|v| [v[i]] }
|
82
|
+
else
|
83
|
+
v_i = values.collect{|v| v[i] }
|
84
|
+
end
|
85
|
+
yield key_i, v_i
|
86
|
+
end
|
87
|
+
end
|
88
|
+
else
|
89
|
+
key.each_with_index do |key_i, i|
|
90
|
+
if type == :double
|
91
|
+
yield key_i, values
|
92
|
+
elsif type == :list
|
93
|
+
yield key_i, values.collect{|v| v[i] }
|
94
|
+
elsif type == :flat
|
95
|
+
yield key_i, values.flatten
|
96
|
+
elsif type == :single
|
97
|
+
yield key_i, values.first
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
else
|
102
|
+
if type == @type
|
103
|
+
if type == :single
|
104
|
+
yield key, values.first
|
105
|
+
else
|
106
|
+
yield key, values
|
107
|
+
end
|
108
|
+
else
|
109
|
+
case [type, @type]
|
110
|
+
when [:double, :list]
|
111
|
+
yield key, values.collect{|v| [v] }
|
112
|
+
when [:double, :flat]
|
113
|
+
yield key, [values]
|
114
|
+
when [:double, :single]
|
115
|
+
yield key, [values]
|
116
|
+
when [:list, :double]
|
117
|
+
yield key, values.collect{|v| v.first }
|
118
|
+
when [:list, :flat]
|
119
|
+
yield key, [values.first]
|
120
|
+
when [:list, :single]
|
121
|
+
yield key, values
|
122
|
+
when [:flat, :double]
|
123
|
+
yield key, values.flatten
|
124
|
+
when [:flat, :list]
|
125
|
+
yield key, values.flatten
|
126
|
+
when [:flat, :single]
|
127
|
+
yield key, values
|
128
|
+
when [:single, :double]
|
129
|
+
yield key, values.flatten.first
|
130
|
+
when [:single, :list]
|
131
|
+
yield key, values.first
|
132
|
+
when [:single, :flat]
|
133
|
+
yield key, values.first
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
[key_name, field_names]
|
140
|
+
end
|
141
|
+
|
142
|
+
alias through traverse
|
143
|
+
end
|