rbbt-util 4.2.0 → 4.3.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.
@@ -13,6 +13,7 @@ module Annotated
13
13
  end
14
14
 
15
15
  def annotations
16
+ raise "Annotation types is nil for object: #{self.inspect}" if annotation_types.nil?
16
17
  annotation_types.collect do |mod|
17
18
  mod.annotations
18
19
  end.flatten.uniq
@@ -32,9 +33,11 @@ module Annotated
32
33
  end
33
34
 
34
35
  def self.load(object, info)
35
- annotation_types = info[:annotation_types]
36
+ annotation_types = info[:annotation_types] || []
36
37
  annotation_types = annotation_types.split("+") if String === annotation_types
37
38
 
39
+ return object if annotation_types.nil? or annotation_types.empty?
40
+
38
41
  annotation_types.each do |mod|
39
42
  mod = Misc.string2const(mod) if String === mod
40
43
  mod.setup(object, *info.values_at(*mod.all_annotations))
@@ -59,10 +62,8 @@ module Annotated
59
62
  info.to_json
60
63
  when field == "annotation_types"
61
64
  annotation_types.collect{|t| t.to_s} * "+"
62
- when field == "literal array"
63
- (self * "|").gsub(/\n|\t/, ' ')
64
65
  when field == "literal"
65
- self.gsub(/\n|\t/, ' ')
66
+ (Array === self ? "Array:" << self * "|" : self).gsub(/\n|\t/, ' ')
66
67
  when info.include?(field.to_sym)
67
68
  info.delete(field.to_sym)
68
69
  when self.respond_to?(field)
@@ -78,17 +79,16 @@ module Annotated
78
79
  fields = fields.flatten
79
80
  info = {}
80
81
  literal_pos = fields.index "literal"
81
- literal_array_pos = fields.index "literal array"
82
82
 
83
83
  object = case
84
84
  when literal_pos
85
85
  values[literal_pos]
86
- when literal_array_pos
87
- values[literal_array_pos].split("|").extend AnnotatedArray
88
86
  else
89
87
  id.dup
90
88
  end
91
89
 
90
+ object = object["Array:".length..-1].split("|") if object =~ /^Array:/
91
+
92
92
  if Array === values
93
93
  Misc.zip_fields(values).collect do |list|
94
94
  fields.each_with_index do |field,i|
@@ -120,23 +120,23 @@ module Annotated
120
120
  return nil if annotations.nil?
121
121
  fields = case
122
122
  when ((fields.compact.empty?) and not annotations.empty?)
123
- fields = AnnotatedArray === annotations ? annotations.annotations : annotations.first.annotations
123
+ fields = AnnotatedArray === annotations ? annotations.annotations : annotations.compact.first.annotations
124
124
  fields << :annotation_types
125
125
  when (fields == [:literal] and not annotations.empty?)
126
126
  fields << :literal
127
127
  when (fields == [:all] and not annotations.empty?)
128
- fields = [:annotation_types] + (Annotated === annotations ? annotations.annotations : annotations.first.annotations)
128
+ fields = [:annotation_types] + (Annotated === annotations ? annotations.annotations : annotations.compact.first.annotations)
129
129
  fields << :literal
130
+ when annotations.empty?
131
+ [:annotation_types, :literal]
130
132
  else
131
133
  fields.flatten
132
134
  end
133
135
 
134
136
  fields = fields.collect{|f| f.to_s}
135
137
 
136
- fields = fields.collect{|f| ((f == "literal" and AnnotatedArray === annotations) ? "literal array" : f)}
137
-
138
138
  case
139
- when (Annotated === annotations and not annotations.double_array)
139
+ when (Annotated === annotations and not (AnnotatedArray === annotations and annotations.double_array))
140
140
  tsv = TSV.setup({}, :key_field => "Single", :fields => fields, :type => :list, :unnamed => true)
141
141
  tsv[annotations.id] = annotations.tsv_values(*fields)
142
142
  when Array === annotations
@@ -157,10 +157,11 @@ module Annotated
157
157
  Annotated.load_tsv_values(id, values, tsv.fields)
158
158
  end
159
159
 
160
- if tsv.key_field == "Single"
160
+ case tsv.key_field
161
+ when "Single"
161
162
  annotated_entities.first
162
163
  else
163
- annotated_entities[0].annotate annotated_entities unless annotated_entities.empty?
164
+ annotated_entities
164
165
  end
165
166
  end
166
167
  end
@@ -260,10 +261,11 @@ end
260
261
 
261
262
  module AnnotatedArray
262
263
  extend ChainMethods
264
+
263
265
  self.chain_prefix = :annotated_array
264
266
 
265
267
  def double_array
266
- AnnotatedArray === self.first
268
+ AnnotatedArray === self.annotated_array_clean_get_brackets(0)
267
269
  end
268
270
 
269
271
  def annotated_array_first
@@ -276,6 +278,7 @@ module AnnotatedArray
276
278
 
277
279
  def annotated_array_get_brackets(pos)
278
280
  value = annotated_array_clean_get_brackets(pos)
281
+ value = value.dup if value.frozen?
279
282
  annotation_types.each do |mod|
280
283
  mod.setup(value, *info.values_at(*mod.all_annotations))
281
284
  end
@@ -303,8 +306,14 @@ module AnnotatedArray
303
306
  def annotated_array_collect
304
307
  res = []
305
308
 
306
- annotated_array_each do |value|
307
- res << yield(value)
309
+ if block_given?
310
+ annotated_array_each do |value|
311
+ res << yield(value)
312
+ end
313
+ else
314
+ annotated_array_each do |value|
315
+ res << value
316
+ end
308
317
  end
309
318
 
310
319
  res
@@ -352,11 +361,14 @@ module AnnotatedArray
352
361
 
353
362
  def annotated_array_remove(list)
354
363
  value = (self - list)
364
+
355
365
  annotation_types.each do |mod|
356
366
  mod.setup(value, *info.values_at(*mod.annotations))
357
367
  end
