scout-gear 1.2.0 → 5.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (101) hide show
  1. checksums.yaml +4 -4
  2. data/.gitmodules +4 -0
  3. data/.vimproject +663 -4
  4. data/Rakefile +1 -0
  5. data/VERSION +1 -1
  6. data/bin/scout +235 -0
  7. data/lib/scout/cmd.rb +344 -0
  8. data/lib/scout/concurrent_stream.rb +259 -0
  9. data/lib/scout/exceptions.rb +15 -8
  10. data/lib/scout/indiferent_hash/options.rb +8 -26
  11. data/lib/scout/indiferent_hash.rb +0 -30
  12. data/lib/scout/log/color.rb +2 -2
  13. data/lib/scout/log/fingerprint.rb +11 -1
  14. data/lib/scout/log/progress/report.rb +0 -1
  15. data/lib/scout/log/progress/util.rb +1 -1
  16. data/lib/scout/log/progress.rb +4 -4
  17. data/lib/scout/log.rb +10 -2
  18. data/lib/scout/meta_extension.rb +15 -1
  19. data/lib/scout/misc/digest.rb +56 -0
  20. data/lib/scout/misc/filesystem.rb +26 -0
  21. data/lib/scout/misc/format.rb +226 -0
  22. data/lib/scout/misc/insist.rb +56 -0
  23. data/lib/scout/misc.rb +5 -11
  24. data/lib/scout/open/lock.rb +61 -0
  25. data/lib/scout/open/remote.rb +120 -0
  26. data/lib/scout/open/stream.rb +372 -0
  27. data/lib/scout/open/util.rb +225 -0
  28. data/lib/scout/open.rb +169 -0
  29. data/lib/scout/path/find.rb +78 -26
  30. data/lib/scout/path/tmpfile.rb +8 -0
  31. data/lib/scout/path/util.rb +17 -5
  32. data/lib/scout/path.rb +13 -31
  33. data/lib/scout/persist/open.rb +17 -0
  34. data/lib/scout/persist/path.rb +15 -0
  35. data/lib/scout/persist/serialize.rb +140 -0
  36. data/lib/scout/persist.rb +54 -0
  37. data/lib/scout/resource/path.rb +15 -0
  38. data/lib/scout/resource/produce/rake.rb +69 -0
  39. data/lib/scout/resource/produce.rb +246 -0
  40. data/lib/scout/resource/scout.rb +3 -0
  41. data/lib/scout/resource.rb +37 -0
  42. data/lib/scout/simple_opt/accessor.rb +54 -0
  43. data/lib/scout/simple_opt/doc.rb +102 -0
  44. data/lib/scout/simple_opt/get.rb +57 -0
  45. data/lib/scout/simple_opt/parse.rb +67 -0
  46. data/lib/scout/simple_opt/setup.rb +26 -0
  47. data/lib/scout/simple_opt.rb +5 -0
  48. data/lib/scout/tmpfile.rb +39 -1
  49. data/lib/scout/workflow/definition.rb +72 -0
  50. data/lib/scout/workflow/documentation.rb +77 -0
  51. data/lib/scout/workflow/step/info.rb +77 -0
  52. data/lib/scout/workflow/step.rb +96 -0
  53. data/lib/scout/workflow/task/inputs.rb +112 -0
  54. data/lib/scout/workflow/task.rb +141 -0
  55. data/lib/scout/workflow/usage.rb +294 -0
  56. data/lib/scout/workflow/util.rb +11 -0
  57. data/lib/scout/workflow.rb +39 -0
  58. data/lib/scout-gear.rb +5 -0
  59. data/lib/scout.rb +1 -0
  60. data/lib/workflow-scout.rb +2 -0
  61. data/scout-gear.gemspec +78 -5
  62. data/scout_commands/alias +48 -0
  63. data/scout_commands/find +83 -0
  64. data/scout_commands/glob +0 -0
  65. data/scout_commands/rbbt +23 -0
  66. data/scout_commands/workflow/task +707 -0
  67. data/test/scout/indiferent_hash/test_case_insensitive.rb +16 -0
  68. data/test/scout/indiferent_hash/test_options.rb +11 -1
  69. data/test/scout/misc/test_digest.rb +30 -0
  70. data/test/scout/misc/test_filesystem.rb +30 -0
  71. data/test/scout/misc/test_insist.rb +13 -0
  72. data/test/scout/open/test_lock.rb +52 -0
  73. data/test/scout/open/test_remote.rb +25 -0
  74. data/test/scout/open/test_stream.rb +515 -0
  75. data/test/scout/open/test_util.rb +73 -0
  76. data/test/scout/path/test_find.rb +37 -1
  77. data/test/scout/persist/test_open.rb +37 -0
  78. data/test/scout/persist/test_path.rb +37 -0
  79. data/test/scout/persist/test_serialize.rb +114 -0
  80. data/test/scout/resource/test_path.rb +40 -0
  81. data/test/scout/resource/test_produce.rb +62 -0
  82. data/test/scout/simple_opt/test_get.rb +11 -0
  83. data/test/scout/simple_opt/test_parse.rb +10 -0
  84. data/test/scout/simple_opt/test_setup.rb +77 -0
  85. data/test/scout/test_cmd.rb +85 -0
  86. data/test/scout/test_concurrent_stream.rb +29 -0
  87. data/test/scout/test_misc.rb +0 -7
  88. data/test/scout/test_open.rb +146 -0
  89. data/test/scout/test_path.rb +3 -1
  90. data/test/scout/test_persist.rb +83 -0
  91. data/test/scout/test_resource.rb +26 -0
  92. data/test/scout/test_workflow.rb +87 -0
  93. data/test/scout/workflow/step/test_info.rb +28 -0
  94. data/test/scout/workflow/task/test_inputs.rb +182 -0
  95. data/test/scout/workflow/test_step.rb +36 -0
  96. data/test/scout/workflow/test_task.rb +178 -0
  97. data/test/scout/workflow/test_usage.rb +26 -0
  98. data/test/scout/workflow/test_util.rb +17 -0
  99. data/test/test_helper.rb +17 -0
  100. data/test/test_scout-gear.rb +0 -0
  101. metadata +76 -3
