scout-gear 7.2.0 → 7.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.vimproject +37 -3
- data/VERSION +1 -1
- data/lib/scout/concurrent_stream.rb +9 -8
- data/lib/scout/exceptions.rb +1 -0
- data/lib/scout/log/color.rb +0 -1
- data/lib/scout/log/progress/util.rb +65 -0
- data/lib/scout/misc/helper.rb +31 -0
- data/lib/scout/misc/monitor.rb +1 -1
- data/lib/scout/misc.rb +1 -0
- data/lib/scout/open/stream.rb +21 -27
- data/lib/scout/persist.rb +42 -28
- data/lib/scout/semaphore.rb +8 -1
- data/lib/scout/tsv/dumper.rb +13 -8
- data/lib/scout/tsv/index.rb +127 -15
- data/lib/scout/tsv/open.rb +128 -0
- data/lib/scout/tsv/parser.rb +70 -43
- data/lib/scout/tsv/path.rb +4 -4
- data/lib/scout/tsv/persist/adapter.rb +52 -33
- 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 +3 -3
- data/lib/scout/tsv/persist.rb +0 -2
- data/lib/scout/tsv/traverse.rb +130 -35
- 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 +77 -19
- data/lib/scout/tsv.rb +2 -2
- data/lib/scout/work_queue/worker.rb +1 -1
- data/lib/scout/workflow/definition.rb +8 -0
- data/lib/scout/workflow/step/info.rb +4 -0
- data/lib/scout/workflow/step/progress.rb +14 -0
- data/lib/scout/workflow/step.rb +10 -5
- data/lib/scout/workflow/task.rb +8 -4
- data/lib/scout/workflow/usage.rb +2 -0
- data/scout-gear.gemspec +33 -10
- data/scout_commands/workflow/task +3 -2
- data/scout_commands/workflow/task_old +2 -2
- data/test/scout/open/test_stream.rb +1 -1
- data/test/scout/test_persist.rb +61 -0
- data/test/scout/test_tmpfile.rb +1 -1
- data/test/scout/test_tsv.rb +10 -1
- data/test/scout/test_work_queue.rb +1 -0
- data/test/scout/tsv/persist/test_adapter.rb +10 -0
- data/test/scout/tsv/persist/test_fix_width_table.rb +134 -0
- data/test/scout/tsv/test_index.rb +94 -2
- data/test/scout/tsv/test_open.rb +9 -0
- data/test/scout/tsv/test_parser.rb +28 -3
- data/test/scout/tsv/test_persist.rb +7 -0
- data/test/scout/tsv/test_traverse.rb +110 -3
- 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 +63 -6
- data/test/scout/workflow/step/test_load.rb +3 -3
- data/test/scout/workflow/test_step.rb +10 -10
- data/test/test_helper.rb +3 -1
- metadata +19 -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
|
@@ -4,8 +4,8 @@ require_relative 'adapter'
|
|
4
4
|
module ScoutCabinet
|
5
5
|
attr_accessor :persistence_path, :persistence_class
|
6
6
|
|
7
|
-
CONNECTIONS = {}
|
8
7
|
def self.open(path, write, tokyocabinet_class = TokyoCabinet::HDB)
|
8
|
+
path = path.find if Path === path
|
9
9
|
if String === tokyocabinet_class && tokyocabinet_class.include?(":big")
|
10
10
|
big = true
|
11
11
|
tokyocabinet_class = tokyocabinet_class.split(":").first
|
@@ -19,7 +19,7 @@ module ScoutCabinet
|
|
19
19
|
tokyocabinet_class = TokyoCabinet::HDB if tokyocabinet_class == "HDB" or tokyocabinet_class.nil?
|
20
20
|
tokyocabinet_class = TokyoCabinet::BDB if tokyocabinet_class == "BDB"
|
21
21
|
|
22
|
-
database = CONNECTIONS[path] ||= tokyocabinet_class.new
|
22
|
+
database = Persist::CONNECTIONS[path] ||= tokyocabinet_class.new
|
23
23
|
|
24
24
|
if big and not Open.exists?(path)
|
25
25
|
database.tune(nil,nil,nil,tokyocabinet_class::TLARGE | tokyocabinet_class::TDEFLATE)
|
@@ -39,7 +39,7 @@ module ScoutCabinet
|
|
39
39
|
|
40
40
|
database.open(path, tokyocabinet_class::OREADER)
|
41
41
|
|
42
|
-
CONNECTIONS[path] = database
|
42
|
+
Persist::CONNECTIONS[path] = database
|
43
43
|
|
44
44
|
database
|
45
45
|
end
|
data/lib/scout/tsv/persist.rb
CHANGED
data/lib/scout/tsv/traverse.rb
CHANGED
@@ -1,48 +1,143 @@
|
|
1
1
|
require_relative 'parser'
|
2
2
|
module TSV
|
3
|
-
def self.
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
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
11
|
end
|
12
12
|
|
13
|
-
def
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
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
|
33
98
|
end
|
34
|
-
nil
|
35
99
|
end
|
36
|
-
into.close if into.respond_to?(:close)
|
37
100
|
end
|
38
|
-
Thread.pass until into_thread
|
39
|
-
into
|
40
101
|
else
|
41
|
-
|
42
|
-
|
43
|
-
|
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
|
44
135
|
end
|
45
136
|
end
|
46
137
|
end
|
138
|
+
|
139
|
+
[key_name, field_names]
|
47
140
|
end
|
141
|
+
|
142
|
+
alias through traverse
|
48
143
|
end
|