368
+
358
369
  value.context = self.context
359
370
  value.container = self.container
371
+
360
372
  value
361
373
  end
362
374
 
@@ -369,6 +381,7 @@ module AnnotatedArray
369
381
 
370
382
  value.context = self.context
371
383
  value.container = self.container
384
+
372
385
  value
373
386
  end
374
387
 
@@ -381,8 +394,10 @@ module AnnotatedArray
381
394
 
382
395
  value.context = self.context
383
396
  value.container = self.container
397
+
384
398
  value
385
399
  end
400
+
386
401
  def annotated_array_flatten
387
402
  value = self.annotated_array_clean_flatten.dup
388
403
 
@@ -392,27 +407,33 @@ module AnnotatedArray
392
407
 
393
408
  value.context = self.context
394
409
  value.container = self.container
410
+
395
411
  value
396
412
  end
397
413
 
398
414
  def annotated_array_reverse
399
415
  value = self.annotated_array_clean_reverse
416
+
400
417
  annotation_types.each do |mod|
401
418
  mod.setup(value, *info.values_at(*mod.annotations))
402
419
  end
420
+
403
421
  value.context = self.context
404
422
  value.container = self.container
423
+
405
424
  value
406
425
  end
407
426
 
408
-
409
427
  def annotated_array_sort_by(&block)
410
428
  value = self.annotated_array_clean_sort_by &block
429
+
411
430
  annotation_types.each do |mod|
412
431
  mod.setup(value, *info.values_at(*mod.annotations))
413
432
  end
433
+
414
434
  value.context = self.context
415
435
  value.container = self.container
436
+
416
437
  value
417
438
  end
418
439
 
@@ -422,8 +443,10 @@ module AnnotatedArray
422
443
  annotation_types.each do |mod|
423
444
  mod.setup(value, *info.values_at(*mod.annotations))
424
445
  end
446
+
425
447
  value.context = self.context
426
448
  value.container = self.container
449
+
427
450
  value
428
451
  end
429
452
 
@@ -445,4 +468,9 @@ module AnnotatedArray
445
468
 
446
469
  value
447
470
  end
471
+
472
+ def self.annotate(list)
473
+ list[0].annotate list unless AnnotatedArray === list or list[0].nil? or (not list[0].respond_to? :annotate)
474
+ end
475
+
448
476
  end
@@ -155,9 +155,72 @@ module Persist
155
155
  if persist_options[:persist]
156
156
  path = persistence_path(name, persist_options, other_options || {})
157
157
 
158
- case type
159
- when :memory
158
+ case
159
+ when type.to_sym === :memory
160
160
  Persist::MEMORY[path] ||= yield
161
+
162
+ when (type.to_sym == :annotations and persist_options.include? :annotation_repo)
163
+
164
+ repo = persist_options[:annotation_repo]
165
+
166
+ keys = nil
167
+ subkey = name + ":"
168
+
169
+ if String === repo
170
+ repo = Persist.open_tokyocabinet(repo, false, :list, "BDB")
171
+ repo.read_and_close do
172
+ keys = repo.range subkey + 0.chr, true, subkey + 254.chr, true
173
+ end
174
+ repo.close
175
+ else
176
+ repo.read_and_close do
177
+ keys = repo.range subkey + 0.chr, true, subkey + 254.chr, true
178
+ end
179
+ end
180
+
181
+ case
182
+ when (keys.length == 1 and keys.first == subkey + 'NIL')
183
+ nil
184
+ when (keys.length == 1 and keys.first == subkey + 'EMPTY')
185
+ []
186
+ when (keys.length == 1 and keys.first =~ /:SINGLE$/)
187
+ key = keys.first
188
+ values = repo.write_and_close do
189
+ repo[key]
190
+ end
191
+ Annotated.load_tsv_values(key, values, "literal", "annotation_types", "JSON")
192
+ when keys.any?
193
+ repo.read_and_close do
194
+ keys.collect{|key|
195
+ v = repo[key]
196
+ Annotated.load_tsv_values(key, v, "literal", "annotation_types", "JSON")
197
+ }
198
+ end
199
+ else
200
+ entities = yield
201
+
202
+ Misc.lock(repo.persistence_path) do
203
+ repo.write_and_close do
204
+ case
205
+ when entities.nil?
206
+ repo[subkey + "NIL"] = nil
207
+ when entities.empty?
208
+ repo[subkey + "EMPTY"] = nil
209
+ when (not Array === entities or AnnotatedArray === entities)
210
+ tsv_values = entities.tsv_values("literal", "annotation_types", "JSON")
211
+ repo[subkey + entities.id << ":" << "SINGLE"] = tsv_values
212
+ else
213
+ entities.each do |e|
214
+ tsv_values = e.tsv_values("literal", "annotation_types", "JSON")
215
+ repo[subkey + e.id] = tsv_values
216
+ end
217
+ end
218
+ end
219
+ end
220
+
221
+ entities
222
+ end
223
+
161
224
  else
162
225
  Misc.lock(path) do
163
226
  if is_persisted?(path, persist_options)
@@ -167,11 +230,18 @@ module Persist
167
230
  else
168
231
  Log.debug "Persist create: #{ path } - #{persist_options.inspect[0..100]}"
169
232
  end
170
- res = yield
171
- save_file(path, type, res)
172
- res
233
+ begin
234
+ res = yield
235
+ save_file(path, type, res)
236
+ res
237
+ rescue
238
+ Log.high "Error in persist. Erasing '#{ path }'"
239
+ FileUtils.rm path if File.exists? path
240
+ raise $!
241
+ end
173
242
  end
174
243
  end
244
+
175
245
  else
176
246
  yield
177
247
  end
@@ -2,13 +2,18 @@ require 'tokyocabinet'
2
2
 
3
3
  module Persist
4
4
  TC_CONNECTIONS = {}
5
- def self.open_tokyocabinet(path, write, serializer = nil)
5
+
6
+ def self.open_tokyocabinet(path, write, serializer = nil, tokyocabinet_class = TokyoCabinet::HDB)
6
7
  write = true if not File.exists?(path)