@@ -0,0 +1,259 @@
1
+ require_relative 'indiferent_hash'
2
+
3
+ module AbortedStream
4
+ attr_accessor :exception
5
+ def self.setup(obj, exception = nil)
6
+ obj.extend AbortedStream
7
+ obj.exception = exception
8
+ end
9
+ end
10
+
11
+ module ConcurrentStream
12
+ attr_accessor :threads, :pids, :callback, :abort_callback, :filename, :joined, :aborted, :autojoin, :lockfile, :no_fail, :pair, :thread, :stream_exception, :log, :std_err
13
+
14
+ def self.setup(stream, options = {}, &block)
15
+
16
+ threads, pids, callback, abort_callback, filename, autojoin, lockfile, no_fail, pair = IndiferentHash.process_options options, :threads, :pids, :callback, :abort_callback, :filename, :autojoin, :lockfile, :no_fail, :pair
17
+ stream.extend ConcurrentStream unless ConcurrentStream === stream
18
+
19
+ stream.threads ||= []
20
+ stream.pids ||= []
21
+ stream.threads.concat(Array === threads ? threads : [threads]) unless threads.nil?
22
+ stream.pids.concat(Array === pids ? pids : [pids]) unless pids.nil? or pids.empty?
23
+ stream.autojoin = autojoin unless autojoin.nil?
24
+ stream.no_fail = no_fail unless no_fail.nil?
25
+ stream.std_err = ""
26
+
27
+ stream.pair = pair unless pair.nil?
28
+
29
+ callback = block if block_given?
30
+ if callback
31
+ if stream.callback
32
+ old_callback = stream.callback
33
+ stream.callback = Proc.new do
34
+ old_callback.call
35
+ callback.call
36
+ end
37
+ else
38
+ stream.callback = callback
39
+ end
40
+ end
41
+
42
+ if abort_callback
43
+ if stream.abort_callback
44
+ old_abort_callback = stream.abort_callback
45
+ stream.abort_callback = Proc.new do
46
+ old_abort_callback.call
47
+ abort_callback.call
48
+ end
49
+ else
50
+ stream.abort_callback = abort_callback
51
+ end
52
+ end
53
+
54
+ stream.filename = filename.nil? ? stream.inspect.split(":").last[0..-2] : filename
55
+
56
+ stream.lockfile = lockfile unless lockfile.nil?
57
+
58
+ stream.aborted = false
59
+
60
+ stream
61
+ end
62
+
63
+ def annotate(stream)
64
+ ConcurrentStream.setup(stream, :threads => threads, :pids => pids, :callback => callback, :abort_callback => abort_callback, :filename => filename, :autojoin => autojoin, :lockfile => lockfile)
65
+ stream
66
+ end
67
+
68
+ def clear
69
+ @threads = @pids = @callback = @abort_callback = @joined = nil
70
+ end
71
+
72
+ def joined?
73
+ @joined
74
+ end
75
+
76
+ def aborted?
77
+ @aborted
78
+ end
79
+
80
+ def join_threads
81
+ if @threads
82
+ @threads.each do |t|
83
+ next if t == Thread.current
84
+ begin
85
+ t.join
86
+ if Process::Status === t.value
87
+ if ! (t.value.success? || no_fail)
88
+
89
+ if log
90
+ msg = "Error joining #{self.filename || self.inspect}. Last log line: #{log}"
91
+ else
92
+ msg = "Error joining #{self.filename || self.inspect}"
93
+ end
94
+
95
+ raise ConcurrentStreamProcessFailed.new t.pid, msg, self
96
+ end
97
+ end
98
+ rescue Exception
99
+ if no_fail
100
+ Log.low "Not failing on exception joining thread in ConcurrenStream - #{filename} - #{$!.message}"
101
+ else
102
+ Log.low "Exception joining thread in ConcurrenStream #{Log.fingerprint self} - #{Log.fingerprint t} - #{$!.message}"
103
+ stream_raise_exception $!
104
+ end
105
+ end
106
+ end
107
+ end
108
+ @threads = []
109
+ end
110
+
111
+ def join_pids
112
+ if @pids and @pids.any?
113
+ @pids.each do |pid|
114
+ begin
115
+ Process.waitpid(pid, Process::WUNTRACED)
116
+ stream_raise_exception ConcurrentStreamProcessFailed.new(pid, "Error in waitpid", self) unless $?.success? or no_fail
117
+ rescue Errno::ECHILD
118
+ end
119
+ end
120
+ @pids = []
121
+ end
122
+ end
123
+
124
+ def join_callback
125
+ if @callback and not joined?
126
+ begin
127
+ @callback.call
128
+ ensure
129
+ @callback = nil
130
+ end
131
+ end
132
+ end
133
+
134
+ def join
135
+ begin
136
+ join_threads
137
+ join_pids
138
+ join_callback
139
+ close unless closed?
140
+ ensure
141
+ @joined = true
142
+ lockfile.unlock if lockfile and lockfile.locked?
143
+ raise stream_exception if stream_exception
144
+ end
145
+ end
146
+
147
+ def abort_threads(exception = nil)
148
+ return unless @threads and @threads.any?
149
+ name = Log.fingerprint(Thread.current)
150
+ name += " - file:#{filename}" if filename
151
+ Log.low "Aborting threads (#{name}) - #{@threads.collect{|t| Log.fingerprint(t) } * ", "}"
152
+
153
+ @threads.each do |t|
154
+ next if t == Thread.current
155
+ Log.debug "Aborting thread #{Log.fingerprint(t)} with exception: #{exception}"
156
+ t.raise((exception.nil? ? Aborted.new : exception))
157
+ end
158
+
159
+ @threads.each do |t|
160
+ next if t == Thread.current
161
+ begin
162
+ t.join unless t == Thread.current
163
+ rescue Aborted
164
+ rescue Exception
165
+ Log.debug "Thread (#{name}) exception: #{$!.message}"
166
+ end
167
+ end
168
+ end
169
+
170
+ def abort_pids
171
+ @pids.each do |pid|
172
+ begin
173
+ Log.low "Killing PID #{pid} in ConcurrentStream #{filename}"
174
+ Process.kill :INT, pid
175
+ rescue Errno::ESRCH
176
+ end
177
+ end if @pids
178
+ @pids = []
179
+ end
180
+
181
+ def abort(exception = nil)
182
+ self.stream_exception ||= exception
183
+ if @aborted
184
+ Log.medium "Already aborted stream #{Log.fingerprint self} [#{@aborted}]"
185
+ return
186
+ else
187
+ Log.medium "Aborting stream #{Log.fingerprint self} [#{@aborted}]"
188
+ end
189
+ AbortedStream.setup(self, exception)
190
+ @aborted = true
191
+ begin
192
+ @abort_callback.call exception if @abort_callback
193
+
194
+ abort_threads(exception)
195
+ abort_pids
196
+
197
+ @callback = nil
198
+ @abort_callback = nil
199
+
200
+ if @pair && @pair.respond_to?(:abort) && ! @pair.aborted?
201
+ Log.medium "Aborting pair stream #{Log.fingerprint self}: #{Log.fingerprint @pair }"
202
+ @pair.abort exception
203
+ end
204
+ ensure
205
+ close unless closed?
206
+
207
+ if lockfile and lockfile.locked?
208
+ lockfile.unlock
209
+ end
210
+ end
211
+ end
212
+
213
+ def close(*args)
214
+ if autojoin
215
+ begin
216
+ super(*args)
217
+ rescue
218
+ Log.exception $!
219
+ self.abort
220
+ self.join
221
+ stream_raise_exception $!
222
+ ensure
223
+ self.join if self.closed? or self.eof?
224
+ end
225
+ else
226
+ super(*args)
227
+ end
228
+ end
229
+
230
+ def read(*args)
231
+ begin
232
+ super(*args)
233
+ ensure
234
+ begin
235
+ close unless closed?
236
+ rescue Exception
237
+ raise $! if ConcurrentStreamProcessFailed === $!
238
+ end if autojoin && ! closed? && eof?
239
+ end
240
+ end
241
+
242
+ def add_callback(&block)
243
+ old_callback = callback
244
+ @callback = Proc.new do
245
+ old_callback.call if old_callback
246
+ block.call
247
+ end
248
+ end
249
+
250
+ def stream_raise_exception(exception)
251
+ threads.each do |thread|
252
+ thread.raise exception
253
+ end
254
+ self.stream_exception = exception
255
+
256
+ self.abort
257
+ end
258
+
259
+ end
@@ -13,6 +13,12 @@ end
13
13
 
