scout-essentials 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. checksums.yaml +7 -0
  2. data/.document +5 -0
  3. data/.vimproject +78 -0
  4. data/Gemfile +14 -0
  5. data/LICENSE.txt +20 -0
  6. data/README.rdoc +18 -0
  7. data/Rakefile +47 -0
  8. data/VERSION +1 -0
  9. data/lib/scout/cmd.rb +348 -0
  10. data/lib/scout/concurrent_stream.rb +284 -0
  11. data/lib/scout/config.rb +168 -0
  12. data/lib/scout/exceptions.rb +77 -0
  13. data/lib/scout/indiferent_hash/case_insensitive.rb +30 -0
  14. data/lib/scout/indiferent_hash/options.rb +115 -0
  15. data/lib/scout/indiferent_hash.rb +96 -0
  16. data/lib/scout/log/color.rb +224 -0
  17. data/lib/scout/log/color_class.rb +269 -0
  18. data/lib/scout/log/fingerprint.rb +69 -0
  19. data/lib/scout/log/progress/report.rb +244 -0
  20. data/lib/scout/log/progress/util.rb +173 -0
  21. data/lib/scout/log/progress.rb +106 -0
  22. data/lib/scout/log/trap.rb +107 -0
  23. data/lib/scout/log.rb +441 -0
  24. data/lib/scout/meta_extension.rb +100 -0
  25. data/lib/scout/misc/digest.rb +63 -0
  26. data/lib/scout/misc/filesystem.rb +25 -0
  27. data/lib/scout/misc/format.rb +255 -0
  28. data/lib/scout/misc/helper.rb +31 -0
  29. data/lib/scout/misc/insist.rb +56 -0
  30. data/lib/scout/misc/monitor.rb +66 -0
  31. data/lib/scout/misc/system.rb +73 -0
  32. data/lib/scout/misc.rb +10 -0
  33. data/lib/scout/named_array.rb +138 -0
  34. data/lib/scout/open/lock/lockfile.rb +587 -0
  35. data/lib/scout/open/lock.rb +68 -0
  36. data/lib/scout/open/remote.rb +135 -0
  37. data/lib/scout/open/stream.rb +491 -0
  38. data/lib/scout/open/util.rb +244 -0
  39. data/lib/scout/open.rb +170 -0
  40. data/lib/scout/path/find.rb +204 -0
  41. data/lib/scout/path/tmpfile.rb +8 -0
  42. data/lib/scout/path/util.rb +127 -0
  43. data/lib/scout/path.rb +51 -0
  44. data/lib/scout/persist/open.rb +17 -0
  45. data/lib/scout/persist/path.rb +15 -0
  46. data/lib/scout/persist/serialize.rb +157 -0
  47. data/lib/scout/persist.rb +104 -0
  48. data/lib/scout/resource/open.rb +8 -0
  49. data/lib/scout/resource/path.rb +80 -0
  50. data/lib/scout/resource/produce/rake.rb +69 -0
  51. data/lib/scout/resource/produce.rb +151 -0
  52. data/lib/scout/resource/scout.rb +3 -0
  53. data/lib/scout/resource/software.rb +178 -0
  54. data/lib/scout/resource/util.rb +59 -0
  55. data/lib/scout/resource.rb +40 -0
  56. data/lib/scout/simple_opt/accessor.rb +54 -0
  57. data/lib/scout/simple_opt/doc.rb +126 -0
  58. data/lib/scout/simple_opt/get.rb +57 -0
  59. data/lib/scout/simple_opt/parse.rb +67 -0
  60. data/lib/scout/simple_opt/setup.rb +26 -0
  61. data/lib/scout/simple_opt.rb +5 -0
  62. data/lib/scout/tmpfile.rb +129 -0
  63. data/lib/scout-essentials.rb +10 -0
  64. data/scout-essentials.gemspec +143 -0
  65. data/share/color/color_names +507 -0
  66. data/share/color/diverging_colors.hex +12 -0
  67. data/share/software/install_helpers +523 -0
  68. data/test/scout/indiferent_hash/test_case_insensitive.rb +16 -0
  69. data/test/scout/indiferent_hash/test_options.rb +46 -0
  70. data/test/scout/log/test_color.rb +0 -0
  71. data/test/scout/log/test_progress.rb +108 -0
  72. data/test/scout/misc/test_digest.rb +30 -0
  73. data/test/scout/misc/test_filesystem.rb +30 -0
  74. data/test/scout/misc/test_insist.rb +13 -0
  75. data/test/scout/misc/test_system.rb +21 -0
  76. data/test/scout/open/test_lock.rb +52 -0
  77. data/test/scout/open/test_remote.rb +25 -0
  78. data/test/scout/open/test_stream.rb +676 -0
  79. data/test/scout/open/test_util.rb +73 -0
  80. data/test/scout/path/test_find.rb +110 -0
  81. data/test/scout/path/test_util.rb +22 -0
  82. data/test/scout/persist/test_open.rb +37 -0
  83. data/test/scout/persist/test_path.rb +37 -0
  84. data/test/scout/persist/test_serialize.rb +114 -0
  85. data/test/scout/resource/test_path.rb +58 -0
  86. data/test/scout/resource/test_produce.rb +94 -0
  87. data/test/scout/resource/test_software.rb +24 -0
  88. data/test/scout/resource/test_util.rb +38 -0
  89. data/test/scout/simple_opt/test_doc.rb +16 -0
  90. data/test/scout/simple_opt/test_get.rb +11 -0
  91. data/test/scout/simple_opt/test_parse.rb +10 -0
  92. data/test/scout/simple_opt/test_setup.rb +77 -0
  93. data/test/scout/test_cmd.rb +85 -0
  94. data/test/scout/test_concurrent_stream.rb +29 -0
  95. data/test/scout/test_config.rb +66 -0
  96. data/test/scout/test_indiferent_hash.rb +26 -0
  97. data/test/scout/test_log.rb +32 -0
  98. data/test/scout/test_meta_extension.rb +80 -0
  99. data/test/scout/test_misc.rb +6 -0
  100. data/test/scout/test_named_array.rb +43 -0
  101. data/test/scout/test_open.rb +146 -0
  102. data/test/scout/test_path.rb +54 -0
  103. data/test/scout/test_persist.rb +186 -0
  104. data/test/scout/test_resource.rb +26 -0
  105. data/test/scout/test_tmpfile.rb +53 -0
  106. data/test/test_helper.rb +50 -0
  107. metadata +247 -0