7
- flags = (write ? TokyoCabinet::HDB::OWRITER | TokyoCabinet::HDB::OCREAT : TokyoCabinet::HDB::OREADER)
8
+
9
+ tokyocabinet_class = TokyoCabinet::HDB if tokyocabinet_class == "HDB"
10
+ tokyocabinet_class = TokyoCabinet::BDB if tokyocabinet_class == "BDB"
11
+
12
+ flags = (write ? tokyocabinet_class::OWRITER | tokyocabinet_class::OCREAT : tokyocabinet_class::OREADER)
8
13
 
9
14
  FileUtils.mkdir_p File.dirname(path) unless File.exists?(File.dirname(path))
10
15
 
11
- database = TC_CONNECTIONS[path] ||= TokyoCabinet::HDB.new
16
+ database = TC_CONNECTIONS[path] ||= tokyocabinet_class.new
12
17
  database.close
13
18
 
14
19
  if !database.open(path, flags)
@@ -18,7 +23,11 @@ module Persist
18
23
 
19
24
  if not database.respond_to? :old_close
20
25
  class << database
21
- attr_accessor :writable, :closed, :persistence_path
26
+ attr_accessor :writable, :closed, :persistence_path, :tokyocabinet_class
27
+
28
+ def closed?
29
+ @closed
30
+ end
22
31
 
23
32
  alias old_close close
24
33
  def close
@@ -29,7 +38,7 @@ module Persist
29
38
  def read(force = false)
30
39
  return if not write? and not closed and not force
31
40
  self.close
32
- if !self.open(@persistence_path, TokyoCabinet::BDB::OREADER)
41
+ if !self.open(@persistence_path, tokyocabinet_class::OREADER)
33
42
  ecode = self.ecode
34
43
  raise "Open error: #{self.errmsg(ecode)}. Trying to open file #{@persistence_path}"
35
44
  end
@@ -41,10 +50,12 @@ module Persist
41
50
  def write(force = true)
42
51
  return if write? and not closed and not force
43
52
  self.close
44
- if !self.open(@persistence_path, TokyoCabinet::HDB::OWRITER | TokyoCabinet::HDB::OCREAT)
53
+
54
+ if !self.open(@persistence_path, tokyocabinet_class::OWRITER)
45
55
  ecode = self.ecode
46
56
  raise "Open error: #{self.errmsg(ecode)}. Trying to open file #{@persistence_path}"
47
57
  end
58
+
48
59
  @writable = true
49
60
  @closed = false
50
61
  self
@@ -70,6 +81,19 @@ module Persist
70
81
  out(key)
71
82
  end
72
83
 
84
+ def write_and_close
85
+ write if @closed or not write?
86
+ res = yield
87
+ close
88
+ res
89
+ end
90
+
91
+ def read_and_close
92
+ read if @closed or write?
93
+ res = yield
94
+ close
95
+ res
96
+ end
73
97
 
74
98
  def merge!(hash)
75
99
  hash.each do |key,values|
@@ -77,10 +101,19 @@ module Persist
77
101
  end
78
102
  end
79
103
 
104
+ if instance_methods.include? "range"
105
+ alias old_range range
106
+
107
+ def range(*args)
108
+ keys = old_range(*args)
109
+ keys - TSV::ENTRY_KEYS
110
+ end
111
+ end
80
112
  end
81
113
  end
82
114
 
83
115
  database.persistence_path ||= path
116
+ database.tokyocabinet_class = tokyocabinet_class
84
117
 
85
118
  TSV.setup database
86
119
  database.serializer = serializer || database.serializer
@@ -103,7 +136,7 @@ module Persist
103
136
 
104
137
  if is_persisted? path
105
138
  Log.debug "TSV persistence up-to-date: #{ path }"
106
- return open_tokyocabinet(path, false)
139
+ return Misc.lock(path) do open_tokyocabinet(path, false); end
107
140
  else
108
141
  Log.debug "TSV persistence creating: #{ path }"
109
142
  end
@@ -112,13 +145,24 @@ module Persist
112
145
 
113
146
  data = open_tokyocabinet(path, true, persist_options[:serializer])
114
147
  data.serializer = :type unless data.serializer
148
+
149
+ data.close
150
+
115
151
  data
116
152
  else
117
- data = {}
153
+ {}
118
154
  end
119
155
 
120
156
  begin
121
- yield data
157
+ if data.respond_to? :persistence_path and data != persist_options[:data]
158
+ Misc.lock data.persistence_path do
159
+ data.write_and_close do
160
+ yield data
161
+ end
162
+ end
163
+ else
164
+ yield data
165
+ end
122
166
  rescue Exception
123
167
  begin
124
168
  data.close if data.respondo_to? :close
@@ -128,7 +172,7 @@ module Persist
128
172
  raise $!
129
173
  end
130
174
 
131
- data.read if data.respond_to? :read and data.respond_to? :write? and data.write?
175
+ data.read if data.respond_to? :read and ((data.respond_to?(:write?) and data.write?) or (data.respond_to?(:closed?) and data.closed?))
132
176
 
133
177
  data
134
178
  end
@@ -66,33 +66,42 @@ module Resource
66
66
  raise "Resource #{ path } does not seem to be claimed"
67
67
  end
68
68
 
69
- case type
70
- when :string
71
- Open.write(path, content)
72
- when :url
73
- Open.write(path, Open.open(content))
74
- when :proc
75
- data = content.call
76
- Open.write(path, data) unless File.exists? path
77
- when :rake
78
- run_rake(path, content, rake_dir)
79
- when :install
80
- Log.debug "Installing software: #{path}"
81
- software_dir = path.resource.root.software.find :user
82
- preamble = <<-EOF
69
+ if not File.exists? path
70
+ Misc.lock path + '.produce' do
71
+ begin
72
+ case type
73
+ when :string
74
+ Open.write(path, content)
75
+ when :url
76
+ Open.write(path, Open.open(content))
77
+ when :proc
78
+ data = content.call
79
+ Open.write(path, data)
80
+ when :rake
81
+ run_rake(path, content, rake_dir)
82
+ when :install
83
+ Log.debug "Installing software: #{path}"
84
+ software_dir = path.resource.root.software.find :user
85
+ preamble = <<-EOF
83
86
  #!/bin/bash
