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.
Files changed (45) hide show
  1. checksums.yaml +8 -8
  2. data/bin/rbbt_monitor.rb +8 -4
  3. data/lib/rbbt.rb +4 -11
  4. data/lib/rbbt/annotations.rb +4 -1
  5. data/lib/rbbt/association.rb +218 -157
  6. data/lib/rbbt/association/index.rb +92 -0
  7. data/lib/rbbt/association/item.rb +44 -0
  8. data/lib/rbbt/entity.rb +4 -0
  9. data/lib/rbbt/fix_width_table.rb +14 -9
  10. data/lib/rbbt/knowledge_base.rb +269 -0
  11. data/lib/rbbt/persist.rb +1 -1
  12. data/lib/rbbt/persist/tsv.rb +22 -2
  13. data/lib/rbbt/resource.rb +0 -1
  14. data/lib/rbbt/resource/path.rb +1 -1
  15. data/lib/rbbt/resource/util.rb +0 -1
  16. data/lib/rbbt/tsv.rb +15 -14
  17. data/lib/rbbt/tsv/accessor.rb +21 -16
  18. data/lib/rbbt/tsv/attach.rb +5 -5
  19. data/lib/rbbt/tsv/attach/util.rb +4 -2
  20. data/lib/rbbt/tsv/change_id.rb +67 -0
  21. data/lib/rbbt/tsv/index.rb +5 -3
  22. data/lib/rbbt/tsv/manipulate.rb +83 -37
  23. data/lib/rbbt/tsv/parser.rb +2 -1
  24. data/lib/rbbt/tsv/util.rb +2 -0
  25. data/lib/rbbt/util/cmd.rb +1 -2
  26. data/lib/rbbt/util/log.rb +42 -38
  27. data/lib/rbbt/util/misc.rb +134 -46
  28. data/lib/rbbt/util/open.rb +3 -17
  29. data/lib/rbbt/util/semaphore.rb +8 -2
  30. data/lib/rbbt/workflow.rb +31 -46
  31. data/lib/rbbt/workflow/accessor.rb +1 -1
  32. data/lib/rbbt/workflow/step.rb +5 -3
  33. data/share/rbbt_commands/workflow/server +1 -0
  34. data/share/rbbt_commands/workflow/task +12 -2
  35. data/test/rbbt/association/test_index.rb +36 -0
  36. data/test/rbbt/test_annotations.rb +5 -4
  37. data/test/rbbt/test_association.rb +40 -13
  38. data/test/rbbt/test_knowledge_base.rb +103 -0
  39. data/test/rbbt/test_workflow.rb +4 -2
  40. data/test/rbbt/tsv/test_change_id.rb +43 -0
  41. data/test/rbbt/tsv/test_index.rb +2 -1
  42. data/test/rbbt/tsv/test_manipulate.rb +51 -0
  43. data/test/rbbt/util/test_misc.rb +21 -1
  44. data/test/test_helper.rb +8 -4
  45. metadata +12 -86
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- MzQxM2Y1NGU5NjBmNjc1M2IzZWExM2ZiNzU0OWIxNzRiZjYyMjdkNg==
4
+ YmMyOGI4NDhhMTM5MzQxYjE2NjdlZWFhMDFlZjdjZmQxMjdkMjU5Nw==
5
5
  data.tar.gz: !binary |-
6
- MjUwNjI3OTFhODA5YTQ0ZThkYzM5OWNiZjFjZGRmMDU1MWY3ZjI0NQ==
6
+ ZTZkNGIyYzI2OWU4MGJiOWFlYmFmMTcwNDViOWNhNDgwNDdjMjM5Zg==
7
7
  !binary "U0hBNTEy":
8
8
  metadata.gz: !binary |-
9
- Y2Y5ZjBmNWVkOTkwZDNiNmI5NTkyMzJkZjZkN2M1OTY1ZmQzZWQyMWY2Zjkz
10
- YTM3MTIyODJkMmRhZjQwNzI3NTIxZjNiNWQzY2Q0ODlmZDg2ODU1OWUxYTkx
11
- YTIyOTRjNzA2MGNhZjNmOGVhYmEwMjJjMzZkYjM0ZDNhM2QwZjc=
9
+ OTBiNzM0NzhiMTk5YzFmMzIzMGU2NDI4YjVkNjM5OWIzZmExZmIyYTllODNj
10
+ MzQwZThkNTg4MzQ2NGQ2OTljZTI4Y2I4MmIxOTdlMjA5M2FlNDQ2ZWU2MjM4
11
+ NThjYTM1OGM4MjAxYTUxZWZlZmM3MGIzNWRiMzlhYjU4ZjA4NDk=
12
12
  data.tar.gz: !binary |-
13
- ZDI1MmRlNzhmZGI5ZDJmZGJiNzMyMTkzNjY1ODUyYjQ2ZTM1ZTZlZTBkOTZk
14
- N2ZlMDAzOGRmOWY3ZmUyZDExZGVlNDVhOTIzMDQzMjhiNTc3MjgzMjA1Y2Rk
15
- YjUxN2MxMjM3NDEyNTk4MGZjYTI2YjlhODZkOTY0YTg0NjFlOTU=
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
- str += " (#{running?(info)? :running : :dead} #{info[:pid]})" if info[:pid]
30
- str += " (children: #{info[:children_pids].collect{|pid| [pid, Misc.pid_exists?(pid) ? "R" : "D"] * ":"} * ", "})" if info.include? :children_pids
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
 
