rbbt-util 4.0.2 → 4.1.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.
- data/README.rdoc +7 -0
- data/lib/rbbt/annotations.rb +147 -10
- data/lib/rbbt/persist.rb +5 -1
- data/lib/rbbt/persist/tsv.rb +4 -3
- data/lib/rbbt/resource/path.rb +8 -1
- data/lib/rbbt/tsv.rb +3 -2
- data/lib/rbbt/tsv/accessor.rb +140 -51
- data/lib/rbbt/tsv/attach/util.rb +124 -106
- data/lib/rbbt/tsv/filter.rb +4 -2
- data/lib/rbbt/tsv/manipulate.rb +68 -13
- data/lib/rbbt/tsv/parser.rb +110 -20
- data/lib/rbbt/tsv/serializers.rb +6 -0
- data/lib/rbbt/tsv/util.rb +35 -1
- data/lib/rbbt/util/chain_methods.rb +25 -10
- data/lib/rbbt/util/misc.rb +109 -27
- data/lib/rbbt/util/open.rb +15 -4
- data/lib/rbbt/workflow.rb +18 -3
- data/lib/rbbt/workflow/annotate.rb +6 -1
- data/lib/rbbt/workflow/soap.rb +1 -1
- data/lib/rbbt/workflow/step.rb +13 -3
- data/lib/rbbt/workflow/task.rb +2 -2
- data/share/install/software/lib/install_helpers +6 -0
- data/share/lib/R/util.R +6 -1
- data/test/rbbt/test_annotations.rb +7 -0
- data/test/rbbt/test_persist.rb +32 -0
- data/test/rbbt/test_tsv.rb +101 -2
- data/test/rbbt/test_workflow.rb +11 -0
- data/test/rbbt/tsv/test_accessor.rb +15 -0
- data/test/rbbt/tsv/test_attach.rb +1 -1
- data/test/rbbt/tsv/test_manipulate.rb +37 -3
- data/test/rbbt/tsv/test_util.rb +25 -0
- data/test/rbbt/util/test_misc.rb +8 -0
- metadata +7 -4
- data/lib/rbbt/util/persistence.rb +0 -406
data/lib/rbbt/util/open.rb
CHANGED
@@ -78,9 +78,9 @@ module Open
|
|
78
78
|
end
|
79
79
|
|
80
80
|
# Cache
|
81
|
-
|
81
|
+
#
|
82
82
|
def self.in_cache(url, options = {})
|
83
|
-
digest = Digest::MD5.hexdigest([url, options
|
83
|
+
digest = Digest::MD5.hexdigest([url, options.values_at("--post-data", "--post-data="), (options.include?("--post-file")? Open.read(options["--post-file"]) : "")].inspect)
|
84
84
|
|
85
85
|
filename = File.join(REMOTE_CACHEDIR, digest)
|
86
86
|
if File.exists? filename
|
@@ -89,9 +89,20 @@ module Open
|
|
89
89
|
nil
|
90
90
|
end
|
91
91
|
end
|
92
|
+
|
93
|
+
def self.remove_from_cache(url, options = {})
|
94
|
+
digest = Digest::MD5.hexdigest([url, options.values_at("--post-data", "--post-data="), (options.include?("--post-file")? Open.read(options["--post-file"]) : "")].inspect)
|
95
|
+
|
96
|
+
filename = File.join(REMOTE_CACHEDIR, digest)
|
97
|
+
if File.exists? filename
|
98
|
+
FileUtils.rm filename
|
99
|
+
else
|
100
|
+
nil
|
101
|
+
end
|
102
|
+
end
|
92
103
|
|
93
104
|
def self.add_cache(url, data, options = {})
|
94
|
-
digest = Digest::MD5.hexdigest([url, options
|
105
|
+
digest = Digest::MD5.hexdigest([url, options.values_at("--post-data", "--post-data="), (options.include?("--post-file")? Open.read(options["--post-file"]) : "")].inspect)
|
95
106
|
Misc.sensiblewrite(File.join(REMOTE_CACHEDIR, digest), data)
|
96
107
|
end
|
97
108
|
|
@@ -156,7 +167,7 @@ module Open
|
|
156
167
|
wget_options[:nice] = options.delete(:nice)
|
157
168
|
wget_options[:nice_key] = options.delete(:nice_key)
|
158
169
|
wget_options[:quiet] = options.delete(:quiet)
|
159
|
-
wget_options[
|
170
|
+
wget_options["--post-data="] = options.delete(:post) if options.include? :post
|
160
171
|
wget_options[:cookies] = options.delete(:cookies)
|
161
172
|
|
162
173
|
io = case
|
data/lib/rbbt/workflow.rb
CHANGED
@@ -148,6 +148,16 @@ module Workflow
|
|
148
148
|
base.extend AnnotatedModule
|
149
149
|
class << base
|
150
150
|
attr_accessor :libdir, :workdir, :tasks, :task_dependencies, :task_description, :dependencies, :asynchronous_exports, :synchronous_exports, :exec_exports, :last_task
|
151
|
+
|
152
|
+
alias prev_workflow_extended extended if methods.include? "extended"
|
153
|
+
|
154
|
+
def extended(object)
|
155
|
+
self.send(:prev_workflow_extended, object) if methods.include? "prev_workflow_extended"
|
156
|
+
object.extend Workflow unless Workflow === object
|
157
|
+
|
158
|
+
object.tasks.merge! self.tasks
|
159
|
+
object.task_dependencies.merge! self.task_dependencies
|
160
|
+
end
|
151
161
|
|
152
162
|
def dependencies
|
153
163
|
i = @dependencies; @dependencies = []; i
|
@@ -242,12 +252,10 @@ module Workflow
|
|
242
252
|
task = tasks[taskname]
|
243
253
|
raise "Task not found: #{ taskname }" if task.nil?
|
244
254
|
|
245
|
-
|
246
255
|
IndiferentHash.setup(inputs)
|
247
256
|
|
248
257
|
resolve_locals(inputs)
|
249
258
|
|
250
|
-
|
251
259
|
dependencies = real_dependencies(task, jobname, inputs, task_dependencies[taskname] || [])
|
252
260
|
|
253
261
|
input_values = task.take_input_values(inputs)
|
@@ -256,10 +264,17 @@ module Workflow
|
|
256
264
|
|
257
265
|
step = Step.new step_path, task, input_values, dependencies
|
258
266
|
|
267
|
+
helpers = @helpers
|
268
|
+
(class << step; self; end).class_eval do
|
269
|
+
helpers.each do |name, block|
|
270
|
+
define_method name, &block
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
259
274
|
step
|
260
275
|
end
|
261
276
|
|
262
|
-
def
|
277
|
+
def load_step(path)
|
263
278
|
task = task_for path
|
264
279
|
Step.new path, tasks[task]
|
265
280
|
end
|
@@ -2,7 +2,7 @@ module AnnotatedModule
|
|
2
2
|
def self.extended(base)
|
3
3
|
if not base.respond_to? :inputs
|
4
4
|
class << base
|
5
|
-
attr_accessor :description, :inputs, :input_types, :input_descriptions, :input_defaults, :result_description
|
5
|
+
attr_accessor :description, :inputs, :input_types, :input_descriptions, :input_defaults, :result_description, :helpers
|
6
6
|
|
7
7
|
def description
|
8
8
|
i = @description; @description = ""; i
|
@@ -38,10 +38,15 @@ module AnnotatedModule
|
|
38
38
|
base.input_types = {}
|
39
39
|
base.input_descriptions = {}
|
40
40
|
base.input_defaults = {}
|
41
|
+
base.helpers = {}
|
41
42
|
|
42
43
|
end
|
43
44
|
end
|
44
45
|
|
46
|
+
def helper(name, &block)
|
47
|
+
@helpers[name] = block
|
48
|
+
end
|
49
|
+
|
45
50
|
def returns(text)
|
46
51
|
@result_description = text
|
47
52
|
end
|
data/lib/rbbt/workflow/soap.rb
CHANGED
data/lib/rbbt/workflow/step.rb
CHANGED
@@ -23,8 +23,15 @@ class Step
|
|
23
23
|
@inputs = inputs || []
|
24
24
|
end
|
25
25
|
|
26
|
+
def prepare_entity(value, description = nil, info = {})
|
27
|
+
return value if description.nil?
|
28
|
+
Entity.formats[description].setup(value, info.merge(:format => description)) if defined?(Entity) and Entity.respond_to?(:formats) and Entity.formats.include? description
|
29
|
+
value
|
30
|
+
end
|
31
|
+
|
26
32
|
def exec
|
27
|
-
@task.exec_in self, *@inputs
|
33
|
+
result = @task.exec_in self, *@inputs
|
34
|
+
prepare_entity result, @task.result_description
|
28
35
|
end
|
29
36
|
|
30
37
|
def join
|
@@ -41,7 +48,7 @@ class Step
|
|
41
48
|
end
|
42
49
|
|
43
50
|
def run
|
44
|
-
Persist.persist "Job", @task.result_type, :file => @path, :check => rec_dependencies.collect{|dependency| dependency.path}.uniq do
|
51
|
+
result = Persist.persist "Job", @task.result_type, :file => @path, :check => rec_dependencies.collect{|dependency| dependency.path}.uniq do
|
45
52
|
log task.name, "Starting task: #{ name }"
|
46
53
|
set_info :dependencies, @dependencies.collect{|dep| [dep.task.name, dep.name]}
|
47
54
|
@dependencies.each{|dependency| dependency.run}
|
@@ -51,6 +58,8 @@ class Step
|
|
51
58
|
set_info :status, :done
|
52
59
|
res
|
53
60
|
end
|
61
|
+
|
62
|
+
prepare_entity result, @task.result_description, info
|
54
63
|
end
|
55
64
|
|
56
65
|
def fork
|
@@ -78,9 +87,10 @@ class Step
|
|
78
87
|
|
79
88
|
def load
|
80
89
|
raise "Can not load: Step is waiting for proces #{@pid} to finish" if not done?
|
81
|
-
Persist.persist "Job", @task.result_type, :file => @path, :check => rec_dependencies.collect{|dependency| dependency.path} do
|
90
|
+
result = Persist.persist "Job", @task.result_type, :file => @path, :check => rec_dependencies.collect{|dependency| dependency.path} do
|
82
91
|
exec
|
83
92
|
end
|
93
|
+
prepare_entity result, @task.result_description, info
|
84
94
|
end
|
85
95
|
|
86
96
|
def clean
|
data/lib/rbbt/workflow/task.rb
CHANGED
@@ -36,8 +36,8 @@ module Task
|
|
36
36
|
return [] if @inputs.nil?
|
37
37
|
values = []
|
38
38
|
@inputs.each do |input|
|
39
|
-
value = input_values[input]
|
40
|
-
|
39
|
+
value = input_values[input]
|
40
|
+
value = IndiferentHash.setup(@input_defaults || {})[input] if value.nil?
|
41
41
|
values << value
|
42
42
|
end
|
43
43
|
values
|
data/share/lib/R/util.R
CHANGED
@@ -48,11 +48,16 @@ rbbt.tsv2matrix <- function(data){
|
|
48
48
|
return(new);
|
49
49
|
}
|
50
50
|
|
51
|
-
rbbt.tsv.write <- function(filename, data, key.field = NULL){
|
51
|
+
rbbt.tsv.write <- function(filename, data, key.field = NULL, extra_headers = NULL){
|
52
52
|
if (is.null(key.field)){ key.field = "ID";}
|
53
53
|
|
54
54
|
f = file(filename, 'w');
|
55
55
|
|
56
|
+
if (!is.null(extra_headers)){
|
57
|
+
extra_headers = paste("#: ", extra_headers, "\n", sep="");
|
58
|
+
cat(extra_headers, file=f);
|
59
|
+
}
|
60
|
+
|
56
61
|
header = paste("#", key.field, sep="");
|
57
62
|
for (name in colnames(data)){ header = paste(header, name, sep="\t");}
|
58
63
|
header = paste(header, "\n", sep="");
|
@@ -103,4 +103,11 @@ class TestAnnotations < Test::Unit::TestCase
|
|
103
103
|
AnnotatedString.setup(str, annotation_str)
|
104
104
|
assert_equal str + annotation_str, str.add_annot
|
105
105
|
end
|
106
|
+
|
107
|
+
def test_annotation_positional2hash
|
108
|
+
str = "string"
|
109
|
+
annotation_str = "Annotation String"
|
110
|
+
AnnotatedString.setup(str, :annotation_str => annotation_str)
|
111
|
+
assert_equal str + annotation_str, str.add_annot
|
112
|
+
end
|
106
113
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
|
2
|
+
require 'rbbt/persist'
|
3
|
+
require 'rbbt/util/tmpfile'
|
4
|
+
require 'test/unit'
|
5
|
+
|
6
|
+
class TestPersist < Test::Unit::TestCase
|
7
|
+
def test_array_persist
|
8
|
+
TmpFile.with_file do |tmp|
|
9
|
+
10.times do
|
10
|
+
assert_equal ["1", "2"],(Persist.persist("Test", :array, :file => tmp) do
|
11
|
+
["1", "2"]
|
12
|
+
end)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
TmpFile.with_file do |tmp|
|
17
|
+
10.times do
|
18
|
+
assert_equal [],(Persist.persist("Test", :array, :file => tmp) do
|
19
|
+
[]
|
20
|
+
end)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
TmpFile.with_file do |tmp|
|
25
|
+
10.times do
|
26
|
+
assert_equal ["1"],(Persist.persist("Test", :array, :file => tmp) do
|
27
|
+
["1"]
|
28
|
+
end)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/test/rbbt/test_tsv.rb
CHANGED
@@ -19,7 +19,7 @@ class TestTSV < Test::Unit::TestCase
|
|
19
19
|
|
20
20
|
assert_equal "1", a["one"]
|
21
21
|
end
|
22
|
-
|
22
|
+
|
23
23
|
def test_tsv
|
24
24
|
content =<<-EOF
|
25
25
|
#Id ValueA ValueB OtherID
|
@@ -78,7 +78,7 @@ row2 A B Id3
|
|
78
78
|
EOF
|
79
79
|
|
80
80
|
TmpFile.with_file(content) do |filename|
|
81
|
-
tsv = TSV.open(filename, :sep => /\s+/, :fields => 1)
|
81
|
+
tsv = TSV.open(filename, :sep => /\s+/, :fields => [1])
|
82
82
|
assert_equal ["a", "aa", "aaa"], tsv["row1"][0]
|
83
83
|
assert_equal :double, tsv.type
|
84
84
|
assert_equal [%w(a aa aaa)], tsv["row1"]
|
@@ -359,5 +359,104 @@ row2 A B Id3
|
|
359
359
|
end
|
360
360
|
end
|
361
361
|
|
362
|
+
def test_flat
|
363
|
+
content =<<-EOF
|
364
|
+
#Id ValueA
|
365
|
+
row1 a aa aaa
|
366
|
+
row2 b bbb bbbb bb
|
367
|
+
EOF
|
368
|
+
|
369
|
+
TmpFile.with_file(content) do |filename|
|
370
|
+
tsv = TSV.open(filename, :sep => /\s+/, :merge => true, :type => :flat, :fields => ["ValueA"])
|
371
|
+
assert_equal ["a", "aa", "aaa"], tsv["row1"]
|
372
|
+
end
|
373
|
+
end
|
374
|
+
|
375
|
+
def test_zipped
|
376
|
+
content =<<-EOF
|
377
|
+
#Id ValueA ValueB
|
378
|
+
row1 a|aa|aaa b|bb|bbb
|
379
|
+
row2 a|aa|aaa c|cc|ccc
|
380
|
+
EOF
|
381
|
+
|
382
|
+
TmpFile.with_file(content) do |filename|
|
383
|
+
tsv = TSV.open(filename, :sep => /\s+/, :merge => true, :type => :double, :key_field => "ValueA", :zipped => true)
|
384
|
+
assert_equal [["row1", "row2"], ["b", "c"]], tsv["a"]
|
385
|
+
end
|
386
|
+
end
|
387
|
+
|
388
|
+
def test_named_array_key
|
389
|
+
content =<<-EOF
|
390
|
+
#Id ValueA ValueB OtherID
|
391
|
+
row1 a|aa|aaa b Id1|Id2
|
392
|
+
row2 A B Id3
|
393
|
+
EOF
|
394
|
+
|
395
|
+
TmpFile.with_file(content) do |filename|
|
396
|
+
tsv = TSV.open(filename, :sep => /\s+/)
|
397
|
+
assert_equal "row1", tsv["row1"].key
|
398
|
+
|
399
|
+
end
|
400
|
+
|
401
|
+
end
|
402
|
+
|
403
|
+
def test_unnamed_key
|
404
|
+
content =<<-EOF
|
405
|
+
row1 a|aa|aaa b Id1|Id2
|
406
|
+
row2 A B Id3
|
407
|
+
EOF
|
408
|
+
|
409
|
+
TmpFile.with_file(content) do |filename|
|
410
|
+
tsv = TSV.open(filename, :sep => /\s+/, :key_field => 1)
|
411
|
+
assert tsv.keys.include? "a"
|
412
|
+
end
|
413
|
+
|
414
|
+
end
|
415
|
+
|
416
|
+
def test_float_array
|
417
|
+
content =<<-EOF
|
418
|
+
#Id ValueA ValueB OtherID
|
419
|
+
row1 0.2 0.3 0
|
420
|
+
row2 0.1 4.5 0
|
421
|
+
EOF
|
422
|
+
|
423
|
+
TmpFile.with_file(content) do |filename|
|
424
|
+
tsv = TSV.open(filename, :sep => /\s+/, :persist => true, :type => :list, :cast => :to_f)
|
425
|
+
assert_equal [0.2, 0.3, 0], tsv["row1"]
|
426
|
+
assert_equal :float_array, tsv.serializer
|
427
|
+
end
|
428
|
+
|
429
|
+
end
|
430
|
+
|
431
|
+
def test_flat_field_select
|
432
|
+
content =<<-EOF
|
433
|
+
#: :type=:flat
|
434
|
+
#Id Value
|
435
|
+
row1 a aa aaa
|
436
|
+
row2 b bb bbb
|
437
|
+
EOF
|
438
|
+
|
439
|
+
TmpFile.with_file(content) do |filename|
|
440
|
+
assert TSV.open(filename, :sep => /\s+/, :key_field => "Value").include? "aa"
|
441
|
+
end
|
442
|
+
end
|
443
|
+
|
444
|
+
def test_flat2
|
445
|
+
content =<<-EOF
|
446
|
+
#: :type=:flat
|
447
|
+
#Id Value
|
448
|
+
row1 a|aa|aaa
|
449
|
+
row2 A|AA|AAA
|
450
|
+
EOF
|
451
|
+
|
452
|
+
TmpFile.with_file(content) do |filename|
|
453
|
+
assert TSV.open(filename, :sep => /\s+/, :type => :flat).include? "row1"
|
454
|
+
assert TSV.open(filename, :sep => /\s+/, :type => :flat)["row1"].include? "a"
|
455
|
+
assert TSV.open(filename, :sep => /\s+/, :type => :flat, :key_field => "Id")["row1"].include? "a"
|
456
|
+
assert TSV.open(filename, :sep => /\s+/, :type => :flat, :key_field => "Id", :fields => ["Value"])["row1"].include? "a"
|
457
|
+
end
|
458
|
+
|
459
|
+
end
|
460
|
+
|
362
461
|
|
363
462
|
end
|
data/test/rbbt/test_workflow.rb
CHANGED
@@ -7,6 +7,14 @@ require 'test/unit'
|
|
7
7
|
module TestWF
|
8
8
|
extend Workflow
|
9
9
|
|
10
|
+
helper :user do
|
11
|
+
"User"
|
12
|
+
end
|
13
|
+
|
14
|
+
task :user => :string do
|
15
|
+
user
|
16
|
+
end
|
17
|
+
|
10
18
|
str = "TEST"
|
11
19
|
task :str => :string do
|
12
20
|
str
|
@@ -56,6 +64,9 @@ TestWF.workdir = Rbbt.tmp.test.workflow
|
|
56
64
|
|
57
65
|
class TestWorkflow < Test::Unit::TestCase
|
58
66
|
|
67
|
+
def test_helper
|
68
|
+
assert_equal "User", TestWF.job(:user, "Default", :number => 3).run
|
69
|
+
end
|
59
70
|
def test_job
|
60
71
|
str = "TEST"
|
61
72
|
job = TestWF.job(:repeat2, "Default", :number => 3).fork
|
@@ -5,6 +5,21 @@ require 'test/unit'
|
|
5
5
|
|
6
6
|
class TestTSV < Test::Unit::TestCase
|
7
7
|
|
8
|
+
def test_to_hash
|
9
|
+
content =<<-EOF
|
10
|
+
#Id ValueA ValueB OtherID
|
11
|
+
row1 a|aa|aaa b Id1|Id2
|
12
|
+
row2 A B Id3
|
13
|
+
EOF
|
14
|
+
|
15
|
+
TmpFile.with_file(content) do |filename|
|
16
|
+
tsv = TSV.open(filename, :sep => /\s+/)
|
17
|
+
hash = tsv.to_hash
|
18
|
+
assert hash.methods.select{|m| m =~ /#{ TSV::KEY_PREFIX }/}.empty?
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
|
8
23
|
def test_tsv
|
9
24
|
content =<<-EOF
|
10
25
|
#Id ValueA ValueB OtherID
|
@@ -170,7 +170,7 @@ B Id3
|
|
170
170
|
tsv3 = TSV.open(File.open(filename), :double, :sep => /\s+/)
|
171
171
|
end
|
172
172
|
|
173
|
-
tsv1.attach tsv2, "OtherID"
|
173
|
+
tsv1.attach tsv2, :fields => "OtherID"
|
174
174
|
|
175
175
|
assert_equal %w(ValueA ValueB OtherID), tsv1.fields
|
176
176
|
assert_equal %w(Id1 Id2), tsv1["row1"]["OtherID"]
|
@@ -4,6 +4,20 @@ require 'rbbt/tsv/manipulate'
|
|
4
4
|
|
5
5
|
class TestTSVManipulate < Test::Unit::TestCase
|
6
6
|
|
7
|
+
def test_zipped
|
8
|
+
content =<<-EOF
|
9
|
+
#Id ValueA ValueB ValueC
|
10
|
+
rowA A|AA B|BB C|CC
|
11
|
+
rowa a|aa b|BB C|CC
|
12
|
+
EOF
|
13
|
+
|
14
|
+
TmpFile.with_file(content) do |filename|
|
15
|
+
tsv = TSV.open(File.open(filename), :sep => /\s+/, :type => :double)
|
16
|
+
assert_equal ["A", "AA", "a", "aa"].sort, tsv.reorder("ValueA", nil, :zipped => true).keys.sort
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
|
7
21
|
def test_through
|
8
22
|
content =<<-EOF
|
9
23
|
#Id ValueA ValueB OtherID
|
@@ -104,14 +118,17 @@ row3 a C Id4
|
|
104
118
|
new = tsv.select "ValueB" => /b|Id4/
|
105
119
|
assert_equal %w(row1).sort, new.keys
|
106
120
|
|
107
|
-
tsv = TSV.open(filename, :sep => /\s+/, :type => :flat)
|
108
|
-
assert tsv.type != :double
|
109
121
|
|
110
122
|
new = tsv.select %w(b Id4)
|
111
123
|
assert_equal %w(row1 row3).sort, new.keys.sort
|
112
124
|
|
113
|
-
new = tsv.select do |k,v|
|
125
|
+
new = tsv.select do |k,v|
|
126
|
+
v["ValueA"].include? "A"
|
127
|
+
end
|
114
128
|
assert_equal %w(row2).sort, new.keys.sort
|
129
|
+
|
130
|
+
tsv = TSV.open(filename, :sep => /\s+/, :type => :flat)
|
131
|
+
assert tsv.type != :double
|
115
132
|
end
|
116
133
|
end
|
117
134
|
|
@@ -191,4 +208,21 @@ row3 a C Id4
|
|
191
208
|
|
192
209
|
end
|
193
210
|
|
211
|
+
def test_reorder_flat
|
212
|
+
content =<<-EOF
|
213
|
+
#Id ValueA
|
214
|
+
row1 a aa aaa
|
215
|
+
row2 A
|
216
|
+
row3 a
|
217
|
+
EOF
|
218
|
+
|
219
|
+
TmpFile.with_file(content) do |filename|
|
220
|
+
tsv = TSV.open(File.open(filename), :sep => /\s+/, :type => :flat)
|
221
|
+
|
222
|
+
assert_equal ["row1", "row3"].sort, tsv.reorder("ValueA")["a"]
|
223
|
+
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
|
194
228
|
end
|