rbbt-util 5.11.4 → 5.11.5
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.
- 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
|