84
87
 
85
88
  RBBT_SOFTWARE_DIR="#{software_dir}"
86
89
 
87
90
  INSTALL_HELPER_FILE="#{Rbbt.share.install.software.lib.install_helpers.find :lib, caller_lib_dir(__FILE__)}"
88
91
  source "$INSTALL_HELPER_FILE"
89
- EOF
92
+ EOF
90
93
 
91
- CMD.cmd('bash', :in => preamble + "\n" + Open.read(content))
94
+ CMD.cmd('bash', :in => preamble + "\n" + Open.read(content))
92
95
 
93
- set_software_env(software_dir)
94
- else
95
- raise "Could not produce #{ resource }. (#{ type }, #{ content })"
96
+ set_software_env(software_dir)
97
+ else
98
+ raise "Could not produce #{ resource }. (#{ type }, #{ content })"
99
+ end
100
+ rescue
101
+ FileUtils.rm path if File.exists? path
102
+ raise $!
103
+ end
104
+ end
96
105
  end
97
106
 
98
107
  path
@@ -59,9 +59,12 @@ module Path
59
59
  :global => File.join('/', "{TOPLEVEL}", "{PKGDIR}", "{SUBPATH}"),
60
60
  :local => File.join('/usr/local', "{TOPLEVEL}", "{PKGDIR}", "{SUBPATH}"),
61
61
  :lib => File.join('{LIBDIR}', "{TOPLEVEL}", "{SUBPATH}"),
62
+ :default => :user
62
63
  }
63
64
 
64
- def find(where = nil, caller_lib = nil)
65
+ def find(where = nil, caller_lib = nil, search_paths = nil)
66
+ where = search_paths[:default] if where == :default
67
+ search_paths ||= SEARCH_PATHS
65
68
  return self if located?
66
69
  if self.match(/(.*?)\/(.*)/)
67
70
  toplevel, subpath = self.match(/(.*?)\/(.*)/).values_at 1, 2
@@ -71,15 +74,19 @@ module Path
71
74
 
72
75
  path = nil
73
76
  if where.nil?
74
- SEARCH_PATHS.keys.each do |w|
75
- path = find(w, caller_lib)
77
+ search_paths.keys.each do |w|
78
+ path = find(w, caller_lib, search_paths)
76
79
  return path if File.exists? path
77
80
  end
78
- find :user
81
+ if search_paths.include? :default
82
+ find((search_paths[:default] || :user), caller_lib, search_paths)
83
+ else
84
+ raise "Path '#{ path }' not found, and no default specified in search paths: #{search_paths.inspect}"
85
+ end
79
86
  else
80
87
  libdir = where == :lib ? Path.caller_lib_dir(caller_lib) : ""
81
88
  libdir ||= ""
82
- Path.setup SEARCH_PATHS[where].sub('{PKGDIR}', pkgdir).sub('{TOPLEVEL}', toplevel).sub('{SUBPATH}', subpath).sub('{LIBDIR}', libdir), @pkgdir, @resource
89
+ Path.setup search_paths[where].sub('{PKGDIR}', pkgdir).sub('{TOPLEVEL}', toplevel).sub('{SUBPATH}', subpath).sub('{LIBDIR}', libdir), @pkgdir, @resource
83
90
  end
84
91
  end
85
92
 
@@ -306,11 +306,17 @@ if '#{entry}' == 'serializer'
306
306
 
307
307
  define_method :serialized_get do |key|
308
308
  return nil unless self.include? key
309
- self.serializer_module.load(tsv_clean_get_brackets(key))
309
+ res = tsv_clean_get_brackets(key)
310
+ return res if res.nil?
311
+ self.serializer_module.load(res)
310
312
  end
311
313
 
312
314
  define_method :serialized_set do |key, value|
313
- tsv_clean_set_brackets key, self.serializer_module.dump(value)
315
+ if value.nil?
316
+ tsv_clean_set_brackets key, value
317
+ else
318
+ tsv_clean_set_brackets key, self.serializer_module.dump(value)
319
+ end
314
320
  end
315
321
  end
316
322
  end
@@ -258,6 +258,7 @@ module TSV
258
258
  @key_position = key_field
259
259
  when String === key_field
260
260
  @key_position = @fields.dup.unshift(@key_field).index key_field
261
+ raise "Key field #{ key_field } was not found" if @key_position.nil?
261
262
  else
262
263
  raise "Format of key_field not understood: #{key_field.inspect}"
263
264
  end
@@ -87,6 +87,7 @@ module TSV
87
87
  when (field.nil? or field == :key or key_field == field)
88
88
  :key
89
89
  when String === field
90
+ raise "No fields specified in TSV.identify_field" if fields.nil?
90
91
  pos = fields.index field
91
92
  Log.medium "Field #{ field } was not found. Options: #{fields * ", "}" if pos.nil?
92
93
  pos
@@ -54,13 +54,17 @@ module CMD
54
54
  def force_close
55
55
  if @pid
56
56
  Log.debug "Forcing close by killing '#{@pid}'" if log
57
- Process.kill("KILL", @pid)
58
- Process.waitpid(@pid)
57
+ begin
58
+ Process.kill("KILL", @pid)
59
+ Process.waitpid(@pid)
60
+ rescue
61
+ Log.low "Exception in forcing close of command [#{ @pid }, #{cmd}]: #{$!.message}"
62
+ end
59
63
  end
60
64
 
61
65
  @post.call if @post
62
66
 
63
- original_close
67
+ original_close unless self.closed?
64
68
  end
65
69
 
66
70
  def read(*args)