14
14
  class Aborted < StandardError; end
15
15
 
16
+ class ParameterException < ScoutException; end
17
+ class MissingParameterException < ParameterException
18
+ def initialize(parameter)
19
+ super("Missing parameter '#{parameter}'")
20
+ end
21
+ end
16
22
  class ProcessFailed < StandardError;
17
23
  attr_accessor :pid, :msg
18
24
  def initialize(pid = Process.pid, msg = nil)
@@ -41,7 +47,13 @@ end
41
47
 
42
48
  class OpenURLError < StandardError; end
43
49
 
44
- class DontClose < Exception; end
50
+ class DontClose < Exception
51
+ attr_accessor :payload
52
+ def initialize(payload = nil)
53
+ @payload = payload
54
+ end
55
+ end
56
+
45
57
 
46
58
  class KeepLocked < Exception
47
59
  attr_accessor :payload
@@ -57,13 +69,9 @@ class KeepBar < Exception
57
69
  end
58
70
  end
59
71
 
60
- #class ParameterException < ScoutException; end
72
+ class LockInterrupted < TryAgain; end
73
+
61
74
  #
62
- #class MissingParameterException < ParameterException
63
- # def initialize(parameter)
64
- # super("Missing parameter '#{parameter}'")
65
- # end
66
- #end
67
75
  #class ClosedStream < StandardError; end
