rbbt-util 5.11.4 → 5.11.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/rbbt/tsv/parallel/traverse.rb +2 -1
- data/lib/rbbt/util/cmd.rb +1 -1
- data/lib/rbbt/util/misc.rb +11 -1173
- data/lib/rbbt/util/misc/concurrent_stream.rb +69 -0
- data/lib/rbbt/util/misc/development.rb +95 -0
- data/lib/rbbt/util/misc/exceptions.rb +11 -0
- data/lib/rbbt/util/misc/format.rb +170 -0
- data/lib/rbbt/util/misc/indiferent_hash.rb +56 -0
- data/lib/rbbt/util/misc/inspect.rb +181 -0
- data/lib/rbbt/util/misc/lock.rb +87 -0
- data/lib/rbbt/util/misc/math.rb +32 -0
- data/lib/rbbt/util/misc/objects.rb +0 -0
- data/lib/rbbt/util/misc/omics.rb +183 -0
- data/lib/rbbt/util/misc/pipes.rb +224 -0
- data/lib/rbbt/workflow/accessor.rb +1 -0
- data/lib/rbbt/workflow/step.rb +15 -9
- data/share/rbbt_commands/workflow/task +2 -0
- metadata +13 -2
@@ -0,0 +1,69 @@
|
|
1
|
+
module ConcurrentStream
|
2
|
+
attr_accessor :threads, :pids, :callback, :abort_callback, :filename, :joined
|
3
|
+
|
4
|
+
def joined?
|
5
|
+
@joined
|
6
|
+
end
|
7
|
+
|
8
|
+
def join
|
9
|
+
|
10
|
+
if @threads and @threads.any?
|
11
|
+
@threads.each do |t|
|
12
|
+
t.join unless t == Thread.current
|
13
|
+
end
|
14
|
+
@threads = []
|
15
|
+
end
|
16
|
+
|
17
|
+
if @pids and @pids.any?
|
18
|
+
@pids.each do |pid|
|
19
|
+
begin
|
20
|
+
Process.waitpid(pid, Process::WUNTRACED)
|
21
|
+
raise "Error joining process #{pid} in #{self.inspect}" unless $?.success?
|
22
|
+
rescue Errno::ECHILD
|
23
|
+
end
|
24
|
+
end
|
25
|
+
@pids = []
|
26
|
+
end
|
27
|
+
|
28
|
+
if @callback and not joined?
|
29
|
+
@callback.call
|
30
|
+
@callback = nil
|
31
|
+
end
|
32
|
+
|
33
|
+
@joined = true
|
34
|
+
end
|
35
|
+
|
36
|
+
def abort
|
37
|
+
@threads.each{|t| t.raise Aborted.new unless t = Thread.current } if @threads
|
38
|
+
@threads.each{|t| t.join unless t = Thread.current } if @threads
|
39
|
+
@pids.each{|pid| Process.kill :INT, pid } if @pids
|
40
|
+
@pids.each{|pid| Process.waitpid pid } if @pids
|
41
|
+
@abort_callback.call if @abort_callback
|
42
|
+
@abort_callback = nil
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.setup(stream, options = {}, &block)
|
46
|
+
threads, pids, callback, filename = Misc.process_options options, :threads, :pids, :callback, :filename
|
47
|
+
stream.extend ConcurrentStream unless ConcurrentStream === stream
|
48
|
+
|
49
|
+
stream.threads ||= []
|
50
|
+
stream.pids ||= []
|
51
|
+
stream.threads.concat(Array === threads ? threads : [threads]) unless threads.nil?
|
52
|
+
stream.pids.concat(Array === pids ? pids : [pids]) unless pids.nil? or pids.empty?
|
53
|
+
|
54
|
+
callback = block if block_given?
|
55
|
+
if stream.callback and callback
|
56
|
+
old_callback = stream.callback
|
57
|
+
stream.callback = Proc.new do
|
58
|
+
old_callback.call
|
59
|
+
callback.call
|
60
|
+
end
|
61
|
+
else
|
62
|
+
stream.callback = callback
|
63
|
+
end
|
64
|
+
|
65
|
+
stream.filename = filename unless filename.nil?
|
66
|
+
|
67
|
+
stream
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
module Misc
|
2
|
+
def self.benchmark(repeats = 1, message = nil)
|
3
|
+
require 'benchmark'
|
4
|
+
res = nil
|
5
|
+
begin
|
6
|
+
measure = Benchmark.measure do
|
7
|
+
repeats.times do
|
8
|
+
res = yield
|
9
|
+
end
|
10
|
+
end
|
11
|
+
if message
|
12
|
+
puts "#{message }: #{ repeats } repeats"
|
13
|
+
else
|
14
|
+
puts "Benchmark for #{ repeats } repeats"
|
15
|
+
end
|
16
|
+
puts measure
|
17
|
+
rescue Exception
|
18
|
+
puts "Benchmark aborted"
|
19
|
+
raise $!
|
20
|
+
end
|
21
|
+
res
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.profile_html(options = {})
|
25
|
+
require 'ruby-prof'
|
26
|
+
RubyProf.start
|
27
|
+
begin
|
28
|
+
res = yield
|
29
|
+
rescue Exception
|
30
|
+
puts "Profiling aborted"
|
31
|
+
raise $!
|
32
|
+
ensure
|
33
|
+
result = RubyProf.stop
|
34
|
+
printer = RubyProf::MultiPrinter.new(result)
|
35
|
+
TmpFile.with_file do |dir|
|
36
|
+
FileUtils.mkdir_p dir unless File.exists? dir
|
37
|
+
printer.print(:path => dir, :profile => 'profile')
|
38
|
+
CMD.cmd("firefox -no-remote '#{ dir }'")
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
res
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.profile_graph(options = {})
|
46
|
+
require 'ruby-prof'
|
47
|
+
RubyProf.start
|
48
|
+
begin
|
49
|
+
res = yield
|
50
|
+
rescue Exception
|
51
|
+
puts "Profiling aborted"
|
52
|
+
raise $!
|
53
|
+
ensure
|
54
|
+
result = RubyProf.stop
|
55
|
+
#result.eliminate_methods!([/annotated_array_clean_/])
|
56
|
+
printer = RubyProf::GraphPrinter.new(result)
|
57
|
+
printer.print(STDOUT, options)
|
58
|
+
end
|
59
|
+
|
60
|
+
res
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.profile(options = {})
|
64
|
+
require 'ruby-prof'
|
65
|
+
RubyProf.start
|
66
|
+
begin
|
67
|
+
res = yield
|
68
|
+
rescue Exception
|
69
|
+
puts "Profiling aborted"
|
70
|
+
raise $!
|
71
|
+
ensure
|
72
|
+
result = RubyProf.stop
|
73
|
+
printer = RubyProf::FlatPrinter.new(result)
|
74
|
+
printer.print(STDOUT, options)
|
75
|
+
end
|
76
|
+
|
77
|
+
res
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.memprof
|
81
|
+
require 'memprof'
|
82
|
+
Memprof.start
|
83
|
+
begin
|
84
|
+
res = yield
|
85
|
+
rescue Exception
|
86
|
+
puts "Profiling aborted"
|
87
|
+
raise $!
|
88
|
+
ensure
|
89
|
+
Memprof.stop
|
90
|
+
print Memprof.stats
|
91
|
+
end
|
92
|
+
|
93
|
+
res
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
class ParameterException < Exception; end
|
2
|
+
class FieldNotFoundError < Exception;end
|
3
|
+
class Aborted < Exception; end
|
4
|
+
class TryAgain < Exception; end
|
5
|
+
class ClosedStream < Exception; end
|
6
|
+
class KeepLocked < Exception
|
7
|
+
attr_accessor :payload
|
8
|
+
def initialize(payload)
|
9
|
+
@payload = payload
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,170 @@
|
|
1
|
+
module Misc
|
2
|
+
COLOR_LIST = %w(#BC80BD #CCEBC5 #FFED6F #8DD3C7 #FFFFB3 #BEBADA #FB8072 #80B1D3 #FDB462 #B3DE69 #FCCDE5 #D9D9D9)
|
3
|
+
|
4
|
+
def self.colors_for(list)
|
5
|
+
unused = COLOR_LIST.dup
|
6
|
+
|
7
|
+
used = {}
|
8
|
+
colors = list.collect do |elem|
|
9
|
+
if used.include? elem
|
10
|
+
used[elem]
|
11
|
+
else
|
12
|
+
color = unused.shift
|
13
|
+
used[elem]=color
|
14
|
+
color
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
[colors, used]
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.format_paragraph(text, size = 80, indent = 0, offset = 0)
|
22
|
+
i = 0
|
23
|
+
re = /((?:\n\s*\n\s*)|(?:\n\s*(?=\*)))/
|
24
|
+
text.split(re).collect do |paragraph|
|
25
|
+
i += 1
|
26
|
+
str = if i % 2 == 1
|
27
|
+
words = paragraph.gsub(/\s+/, "\s").split(" ")
|
28
|
+
lines = []
|
29
|
+
line = " "*offset
|
30
|
+
word = words.shift
|
31
|
+
while word
|
32
|
+
word = word[0..size-indent-offset-4] + '...' if word.length >= size - indent - offset
|
33
|
+
while word and Log.uncolor(line).length + Log.uncolor(word).length <= size - indent
|
34
|
+
line << word << " "
|
35
|
+
word = words.shift
|
36
|
+
end
|
37
|
+
lines << ((" " * indent) << line[0..-2])
|
38
|
+
line = ""
|
39
|
+
end
|
40
|
+
(lines * "\n")
|
41
|
+
else
|
42
|
+
paragraph
|
43
|
+
end
|
44
|
+
offset = 0
|
45
|
+
str
|
46
|
+
end*""
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.format_definition_list_item(dt, dd, size = 80, indent = 20, color = :yellow)
|
50
|
+
dd = "" if dd.nil?
|
51
|
+
dt = dt.to_s + ":" unless dd.empty?
|
52
|
+
dt = Log.color color, dt if color
|
53
|
+
len = Log.uncolor(dt).length
|
54
|
+
|
55
|
+
if indent < 0
|
56
|
+
text = format_paragraph(dd, size, indent.abs+1, 0)
|
57
|
+
text = dt << "\n" << text
|
58
|
+
else
|
59
|
+
offset = len - indent
|
60
|
+
offset = 0 if offset < 0
|
61
|
+
text = format_paragraph(dd, size, indent.abs+1, offset)
|
62
|
+
text[0..len-1] = dt
|
63
|
+
end
|
64
|
+
text
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.format_definition_list(defs, size = 80, indent = 20, color = :yellow)
|
68
|
+
entries = []
|
69
|
+
defs.each do |dt,dd|
|
70
|
+
text = format_definition_list_item(dt,dd,size,indent,color)
|
71
|
+
entries << text
|
72
|
+
end
|
73
|
+
entries * "\n\n"
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.camel_case(string)
|
77
|
+
return string if string !~ /_/ && string =~ /[A-Z]+.*/
|
78
|
+
string.split(/_|(\d+)/).map{|e|
|
79
|
+
(e =~ /^[A-Z]{2,}$/ ? e : e.capitalize)
|
80
|
+
}.join
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.camel_case_lower(string)
|
84
|
+
string.split('_').inject([]){ |buffer,e|
|
85
|
+
buffer.push(buffer.empty? ? e.downcase : (e =~ /^[A-Z]{2,}$/ ? e : e.capitalize))
|
86
|
+
}.join
|
87
|
+
end
|
88
|
+
|
89
|
+
def self.snake_case(string)
|
90
|
+
return nil if string.nil?
|
91
|
+
string = string.to_s if Symbol === string
|
92
|
+
string.
|
93
|
+
gsub(/([A-Z]{2,})([A-Z][a-z])/,'\1_\2').
|
94
|
+
gsub(/([a-z])([A-Z])/,'\1_\2').
|
95
|
+
gsub(/\s/,'_').gsub(/[^\w_]/, '').
|
96
|
+
split("_").collect{|p| p.match(/[A-Z]{2,}/) ? p : p.downcase } * "_"
|
97
|
+
end
|
98
|
+
|
99
|
+
# source: https://gist.github.com/ekdevdes/2450285
|
100
|
+
# author: Ethan Kramer (https://github.com/ekdevdes)
|
101
|
+
def self.humanize(value, options = {})
|
102
|
+
if options.empty?
|
103
|
+
options[:format] = :sentence
|
104
|
+
end
|
105
|
+
|
106
|
+
values = []
|
107
|
+
values = value.split('_')
|
108
|
+
values.each_index do |index|
|
109
|
+
# lower case each item in array
|
110
|
+
# Miguel Vazquez edit: Except for acronyms
|
111
|
+
values[index].downcase! unless values[index].match(/[a-zA-Z][A-Z]/)
|
112
|
+
end
|
113
|
+
if options[:format] == :allcaps
|
114
|
+
values.each do |value|
|
115
|
+
value.capitalize!
|
116
|
+
end
|
117
|
+
|
118
|
+
if options.empty?
|
119
|
+
options[:seperator] = " "
|
120
|
+
end
|
121
|
+
|
122
|
+
return values.join " "
|
123
|
+
end
|
124
|
+
|
125
|
+
if options[:format] == :class
|
126
|
+
values.each do |value|
|
127
|
+
value.capitalize!
|
128
|
+
end
|
129
|
+
|
130
|
+
return values.join ""
|
131
|
+
end
|
132
|
+
|
133
|
+
if options[:format] == :sentence
|
134
|
+
values[0].capitalize! unless values[0].match(/[a-zA-Z][A-Z]/)
|
135
|
+
|
136
|
+
return values.join " "
|
137
|
+
end
|
138
|
+
|
139
|
+
if options[:format] == :nocaps
|
140
|
+
return values.join " "
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def self.fixascii(string)
|
145
|
+
if string.respond_to?(:encode)
|
146
|
+
self.fixutf8(string).encode("ASCII-8BIT")
|
147
|
+
else
|
148
|
+
string
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def self.to_utf8(string)
|
153
|
+
string.encode("UTF-16BE", :invalid => :replace, :undef => :replace, :replace => "?").encode('UTF-8')
|
154
|
+
end
|
155
|
+
|
156
|
+
def self.fixutf8(string)
|
157
|
+
return nil if string.nil?
|
158
|
+
return string if (string.respond_to? :valid_encoding? and string.valid_encoding?) or
|
159
|
+
(string.respond_to? :valid_encoding and string.valid_encoding)
|
160
|
+
|
161
|
+
if string.respond_to?(:encode)
|
162
|
+
string.encode("UTF-16BE", :invalid => :replace, :undef => :replace, :replace => "?").encode('UTF-8')
|
163
|
+
else
|
164
|
+
require 'iconv'
|
165
|
+
@@ic ||= Iconv.new('UTF-8//IGNORE', 'UTF-8')
|
166
|
+
@@ic.iconv(string)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module IndiferentHash
|
2
|
+
|
3
|
+
def self.setup(hash)
|
4
|
+
hash.extend IndiferentHash
|
5
|
+
end
|
6
|
+
|
7
|
+
def merge(other)
|
8
|
+
new = self.dup
|
9
|
+
IndiferentHash.setup(new)
|
10
|
+
other.each do |k,value|
|
11
|
+
new.delete k
|
12
|
+
new[k] = value
|
13
|
+
end
|
14
|
+
new
|
15
|
+
end
|
16
|
+
|
17
|
+
def [](key)
|
18
|
+
res = super(key)
|
19
|
+
return res unless res.nil?
|
20
|
+
|
21
|
+
case key
|
22
|
+
when Symbol, Module
|
23
|
+
super(key.to_s)
|
24
|
+
when String
|
25
|
+
super(key.to_sym)
|
26
|
+
else
|
27
|
+
super(key)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def values_at(*key_list)
|
32
|
+
key_list.inject([]){|acc,key| acc << self[key]}
|
33
|
+
end
|
34
|
+
|
35
|
+
def include?(key)
|
36
|
+
case key
|
37
|
+
when Symbol, Module
|
38
|
+
super(key) || super(key.to_s)
|
39
|
+
when String
|
40
|
+
super(key) || super(key.to_sym)
|
41
|
+
else
|
42
|
+
super(key)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def delete(key)
|
47
|
+
case key
|
48
|
+
when Symbol, Module
|
49
|
+
super(key) || super(key.to_s)
|
50
|
+
when String
|
51
|
+
super(key) || super(key.to_sym)
|
52
|
+
else
|
53
|
+
super(key)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,181 @@
|
|
1
|
+
module Misc
|
2
|
+
ARRAY_MAX_LENGTH = 1000
|
3
|
+
STRING_MAX_LENGTH = ARRAY_MAX_LENGTH * 10
|
4
|
+
|
5
|
+
def self.sanitize_filename(filename, length = 254)
|
6
|
+
if filename.length > length
|
7
|
+
if filename =~ /(\..{2,9})$/
|
8
|
+
extension = $1
|
9
|
+
else
|
10
|
+
extension = ''
|
11
|
+
end
|
12
|
+
|
13
|
+
post_fix = "--#{filename.length}@#{length}_#{Misc.digest(filename)[0..4]}" + extension
|
14
|
+
|
15
|
+
filename = filename[0..(length - post_fix.length - 1)] << post_fix
|
16
|
+
else
|
17
|
+
filename
|
18
|
+
end
|
19
|
+
filename
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.fingerprint(obj)
|
23
|
+
case obj
|
24
|
+
when nil
|
25
|
+
"nil"
|
26
|
+
when (defined? Step and Step)
|
27
|
+
obj.path || Misc.fingerprint([obj.task.name, obj.inputs])
|
28
|
+
when TrueClass
|
29
|
+
"true"
|
30
|
+
when FalseClass
|
31
|
+
"false"
|
32
|
+
when Symbol
|
33
|
+
":" << obj.to_s
|
34
|
+
when String
|
35
|
+
if obj.length > 100
|
36
|
+
"'" << obj.slice(0,20) << "<...#{obj.length}...>" << obj.slice(-10,10) << " " << "'"
|
37
|
+
else
|
38
|
+
"'" << obj << "'"
|
39
|
+
end
|
40
|
+
when (defined? AnnotatedArray and AnnotatedArray)
|
41
|
+
"<A: #{fingerprint Annotated.purge(obj)} #{fingerprint obj.info}>"
|
42
|
+
when (defined? TSV and TSV::Parser)
|
43
|
+
"<TSVStream:" + obj.filename + "--" << Misc.fingerprint(obj.options) << ">"
|
44
|
+
when IO
|
45
|
+
"<IO:" + (obj.respond_to?(:filename) ? obj.filename : obj.inspect) + ">"
|
46
|
+
when File
|
47
|
+
"<File:" + obj.path + ">"
|
48
|
+
when Array
|
49
|
+
if (length = obj.length) > 10
|
50
|
+
"[#{length}--" << (obj.values_at(0,1, length / 2, -2, -1).collect{|e| fingerprint(e)} * ",") << "]"
|
51
|
+
else
|
52
|
+
"[" << (obj.collect{|e| fingerprint(e) } * ",") << "]"
|
53
|
+
end
|
54
|
+
when (defined? TSV and TSV)
|
55
|
+
obj.with_unnamed do
|
56
|
+
"TSV:{"<< fingerprint(obj.all_fields|| []).inspect << ";" << fingerprint(obj.keys).inspect << "}"
|
57
|
+
end
|
58
|
+
when Hash
|
59
|
+
if obj.length > 10
|
60
|
+
"H:{"<< fingerprint(obj.keys) << ";" << fingerprint(obj.values) << "}"
|
61
|
+
else
|
62
|
+
new = "{"
|
63
|
+
obj.each do |k,v|
|
64
|
+
new << k.to_s << '=>' << fingerprint(v) << ' '
|
65
|
+
end
|
66
|
+
if new.length > 1
|
67
|
+
new[-1] = "}"
|
68
|
+
else
|
69
|
+
new << '}'
|
70
|
+
end
|
71
|
+
new
|
72
|
+
end
|
73
|
+
else
|
74
|
+
obj.to_s
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
|
79
|
+
def self.remove_long_items(obj)
|
80
|
+
case
|
81
|
+
when IO === obj
|
82
|
+
remove_long_items("IO: " + obj.filename)
|
83
|
+
when obj.respond_to?(:path)
|
84
|
+
remove_long_items("File: " + obj.path)
|
85
|
+
when TSV::Parser === obj
|
86
|
+
remove_long_items("TSV Stream: " + obj.filename + " -- " << Misc.fingerprint(obj.options))
|
87
|
+
when TSV === obj
|
88
|
+
remove_long_items((obj.all_fields || []) + obj.keys.sort)
|
89
|
+
when (Array === obj and obj.length > ARRAY_MAX_LENGTH)
|
90
|
+
remove_long_items(obj[0..ARRAY_MAX_LENGTH-2] << "TRUNCATED at #{ ARRAY_MAX_LENGTH } (#{obj.length})")
|
91
|
+
when (Hash === obj and obj.length > ARRAY_MAX_LENGTH)
|
92
|
+
remove_long_items(obj.collect.compact[0..ARRAY_MAX_LENGTH-2] << ["TRUNCATED", "at #{ ARRAY_MAX_LENGTH } (#{obj.length})"])
|
93
|
+
when (String === obj and obj.length > STRING_MAX_LENGTH)
|
94
|
+
obj[0..STRING_MAX_LENGTH-1] << " TRUNCATED at #{STRING_MAX_LENGTH} (#{obj.length})"
|
95
|
+
when Hash === obj
|
96
|
+
new = {}
|
97
|
+
obj.each do |k,v|
|
98
|
+
new[k] = remove_long_items(v)
|
99
|
+
end
|
100
|
+
new
|
101
|
+
when Array === obj
|
102
|
+
obj.collect do |e| remove_long_items(e) end
|
103
|
+
else
|
104
|
+
obj
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def self.filename?(string)
|
109
|
+
String === string and string.length > 0 and string.length < 250 and File.exists?(string)
|
110
|
+
end
|
111
|
+
|
112
|
+
def self.is_filename?(string)
|
113
|
+
return true if string.respond_to? :exists
|
114
|
+
return true if String === string and string.length < 265 and File.exists? string
|
115
|
+
return false
|
116
|
+
end
|
117
|
+
|
118
|
+
def self.digest(text)
|
119
|
+
Digest::MD5.hexdigest(text)
|
120
|
+
end
|
121
|
+
|
122
|
+
HASH2MD5_MAX_STRING_LENGTH = 1000
|
123
|
+
HASH2MD5_MAX_ARRAY_LENGTH = 100
|
124
|
+
def self.hash2md5(hash)
|
125
|
+
str = ""
|
126
|
+
keys = hash.keys
|
127
|
+
keys = keys.clean_annotations if keys.respond_to? :clean_annotations
|
128
|
+
keys = keys.sort_by{|k| k.to_s}
|
129
|
+
|
130
|
+
if hash.respond_to? :unnamed
|
131
|
+
unnamed = hash.unnamed
|
132
|
+
hash.unnamed = true
|
133
|
+
end
|
134
|
+
keys.each do |k|
|
135
|
+
next if k == :monitor or k == "monitor" or k == :in_situ_persistence or k == "in_situ_persistence"
|
136
|
+
v = hash[k]
|
137
|
+
case
|
138
|
+
when TrueClass === v
|
139
|
+
str << k.to_s << "=>true"
|
140
|
+
when FalseClass === v
|
141
|
+
str << k.to_s << "=>false"
|
142
|
+
when Hash === v
|
143
|
+
str << k.to_s << "=>" << hash2md5(v)
|
144
|
+
when Symbol === v
|
145
|
+
str << k.to_s << "=>" << v.to_s
|
146
|
+
when (String === v and v.length > HASH2MD5_MAX_STRING_LENGTH)
|
147
|
+
str << k.to_s << "=>" << v[0..HASH2MD5_MAX_STRING_LENGTH] << "; #{ v.length }"
|
148
|
+
when String === v
|
149
|
+
str << k.to_s << "=>" << v
|
150
|
+
when (Array === v and v.length > HASH2MD5_MAX_ARRAY_LENGTH)
|
151
|
+
str << k.to_s << "=>[" << v[0..HASH2MD5_MAX_ARRAY_LENGTH] * "," << "; #{ v.length }]"
|
152
|
+
when TSV::Parser === v
|
153
|
+
str << remove_long_items(v)
|
154
|
+
when Array === v
|
155
|
+
str << k.to_s << "=>[" << v * "," << "]"
|
156
|
+
when File === v
|
157
|
+
str << k.to_s << "=>[File:" << v.path << "]"
|
158
|
+
else
|
159
|
+
v_ins = v.inspect
|
160
|
+
|
161
|
+
case
|
162
|
+
when v_ins =~ /:0x0/
|
163
|
+
str << k.to_s << "=>" << v_ins.sub(/:0x[a-f0-9]+@/,'')
|
164
|
+
else
|
165
|
+
str << k.to_s << "=>" << v_ins
|
166
|
+
end
|
167
|
+
|
168
|
+
end
|
169
|
+
|
170
|
+
str << "_" << hash2md5(v.info) if defined? Annotated and Annotated === v
|
171
|
+
end
|
172
|
+
hash.unnamed = unnamed if hash.respond_to? :unnamed
|
173
|
+
|
174
|
+
if str.empty?
|
175
|
+
""
|
176
|
+
else
|
177
|
+
digest(str)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
end
|