@@ -8,14 +8,23 @@ module Log
8
8
  WARN = 5
9
9
  ERROR = 6
10
10
 
11
- def self.severity=(severity)
12
- @@severity = severity
11
+ class << self
12
+ attr_accessor :logfile, :severity
13
13
  end
14
14
 
15
- def self.severity
16
- @@severity
15
+
16
+ def self.logfile
17
+ @logfile = nil
17
18
  end
18
19
 
20
+ #def self.severity=(severity)
21
+ # @severity = severity
22
+ #end
23
+
24
+ #def self.severity
25
+ # @severity
26
+ #end
27
+
19
28
  SEVERITY_COLOR = ["0;37m", "0;32m", "0;33m", "0;31m", "1;0m" ].collect{|e| "\033[#{e}"}
20
29
 
21
30
  def self.log(message, severity = MEDIUM)
@@ -23,9 +32,11 @@ module Log
23
32
  severity_color = SEVERITY_COLOR[severity]
24
33
  font_color = {false => "\033[0;37m", true => "\033[0m"}[severity >= INFO]
25
34
 
26
- STDERR.puts caller.select{|l| l =~ /rbbt/} * "\n" if @@severity == -1 and not message.empty?
27
- #STDERR.puts "#{Time.now.strftime("[%m/%d/%y-%H:%M:%S]")}[#{severity.to_s}]: " + message if severity >= @@severity
28
- STDERR.puts "\033[0;37m#{Time.now.strftime("[%m/%d/%y-%H:%M:%S]")}#{severity_color}[#{severity.to_s}]\033[0m:#{font_color} " << message.strip << "\033[0m" if severity >= @@severity
35
+ if severity >= self.severity and not message.empty?
36
+ str = "\033[0;37m#{Time.now.strftime("[%m/%d/%y-%H:%M:%S]")}#{severity_color}[#{severity.to_s}]\033[0m:#{font_color} " << message.strip << "\033[0m"
37
+ STDERR.puts str
38
+ logfile.puts str unless logfile.nil?
39
+ end
29
40
  end
30
41
 
31
42
  def self.debug(message)
@@ -59,17 +70,17 @@ module Log
59
70
 
60
71
  case ENV['RBBT_LOG']
61
72
  when 'DEBUG'
62
- @@severity = DEBUG
73
+ self.severity = DEBUG
63
74
  when 'LOW'
64
- @@severity = LOW
75
+ self.severity = LOW
65
76
  when 'MEDIUM'
66
- @@severity = MEDIUM
77
+ self.severity = MEDIUM
67
78
  when 'HIGH'
68
- @@severity = HIGH
79
+ self.severity = HIGH
69
80
  when nil
70
- @@severity = INFO
81
+ self.severity = INFO
71
82
  else
72
- @@severity = ENV['RBBT_LOG'].to_i
83
+ self.severity = ENV['RBBT_LOG'].to_i
73
84
  end
74
85
  end
75
86
 
@@ -3,10 +3,34 @@ require 'rbbt/util/chain_methods'
3
3
  require 'rbbt/resource/path'
4
4
  require 'rbbt/annotations'
5
5
  require 'net/smtp'
6
+ require 'narray'
6
7
 
7
8
  module Misc
8
9
  class FieldNotFoundError < StandardError;end
9
10
 
11
+ ARRAY_MAX_LENGTH = 10000
12
+ STRING_MAX_LENGTH = ARRAY_MAX_LENGTH * 10
13
+ def self.remove_long_items(obj)
14
+ case
15
+ when (Array === obj and obj.length > ARRAY_MAX_LENGTH)
16
+ remove_long_items(obj[0..ARRAY_MAX_LENGTH-2] << "TRUNCATED at #{ ARRAY_MAX_LENGTH } (#{obj.length})")
17
+ when (Hash === obj and obj.length > ARRAY_MAX_LENGTH)
18
+ remove_long_items(obj.compact[0..ARRAY_MAX_LENGTH-2] << ["TRUNCATED", "at #{ ARRAY_MAX_LENGTH } (#{obj.length})"])
19
+ when (String === obj and obj.length > STRING_MAX_LENGTH)
20
+ obj[0..STRING_MAX_LENGTH-1] << " TRUNCATED at #{STRING_MAX_LENGTH} (#{obj.length})"
21
+ when Hash === obj
22
+ new = {}
23
+ obj.each do |k,v|
24
+ new[k] = remove_long_items(v)
25
+ end
26
+ new
27
+ when Array === obj
28
+ obj.collect do |e| remove_long_items(e) end
29
+ else
30
+ obj
31
+ end
32
+ end
33
+
10
34
  def self.ensembl_server(organism)
11
35
  date = organism.split("/")[1]
12
36
  if date.nil?
@@ -192,6 +216,77 @@ end
192
216
  "val" => "V"
193
217
  }
194
218
 