68
76
  #class OpenGzipError < StandardError; end
69
77
  #
@@ -76,7 +84,6 @@ end
76
84
  #end
77
85
  #
78
86
  #class SemaphoreInterrupted < TryAgain; end
79
- #class LockInterrupted < TryAgain; end
80
87
  #
81
88
  #class RemoteServerError < StandardError; end
82
89
  #
@@ -1,32 +1,24 @@
1
1
  module IndiferentHash
2
2
  def self.add_defaults(options, defaults = {})
3
- options ||= {}
3
+ options = string2hash options if String === options
4
4
  IndiferentHash.setup(options)
5
- case
6
- when Hash === options
7
- new_options = options.dup
8
- when String === options
9
- new_options = string2hash options
10
- else
11
- raise "Format of '#{options.inspect}' not understood. It should be a hash"
12
- end
5
+
6
+ defaults = string2hash defaults if String === defaults
13
7
 
14
8
  defaults.each do |key, value|
15
9
  next if options.include? key
16
10
 
17
- new_options[key] = value
11
+ options[key] = value
18
12
  end
19
13
 
20
- new_options
21
-
22
- options.replace new_options
14
+ options
23
15
  end
24
16
 
25
17
  def self.process_options(hash, *keys)
26
18
  IndiferentHash.setup(hash)
