scout-gear 7.3.0 → 8.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.vimproject +44 -16
- data/Rakefile +6 -1
- data/VERSION +1 -1
- data/bin/scout +21 -7
- data/doc/lib/scout/path.md +35 -0
- data/doc/lib/scout/workflow/task.md +13 -0
- data/lib/rbbt-scout.rb +1 -0
- data/lib/scout/cmd.rb +24 -25
- data/lib/scout/concurrent_stream.rb +59 -39
- data/lib/scout/config.rb +1 -1
- data/lib/scout/exceptions.rb +10 -0
- data/lib/scout/log/color.rb +15 -12
- data/lib/scout/log/progress/report.rb +8 -6
- data/lib/scout/log/progress/util.rb +61 -54
- data/lib/scout/log/progress.rb +1 -1
- data/lib/scout/log/trap.rb +107 -0
- data/lib/scout/log.rb +115 -52
- data/lib/scout/meta_extension.rb +47 -6
- data/lib/scout/misc/digest.rb +12 -3
- data/lib/scout/misc/format.rb +24 -7
- data/lib/scout/misc/insist.rb +1 -1
- data/lib/scout/misc/monitor.rb +22 -0
- data/lib/scout/misc/system.rb +58 -0
- data/lib/scout/named_array.rb +73 -3
- data/lib/scout/offsite/ssh.rb +171 -0
- data/lib/scout/offsite/step.rb +83 -0
- data/lib/scout/offsite/sync.rb +55 -0
- data/lib/scout/offsite.rb +3 -0
- data/lib/scout/open/lock/lockfile.rb +587 -0
- data/lib/scout/open/lock.rb +9 -2
- data/lib/scout/open/remote.rb +16 -1
- data/lib/scout/open/stream.rb +146 -83
- data/lib/scout/open/util.rb +22 -3
- data/lib/scout/open.rb +5 -4
- data/lib/scout/path/find.rb +24 -11
- data/lib/scout/path/util.rb +40 -0
- data/lib/scout/persist/serialize.rb +19 -6
- data/lib/scout/persist.rb +29 -13
- data/lib/scout/resource/path.rb +57 -0
- data/lib/scout/resource/produce.rb +0 -8
- data/lib/scout/resource/util.rb +12 -5
- data/lib/scout/tmpfile.rb +7 -8
- data/lib/scout/tsv/attach.rb +177 -0
- data/lib/scout/tsv/change_id.rb +40 -0
- data/lib/scout/tsv/dumper.rb +74 -46
- data/lib/scout/tsv/index.rb +85 -87
- data/lib/scout/tsv/open.rb +160 -85
- data/lib/scout/tsv/parser.rb +142 -80
- data/lib/scout/tsv/path.rb +1 -2
- data/lib/scout/tsv/persist/adapter.rb +15 -45
- data/lib/scout/tsv/persist/fix_width_table.rb +3 -0
- data/lib/scout/tsv/persist/tokyocabinet.rb +6 -1
- data/lib/scout/tsv/persist.rb +4 -0
- data/lib/scout/tsv/stream.rb +204 -0
- data/lib/scout/tsv/transformer.rb +152 -0
- data/lib/scout/tsv/traverse.rb +96 -92
- data/lib/scout/tsv/util/filter.rb +9 -0
- data/lib/scout/tsv/util/reorder.rb +81 -0
- data/lib/scout/tsv/util/select.rb +78 -33
- data/lib/scout/tsv/util/unzip.rb +86 -0
- data/lib/scout/tsv/util.rb +60 -11
- data/lib/scout/tsv.rb +34 -4
- data/lib/scout/work_queue/socket.rb +6 -1
- data/lib/scout/work_queue/worker.rb +5 -2
- data/lib/scout/work_queue.rb +51 -20
- data/lib/scout/workflow/definition.rb +23 -3
- data/lib/scout/workflow/deployment/orchestrator.rb +245 -0
- data/lib/scout/workflow/deployment.rb +1 -0
- data/lib/scout/workflow/step/dependencies.rb +56 -10
- data/lib/scout/workflow/step/file.rb +5 -0
- data/lib/scout/workflow/step/info.rb +40 -7
- data/lib/scout/workflow/step/load.rb +1 -1
- data/lib/scout/workflow/step/provenance.rb +9 -7
- data/lib/scout/workflow/step/status.rb +43 -0
- data/lib/scout/workflow/step.rb +160 -49
- data/lib/scout/workflow/task/dependencies.rb +114 -0
- data/lib/scout/workflow/task/inputs.rb +40 -32
- data/lib/scout/workflow/task.rb +38 -102
- data/lib/scout/workflow/usage.rb +48 -18
- data/lib/scout/workflow.rb +4 -2
- data/lib/scout-gear.rb +2 -0
- data/lib/scout.rb +6 -0
- data/scout-gear.gemspec +52 -23
- data/scout_commands/doc +37 -0
- data/scout_commands/find +1 -0
- data/scout_commands/offsite +30 -0
- data/scout_commands/update +29 -0
- data/scout_commands/workflow/info +15 -3
- data/scout_commands/workflow/install +102 -0
- data/scout_commands/workflow/task +57 -9
- data/test/scout/offsite/test_ssh.rb +15 -0
- data/test/scout/offsite/test_step.rb +33 -0
- data/test/scout/offsite/test_sync.rb +36 -0
- data/test/scout/offsite/test_task.rb +0 -0
- data/test/scout/open/test_stream.rb +60 -58
- data/test/scout/path/test_find.rb +10 -1
- data/test/scout/resource/test_path.rb +6 -0
- data/test/scout/resource/test_produce.rb +15 -0
- data/test/scout/test_meta_extension.rb +25 -0
- data/test/scout/test_named_array.rb +24 -0
- data/test/scout/test_persist.rb +9 -2
- data/test/scout/test_tsv.rb +229 -2
- data/test/scout/test_work_queue.rb +65 -41
- data/test/scout/tsv/persist/test_tokyocabinet.rb +29 -1
- data/test/scout/tsv/test_attach.rb +227 -0
- data/test/scout/tsv/test_change_id.rb +98 -0
- data/test/scout/tsv/test_dumper.rb +1 -1
- data/test/scout/tsv/test_index.rb +49 -3
- data/test/scout/tsv/test_open.rb +160 -2
- data/test/scout/tsv/test_parser.rb +33 -2
- data/test/scout/tsv/test_persist.rb +2 -0
- data/test/scout/tsv/test_stream.rb +200 -0
- data/test/scout/tsv/test_transformer.rb +120 -0
- data/test/scout/tsv/test_traverse.rb +88 -3
- data/test/scout/tsv/test_util.rb +1 -0
- data/test/scout/tsv/util/test_reorder.rb +94 -0
- data/test/scout/tsv/util/test_select.rb +25 -11
- data/test/scout/tsv/util/test_unzip.rb +112 -0
- data/test/scout/work_queue/test_socket.rb +0 -1
- data/test/scout/workflow/deployment/test_orchestrator.rb +272 -0
- data/test/scout/workflow/step/test_dependencies.rb +68 -0
- data/test/scout/workflow/step/test_info.rb +18 -0
- data/test/scout/workflow/step/test_status.rb +30 -0
- data/test/scout/workflow/task/test_dependencies.rb +355 -0
- data/test/scout/workflow/task/test_inputs.rb +67 -14
- data/test/scout/workflow/test_definition.rb +18 -0
- data/test/scout/workflow/test_documentation.rb +24 -0
- data/test/scout/workflow/test_step.rb +112 -3
- data/test/scout/workflow/test_task.rb +0 -151
- data/test/scout/workflow/test_usage.rb +33 -6
- data/test/test_scout.rb +9 -0
- metadata +100 -8
- data/scout_commands/workflow/task_old +0 -706
@@ -1,5 +1,6 @@
|
|
1
1
|
require_relative '../open'
|
2
2
|
require_relative 'open'
|
3
|
+
require 'set'
|
3
4
|
|
4
5
|
module Persist
|
5
6
|
TRUE_STRINGS = Set.new ["true", "True", "TRUE", "t", "T", "1", "yes", "Yes", "YES", "y", "Y", "ON", "on"] unless defined? TRUE_STRINGS
|
@@ -19,7 +20,7 @@ module Persist
|
|
19
20
|
type = type.to_sym if String === type
|
20
21
|
type = SERIALIZER if type == :serializer
|
21
22
|
case type
|
22
|
-
when nil, :string, :integer, :float, :boolean, :file, :path
|
23
|
+
when nil, :string, :text, :integer, :float, :boolean, :file, :path, :select, :folder, :binary
|
23
24
|
if IO === content || StringIO === content
|
24
25
|
content.read
|
25
26
|
else
|
@@ -46,8 +47,9 @@ module Persist
|
|
46
47
|
def self.deserialize(serialized, type)
|
47
48
|
type = type.to_sym if String === type
|
48
49
|
type = SERIALIZER if type == :serializer
|
50
|
+
|
49
51
|
case type
|
50
|
-
when nil, :string, :file, :stream
|
52
|
+
when nil, :string, :text, :file, :stream, :select, :folder
|
51
53
|
serialized
|
52
54
|
when :path
|
53
55
|
Path.setup(serialized)
|
@@ -56,7 +58,7 @@ module Persist
|
|
56
58
|
when :float
|
57
59
|
serialized.to_f
|
58
60
|
when :boolean
|
59
|
-
TRUE_STRINGS.include? serialized
|
61
|
+
TRUE_STRINGS.include? serialized.strip
|
60
62
|
when :array
|
61
63
|
serialized.split("\n")
|
62
64
|
when :yaml
|
@@ -99,9 +101,18 @@ module Persist
|
|
99
101
|
return save_drivers[type].call(file, content)
|
100
102
|
end
|
101
103
|
end
|
102
|
-
|
103
|
-
|
104
|
-
|
104
|
+
|
105
|
+
if type == :binary
|
106
|
+
content.force_encoding("ASCII-8BIT") if content.respond_to? :force_encoding
|
107
|
+
Open.open(path, :mode => 'wb') do |f|
|
108
|
+
f.puts content
|
109
|
+
end
|
110
|
+
content
|
111
|
+
else
|
112
|
+
serialized = serialize(content, type)
|
113
|
+
Open.sensible_write(file, serialized, :force => true)
|
114
|
+
return nil
|
115
|
+
end
|
105
116
|
end
|
106
117
|
|
107
118
|
def self.load(file, type = :serializer)
|
@@ -118,6 +129,8 @@ module Persist
|
|
118
129
|
end
|
119
130
|
|
120
131
|
case type
|
132
|
+
when :binary
|
133
|
+
Open.read(file, :mode => 'rb')
|
121
134
|
when :yaml
|
122
135
|
Open.yaml(file)
|
123
136
|
when :json
|
data/lib/scout/persist.rb
CHANGED
@@ -21,7 +21,9 @@ module Persist
|
|
21
21
|
def self.persistence_path(name, options = {})
|
22
22
|
options = IndiferentHash.add_defaults options, :dir => Persist.cache_dir
|
23
23
|
other_options = IndiferentHash.pull_keys options, :other
|
24
|
-
|
24
|
+
name = name.filename if name.respond_to?(:filename) && name.filename
|
25
|
+
persist_options = {}
|
26
|
+
TmpFile.tmp_for_file(name, options.merge(persist_options), other_options)
|
25
27
|
end
|
26
28
|
|
27
29
|
MEMORY_CACHE = {}
|
@@ -31,25 +33,31 @@ module Persist
|
|
31
33
|
return yield if FalseClass === persist_options[:persist]
|
32
34
|
file = persist_options[:path] || options[:path] || persistence_path(name, options)
|
33
35
|
|
34
|
-
lockfile = persist_options[:lockfile] || options[:lockfile] || Persist.persistence_path(file + '.persist', {:dir => Persist.lock_dir})
|
35
|
-
|
36
|
-
update = options[:update] || persist_options[:update]
|
37
|
-
update = Open.mtime(update) if Path === update
|
38
|
-
update = Open.mtime(file) >= update ? false : true if Time === update
|
39
|
-
|
40
36
|
if type == :memory
|
41
37
|
repo = options[:memory] || options[:repo] || MEMORY_CACHE
|
42
38
|
repo[file] ||= yield
|
43
39
|
return repo[file]
|
44
40
|
end
|
45
41
|
|
42
|
+
update = options[:update] || persist_options[:update]
|
43
|
+
update = Open.mtime(update) if Path === update
|
44
|
+
update = Open.mtime(file) >= update ? false : true if Time === update
|
45
|
+
|
46
|
+
lockfile = persist_options[:lockfile] || options[:lockfile] || Persist.persistence_path(file + '.persist', {:dir => Persist.lock_dir})
|
47
|
+
|
46
48
|
Open.lock lockfile do |lock|
|
47
49
|
if Open.exist?(file) && ! update
|
48
50
|
Persist.load(file, type)
|
49
51
|
else
|
50
|
-
return yield(file) if block.arity == 1
|
51
|
-
res = yield
|
52
52
|
begin
|
53
|
+
file = file.find if Path === file
|
54
|
+
return yield(file) if block.arity == 1
|
55
|
+
res = yield
|
56
|
+
|
57
|
+
if res.nil?
|
58
|
+
return Persist.load(file, type)
|
59
|
+
end
|
60
|
+
|
53
61
|
Open.rm(file)
|
54
62
|
|
55
63
|
if IO === res || StringIO === res
|
@@ -72,17 +80,25 @@ module Persist
|
|
72
80
|
pres = Persist.save(res, file, type)
|
73
81
|
res = pres unless pres.nil?
|
74
82
|
end
|
75
|
-
rescue
|
83
|
+
rescue Exception
|
84
|
+
Thread.handle_interrupt(Exception => :never) do
|
85
|
+
if Open.exist?(file)
|
86
|
+
Log.debug "Failed persistence #{file} - erasing"
|
87
|
+
Open.rm file
|
88
|
+
else
|
89
|
+
Log.debug "Failed persistence #{file}"
|
90
|
+
end
|
91
|
+
end
|
76
92
|
raise $! unless options[:canfail]
|
77
|
-
Log.debug "Could not persist #{type} on #{file}"
|
78
93
|
end
|
79
94
|
res
|
80
95
|
end
|
81
96
|
end
|
82
97
|
end
|
83
98
|
|
84
|
-
def self.memory(name,
|
85
|
-
|
99
|
+
def self.memory(name, options = {}, &block)
|
100
|
+
options[:persist_path] ||= options[:path] ||= [name, options[:key]].compact * ":"
|
101
|
+
self.persist(name, :memory, options, &block)
|
86
102
|
end
|
87
103
|
|
88
104
|
end
|
data/lib/scout/resource/path.rb
CHANGED
@@ -1,9 +1,56 @@
|
|
1
1
|
module Path
|
2
|
+
def produce(force = false)
|
3
|
+
return self if ! force && (Open.exist?(self) || @produced)
|
4
|
+
begin
|
5
|
+
if Resource === self.pkgdir
|
6
|
+
self.pkgdir.produce self
|
7
|
+
else
|
8
|
+
false
|
9
|
+
end
|
10
|
+
rescue ResourceNotFound
|
11
|
+
false
|
12
|
+
rescue
|
13
|
+
message = $!.message
|
14
|
+
message = "No exception message" if message.nil? || message.empty?
|
15
|
+
Log.warn "Error producing #{self}: #{message}"
|
16
|
+
false
|
17
|
+
ensure
|
18
|
+
@produced = true
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def produce_with_extension(extension, *args)
|
23
|
+
begin
|
24
|
+
self.produce(*args)
|
25
|
+
rescue Exception
|
26
|
+
exception = $!
|
27
|
+
begin
|
28
|
+
self.set_extension(extension).produce(*args)
|
29
|
+
rescue Exception
|
30
|
+
raise exception
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def produce_and_find(extension = nil, *args)
|
36
|
+
if extension
|
37
|
+
found = find_with_extension(extension, *args)
|
38
|
+
found.exists? ? found : produce_with_extension(extension, *args)
|
39
|
+
else
|
40
|
+
found = find
|
41
|
+
found.exists? ? found : produce(*args)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
2
45
|
def relocate
|
3
46
|
return self if Open.exists?(self)
|
4
47
|
Resource.relocate(self)
|
5
48
|
end
|
6
49
|
|
50
|
+
def identify
|
51
|
+
Resource.identify(self)
|
52
|
+
end
|
53
|
+
|
7
54
|
def open(*args, &block)
|
8
55
|
produce
|
9
56
|
Open.open(self, *args, &block)
|
@@ -17,4 +64,14 @@ module Path
|
|
17
64
|
def write(*args, &block)
|
18
65
|
Open.write(self.find, *args, &block)
|
19
66
|
end
|
67
|
+
|
68
|
+
def list
|
69
|
+
found = produce_and_find('list')
|
70
|
+
Open.list(found)
|
71
|
+
end
|
72
|
+
|
73
|
+
def exists?
|
74
|
+
return true if Open.exists?(self.find)
|
75
|
+
self.produce
|
76
|
+
end
|
20
77
|
end
|
data/lib/scout/resource/util.rb
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
module Resource
|
2
2
|
def identify(path)
|
3
3
|
return path unless path.start_with?("/")
|
4
|
-
path_maps = path.path_maps
|
4
|
+
path_maps = path.path_maps if Path === path
|
5
|
+
path_maps ||= self.path_maps || Path.path_maps
|
5
6
|
path = File.expand_path(path)
|
6
7
|
path += "/" if File.directory?(path)
|
7
8
|
|
8
9
|
map_order ||= (path_maps.keys & Path.basic_map_order) + (path_maps.keys - Path.basic_map_order)
|
9
10
|
map_order -= [:current, "current"]
|
10
|
-
map_order << :current
|
11
11
|
|
12
12
|
choices = []
|
13
13
|
map_order.uniq.each do |name|
|
@@ -16,6 +16,7 @@ module Resource
|
|
16
16
|
next if pattern.nil?
|
17
17
|
|
18
18
|
pattern = pattern.sub('{PWD}', Dir.pwd)
|
19
|
+
pattern = pattern.sub('{HOME}', ENV["HOME"])
|
19
20
|
if String === pattern and pattern.include?('{')
|
20
21
|
regexp = "^" + pattern
|
21
22
|
.gsub(/{(TOPLEVEL)}/,'(?<\1>[^/]+)')
|
@@ -34,14 +35,20 @@ module Resource
|
|
34
35
|
end
|
35
36
|
end
|
36
37
|
|
37
|
-
|
38
|
+
identified = choices.sort_by{|s| s.length }.first
|
39
|
+
|
40
|
+
Path.setup(identified || path, self, nil, path_maps)
|
38
41
|
end
|
39
42
|
|
40
|
-
def self.
|
41
|
-
return path if Open.exists?(path)
|
43
|
+
def self.identify(path)
|
42
44
|
resource = path.pkgdir if Path === path
|
43
45
|
resource = Scout unless Resource === resource
|
44
46
|
unlocated = resource.identify path
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.relocate(path)
|
50
|
+
return path if Open.exists?(path)
|
51
|
+
unlocated = identify(path)
|
45
52
|
unlocated.find
|
46
53
|
end
|
47
54
|
end
|
data/lib/scout/tmpfile.rb
CHANGED
@@ -93,19 +93,18 @@ module TmpFile
|
|
93
93
|
end
|
94
94
|
end
|
95
95
|
|
96
|
+
SLASH_REPLACE = '·'
|
96
97
|
def self.tmp_for_file(file, tmp_options = {}, other_options = {})
|
97
|
-
tmp_for_file = IndiferentHash.process_options tmp_options, :file
|
98
|
+
tmp_for_file, prefix, key, persistence_dir = IndiferentHash.process_options tmp_options, :file, :prefix, :key, :dir
|
98
99
|
return tmp_for_file unless tmp_for_file.nil?
|
99
100
|
|
100
|
-
prefix = IndiferentHash.process_options tmp_options, :prefix
|
101
|
-
|
102
101
|
if prefix.nil?
|
103
|
-
perfile = file.to_s.
|
102
|
+
perfile = file.to_s.sub(/\.b?gz$/,'')
|
104
103
|
else
|
105
|
-
perfile = prefix.to_s + ":" + file.to_s.
|
104
|
+
perfile = prefix.to_s + ":" + file.to_s.sub(/\.b?gz$/,'')
|
106
105
|
end
|
107
106
|
|
108
|
-
perfile
|
107
|
+
perfile += "[#{ key }]" if key
|
109
108
|
|
110
109
|
if other_options.include? :filters
|
111
110
|
other_options[:filters].each do |match,value|
|
@@ -113,10 +112,10 @@ module TmpFile
|
|
113
112
|
end
|
114
113
|
end
|
115
114
|
|
116
|
-
persistence_dir =
|
115
|
+
persistence_dir = TmpFile.tmpdir if persistence_dir.nil?
|
117
116
|
Path.setup(persistence_dir) unless Path === persistence_dir
|
118
117
|
|
119
|
-
filename = perfile.gsub(/\s/,'_').gsub(
|
118
|
+
filename = perfile.gsub(/\s/,'_').gsub('/', SLASH_REPLACE)
|
120
119
|
clean_options = other_options.dup
|
121
120
|
clean_options.delete :unnamed
|
122
121
|
clean_options.delete "unnamed"
|
@@ -0,0 +1,177 @@
|
|
1
|
+
module TSV
|
2
|
+
|
3
|
+
def self.match_keys(source, other, match_key: nil, other_key: nil)
|
4
|
+
match_key = (source.all_fields & other.all_fields).first if match_key.nil?
|
5
|
+
|
6
|
+
if match_key.nil?
|
7
|
+
source.all_fields.collect do |f|
|
8
|
+
other_key = other.identify_field(f)
|
9
|
+
if other_key
|
10
|
+
other_key = other.key_field if other_key == :key
|
11
|
+
match_key = f
|
12
|
+
break
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
if match_key.nil?
|
18
|
+
other.all_fields.collect do |f|
|
19
|
+
match_key = source.identify_field(f)
|
20
|
+
if match_key
|
21
|
+
other_key = f
|
22
|
+
break
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
match_key = source.key_field if match_key.nil?
|
28
|
+
|
29
|
+
if other_key.nil?
|
30
|
+
other_key = other.identify_field(match_key)
|
31
|
+
end
|
32
|
+
|
33
|
+
other_key = other.key_field if other_key.nil?
|
34
|
+
|
35
|
+
match_key = :key if match_key == source.key_field
|
36
|
+
other_key = :key if other_key == other.key_field
|
37
|
+
|
38
|
+
[match_key, other_key]
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.attach(source, other, target: nil, fields: nil, match_key: nil, other_key: nil, one2one: true, complete: false, insitu: nil, persist_input: false, bar: nil)
|
42
|
+
source = TSV::Transformer.new source unless TSV === source || TSV::Parser === source
|
43
|
+
other = TSV.open other, persist: persist_input unless TSV === other
|
44
|
+
|
45
|
+
fields = [fields] if String === fields
|
46
|
+
|
47
|
+
match_key, other_key = TSV.match_keys(source, other, match_key: match_key, other_key: other_key)
|
48
|
+
|
49
|
+
if TSV::Transformer === source
|
50
|
+
source.dumper = case target
|
51
|
+
when :stream
|
52
|
+
TSV::Dumper.new(source.options.merge(sep: "\t"))
|
53
|
+
when nil
|
54
|
+
TSV.setup({}, **source.options.dup)
|
55
|
+
else
|
56
|
+
target
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
other.with_unnamed do
|
61
|
+
source.with_unnamed do
|
62
|
+
|
63
|
+
other_key_name = other_key == :key ? other.key_field : other_key
|
64
|
+
other_key_name = other.fields[other_key_name] if Integer === other_key
|
65
|
+
fields = other.all_fields - [other_key_name, source.key_field] if fields.nil?
|
66
|
+
|
67
|
+
if other_key != :key
|
68
|
+
other = other.reorder other_key, fields, one2one: one2one
|
69
|
+
end
|
70
|
+
|
71
|
+
other_field_positions = other.identify_field(fields)
|
72
|
+
|
73
|
+
log_message = "Attach #{Log.fingerprint fields - source.fields} to #{Log.fingerprint source} (#{[match_key, other_key] * "=~"})"
|
74
|
+
Log.debug log_message
|
75
|
+
bar = log_message if TrueClass === bar
|
76
|
+
|
77
|
+
new = fields - source.fields
|
78
|
+
|
79
|
+
source.fields = (source.fields + fields).uniq
|
80
|
+
|
81
|
+
overlaps = source.identify_field(fields)
|
82
|
+
|
83
|
+
empty_other_values = case source.type
|
84
|
+
when :list
|
85
|
+
[nil] * other.fields.length
|
86
|
+
when :flat
|
87
|
+
[]
|
88
|
+
when :double
|
89
|
+
[[]] * other.fields.length
|
90
|
+
end
|
91
|
+
|
92
|
+
insitu = TSV === source ? true : false if insitu.nil?
|
93
|
+
|
94
|
+
match_key_pos = source.identify_field(match_key)
|
95
|
+
source.traverse bar: bar, unnamed: true do |orig_key,current_values|
|
96
|
+
keys = (match_key == :key || match_key_pos == :key) ? [orig_key] : current_values[match_key_pos]
|
97
|
+
keys = [keys] unless Array === keys
|
98
|
+
|
99
|
+
current_values = current_values.dup unless insitu
|
100
|
+
keys.each do |current_key|
|
101
|
+
other_values = other[current_key]
|
102
|
+
|
103
|
+
if other_values.nil?
|
104
|
+
other_values = empty_other_values
|
105
|
+
elsif other.type == :flat
|
106
|
+
other_values = [other_values]
|
107
|
+
elsif other.type == :list && source.type == :double
|
108
|
+
other_values = other_values.collect{|v| [v] }
|
109
|
+
elsif other.type == :double && source.type == :list
|
110
|
+
other_values = other_values.collect{|v| v.first }
|
111
|
+
end
|
112
|
+
|
113
|
+
other_values = other_values.values_at *other_field_positions
|
114
|
+
|
115
|
+
other_values.zip(overlaps).each do |v,overlap|
|
116
|
+
if source.type == :list
|
117
|
+
current_values[overlap] = v if current_values[overlap].nil? || String === current_values[overlap] && current_values[overlap].empty?
|
118
|
+
else
|
119
|
+
current_values[overlap] ||= []
|
120
|
+
current_values[overlap].concat (v - current_values[overlap])
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
source[orig_key] = current_values unless insitu
|
125
|
+
nil
|
126
|
+
end
|
127
|
+
|
128
|
+
if complete && match_key == :key
|
129
|
+
empty_self_values = case source.type
|
130
|
+
when :list
|
131
|
+
[nil] * source.fields.length
|
132
|
+
when :flat
|
133
|
+
[]
|
134
|
+
when :double
|
135
|
+
[[]] * source.fields.length
|
136
|
+
end
|
137
|
+
other.each do |other_key,other_values|
|
138
|
+
next if source.include?(other_key)
|
139
|
+
if other.type == :flat
|
140
|
+
other_values = [other_values]
|
141
|
+
elsif other.type == :list && source.type == :double
|
142
|
+
other_values = other_values.collect{|v| [v] }
|
143
|
+
elsif other.type == :double && source.type == :list
|
144
|
+
other_values = other_values.collect{|v| v.first }
|
145
|
+
end
|
146
|
+
|
147
|
+
new_values = case source.type
|
148
|
+
when :list
|
149
|
+
[nil] * source.fields.length
|
150
|
+
when :flat
|
151
|
+
[]
|
152
|
+
when :double
|
153
|
+
source.fields.length.times.collect{ [] }
|
154
|
+
end
|
155
|
+
|
156
|
+
other_values.zip(overlaps).each do |v,overlap|
|
157
|
+
if false && overlap == :key
|
158
|
+
other_key = Array === v ? v : v.first
|
159
|
+
elsif source.type == :list
|
160
|
+
new_values[overlap] = v if v[overlap].nil? || String === v[overlap] && v[overlap].empty?
|
161
|
+
else
|
162
|
+
new_values[overlap].concat v
|
163
|
+
end
|
164
|
+
end
|
165
|
+
source[other_key] = new_values
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
source
|
172
|
+
end
|
173
|
+
|
174
|
+
def attach(*args, **kwargs)
|
175
|
+
TSV.attach(self, *args, **kwargs)
|
176
|
+
end
|
177
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module TSV
|
2
|
+
def self.change_key(source, new_key_field, identifiers: nil, one2one: false, stream: false, keep: false, persist_identifiers: nil)
|
3
|
+
source = TSV::Parser.new source if String === source
|
4
|
+
if identifiers && source.identify_field(new_key_field, strict: true).nil?
|
5
|
+
identifiers = identifiers.nil? ? source.identifiers : identifiers
|
6
|
+
new = source.attach(identifiers, fields: [new_key_field], insitu: false, one2one: true, persist_input: persist_identifiers)
|
7
|
+
new = new.change_key(new_key_field, keep: keep, stream: stream, one2one: one2one)
|
8
|
+
return new
|
9
|
+
end
|
10
|
+
|
11
|
+
fields = source.fields.dup - [new_key_field]
|
12
|
+
fields.unshift source.key_field if keep
|
13
|
+
transformer = TSV::Transformer.new source
|
14
|
+
transformer.key_field = new_key_field
|
15
|
+
transformer.fields = fields
|
16
|
+
transformer.traverse key_field: new_key_field, fields: fields, one2one: one2one, unnamed: true do |k,v|
|
17
|
+
[k, v]
|
18
|
+
end
|
19
|
+
|
20
|
+
stream ? transformer : transformer.tsv
|
21
|
+
end
|
22
|
+
|
23
|
+
def change_key(*args, **kwargs)
|
24
|
+
TSV.change_key(self, *args, **kwargs)
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.change_id(source, source_id, new_id, identifiers: nil, one2one: false, insitu: false)
|
28
|
+
source = TSV::Parser.new source if String === source
|
29
|
+
|
30
|
+
identifiers = identifiers.nil? ? source.identifiers : identifiers
|
31
|
+
|
32
|
+
new_fields = source.fields.dup
|
33
|
+
new_fields[new_fields.index(source_id)] = new_id
|
34
|
+
return source.attach(identifiers, fields: [new_id], insitu: insitu).slice(new_fields)
|
35
|
+
end
|
36
|
+
|
37
|
+
def change_id(*args, **kwargs)
|
38
|
+
TSV.change_id(self, *args, **kwargs)
|
39
|
+
end
|
40
|
+
end
|