219
+ def self.fast_align(reference, sequence)
220
+ init_gap = -1
221
+ gap = -2
222
+ diff = -2
223
+ same = 2
224
+
225
+ cols = sequence.length + 1
226
+ rows = reference.length + 1
227
+
228
+ a = NArray.int(cols, rows)
229
+
230
+
231
+ for spos in 0..cols-1 do a[spos, 0] = spos * init_gap end
232
+ for rpos in 0..rows-1 do a[0, rpos] = rpos * init_gap end
233
+
234
+ for spos in 1..cols-1 do
235
+ for rpos in 1..rows-1 do
236
+ match = a[spos-1,rpos-1] + (sequence[spos-1] != reference[rpos-1] ? diff : same)
237
+ skip_sequence = a[spos-1,rpos] + gap
238
+ skip_reference = a[spos,rpos-1] + gap
239
+ a[spos,rpos] = [match, skip_sequence, skip_reference].max
240
+ end
241
+ end
242
+
243
+ start = Misc.max(a[-1,0..rows-1])
244
+ start_pos = a[-1,0..rows-1].to_a.index start
245
+
246
+ ref = ''
247
+ seq = ''
248
+ rpos = start_pos
249
+ spos = cols - 1
250
+
251
+ while spos > 0 and rpos > 0
252
+ score = a[spos,rpos]
253
+ score_match = a[spos-1,rpos-1]
254
+ score_skip_reference = a[spos,rpos-1]
255
+ score_skip_sequence = a[spos-1,rpos]
256
+
257
+ case
258
+ when score == score_match + (sequence[spos-1] != reference[rpos-1] ? diff : same)
259
+ ref << reference[rpos-1]
260
+ seq << sequence[spos-1]
261
+ spos -= 1
262
+ rpos -= 1
263
+ when score == score_skip_reference + gap
264
+ ref << reference[rpos-1]
265
+ seq << '-'
266
+ rpos -= 1
267
+ when score == score_skip_sequence + gap
268
+ seq << sequence[spos-1]
269
+ ref << '-'
270
+ spos -= 1
271
+ else
272
+ raise "stop"
273
+ end
274
+ end
275
+
276
+ while (rpos > 0)
277
+ ref << reference[rpos-1]
278
+ seq = seq << '-'
279
+ rpos -= 1
280
+ end
281
+
282
+ while (spos > 0)
283
+ seq << sequence[spos-1]
284
+ ref = ref + '-'
285
+ spos -= 1
286
+ end
287
+
288
+ [ref.reverse + reference[start_pos..-1], seq.reverse + '-' * (rows - start_pos - 1)]
289
+ end
195
290
  def self.IUPAC_to_base(iupac)
196
291
  IUPAC2BASE[iupac]
197
292
  end
@@ -366,10 +461,14 @@ end
366
461
 
367
462
  def self.lock(file, *args)
368
463
  FileUtils.mkdir_p File.dirname(File.expand_path(file)) unless File.exists? File.dirname(File.expand_path(file))
369
- lockfile = Lockfile.new(file + '.lock', :max_age => 1200, :suspend => 240)
370
- lockfile.lock do
371
- yield file, *args
464
+
465
+ res = nil
466
+
467
+ Lockfile.new(file + '.lock') do
468
+ res = yield file, *args
372
469
  end
470
+
471
+ res
373
472
  end
374
473
 
375
474
  def self.common_path(dir, file)
@@ -410,21 +509,23 @@ end
410
509
  end
411
510
 
412
511
  def self.sensiblewrite(path, content)
413
- begin
414
- case
415
- when String === content
416
- File.open(path, 'w') do |f| f.write content end
417
- when (IO === content or StringIO === content)
418
- File.open(path, 'w') do |f| while l = content.gets; f.write l; end end
419
- else
420
- File.open(path, 'w') do |f| end
512
+ Misc.lock path do
513
+ begin
514
+ case
515
+ when String === content
516
+ File.open(path, 'w') do |f| f.write content end
517
+ when (IO === content or StringIO === content)
518
+ File.open(path, 'w') do |f| while l = content.gets; f.write l; end end
519
+ else
520
+ File.open(path, 'w') do |f| end
521
+ end
522
+ rescue Interrupt
523
+ FileUtils.rm_f path
524
+ raise "Interrupted (Ctrl-c)"
525
+ rescue Exception
526
+ FileUtils.rm_f path
527
+ raise $!
421
528
  end
422
- rescue Interrupt
423
- FileUtils.rm_f path
424
- raise "Interrupted (Ctrl-c)"
425
- rescue Exception
426
- FileUtils.rm_f path
427
- raise $!
428
529
  end
429
530
  end
430
531
 
@@ -456,12 +557,18 @@ end
456
557
  next if k == :monitor or k == "monitor" or k == :in_situ_persistence or k == "in_situ_persistence"
457
558
  v = hash[k]
458
559
  case
560
+ when TrueClass === v
561
+ str << k.to_s << "=>true"
562
+ when FalseClass === v
563
+ str << k.to_s << "=>false"
459
564
  when Hash === v
460
565
  str << k.to_s << "=>" << hash2md5(v)
461
566
  when Symbol === v
462
567
  str << k.to_s << "=>" << v.to_s
463
568
  when String === v
464
- str << k.to_s << "=>" << v
569
+ str << k.to_s << "=>" << v[0..10000]
570
+ when Array === v
571
+ str << k.to_s << "=>[" << v[0..1000] * "," << "]"
465
572
  else
466
573
  v_ins = v.inspect
467
574
 
@@ -469,7 +576,7 @@ end
469
576
  when v_ins =~ /:0x0/
470
577
  str << k.to_s << "=>" << v_ins.sub(/:0x[a-f0-9]+@/,'')
471
578
  else
472
- str << k.to_s << "=>" << v.to_s
579
+ str << k.to_s << "=>" << v_ins
473
580
  end
474
581
 
475
582
  end
@@ -104,9 +104,7 @@ module Open
104
104
 
105
105
  def self.add_cache(url, data, options = {})
106
106
  file = File.join(REMOTE_CACHEDIR, digest_url(url, options))
107
- Misc.lock(file) do
108
- Misc.sensiblewrite(file, data)
109
- end
107
+ Misc.sensiblewrite(file, data)
110
108
  end
111
109
 
112
110
  # Grep
@@ -107,7 +107,7 @@ module Workflow
107
107
  end
108
108
  end
109
109
 
110
- raise "Workflow not found: #{ wf_name }"
110
+ raise "Workflow not found our could not be loaded: #{ wf_name }"
111
111
  end
112
112
 
113
113
  def self.require_workflow(wf_name)
@@ -19,7 +19,14 @@ class Step
19
19
 
20
20
  def info
21
21
  return {} if not File.exists? info_file
22
- YAML.load(Open.open(info_file)) || {}
22
+ begin
23
+ File.open(info_file) do |file|
24
+ YAML.load(file) || {}
25
+ end
26
+ rescue
27
+ Log.debug "Error loading yaml: " + Open.read(info_file)
28
+ raise $!
29
+ end
23
30
  end
24
31
 