27
19
 
28
20
  defaults = keys.pop if Hash === keys.last
29
- hahs = IndiferentHash.add_defaults hash, defaults if defaults
21
+ hash = IndiferentHash.add_defaults hash, defaults if defaults
30
22
 
31
23
  if keys.length == 1
32
24
  hash.include?(keys.first.to_sym) ? hash.delete(keys.first.to_sym) : hash.delete(keys.first.to_s)
@@ -102,7 +94,7 @@ module IndiferentHash
102
94
  options = {}
103
95
 
104
96
  string.split('#').each do |str|
105
- key, sep, value = str.partition "="
97
+ key, _, value = str.partition "="
106
98
 
107
99
  key = key[1..-1].to_sym if key[0] == ":"
108
100
 
@@ -114,17 +106,7 @@ module IndiferentHash
114
106
  options[key] = value.to_f and next if value =~ /^\d*\.\d+$/
115
107
  options[key] = true and next if value == "true"
116
108
  options[key] = false and next if value == "false"
117
- options[key] = value and next
118
-
119
- options[key] = begin
120
- saved_safe = $SAFE
121
- $SAFE = 0
122
- eval(value)
123
- rescue Exception
124
- value
125
- ensure
126
- $SAFE = saved_safe
127
- end
109
+ options[key] = value
128
110
  end
129
111
 
130
112
  IndiferentHash.setup(options)
@@ -75,33 +75,3 @@ module IndiferentHash
75
75
  end
76
76
  end
77
77
 
78
- module CaseInsensitiveHash
79
-
80
- def self.setup(hash)
81
- hash.extend CaseInsensitiveHash
82
- end
83
-
84
- def downcase_keys
85
- @downcase_keys ||= begin
86
- down = {}
87
- keys.collect{|key|
88
- down[key.to_s.downcase] = key
89
- }
90
- down
91
- end
92
- end
93
-
94
- def [](key, *rest)
95
- value = super(key, *rest)
96
- return value unless value.nil?
97
- key_downcase = key.to_s.downcase
98
- super(downcase_keys[key_downcase])
99
- end
100
-
101
- def values_at(*keys)
102
- keys.collect do |key|
103
- self[key]
104
- end
105
- end
106
-
107
- end
@@ -63,7 +63,7 @@ module Colorize
63
63
  if percent
64
64
  array = array.collect{|v| n = v.to_f; n = n > 100 ? 100 : n; n < 0.001 ? 0.001 : n}
65
65
  else
66
- array = array.collect{|v| n = v.to_f; }
66
+ array = array.collect{|v| v.to_f }
67
67
  end
