rbbt-util 4.2.0 → 4.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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)