25
32
  def set_info(key, value)
@@ -44,19 +51,23 @@ class Step
44
51
  end
45
52
 
46
53
  def message(message)
47
- set_info(:messages, messages << message)
54
+ set_info(:messages, (messages || []) << message)
48
55
  end
49
56
 
50
57
  def log(status, message = nil)
51
58
  if message
52
- Log.low "#{ status }: #{ message }"
59
+ Log.low "[#{ status }] #{ message }: #{path}"
53
60
  else
54
- Log.low "#{ status }"
61
+ Log.low "[#{ status }]: #{path}"
55
62
  end
56
63
  self.status = status
57
64
  message(message) unless message.nil?
58
65
  end
59
66
 
67
+ def started?
68
+ File.exists? info_file
69
+ end
70
+
60
71
  def done?
61
72
  status = info[:status]
62
73
  status == :done or status == :error
@@ -4,14 +4,15 @@ require 'rbbt/util/log'
4
4
  require 'rbbt/workflow/accessor'
5
5
 
6
6
  class Step
7
- attr_accessor :path, :task, :inputs, :dependencies
7
+ attr_accessor :path, :task, :inputs, :dependencies, :bindings
8
8
  attr_accessor :pid
9
9
 
10
10
  class Aborted < Exception; end
11
11
 
12
- def initialize(path, task = nil, inputs = nil, dependencies = nil)
12
+ def initialize(path, task = nil, inputs = nil, dependencies = nil, bindings = nil)
13
13
  @path = path
14
14
  @task = task
15
+ @bindings = bindings
15
16
  @dependencies = case
16
17
  when dependencies.nil?
17
18
  []
@@ -30,14 +31,13 @@ class Step
30
31
  end
31
32
 
32
33
  def exec
33
- result = @task.exec_in self, *@inputs
34
+ result = @task.exec_in((bindings ? bindings : self), *@inputs)
34
35
  prepare_result result, @task.result_description
35
36
  end
36
37
 
37
38
  def join
38
39
  if @pid.nil?
39
40
  while not done? do
40
- Log.debug "Waiting: #{info[:step]}"
41
41
  sleep 5
42
42
  end
43
43
  else
@@ -50,12 +50,22 @@ class Step
50
50
 
51
51
  def run(no_load = false)
52
52
  result = Persist.persist "Job", @task.result_type, :file => @path, :check => rec_dependencies.collect{|dependency| dependency.path}.uniq, :no_load => no_load do
53
- log task.name, "Starting task: #{ name }"
53
+ FileUtils.rm info_file if File.exists? info_file
54
+ log((task.name || "unnamed task"), "Starting task")
54
55
  set_info :dependencies, @dependencies.collect{|dep| [dep.task.name, dep.name]}
55
- @dependencies.each{|dependency| dependency.run true}
56
- set_info :status, :start
57
- set_info :inputs, Misc.zip2hash(task.inputs, @inputs) unless task.inputs.nil?
58
- res = exec
56
+ @dependencies.each{|dependency|
57
+ log dependency.task.name || "dependency", "Processing dependency: #{ dependency.path }"
58
+ dependency.run true
59
+ }
60
+ set_info :status, :started
61
+ set_info :inputs, Misc.remove_long_items(Misc.zip2hash(task.inputs, @inputs)) unless task.inputs.nil?
62
+ res = begin
63
+ exec
64
+ rescue Exception
65
+ set_info :backtrace, $!.backtrace
66
+ log(:error, "#{$!.class}: #{$!.message}")
67
+ raise $!
68
+ end
59
69
  set_info :status, :done
60
70
  res
61
71
  end
@@ -66,11 +76,12 @@ class Step
66
76
  def fork
67
77
  raise "Can not fork: Step is waiting for proces #{@pid} to finish" if not @pid.nil?
68
78
  @pid = Process.fork do
79
+ trap(:INT) { raise Step::Aborted.new "INT signal recieved" }
80
+ FileUtils.mkdir_p File.dirname(path) unless File.exists? File.dirname(path)
69
81
  begin
70
- trap(:INT) { raise Step::Aborted.new "INT signal recieved" }
71
82
  run
72
- rescue Exception
73
- log(:error, "#{$!.class}: #{$!.message}")
83
+ rescue
84
+ exit -1
74
85
  end
75
86
  end
76
87
  set_info :pid, @pid
@@ -5,6 +5,18 @@ require 'test/unit'
5
5
 
6
6
  class TestTSV < Test::Unit::TestCase
7
7
 
8
+ def test_find
9
+ name = 'test_name_for_unexistent_file'
10
+ path = Path.setup "tmp/#{ name }"
11
+
12
+ TmpFile.with_file do |dir|
13
+ assert File.join(ENV['HOME'], path), path.find(nil, nil, {:root => File.join(dir, '{TOPLEVEL}/{SUBPATH}'), :default => :user, :user => File.join(ENV['HOME'], "{TOPLEVEL}", "{SUBPATH}")})
14
+ FileUtils.mkdir_p File.dirname(File.join(dir, path))
15
+ FileUtils.touch File.join(dir, path)
16
+ assert File.join(dir, "tmp/test"), path.find(nil, nil, {:root => File.join(dir, '{TOPLEVEL}/{SUBPATH}'), :default => :user, :user => File.join(ENV['HOME'], "{TOPLEVEL}", "{SUBPATH}")})
17
+ end
18
+ end
19
+
8
20
  def test_prev
9
21
  path = Path.setup "/tmp"
10
22
  assert_equal "/tmp/bar/foo", path.foo("bar")
@@ -8,6 +8,7 @@ module TestAnnotation
8
8
 
9
9
  self.annotation :test_annotation
10
10
  end
11
+
11
12
  class TestPersist < Test::Unit::TestCase
12
13
 
13
14
  def test_annotation_persist
@@ -40,6 +41,51 @@ class TestPersist < Test::Unit::TestCase
40
41
  end
41
42
  end
42
43
 