68
68
  max = array.max
69
69
  min = array.min
@@ -103,7 +103,7 @@ module Colorize
103
103
 
104
104
  value_color = Hash[*array.uniq.zip(all_colors).flatten]
105
105
 
106
- value_color.values_at *array
106
+ value_color.values_at(*array)
107
107
  end
108
108
 
109
109
  def self.tsv(tsv, options = {})
@@ -19,8 +19,12 @@ module Log
19
19
  else
20
20
  "'" << obj << "'"
21
21
  end
22
+ when ConcurrentStream
23
+ name = obj.inspect + " " + obj.object_id.to_s
24
+ name += " #{obj.filename}" if obj.filename
25
+ name
22
26
  when IO
23
- (obj.respond_to?(:filename) and obj.filename ) ? "<IO:" + (obj.filename || obj.inspect + rand(100000)) + ">" : obj.inspect
27
+ (obj.respond_to?(:filename) and obj.filename ) ? "<IO:" + (obj.filename || obj.inspect + rand(100000)) + ">" : obj.inspect + " " + obj.object_id.to_s
24
28
  when File
25
29
  "<File:" + obj.path + ">"
26
30
  when Array
@@ -52,6 +56,12 @@ module Log
52
56
  else
53
57
  "%.6f" % obj
54
58
  end
59
+ when Thread
60
+ if obj["name"]
61
+ obj["name"]
62
+ else
63
+ obj.inspect
64
+ end
55
65
  else
56
66
  obj.to_s
57
67
  end
@@ -169,7 +169,6 @@ module Log
169
169
  bar = bars.sort_by{|b| b.depth }.first
170
170
  print(io, Log.color(:magenta ,bar.report_msg) << "\n")
171
171
  else
172
- length = Log::ProgressBar.cleanup_bars
173
172
  print(io, Log.color(:magenta, "···Progress\n"))
174
173
  bars.sort_by{|b| b.depth }.reverse.each do |bar|
175
174
  if SILENCED.include? bar
@@ -91,7 +91,7 @@ module Log
91
91
  error = true
92
92
  raise $!
93
93
  ensure
94
- remove_bar(bar, error) if bar
94
+ remove_bar(bar, error) if bar && ! keep
95
95
  end
96
96
  end
97
97
  end
@@ -17,12 +17,12 @@ module Log
17
17
  attr_accessor :default_file
18
18
  end
19
19
 
20
- attr_accessor :max, :ticks, :frequency, :depth, :desc, :file, :bytes, :process, :callback
20
+ attr_accessor :max, :ticks, :frequency, :depth, :desc, :file, :bytes, :process, :callback, :severity
21
21
 
22
22
  def initialize(max = nil, options = {})
23
- depth, num_reports, desc, io, severity, file, bytes, frequency, process, callback =
24
- IndiferentHash.process_options options, :depth, :num_reports, :desc, :io, :severity, :file, :bytes, :frequency, :process, :callback,
25
- :depth => 0, :num_reports => 100, :io => STDERR, :severity => Log.severity, :frequency => 2
23
+ depth, desc, file, bytes, frequency, process, callback =
24
+ IndiferentHash.process_options options, :depth, :desc, :file, :bytes, :frequency, :process, :callback,
25
+ :depth => 0, :frequency => 2
26
26
 
27
27
  @max = max
28
28
  @ticks = 0
data/lib/scout/log.rb CHANGED
@@ -14,8 +14,8 @@ module Log
14
14
  end
15
15
 
16
16
  class << self
17
- attr_accessor :logfile, :severity
18
- attr_writer :tty_size
17
+ attr_accessor :severity
18
+ attr_writer :tty_size, :logfile
19
19
  end
20
20
 
21
21
 
@@ -360,3 +360,11 @@ def eef(obj=nil, file = $stdout)
360
360
  Log.log_obj_fingerprint(obj, :error, file)
361
361
  end
362
362
 