@@ -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.zip(values).each do |name, value|
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
@@ -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.register(database, file, options = {})
12
- self.databases[database.to_s] = [file, options]
13
- end
13
+ def self.add_reciprocal(tsv)
14
14
 
15
- def self.get_database(database)
16
- self.databases[database.to_s]
17
- end
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
- def self.open_database(database, options = {}, persist_options = {})
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.index_database(database, options = {}, persist_options = {})
25
- file, database_options = databases[database.to_s]
26
- index(file, database_options.merge(options), persist_options)
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, fields)
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, final_type = spec
49
+ field_part, final_format = spec
32
50
 
33
- field, type = field_part.split "=~"
51
+ field, format = field_part.split "=~"
34
52
 
35
- [field, type, final_type]
53
+ [field, format, final_format]
36
54
  end
37
55
 
38
- def self.resolve_field(name, fields)
39
- type = Entity.formats[name]
40
- return "Field #{ name } could not be resolved: #{fields}" if type.nil?
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
- def self.add_reciprocal(tsv)
46
- new_tsv = {}
47
- tsv.with_unnamed do
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
- tsv.through do |target,v|
50
- source_values = tsv.type == :double ? Misc.zip_fields(v) : [v]
51
-
52
- source_values.each do |values|
53
- source = values.shift
54
- values.unshift target
55
- current = new_tsv[source] || tsv[source]
56
-
57
- case tsv.type
58
- when :double
59
- new = current ? current.zip(values).collect{|p| p.flatten} : values.collect{|p| [p]}
60
- when :flat
61
- new = current ? (current + values).compact.uniq : values
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
- new_tsv[source] = new
65
- end
66
- end
97
+ tsv.fields = field_headers
98
+ tsv.key_field = source
67
99
 
68
- tsv.merge! new_tsv
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.load_tsv(file, options)
75
- key_field = TSV.parse_header(file, options).key_field
76
- fields = TSV.parse_header(file, options).fields
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
- zipped = options[:zipped]
90
- undirected = options[:undirected]
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
- source, source_header, source_final_type = parse_field_specification source, all_fields
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
- if source and not all_fields.include? source
96
- Log.debug("Resolving source: #{ source }")
97
- source, source_header, source_final_type = resolve_field source, all_fields
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
- if target and not all_fields.include? target
102
- Log.debug("Resolving target: #{ target }")
103
- target, target_header, target_final_type = resolve_field target, all_fields
104
- Log.debug([target, target_header, target_final_type] * ", ")
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
- source_final_type ||= options[:source_type] if options[:source_type]
108
- target_final_type ||= options[:target_type] if options[:target_type]
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
- Log.debug("Loading associations from: #{ file }")
111
- Log.debug("sources: #{[source, source_header, source_final_type] * ", "}")
112
- Log.debug("targets: #{[target, target_header, target_final_type] * ", "}")
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
- tsv = TSV.open(file, options)
154
+ key_field = all_fields.first if all_fields
119
155
  end
120
156
 
121
- if source_header and tsv.key_field != source_header
122
- tsv.key_field = source_header
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
- if source_final_type and tsv.key_field != source_final_type
126
- Log.debug("Changing source type from #{tsv.key_field} to #{source_final_type}")
127
- tsv.with_unnamed do
128
- tsv = TSVWorkflow.job(:change_id, tsv.filename, :tsv => tsv, :format => source_final_type, :organism => tsv.namespace).exec
129
- end
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
- if target_header and tsv.fields.first != target_header
133
- tsv.fields = tsv.fields.collect{|f| f == target ? target_header : f }
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
- if target_final_type and tsv.fields.first != target_final_type and
137
- Entity.formats[tsv.fields.first] and
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
- Log.debug("Changing target type from #{tsv.fields.first} to #{source_final_type}")
141
- save_key_field = tsv.key_field
142
- tsv.key_field = "MASKED"
143
- tsv.with_unnamed do
144
- tsv = TSVWorkflow.job(:swap_id, tsv.filename, :tsv => tsv, :field => tsv.fields.first, :format => target_final_type, :organism => tsv.namespace).exec
145
- end
146
- tsv.key_field = save_key_field
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
- if undirected
150
- tsv = add_reciprocal tsv
151
- end
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
- tsv = TSV === file ? file : Association.open(file, options, persist_options.merge(:persist => false))
252
+ if file
253
+ tsv = TSV === file ? file : Association.open(file, options, persist_options.merge(:persist => false))
185
254
 
186
- key_field = [tsv.key_field, tsv.fields.first.split(":").last, undirected ? "undirected" : nil].compact * "~"
255
+ fields = tsv.fields
256
+ key_field = [tsv.key_field, fields.first.split(":").last, undirected ? "undirected" : nil].compact * "~"
187
257
 
188
- TSV.setup(assocs, :key_field => key_field, :fields => tsv.fields[1..-1], :type => :list, :serializer => :list)
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] = nil
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
- annotations.each do |target, info|
217
- next if target.nil?
218
- key = [source, target] * "~"
219
- assocs[key] = info
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
+