rbbt-util 5.4.1 → 5.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|