data/lib/scout/log.rb ADDED
@@ -0,0 +1,441 @@
1
+ require_relative 'log/color'
2
+ require_relative 'log/fingerprint'
3
+ require_relative 'log/progress'
4
+ require_relative 'log/trap'
5
+
6
+ require 'io/console'
7
+
8
+ module Log
9
+ class << self
10
+ attr_accessor :severity
11
+ attr_writer :tty_size, :logfile
12
+ end
13
+
14
+ SEVERITY_NAMES ||= begin
15
+ names = %w(DEBUG LOW MEDIUM HIGH INFO WARN ERROR NONE )
16
+ names.each_with_index do |name,i|
17
+ eval "#{ name } = #{ i }"
18
+ end
19
+ names
20
+ end
21
+ def self.default_severity
22
+ @@default_severity ||= begin
23
+ file = File.join(ENV["HOME"], '.scout/etc/log_severity')
24
+ if File.exist? file
25
+ File.open(file) do |f|
26
+ SEVERITY_NAMES.index f.read.strip
27
+ end
28
+ else
29
+ INFO
30
+ end
31
+ end
32
+ @@default_severity
33
+ end
34
+
35
+ case ENV['SCOUT_LOG']
36
+ when 'DEBUG'
37
+ self.severity = DEBUG
38
+ when 'LOW'
39
+ self.severity = LOW
40
+ when 'MEDIUM'
41
+ self.severity = MEDIUM
42
+ when 'HIGH'
43
+ self.severity = HIGH
44
+ when nil
45
+ self.severity = default_severity
46
+ else
47
+ self.severity = default_severity
48
+ end
49
+
50
+
51
+
52
+ def self.tty_size
53
+ @@tty_size ||= Log.ignore_stderr do
54
+ size = begin
55
+ IO.console.winsize.last
56
+ rescue Exception
57
+ begin
58
+ res = `tput li`
59
+ res = nil if res == ""
60
+ res || ENV["TTY_SIZE"] || 80
61
+ rescue Exception
62
+ ENV["TTY_SIZE"] || 80
63
+ end
64
+ end
65
+ size = size.to_i if String === size
66
+ size
67
+ end
68
+ end
69
+
70
+
71
+ def self.last_caller(stack)
72
+ line = nil
73
+ pos ||= 0
74
+ while line.nil? or line =~ /scout\/log\.rb/ and stack.any?
75
+ line = stack.shift
76
+ end
77
+ line ||= caller.first
78
+ line.gsub('`', "'")
79
+ end
80
+
81
+ def self.get_level(level)
82
+ case level
83
+ when Numeric
84
+ level.to_i
85
+ when String
86
+ begin
87
+ Log.const_get(level.upcase)
88
+ rescue
89
+ Log.exception $!
90
+ end
91
+ when Symbol
92
+ get_level(level.to_s)
93
+ end || 0
94
+ end
95
+
96
+ def self.with_severity(level)
97
+ orig = Log.severity
98
+ begin
99
+ Log.severity = level
100
+ yield
101
+ ensure
102
+ Log.severity = orig
103
+ end
104
+ end
105
+
106
+ def self.logfile(file=nil)
107
+ if file.nil?
108
+ @logfile ||= nil
109
+ else
110
+ case file
111
+ when String
112
+ @logfile = File.open(file, :mode => 'a')
113
+ @logfile.sync = true
114
+ when IO, File
115
+ @logfile = file
116
+ else
117
+ raise "Unkown logfile format: #{file.inspect}"
118
+ end
119
+ end
120
+ end
121
+
122
+ def self.up_lines(num = 1)
123
+ nocolor ? "" : "\033[#{num+1}F\033[2K"
124
+ end
125
+
126
+ def self.down_lines(num = 1)
127
+ nocolor ? "" : "\033[#{num+1}E"
128
+ end
129
+
130
+ def self.return_line
131
+ nocolor ? "" : "\033[1A"
132
+ end
133
+
134
+ def self.clear_line(out = STDOUT)
135
+ out.puts Log.return_line << " " * (Log.tty_size || 80) << Log.return_line unless nocolor
136
+ end
137
+
138
+ MUTEX = Mutex.new
139
+ def self.log_write(str)
140
+ MUTEX.synchronize do
141
+ if logfile.nil?
142
+ begin
143
+ STDERR.write str
144
+ rescue
145
+ end
146
+ else
147
+ logfile.write str
148
+ end
149
+ end
150
+ end
151
+
152
+ def self.log_puts(str)
153
+ MUTEX.synchronize do
154
+ if logfile.nil?
155
+ begin
156
+ STDERR.puts str
157
+ rescue
158
+ end
159
+ else
160
+ logfile.puts str
161
+ end
162
+ end
163
+ end
164
+
165
+ LAST = "log"
166
+ def self.logn(message = nil, severity = MEDIUM, &block)
167
+ return if severity < self.severity
168
+ message ||= block.call if block_given?
169
+ return if message.nil?
170
+
171
+ time = Time.now.strftime("%m/%d/%y-%H:%M:%S.%L")
172
+
173
+ sev_str = severity.to_s
174
+
175
+ if ENV["SCOUT_DEBUG_PID"] == "true"
176
+ prefix = time << "[" << Process.pid.to_s << "]" << color(severity) << "[" << sev_str << "]" << color(0)
177
+ else
178
+ prefix = time << color(severity) << "[" << sev_str << "]" << color(0)
179
+ end
180
+ message = "" << highlight << message << color(0) if severity >= INFO
181
+ str = prefix << " " << message.to_s
182
+
183
+ log_write str
184
+
185
+ Log::LAST.replace "log"
186
+ nil
187
+ end
188
+
189
+ def self.log(message = nil, severity = MEDIUM, &block)
190
+ return if severity < self.severity
191
+ message ||= block.call if block_given?
192
+ return if message.nil?
193
+ message = message + "\n" unless message[-1] == "\n"
194
+ self.logn message, severity, &block
195
+ end
196
+
197
+ def self.log_obj_inspect(obj, level, file = $stdout)
198
+ stack = caller
199
+
200
+ line = Log.last_caller stack
201
+
202
+ level = Log.get_level level
203
+ name = Log::SEVERITY_NAMES[level] + ": "
204
+ Log.log Log.color(level, name, true) << line, level
205
+ Log.log "", level
206
+ Log.log Log.color(level, "=> ", true) << obj.inspect, level
207
+ Log.log "", level
208
+ end
209
+
210
+ def self.log_obj_fingerprint(obj, level, file = $stdout)
211
+ stack = caller
212
+
213
+ line = Log.last_caller stack
214
+
215
+ level = Log.get_level level
216
+ name = Log::SEVERITY_NAMES[level] + ": "
217
+ Log.log Log.color(level, name, true) << line, level
218
+ Log.log "", level
219
+ Log.log Log.color(level, "=> ", true) << Log.fingerprint(obj), level
220
+ Log.log "", level
221
+ end
222
+
223
+ def self.debug(message = nil, &block)
224
+ log(message, DEBUG, &block)
225
+ end
226
+
227
+ def self.low(message = nil, &block)
228
+ log(message, LOW, &block)
229
+ end
230
+
231
+ def self.medium(message = nil, &block)
232
+ log(message, MEDIUM, &block)
233
+ end
234
+
235
+ def self.high(message = nil, &block)
236
+ log(message, HIGH, &block)
237
+ end
238
+
239
+ def self.info(message = nil, &block)
240
+ log(message, INFO, &block)
241
+ end
242
+
243
+ def self.warn(message = nil, &block)
244
+ log(message, WARN, &block)
245
+ end
246
+
247
+ def self.error(message = nil, &block)
248
+ log(message, ERROR, &block)
249
+ end
250
+
251
+ def self.exception(e)
252
+ stack = caller
253
+ backtrace = e.backtrace || []
254
+ if ENV["SCOUT_ORIGINAL_STACK"] == 'true'
255
+ error([e.class.to_s, e.message].compact * ": " )
256
+ error("BACKTRACE [#{Process.pid}]: " << Log.last_caller(stack) << "\n" + color_stack(backtrace)*"\n")
257
+ else
258
+ error("BACKTRACE [#{Process.pid}]: " << Log.last_caller(stack) << "\n" + color_stack(backtrace.reverse)*"\n")
259
+ error([e.class.to_s, e.message].compact * ": " )
260
+ end
261
+ end
262
+
263
+ def self.deprecated(m)
264
+ stack = caller
265
+ warn("DEPRECATED: " << Log.last_caller(stack))
266
+ warn("* " << (m || "").to_s)
267
+ end
268
+
269
+ def self.color_stack(stack)
270
+ stack.collect do |line|
271
+ line = line.sub('`',"'")
272
+ color = :green if line =~ /workflow/
273
+ color = :blue if line =~ /scout-/
274
+ color = :cyan if line =~ /rbbt-/
275
+ if color
276
+ Log.color color, line
277
+ else
278
+ line
279
+ end
280
+ end unless stack.nil?
281
+ end
282
+
283
+ def self.tsv(tsv, example = false)
284
+ log_puts Log.color :magenta, "TSV log: " << Log.last_caller(caller).gsub('`',"'")
285
+ log_puts Log.color(:blue, "=> "<< Log.fingerprint(tsv), true)
286
+ log_puts Log.color(:cyan, "=> " << tsv.summary)
287
+ if example && ! tsv.empty?
288
+ key = case example
289
+ when TrueClass, :first, "first"
290
+ tsv.keys.first
291
+ when :random, "random"
292
+ tsv.keys.shuffle.first
293
+ else
294
+ example
295
+ end
296
+
297
+ values = tsv[key]
298
+ values = [values] if tsv.type == :flat || tsv.type == :single
299
+ if values.nil?
300
+ log_puts Log.color(:blue, "Key (#{tsv.key_field}) not present: ") + key
301
+ else
302
+ log_puts Log.color(:blue, "Key (#{tsv.key_field}): ") + key
303
+ tsv.fields.zip(values).each do |field,value|
304
+ log_puts Log.color(:magenta, field + ": ") + (Array === value ? value * ", " : value.to_s)
305
+ end
306
+ end
307
+ end
308
+ end
309
+
310
+ def self.stack(stack)
311
+ if ENV["SCOUT_ORIGINAL_STACK"] == 'true'
312
+ log_puts Log.color :magenta, "Stack trace [#{Process.pid}]: " << Log.last_caller(caller)
313
+ color_stack(stack).each do |line|
314
+ log_puts line
315
+ end
316
+ else
317
+ log_puts Log.color :magenta, "Stack trace [#{Process.pid}]: " << Log.last_caller(caller)
318
+ color_stack(stack.reverse).each do |line|
319
+ log_puts line
320
+ end
321
+ end
322
+ end
323
+
324
+ def self.count_stack
325
+ if ! $count_stacks
326
+ Log.debug "Counting stacks at: " << caller.first
327
+ return
328
+ end
329
+ $stack_counts ||= {}
330
+ head = $count_stacks_head
331
+ stack = caller[1..head+1]
332
+ stack.reverse.each do |line,i|
333
+ $stack_counts[line] ||= 0
334
+ $stack_counts[line] += 1
335
+ end
336
+ end
337
+
338
+ def self.with_stack_counts(head = 10, total = 100)
339
+ $count_stacks_head = head
340
+ $count_stacks = true
341
+ $stack_counts = {}
342
+ res = yield
343
+ $count_stacks = false
344
+ Log.debug "STACK_COUNTS:\n" + $stack_counts.sort_by{|line,c| c}.reverse.collect{|line,c| [c, line] * " - "}[0..total] * "\n"
345
+ $stack_counts = {}
346
+ res
347
+ end
348
+ end
349
+
350
+ def ppp(message)
351
+ stack = caller
352
+ puts "#{Log.color :cyan, "PRINT:"} " << stack.first
353
+ puts ""
354
+ if message.length > 200 or message.include? "\n"
355
+ puts Log.color(:cyan, "=>|") << "\n" << message.to_s
356
+ else
357
+ puts Log.color(:cyan, "=> ") << message.to_s
358
+ end
359
+ puts ""
360
+ end
361
+
362
+ def fff(object)
363
+ stack = caller
364
+ Log.debug{"#{Log.color :cyan, "FINGERPRINT:"} " << stack.first}
365
+ Log.debug{""}
366
+ Log.debug{require 'scout/util/misc'; "=> " << Log.fingerprint(object) }
367
+ Log.debug{""}
368
+ end
369
+
370
+ def ddd(obj, file = $stdout)
371
+ Log.log_obj_inspect(obj, :debug, file)
372
+ end
373
+
374
+ def lll(obj, file = $stdout)
375
+ Log.log_obj_inspect(obj, :low, file)
376
+ end
377
+
378
+ def mmm(obj, file = $stdout)
379
+ Log.log_obj_inspect(obj, :medium, file)
380
+ end
381
+
382
+ def iii(obj=nil, file = $stdout)
383
+ Log.log_obj_inspect(obj, :info, file)
384
+ end
385
+
386
+ def wwww(obj=nil, file = $stdout)
387
+ Log.log_obj_inspect(obj, :warn, file)
388
+ end
389
+
390
+ def eee(obj=nil, file = $stdout)
391
+ Log.log_obj_inspect(obj, :error, file)
392
+ end
393
+
394
+ def ddf(obj=nil, file = $stdout)
395
+ Log.log_obj_fingerprint(obj, :debug, file)
396
+ end
397
+
398
+ def llf(obj=nil, file = $stdout)
399
+ Log.log_obj_fingerprint(obj, :low, file)
400
+ end
401
+
402
+ def mmf(obj=nil, file = $stdout)
403
+ Log.log_obj_fingerprint(obj, :medium, file)
404
+ end
405
+
406
+ def iif(obj=nil, file = $stdout)
407
+ Log.log_obj_fingerprint(obj, :info, file)
408
+ end
409
+
410
+ def wwwf(obj=nil, file = $stdout)
411
+ Log.log_obj_fingerprint(obj, :warn, file)
412
+ end
413
+
414
+ def eef(obj=nil, file = $stdout)
415
+ Log.log_obj_fingerprint(obj, :error, file)
416
+ end
417
+
418
+ def sss(level, &block)
419
+ if block_given?
420
+ Log.with_severity level, &block
421
+ else
422
+ Log.severity = level
423
+ end
424
+ end
425
+
426
+ $scout_debug_log = false
427
+ def ccc(obj=nil, file = $stdout)
428
+ if block_given?
429
+ old_scout_debug_log = $scout_debug_log
430
+ $scout_debug_log = 'true'
431
+ begin
432
+ yield
433
+ ensure
434
+ $scout_debug_log = old_scout_debug_log
435
+ end
436
+ else
437
+ Log.log_obj_inspect(obj, :info, file) if $scout_debug_log
438
+ end
439
+ end
440
+
441
+
@@ -0,0 +1,100 @@
1
+ module MetaExtension
2
+ def self.extended(base)
3
+ meta = class << base; self; end
4
+
5
+ base.class_variable_set("@@extension_attrs", []) unless base.class_variables.include?("@@extension_attrs")
6
+
7
+ meta.define_method(:extension_attr) do |*attrs|
8
+ self.class_variable_get("@@extension_attrs").concat attrs
9
+ attrs.each do |a|
10
+ self.attr_accessor a
11
+ end
12
+ end
13
+
14
+ meta.define_method(:extended) do |obj|
15
+ attrs = self.class_variable_get("@@extension_attrs")
16
+
17
+ obj.instance_variable_set(:@extension_attrs, []) unless obj.instance_variables.include?(:@extension_attrs)
18
+ extension_attrs = obj.instance_variable_get(:@extension_attrs)
19
+ extension_attrs.concat attrs
20
+ end
21
+
22
+ meta.define_method(:setup) do |*args,&block|
23
+ if block_given?
24
+ obj, rest = block, args
25
+ else
26
+ obj, *rest = args
27
+ end
28
+ obj = block if obj.nil?
29
+ obj.extend base unless base === obj
30
+
31
+ attrs = self.class_variable_get("@@extension_attrs")
32
+
33
+ return if attrs.nil? || attrs.empty?
34
+
35
+ if rest.length == 1 && Hash === (rlast = rest.last) &&
36
+ ((! (rlkey = rlast.keys.first).nil? && attrs.include?(rlkey.to_sym)) ||
37
+ (! attrs.length != 1 ))
38
+
39
+ pairs = rlast
40
+ else
41
+ pairs = attrs.zip(rest)
42
+ end
43
+
44
+ pairs.each do |name,value|
45
+ obj.instance_variable_set("@#{name}", value)
46
+ end
47
+
48
+ obj
49
+ end
50
+
51
+ base.define_method(:extension_attr_hash) do
52
+ attr_hash = {}
53
+ @extension_attrs.each do |name|
54
+ attr_hash[name] = self.instance_variable_get("@#{name}")
55
+ end
56
+ attr_hash
57
+ end
58
+
59
+ base.define_method(:annotate) do |other|
60
+ attr_values = @extension_attrs.collect do |a|
61
+ self.instance_variable_get("@#{a}")
62
+ end
63
+ base.setup(other, *attr_values)
64
+ end
65
+
66
+ base.define_method(:purge) do
67
+ new = self.dup
68
+
69
+ if new.instance_variables.include?(:@extension_attrs)
70
+ new.instance_variable_get(:@extension_attrs).each do |a|
71
+ new.remove_instance_variable("@#{a}")
72
+ end
73
+ new.remove_instance_variable("@extension_attrs")
74
+ end
75
+
76
+ new
77
+ end
78
+ end
79
+
80
+ def self.is_extended?(obj)
81
+ obj.respond_to?(:extension_attr_hash)
82
+ end
83
+
84
+ def self.purge(obj)
85
+ case obj
86
+ when nil
87
+ nil
88
+ when Array
89
+ obj.collect{|e| purge(e) }
90
+ when Hash
91
+ new = {}
92
+ obj.each do |k,v|
93
+ new[k] = purge(v)
94
+ end
95
+ new
96
+ else
97
+ is_extended?(obj) ? obj.purge : obj
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,63 @@
1
+ module Misc
2
+ MAX_ARRAY_DIGEST_LENGTH = 100_000
3
+ def self.digest_str(obj)
4
+ if obj.respond_to?(:digest_str)
5
+ obj.digest_str
6
+ else
7
+ case obj
8
+ when String
9
+ #'\'' << obj << '\''
10
+ if Path === obj || ! Open.exists?(obj)
11
+ '\'' << obj << '\''
12
+ else
13
+ "File MD5: #{Misc.file_md5(obj)}"
14
+ end
15
+ when Integer, Symbol
16
+ obj.to_s
17
+ when Array
18
+ if obj.length > MAX_ARRAY_DIGEST_LENGTH
19
+ length = obj.length
20
+ mid = length/2
21
+ sample_pos = [1, 2, mid, length-2, length-1]
22
+ "[#{length}:" << obj.values_at(*sample_pos).inject(""){|acc,o| acc.empty? ? Misc.digest_str(o) : acc << ', ' << Misc.digest_str(o) } << ']'
23
+ else
24
+ '[' << obj.inject(""){|acc,o| acc.empty? ? Misc.digest_str(o) : acc << ', ' << Misc.digest_str(o) } << ']'
25
+ end
26
+ when Hash
27
+ '{' << obj.inject(""){|acc,p| s = Misc.digest_str(p.first) << "=" << Misc.digest_str(p.last); acc.empty? ? s : acc << ', ' << s } << '}'
28
+ when Integer
29
+ obj.to_s
30
+ when Float
31
+ if obj % 1 == 0
32
+ obj.to_i.to_s
33
+ elsif obj.abs > 10
34
+ "%.1f" % obj
35
+ elsif obj.abs > 1
36
+ "%.3f" % obj
37
+ else
38
+ "%.6f" % obj
39
+ end
40
+ when TrueClass
41
+ "true"
42
+ when FalseClass
43
+ "false"
44
+ else
45
+ obj.inspect
46
+ end
47
+ end
48
+ end
49
+
50
+ def self.digest(obj)
51
+ str = String === obj ? obj : Misc.digest_str(obj)
52
+ Digest::MD5.hexdigest(str)
53
+ end
54
+
55
+ def self.file_md5(file)
56
+ file = file.find if Path === file
57
+ file = File.expand_path(file)
58
+ #md5file = file + '.md5'
59
+ Persist.persist("MD5:#{file}", :string) do
60
+ Digest::MD5.file(file).hexdigest
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,25 @@
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.start_with?(basedir)
20
+ return path.slice(basedir.length, basedir.length)
21
+ else
22
+ return nil
23
+ end
24
+ end
25
+ end