363
+ def sss(level, &block)
364
+ if block_given?
365
+ Log.with_severity level, &block
366
+ else
367
+ Log.severity = level
368
+ end
369
+ end
370
+
@@ -11,7 +11,13 @@ module MetaExtension
11
11
  end
12
12
  end
13
13
 
14
- meta.define_method(:setup) do |obj,*rest|
14
+ meta.define_method(:setup) do |*args,&block|
15
+ if block_given?
16
+ obj, rest = block, args
17
+ else
18
+ obj, *rest = args
19
+ end
20
+ obj = block if obj.nil?
15
21
  obj.extend base
16
22
  attrs = self.class_variable_get("@@extension_attrs")
17
23
 
@@ -33,6 +39,14 @@ module MetaExtension
33
39
  obj
34
40
  end
35
41
 
42
+ base.define_method(:extension_attr_hash) do
43
+ attr_hash = {}
44
+ meta.class_variable_get("@@extension_attrs").each do |name|
45
+ attr_hash[name] = self.instance_variable_get("@#{name}")
46
+ end
47
+ attr_hash
48
+ end
49
+
36
50
  base.define_method(:annotate) do |other|
37
51
  attr_values = meta.class_variable_get("@@extension_attrs").collect do |a|
38
52
  self.instance_variable_get("@#{a}")
@@ -0,0 +1,56 @@
1
+ module Misc
2
+ def self.digest_str(obj)
3
+ if obj.respond_to?(:digest_str)
4
+ obj.digest_str
5
+ else
6
+ case obj
7
+ when String
8
+ #'\'' << obj << '\''
9
+ if Path === obj || ! Open.exists?(obj)
10
+ '\'' << obj << '\''
11
+ else
12
+ Misc.file_md5(obj)
13
+ end
14
+ when Integer, Symbol
15
+ obj.to_s
16
+ when Array
17
+ '[' << obj.inject(""){|acc,o| acc.empty? ? Misc.digest_str(o) : acc << ', ' << Misc.digest_str(o) } << ']'
18
+ when Hash
19
+ '{' << obj.inject(""){|acc,p| s = Misc.digest_str(p.first) << "=" << Misc.digest_str(p.last); acc.empty? ? s : acc << ', ' << s } << '}'
20
+ when Integer
21
+ obj.to_s
22
+ when Float
23
+ if obj % 1 == 0
24
+ obj.to_i
25
+ elsif obj.abs > 10
26
+ "%.1f" % obj
27
+ elsif obj.abs > 1
28
+ "%.3f" % obj
29
+ else
30
+ "%.6f" % obj
31
+ end
32
+ when TrueClass
33
+ "true"
34
+ when FalseClass
35
+ "false"
36
+ else
37
+ obj.inspect
38
+ end
39
+ end
40
+ end
41
+
42
+ def self.digest(obj)
43
+ str = Misc.digest_str(obj)
44
+ hash = Digest::MD5.hexdigest(str)
45
+ Log.debug "Digest #{hash} - #{str}"
46
+ hash
47
+ end
48
+
49
+ def self.file_md5(file)
50
+ file = file.find if Path === file
51
+ #md5file = file + '.md5'
52
+ Persist.persist("MD5:#{file}", :string) do
53
+ Digest::MD5.file(file).hexdigest
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,26 @@
1
+ module Misc
2
+ def self.in_dir(dir)
3
+ old_pwd = FileUtils.pwd
4
+ begin
5
+ FileUtils.mkdir_p dir unless File.exist?(dir)
6
+ FileUtils.cd dir
7
+ yield
8
+ ensure
9
+ FileUtils.cd old_pwd
10
+ end
11
+ end
12
+
13
+ def self.path_relative_to(basedir, path)
14
+ path = File.expand_path(path) unless path.slice(0,1) == "/"
15
+ basedir = File.expand_path(basedir) unless basedir.slice(0,1) == "/"
16
+
17
+ basedir += "/" unless basedir.slice(-2,-1) == "/"
18
+
19
+ if path.index(basedir) == 0
20
+ return path[basedir.length..-1]
21
+ else
22
+ return nil
23
+ end
24
+ end
25
+
26
+ end