rbbt-util 5.4.1 → 5.5.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 +8 -8
- data/bin/rbbt_monitor.rb +8 -4
- data/lib/rbbt.rb +4 -11
- data/lib/rbbt/annotations.rb +4 -1
- data/lib/rbbt/association.rb +218 -157
- data/lib/rbbt/association/index.rb +92 -0
- data/lib/rbbt/association/item.rb +44 -0
- data/lib/rbbt/entity.rb +4 -0
- data/lib/rbbt/fix_width_table.rb +14 -9
- data/lib/rbbt/knowledge_base.rb +269 -0
- data/lib/rbbt/persist.rb +1 -1
- data/lib/rbbt/persist/tsv.rb +22 -2
- data/lib/rbbt/resource.rb +0 -1
- data/lib/rbbt/resource/path.rb +1 -1
- data/lib/rbbt/resource/util.rb +0 -1
- data/lib/rbbt/tsv.rb +15 -14
- data/lib/rbbt/tsv/accessor.rb +21 -16
- data/lib/rbbt/tsv/attach.rb +5 -5
- data/lib/rbbt/tsv/attach/util.rb +4 -2
- data/lib/rbbt/tsv/change_id.rb +67 -0
- data/lib/rbbt/tsv/index.rb +5 -3
- data/lib/rbbt/tsv/manipulate.rb +83 -37
- data/lib/rbbt/tsv/parser.rb +2 -1
- data/lib/rbbt/tsv/util.rb +2 -0
- data/lib/rbbt/util/cmd.rb +1 -2
- data/lib/rbbt/util/log.rb +42 -38
- data/lib/rbbt/util/misc.rb +134 -46
- data/lib/rbbt/util/open.rb +3 -17
- data/lib/rbbt/util/semaphore.rb +8 -2
- data/lib/rbbt/workflow.rb +31 -46
- data/lib/rbbt/workflow/accessor.rb +1 -1
- data/lib/rbbt/workflow/step.rb +5 -3
- data/share/rbbt_commands/workflow/server +1 -0
- data/share/rbbt_commands/workflow/task +12 -2
- data/test/rbbt/association/test_index.rb +36 -0
- data/test/rbbt/test_annotations.rb +5 -4
- data/test/rbbt/test_association.rb +40 -13
- data/test/rbbt/test_knowledge_base.rb +103 -0
- data/test/rbbt/test_workflow.rb +4 -2
- data/test/rbbt/tsv/test_change_id.rb +43 -0
- data/test/rbbt/tsv/test_index.rb +2 -1
- data/test/rbbt/tsv/test_manipulate.rb +51 -0
- data/test/rbbt/util/test_misc.rb +21 -1
- data/test/test_helper.rb +8 -4
- metadata +12 -86
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
YmMyOGI4NDhhMTM5MzQxYjE2NjdlZWFhMDFlZjdjZmQxMjdkMjU5Nw==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
ZTZkNGIyYzI2OWU4MGJiOWFlYmFmMTcwNDViOWNhNDgwNDdjMjM5Zg==
|
7
7
|
!binary "U0hBNTEy":
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
OTBiNzM0NzhiMTk5YzFmMzIzMGU2NDI4YjVkNjM5OWIzZmExZmIyYTllODNj
|
10
|
+
MzQwZThkNTg4MzQ2NGQ2OTljZTI4Y2I4MmIxOTdlMjA5M2FlNDQ2ZWU2MjM4
|
11
|
+
NThjYTM1OGM4MjAxYTUxZWZlZmM3MGIzNWRiMzlhYjU4ZjA4NDk=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
Y2M1NWYyYzA4MWE2N2U3ZDc3ZjEzMWUzNDQ4ZjAxNjc4Y2FkMTAzOTIyNmY5
|
14
|
+
NDZmZDNlNDExYTc2MzBlOWE1NDcxNjU5YWE2ZmMzODU5ODdjZGE1YzNjZTVi
|
15
|
+
ZDE4Mjk0YWFkMGEzZDM0MTUzMmI2YjljZWMxNTZlNTE1MGMzZGM=
|
data/bin/rbbt_monitor.rb
CHANGED
@@ -26,8 +26,10 @@ def print_job(file, info, severity_color = nil)
|
|
26
26
|
else
|
27
27
|
info ||= {:status => :missing_info_file}
|
28
28
|
str = [clean_file, info[:status].to_s] * " [ STATUS = " + " ]"
|
29
|
-
|
30
|
-
|
29
|
+
if info[:status] != :error and info[:status] != :aborted
|
30
|
+
str += " (#{running?(info)? :running : :dead} #{info[:pid]})" if info[:pid]
|
31
|
+
str += " (children: #{info[:children_pids].collect{|pid| [pid, Misc.pid_exists?(pid) ? "R" : "D"] * ":"} * ", "})" if info.include? :children_pids
|
32
|
+
end
|
31
33
|
|
32
34
|
str = "#{severity_color}" << str << "\033[0m" if severity_color
|
33
35
|
puts str
|
@@ -52,8 +54,10 @@ def list_jobs(options)
|
|
52
54
|
color = case
|
53
55
|
when (not info)
|
54
56
|
Log::SEVERITY_COLOR[3]
|
55
|
-
when info[:status] == :error
|
57
|
+
when info[:status] == :error
|
56
58
|
Log::SEVERITY_COLOR[3]
|
59
|
+
when info[:status] == :aborted
|
60
|
+
Log::SEVERITY_COLOR[2]
|
57
61
|
when (info[:pid] and not running? info)
|
58
62
|
Log::SEVERITY_COLOR[2]
|
59
63
|
end
|
@@ -93,7 +97,7 @@ def clean_jobs(options)
|
|
93
97
|
case
|
94
98
|
when options[:all]
|
95
99
|
remove_job file
|
96
|
-
when (options[:errors] and (not info or info[:status] == :error))
|
100
|
+
when (options[:errors] and (not info or info[:status] == :error or info[:status] == :aborted))
|
97
101
|
remove_job file
|
98
102
|
when (options[:zombies] and info[:pid] and not running? info)
|
99
103
|
remove_job file
|
data/lib/rbbt.rb
CHANGED
@@ -1,19 +1,12 @@
|
|
1
1
|
require 'rbbt/resource'
|
2
|
-
require 'rbbt/util/open'
|
3
|
-
require 'rbbt/util/cmd'
|
4
|
-
require 'rbbt/util/tmpfile'
|
5
|
-
require 'rbbt/util/filecache'
|
6
|
-
require 'rbbt/tsv'
|
7
|
-
require 'rbbt/persist'
|
8
|
-
require 'rbbt/util/misc'
|
9
2
|
|
10
3
|
module Rbbt
|
11
4
|
extend Resource
|
12
5
|
pkgdir = 'rbbt'
|
13
6
|
end
|
14
7
|
|
15
|
-
Open.cachedir = Rbbt.var.cache["open-remote"].find :user
|
16
|
-
TmpFile.tmpdir = Rbbt.tmp.find :user
|
17
|
-
FileCache.cachedir = Rbbt.var.cache.filecache.find :user
|
18
|
-
Persist.cachedir = Rbbt.var.cache.persistence.find :user
|
8
|
+
#Open.cachedir = Rbbt.var.cache["open-remote"].find :user
|
9
|
+
#TmpFile.tmpdir = Rbbt.tmp.find :user
|
10
|
+
#FileCache.cachedir = Rbbt.var.cache.filecache.find :user
|
11
|
+
#Persist.cachedir = Rbbt.var.cache.persistence.find :user
|
19
12
|
|
data/lib/rbbt/annotations.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'rbbt/tsv'
|
1
2
|
require 'rbbt/util/misc'
|
2
3
|
require 'rbbt/annotations/annotated_array'
|
3
4
|
require 'rbbt/annotations/util'
|
@@ -253,13 +254,15 @@ module Annotation
|
|
253
254
|
annotation_values = annotation_values.nil? ? {} : annotation_values.dup
|
254
255
|
annotation_values.instance_variable_set(:@annotation_md5, nil)
|
255
256
|
|
256
|
-
annotations.
|
257
|
+
annotations.each_with_index do |name,i|
|
258
|
+
value = values[i]
|
257
259
|
|
258
260
|
value = value.split("|") if String === value and value.index "|"
|
259
261
|
|
260
262
|
annotation_values[name] = value
|
261
263
|
end
|
262
264
|
|
265
|
+
|
263
266
|
object.instance_variable_set(:@annotation_values, annotation_values)
|
264
267
|
|
265
268
|
object.reset
|
data/lib/rbbt/association.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
require 'rbbt-util'
|
2
|
+
require 'rbbt/tsv/change_id'
|
3
|
+
require 'rbbt/association/index'
|
2
4
|
|
3
5
|
module Association
|
4
6
|
class << self
|
@@ -8,147 +10,206 @@ module Association
|
|
8
10
|
end
|
9
11
|
end
|
10
12
|
|
11
|
-
def self.
|
12
|
-
self.databases[database.to_s] = [file, options]
|
13
|
-
end
|
13
|
+
def self.add_reciprocal(tsv)
|
14
14
|
|
15
|
-
|
16
|
-
|
17
|
-
|
15
|
+
new = {}
|
16
|
+
tsv.with_unnamed do
|
17
|
+
tsv.through do |key, values|
|
18
|
+
new[key] ||= values
|
19
|
+
Misc.zip_fields(values).each do |fields|
|
20
|
+
target, *rest = fields
|
21
|
+
|
22
|
+
target_values = new[target] || tsv[target] || [[]] * values.length
|
23
|
+
zipped_target_values = Misc.zip_fields(target_values)
|
24
|
+
|
25
|
+
zipped_target_values << ([key].concat rest)
|
26
|
+
|
27
|
+
new_values = Misc.zip_fields zipped_target_values
|
28
|
+
|
29
|
+
new[target] = new_values
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
tsv.annotate(new)
|
18
35
|
|
19
|
-
|
20
|
-
file, database_options = get_database database
|
21
|
-
open(file, database_options.merge(options), persist_options)
|
36
|
+
new
|
22
37
|
end
|
23
38
|
|
24
|
-
def self.
|
25
|
-
|
26
|
-
|
39
|
+
def self.resolve_field(name, fields)
|
40
|
+
entity_type = Entity.formats[name]
|
41
|
+
return "Field #{ name } could not be resolved: #{fields}" if entity_type.nil?
|
42
|
+
field = fields.select{|f| Entity.formats[f] == entity_type}.first
|
43
|
+
[field, nil, name]
|
27
44
|
end
|
28
45
|
|
29
|
-
def self.parse_field_specification(spec
|
46
|
+
def self.parse_field_specification(spec)
|
47
|
+
return [2,nil,nil] if Fixnum === spec
|
30
48
|
spec = spec.split "=>" unless Array === spec
|
31
|
-
field_part,
|
49
|
+
field_part, final_format = spec
|
32
50
|
|
33
|
-
field,
|
51
|
+
field, format = field_part.split "=~"
|
34
52
|
|
35
|
-
[field,
|
53
|
+
[field, format, final_format]
|
36
54
|
end
|
37
55
|
|
38
|
-
def self.
|
39
|
-
|
40
|
-
|
41
|
-
field = fields.select{|f| Entity.formats[f] == type}.first
|
42
|
-
[field, nil, name]
|
43
|
-
end
|
56
|
+
def self.calculate_headers(key_field, fields, spec)
|
57
|
+
all_fields = [key_field].concat fields if fields and key_field
|
58
|
+
field, header, format = parse_field_specification spec if spec
|
44
59
|
|
45
|
-
|
46
|
-
|
47
|
-
|
60
|
+
if field and key_field == field and not all_fields.include? field
|
61
|
+
field, header, format = resolve_field field, all_fields
|
62
|
+
end
|
48
63
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
64
|
+
[field, header, format]
|
65
|
+
end
|
66
|
+
|
67
|
+
#{{{ Open
|
68
|
+
|
69
|
+
def self.open_tsv(file, source, source_header, target, target_header, all_fields, options)
|
70
|
+
fields = all_fields.dup
|
71
|
+
fields.delete source
|
72
|
+
fields.delete target
|
73
|
+
fields.unshift target
|
74
|
+
|
75
|
+
open_options = options.merge({
|
76
|
+
:persist => false,
|
77
|
+
:key_field => all_fields.index(source),
|
78
|
+
:fields => fields.collect{|f| String === f ? all_fields.index(f): f },
|
79
|
+
:type => options[:type].to_s == :flat ? :flat : :double,
|
80
|
+
:merge => options[:type].to_s == :flat ? false : true
|
81
|
+
})
|
82
|
+
|
83
|
+
# Preserve first line, which would have been considered a header otherwise
|
84
|
+
open_options["header_hash"] = "#" if options["header_hash"] == ""
|
85
|
+
|
86
|
+
field_headers = all_fields.values_at *open_options[:fields]
|
87
|
+
|
88
|
+
tsv = case file
|
89
|
+
when TSV
|
90
|
+
file.fields == field_headers ?
|
91
|
+
file :
|
92
|
+
file.reorder(source, field_headers)
|
93
|
+
else
|
94
|
+
TSV.open(file, open_options)
|
62
95
|
end
|
63
96
|
|
64
|
-
|
65
|
-
|
66
|
-
end
|
97
|
+
tsv.fields = field_headers
|
98
|
+
tsv.key_field = source
|
67
99
|
|
68
|
-
|
100
|
+
# Fix source header
|
101
|
+
if source_header and tsv.key_field != source_header
|
102
|
+
tsv.key_field = source_header
|
103
|
+
end
|
104
|
+
|
105
|
+
# Fix target header
|
106
|
+
if target_header and tsv.fields.first != target_header
|
107
|
+
tsv.fields = tsv.fields.collect{|f| f == target ? target_header : f }
|
69
108
|
end
|
70
109
|
|
71
110
|
tsv
|
72
111
|
end
|
73
112
|
|
74
|
-
def self.
|
75
|
-
|
76
|
-
|
77
|
-
all_fields = TSV.parse_header(file, options).all_fields
|
78
|
-
|
79
|
-
source = options[:source] || options[:source_type]
|
80
|
-
source = TSV.identify_field key_field, fields, options[:key_field] if source.nil? and options[:key_field]
|
81
|
-
source = all_fields[source] if Fixnum === source
|
82
|
-
source = key_field if source == :key or source.nil?
|
83
|
-
|
84
|
-
target = options[:target]
|
85
|
-
target = TSV.identify_field key_field, fields, options[:fields].first if target.nil? and options[:fields]
|
86
|
-
target = all_fields[target] if Fixnum === target
|
87
|
-
target = key_field if target == :key
|
113
|
+
def self.translate_tsv(tsv, source_final_format, target_final_format)
|
114
|
+
source_field = tsv.key_field
|
115
|
+
target_field = tsv.fields.first
|
88
116
|
|
89
|
-
|
90
|
-
|
117
|
+
if source_final_format and source_field != source_final_format and
|
118
|
+
Entity.formats[source_field] and
|
119
|
+
Entity.formats[source_field] == Entity.formats[source_final_format]
|
91
120
|
|
92
|
-
|
93
|
-
target, target_header, target_final_type = parse_field_specification target, all_fields if target
|
121
|
+
Log.debug("Changing source format from #{tsv.key_field} to #{source_final_format}")
|
94
122
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
Log.debug([source, source_header, source_final_type] * ", ")
|
123
|
+
tsv.with_unnamed do
|
124
|
+
tsv = tsv.change_key source_final_format, :identifiers => Organism.identifiers(tsv.namespace), :persist => true
|
125
|
+
end
|
99
126
|
end
|
100
127
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
128
|
+
# Translate target
|
129
|
+
if target_final_format and target_field != target_final_format and
|
130
|
+
Entity.formats[target_field] and
|
131
|
+
Entity.formats[target_field] == Entity.formats[target_final_format]
|
132
|
+
|
133
|
+
Log.debug("Changing target format from #{tsv.fields.first} to #{target_final_format}")
|
134
|
+
|
135
|
+
save_key_field = tsv.key_field
|
136
|
+
tsv.key_field = "MASKED"
|
137
|
+
|
138
|
+
tsv.with_unnamed do
|
139
|
+
tsv = tsv.swap_id tsv.fields.first, target_final_format, :identifiers => Organism.identifiers(tsv.namespace), :persist => true
|
140
|
+
end
|
141
|
+
|
142
|
+
tsv.key_field = save_key_field
|
105
143
|
end
|
144
|
+
tsv
|
145
|
+
end
|
106
146
|
|
107
|
-
|
108
|
-
|
147
|
+
def self.specs(all_fields, options = {})
|
148
|
+
source_spec, source_format, target_spec, target_format, format, key_field, fields = Misc.process_options options, :source, :source_format, :target, :target_format, :format, :key_field, :fields
|
109
149
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
if source != all_fields.first or (target and target != all_fields[1])
|
114
|
-
fields = ([target] + (all_fields - [source, target])).compact
|
115
|
-
open_options = options.merge({:key_field => source, :fields => fields})
|
116
|
-
tsv = TSV.open(file, open_options)
|
150
|
+
if key_field and all_fields
|
151
|
+
key_pos = (Fixnum === key_field ? key_field : all_fields.index(key_field) )
|
152
|
+
key_field = all_fields[key_pos]
|
117
153
|
else
|
118
|
-
|
154
|
+
key_field = all_fields.first if all_fields
|
119
155
|
end
|
120
156
|
|
121
|
-
if
|
122
|
-
|
157
|
+
if fields and all_fields
|
158
|
+
field_pos = fields.collect{|f| Fixnum === f ? f : fields.index(f) }
|
159
|
+
fields = all_fields.values_at *field_pos
|
160
|
+
else
|
161
|
+
fields = all_fields[1..-1] if all_fields
|
123
162
|
end
|
124
163
|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
end
|
164
|
+
source, source_header, orig_source_format = calculate_headers(key_field, fields, source_spec)
|
165
|
+
source_format ||= orig_source_format
|
166
|
+
source = key_field if source.nil?
|
167
|
+
source = key_field if source == :key
|
168
|
+
source_header ||= source
|
131
169
|
|
132
|
-
|
133
|
-
|
170
|
+
target, target_header, orig_target_format = calculate_headers(key_field, fields, target_spec)
|
171
|
+
target_format ||= orig_target_format
|
172
|
+
target = (([key_field] + fields) - [source]).first if target.nil?
|
173
|
+
target = key_field if target == :key
|
174
|
+
target_header ||= target
|
175
|
+
|
176
|
+
case format
|
177
|
+
when String
|
178
|
+
source_format ||= format if Entity.formats[source_header] == Entity.formats[format]
|
179
|
+
target_format ||= format if Entity.formats[target_header] == Entity.formats[format]
|
180
|
+
when Hash
|
181
|
+
_type = Entity.formats[source_header].to_s
|
182
|
+
source_format ||= format[_type] if format.include? _type
|
183
|
+
_type = Entity.formats[target_header].to_s
|
184
|
+
target_format ||= format[_type] if format.include? _type
|
134
185
|
end
|
135
186
|
|
136
|
-
|
137
|
-
|
138
|
-
Entity.formats[tsv.fields.first] == Entity.formats[target_final_type]
|
187
|
+
[source, source_header, source_format, target, target_header, target_format]
|
188
|
+
end
|
139
189
|
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
190
|
+
def self.load_tsv(file, options)
|
191
|
+
undirected = Misc.process_options options, :undirected
|
192
|
+
|
193
|
+
case file
|
194
|
+
when Proc
|
195
|
+
return load_tsv(file.call, options)
|
196
|
+
when TSV
|
197
|
+
key_field, *fields = all_fields = file.all_fields
|
198
|
+
else
|
199
|
+
key_field, *fields = all_fields = TSV.parse_header(file, options.merge(:fields => nil, :key_field => nil)).all_fields
|
147
200
|
end
|
148
201
|
|
149
|
-
|
150
|
-
|
151
|
-
|
202
|
+
source, source_header, source_format, target, target_header, target_format = specs(all_fields, options)
|
203
|
+
|
204
|
+
Log.info("Loading associations from: #{ Misc.fingerprint file }")
|
205
|
+
Log.info("sources: #{ [source, source_header, source_format].join(", ") }")
|
206
|
+
Log.info("targets: #{ [target, target_header, target_format].join(", ") }")
|
207
|
+
|
208
|
+
tsv = open_tsv(file, source, source_header, target, target_header, all_fields, options)
|
209
|
+
|
210
|
+
tsv = translate_tsv(tsv, source_format, target_format)
|
211
|
+
|
212
|
+
tsv = add_reciprocal(tsv) if undirected
|
152
213
|
|
153
214
|
tsv
|
154
215
|
end
|
@@ -175,82 +236,82 @@ module Association
|
|
175
236
|
end
|
176
237
|
end
|
177
238
|
|
239
|
+
#{{{ Index
|
240
|
+
|
241
|
+
def self.get_index(index_file, write = false)
|
242
|
+
Persist.open_tokyocabinet(index_file, write, :list, TokyoCabinet::BDB).tap{|r| r.unnamed = true; Association::Index.setup r }
|
243
|
+
end
|
244
|
+
|
178
245
|
def self.index(file, options = {}, persist_options = {})
|
179
246
|
options = {} if options.nil?
|
247
|
+
options = Misc.add_defaults options, :persist => true
|
180
248
|
persist_options = {} if persist_options.nil?
|
181
249
|
|
182
250
|
Persist.persist_tsv(file, nil, options, {:persist => true, :prefix => "Association Index"}.merge(persist_options).merge(:engine => TokyoCabinet::BDB, :serializer => :clean)) do |assocs|
|
183
251
|
undirected = options[:undirected]
|
184
|
-
|
252
|
+
if file
|
253
|
+
tsv = TSV === file ? file : Association.open(file, options, persist_options.merge(:persist => false))
|
185
254
|
|
186
|
-
|
255
|
+
fields = tsv.fields
|
256
|
+
key_field = [tsv.key_field, fields.first.split(":").last, undirected ? "undirected" : nil].compact * "~"
|
187
257
|
|
188
|
-
|
258
|
+
TSV.setup(assocs, :key_field => key_field, :fields => fields[1..-1], :type => :list, :serializer => :list)
|
259
|
+
|
260
|
+
tsv.with_unnamed do
|
261
|
+
tsv.with_monitor :desc => "Extracting associations" do
|
262
|
+
case tsv.type
|
263
|
+
when :list
|
264
|
+
tsv.through do |source, values|
|
265
|
+
target, *rest = values
|
266
|
+
next if source.nil? or source.empty? or target.nil? or target.empty?
|
189
267
|
|
190
|
-
tsv.with_unnamed do
|
191
|
-
tsv.with_monitor :desc => "Extracting annotations" do
|
192
|
-
case tsv.type
|
193
|
-
when :flat
|
194
|
-
tsv.through do |source, targets|
|
195
|
-
next if source.nil? or source.empty? or targets.nil? or targets.empty?
|
196
|
-
|
197
|
-
targets.each do |target|
|
198
|
-
next if target.nil? or target.empty?
|
199
268
|
key = [source, target] * "~"
|
200
|
-
assocs[key] =
|
269
|
+
assocs[key] = rest
|
270
|
+
end
|
271
|
+
when :flat
|
272
|
+
tsv.through do |source, targets|
|
273
|
+
next if source.nil? or source.empty? or targets.nil? or targets.empty?
|
274
|
+
|
275
|
+
targets.each do |target|
|
276
|
+
next if target.nil? or target.empty?
|
277
|
+
key = [source, target] * "~"
|
278
|
+
assocs[key] = nil
|
279
|
+
end
|
201
280
|
end
|
202
|
-
end
|
203
|
-
|
204
|
-
when :double
|
205
|
-
tsv.through do |source, values|
|
206
|
-
next if values.empty?
|
207
|
-
next if source.nil?
|
208
|
-
next if values.empty?
|
209
|
-
targets = values.first
|
210
|
-
rest = Misc.zip_fields values[1..-1]
|
211
|
-
|
212
|
-
annotations = rest.length > 1 ?
|
213
|
-
targets.zip(rest) :
|
214
|
-
targets.zip(rest * targets.length)
|
215
281
|
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
282
|
+
when :double
|
283
|
+
tsv.through do |source, values|
|
284
|
+
next if values.empty?
|
285
|
+
next if source.nil?
|
286
|
+
next if values.empty?
|
287
|
+
targets = values.first
|
288
|
+
rest = Misc.zip_fields values[1..-1]
|
289
|
+
|
290
|
+
annotations = rest.length > 1 ?
|
291
|
+
targets.zip(rest) :
|
292
|
+
targets.zip(rest * targets.length)
|
293
|
+
|
294
|
+
annotations.each do |target, info|
|
295
|
+
next if target.nil?
|
296
|
+
key = [source, target] * "~"
|
297
|
+
assocs[key] = info
|
298
|
+
end
|
220
299
|
end
|
300
|
+
else
|
301
|
+
raise "Type not supported: #{tsv.type}"
|
221
302
|
end
|
222
|
-
else
|
223
|
-
raise "Type not supported: #{tsv.type}"
|
224
303
|
end
|
225
304
|
end
|
305
|
+
else
|
306
|
+
key_field, fields = options.values_at :key_field, :fields
|
307
|
+
TSV.setup(assocs, :key_field => key_field, :fields => fields[1..-1], :type => :list, :serializer => :list)
|
226
308
|
end
|
227
309
|
assocs.close
|
228
310
|
|
229
311
|
assocs
|
312
|
+
end.tap do |assocs|
|
313
|
+
Association::Index.setup assocs
|
230
314
|
end
|
231
315
|
end
|
232
|
-
|
233
|
-
def self.connections(repo, entities)
|
234
|
-
source_field, target_field, undirected = repo.key_field.split("~")
|
235
|
-
|
236
|
-
source_type = Entity.formats[source_field].to_s
|
237
|
-
target_type = Entity.formats[target_field].to_s
|
238
|
-
|
239
|
-
source_entities = entities[source_type] || entities[source_field]
|
240
|
-
target_entities = entities[target_type] || entities[target_field]
|
241
|
-
|
242
|
-
return [] if source_entities.nil? or target_entities.nil?
|
243
|
-
|
244
|
-
source_entities.collect do |entity|
|
245
|
-
keys = repo.prefix(entity + "~")
|
246
|
-
keys.collect do |key|
|
247
|
-
source, target = key.split("~")
|
248
|
-
next unless target_entities.include? target
|
249
|
-
next if undirected and target > source
|
250
|
-
info = Hash[*repo.fields.zip(repo[key]).flatten]
|
251
|
-
|
252
|
-
{:source => source, :target => target, :info => info}
|
253
|
-
end.compact
|
254
|
-
end.flatten
|
255
|
-
end
|
256
316
|
end
|
317
|
+
|