44
+ def test_bdb
45
+ TmpFile.with_file do |tmp|
46
+ repo = Persist.open_tokyocabinet(tmp, true, :double, TokyoCabinet::BDB)
47
+ repo["test:string1"] = [["STR1"]]
48
+ repo["test:string2"] = [["STR2"]]
49
+ repo["other_test:string3"] = [["STR2"]]
50
+
51
+ assert_equal ["test:string1", "test:string2"].sort, repo.range("test:" << 0.chr, false, "test:" << 255.chr, false).sort
52
+ assert_equal ["other_test:string3"].sort, repo.range("other_test:" << 0.chr, false, "other_test:" << 255.chr, false).sort
53
+ end
54
+ end
55
+
56
+ def test_annotation_persist_repo
57
+ TmpFile.with_file do |tmp|
58
+ repo = Persist.open_tokyocabinet(tmp, true, :list, TokyoCabinet::BDB)
59
+
60
+ entity1 = "Entity 1"
61
+ entity2 = "Entity 2"
62
+
63
+ TestAnnotation.setup(entity1, :test_annotation => "1")
64
+ TestAnnotation.setup(entity2, :test_annotation => "2")
65
+
66
+ annotations = [entity1, entity2]
67
+
68
+ persisted_annotations = Persist.persist("Test", :annotations, :annotation_repo => repo) do
69
+ annotations
70
+ end
71
+
72
+ assert_equal "Entity 1", persisted_annotations.first
73
+ assert_equal "Entity 2", persisted_annotations.last
74
+ assert_equal "1", persisted_annotations.first.test_annotation
75
+ assert_equal "2", persisted_annotations.last.test_annotation
76
+
77
+ persisted_annotations = Persist.persist("Test", :annotations, :annotation_repo => repo) do
78
+ annotations
79
+ end
80
+
81
+ assert_equal "Entity 1", persisted_annotations.sort.first
82
+ assert_equal "Entity 2", persisted_annotations.sort.last
83
+ assert_equal "1", persisted_annotations.sort.first.test_annotation
84
+ assert_equal "2", persisted_annotations.sort.last.test_annotation
85
+ end
86
+ end
87
+
88
+
43
89
  def test_array_persist
44
90
  TmpFile.with_file do |tmp|
45
91
  10.times do
@@ -103,7 +103,7 @@ row3 A a|B Id4
103
103
  assert_equal "OtherID", index.fields.first
104
104
 
105
105
  index = tsv.index(:order => true, :persist => false)
106
- assert_equal "Id3", index['a'].first
106
+ assert_equal "Id1", index['a'].first
107
107
  end
108
108
  end
109
109
 
@@ -96,7 +96,6 @@ class TestMisc < Test::Unit::TestCase
96
96
  a = [[1],[2]]
97
97
  a = NamedArray.setup a, %w(1 2)
98
98
  a.merge [3,4]
99
- ddd a
100
99
  assert_equal [1,3], a[0]
101
100
  end
102
101
 
@@ -151,6 +150,18 @@ class TestMisc < Test::Unit::TestCase
151
150
  assert_equal Math.sqrt(2), Misc.sd([1,3])
152
151
  end
153
152
 
153
+ def test_align_small
154
+ reference = "AABCDEBD"
155
+ sequence = "ABCD"
156
+ assert_equal '-ABCD---', Misc.fast_align(reference, sequence).last
157
+ end
158
+
159
+ def test_align_real
160
+ reference = "SGNECNKAIDGNKDTFWHTFYGANGDPKPPPHTYTIDMKTTQNVNGLSMLPRQDGNQNGWIGRHEVYLSSDGTNW"
161
+ sequence = "TYTIDMKTTQNVNGLSML"
162
+ assert_equal "--------------------------------TYTIDMKTTQNVNGLSML-------------------------", Misc.fast_align(reference, sequence).last
163
+ end
164
+
154
165
  # def test_divide
155
166
  # assert_equal 2, Misc.divide(%w(1 2 3 4 5 6 7 8 9),2).length
156
167
  # end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rbbt-util
3
3
  version: !ruby/object:Gem::Version
4
- hash: 55
4
+ hash: 51
5
5
  prerelease:
6
6
  segments:
7
7
  - 4
8
- - 2
8
+ - 3
9
9
  - 0
10
- version: 4.2.0
10
+ version: 4.3.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Miguel Vazquez
@@ -15,8 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2012-01-13 00:00:00 +01:00
19
- default_executable:
18
+ date: 2012-01-31 00:00:00 Z
20
19
  dependencies:
21
20
  - !ruby/object:Gem::Dependency
22
21
  name: rake
@@ -116,6 +115,20 @@ dependencies:
116
115
  version: "0"
117
116
  type: :runtime
118
117
  version_requirements: *id007
118
+ - !ruby/object:Gem::Dependency
119
+ name: narray
120
+ prerelease: false
121
+ requirement: &id008 !ruby/object:Gem::Requirement
122
+ none: false
123
+ requirements:
124
+ - - ">="
125
+ - !ruby/object:Gem::Version
126
+ hash: 3
127
+ segments:
128
+ - 0
129
+ version: "0"
130
+ type: :runtime
131
+ version_requirements: *id008
119
132
  description: Utilities for handling tsv files, caches, etc
120
133
  email: miguel.vazquez@cnio.es
121
134
  executables:
@@ -209,7 +222,6 @@ files:
209
222
  - bin/rbbt_query.rb
210
223
  - bin/rbbt_exec.rb
211
224
  - bin/rbbt_Rutil.rb
212
- has_rdoc: true
213
225
  homepage: http://github.com/mikisvaz/rbbt-util
214
226
  licenses: []
215
227
 
@@ -239,7 +251,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
239
251
  requirements: []
240
252
 
241
253
  rubyforge_project:
242
- rubygems_version: 1.6.2
254
+ rubygems_version: 1.8.12
243
255
  signing_key:
244
256
  specification_version: 3
245
257
  summary: Utilities for the Ruby Bioinformatics Toolkit (rbbt)