scout-essentials 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,96 @@
1
+ require_relative 'indiferent_hash/options'
2
+ require_relative 'indiferent_hash/case_insensitive'
3
+
4
+ module IndiferentHash
5
+
6
+ def self.setup(hash)
7
+ hash.extend IndiferentHash
8
+ hash
9
+ end
10
+
11
+ def merge(other)
12
+ new = self.dup
13
+ IndiferentHash.setup(new)
14
+ other.each do |k,value|
15
+ new[k] = value
16
+ end
17
+ new
18
+ end
19
+
20
+ def []=(key,value)
21
+ delete(key)
22
+ super(key,value)
23
+ end
24
+
25
+ def _default?
26
+ @_default ||= self.default or self.default_proc
27
+ end
28
+
29
+ def [](key)
30
+ res = super(key)
31
+ return res unless res.nil? or (_default? and not keys.include? key)
32
+
33
+ case key
34
+ when Symbol, Module
35
+ super(key.to_s)
36
+ when String
37
+ super(key.to_sym)
38
+ else
39
+ res
40
+ end
41
+ end
42
+
43
+ def values_at(*key_list)
44
+ key_list.inject([]){|acc,key| acc << self[key]}
45
+ end
46
+
47
+ def include?(key)
48
+ case key
49
+ when Symbol, Module
50
+ super(key) || super(key.to_s)
51
+ when String
52
+ super(key) || super(key.to_sym)
53
+ else
54
+ super(key)
55
+ end
56
+ end
57
+
58
+ def delete(key)
59
+ case key
60
+ when Symbol, Module
61
+ v = super(key)
62
+ v.nil? ? super(key.to_s) : v
63
+ when String
64
+ v = super(key)
65
+ v.nil? ? super(key.to_sym) : v
66
+ else
67
+ super(key)
68
+ end
69
+ end
70
+
71
+ def clean_version
72
+ clean = {}
73
+ each do |k,v|
74
+ clean[k.to_s] = v unless clean.include? k.to_s
75
+ end
76
+ clean
77
+ end
78
+
79
+ def slice(*list)
80
+ ext_list = []
81
+ list.each do |e|
82
+ case e
83
+ when Symbol
84
+ ext_list << e
85
+ ext_list << e.to_s
86
+ when String
87
+ ext_list << e
88
+ ext_list << e.to_sym
89
+ else
90
+ ext_list << e
91
+ end
92
+ end
93
+ IndiferentHash.setup(super(*ext_list))
94
+ end
95
+ end
96
+
@@ -0,0 +1,224 @@
1
+ require_relative 'color_class'
2
+ require_relative '../indiferent_hash'
3
+
4
+ require 'term/ansicolor'
5
+
6
+ module Colorize
7
+ def self.colors=(colors)
8
+ @colors = colors
9
+ end
10
+
11
+ def self.colors
12
+ @colors ||= IndiferentHash.setup(Hash[<<-EOF.split("\n").collect{|l| l.split(" ")}])
13
+ green #00cd00
14
+ red #cd0000
15
+ yellow #ffd700
16
+ blue #0000cd
17
+ path blue
18
+ EOF
19
+ end
20
+
21
+ def self.diverging_colors=(colors)
22
+ @diverging_colors = colors
23
+ end
24
+
25
+ def self.diverging_colors
26
+ @diverging_colors ||=<<~EOF.split("\n")
27
+ #a6cee3
28
+ #1f78b4
29
+ #b2df8a
30
+ #33a02c
31
+ #fb9a99
32
+ #e31a1c
33
+ #fdbf6f
34
+ #ff7f00
35
+ #cab2d6
36
+ #6a3d9a
37
+ #ffff99
38
+ #b15928
39
+ EOF
40
+ end
41
+
42
+ def self.from_name(color)
43
+ return color if color =~ /^#?[0-9A-F]+$/i
44
+ return colors[color.to_s] if colors.include?(color.to_s)
45
+
46
+ case color.to_s
47
+ when "white"
48
+ '#000'
49
+ when "black"
50
+ '#fff'
51
+ when 'green'
52
+ colors["green3"]
53
+ when 'red'
54
+ colors["red3"]
55
+ when 'yellow'
56
+ colors["gold1"]
57
+ when 'blue'
58
+ colors["RoyalBlue"]
59
+ else
60
+ colors[color.to_s] || color.to_s
61
+ end
62
+ end
63
+
64
+ def self.continuous(array, start = "#40324F", eend = "#EABD5D", percent = false)
65
+ start_color = Color.new from_name(start)
66
+ end_color = Color.new from_name(eend)
67
+
68
+ if percent
69
+ array = array.collect{|v| n = v.to_f; n = n > 100 ? 100 : n; n < 0.001 ? 0.001 : n}
70
+ else
71
+ array = array.collect{|v| v.to_f }
72
+ end
73
+ max = array.max
74
+ min = array.min
75
+ range = max - min
76
+ array.collect do |v|
77
+ ratio = (v.to_f-min) / range
78
+ start_color.blend end_color, ratio
79
+ end
80
+ end
81
+
82
+ def self.gradient(array, value, start = :green, eend = :red, percent = false)
83
+ index = array.index value
84
+ colors = continuous(array, start, eend, percent)
85
+ colors[index]
86
+ end
87
+
88
+ def self.rank_gradient(array, value, start = :green, eend = :red, percent = false)
89
+ index = array.index value
90
+ sorted = array.sort
91
+ array = array.collect{|e| sorted.index e}
92
+ colors = continuous(array, start, eend, percent)
93
+ colors[index]
94
+ end
95
+
96
+
97
+ def self.distinct(array)
98
+ colors = diverging_colors.collect{|c| Color.new c }
99
+
100
+ num = array.uniq.length
101
+ times = num / 12
102
+
103
+ all_colors = colors.dup
104
+ factor = 0.3 / times
105
+ times.times do
106
+ all_colors.concat colors.collect{|n| n.darken(factor) }
107
+ end
108
+
109
+ value_color = Hash[*array.uniq.zip(all_colors).flatten]
110
+
111
+ value_color.values_at(*array)
112
+ end
113
+
114
+ def self.tsv(tsv, options = {})
115
+ values = tsv.values.flatten
116
+ if Numeric === values.first or (values.first.to_f != 0 and values[0] != "0" and values[0] != "0.0")
117
+ value_colors = IndiferentHash.process_to_hash(values){continuous(values)}
118
+ else
119
+ value_colors = IndiferentHash.process_to_hash(values){distinct(values)}
120
+ end
121
+
122
+ if tsv.type == :single
123
+ Hash[*tsv.keys.zip(value_colors.values_at(*values)).flatten]
124
+ else
125
+ Hash[*tsv.keys.zip(values.collect{|vs| value_colors.values_at(*vs)}).flatten]
126
+ end
127
+ end
128
+ end
129
+
130
+ module Log
131
+ extend Term::ANSIColor
132
+
133
+ class << self
134
+ attr_accessor :nocolor
135
+ end
136
+
137
+ self.nocolor = ENV["SCOUT_NOCOLOR"] == 'true'
138
+
139
+ WHITE, DARK, GREEN, YELLOW, RED = Color::SOLARIZED.values_at :base0, :base00, :green, :yellow, :magenta
140
+
141
+ SEVERITY_COLOR = [reset, cyan, green, magenta, blue, yellow, red] #.collect{|e| "\033[#{e}"}
142
+ CONCEPT_COLORS = IndiferentHash.setup({
143
+ :title => magenta,
144
+ :path => blue,
145
+ :input => cyan,
146
+ :value => green,
147
+ :integer => green,
148
+ :negative => red,
149
+ :float => green,
150
+ :waiting => yellow,
151
+ :started => cyan,
152
+ :start => cyan,
153
+ :done => green,
154
+ :error => red,
155
+ :time => cyan,
156
+ :task => yellow,
157
+ :workflow => yellow,
158
+ })
159
+ HIGHLIGHT = "\033[1m"
160
+
161
+ def self.uncolor(str)
162
+ "" << Term::ANSIColor.uncolor(str)
163
+ end
164
+
165
+ def self.reset_color
166
+ reset
167
+ end
168
+
169
+ def self.color(color, str = nil, reset = false)
170
+ return str.dup || "" if nocolor
171
+
172
+ if (color == :integer || color == :float) && Numeric === str
173
+ color = if str < 0
174
+ :red
175
+ elsif str > 1
176
+ :cyan
177
+ else
178
+ :green
179
+ end
180
+ end
181
+
182
+ if color == :status
183
+ color = case str.to_sym
184
+ when :done
185
+ :green
186
+ when :error, :aborted
187
+ :red
188
+ when :waiting, :queued
189
+ :yellow
190
+ when :started, :streamming
191
+ :cyan
192
+ when :start
193
+ :title
194
+ else
195
+ :cyan
196
+ end
197
+ end
198
+
199
+ color = SEVERITY_COLOR[color] if Integer === color
200
+ color = CONCEPT_COLORS[color] if CONCEPT_COLORS.include?(color)
201
+ color = Term::ANSIColor.send(color) if Symbol === color and Term::ANSIColor.respond_to?(color)
202
+
203
+ str = str.to_s unless str.nil?
204
+ return str if Symbol === color
205
+ color_str = reset ? Term::ANSIColor.reset : ""
206
+ color_str << color
207
+ if str.nil?
208
+ color_str
209
+ else
210
+ color_str + str.to_s + Term::ANSIColor.reset
211
+ end
212
+ end
213
+
214
+ def self.highlight(str = nil)
215
+ if str.nil?
216
+ return "" if nocolor
217
+ HIGHLIGHT
218
+ else
219
+ return str if nocolor
220
+ HIGHLIGHT + str + color(0)
221
+ end
222
+ end
223
+
224
+ end
@@ -0,0 +1,269 @@
1
+ # Copyright (c) 2007 McClain Looney
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+
22
+ # Implements a color (r,g,b + a) with conversion to/from web format (eg #aabbcc), and
23
+ # with a number of utilities to lighten, darken and blend values.
24
+ class Color
25
+
26
+ attr_reader :r, :g, :b, :a
27
+
28
+ # Table for conversion to hex
29
+ HEXVAL = (('0'..'9').to_a).concat(('A'..'F').to_a).freeze
30
+ # Default value for #darken, #lighten etc.
31
+ BRIGHTNESS_DEFAULT = 0.2
32
+
33
+ SOLARIZED = {
34
+ :base03 => '#002b36',
35
+ :base02 => '#073642',
36
+ :base01 => '#586e75',
37
+ :base00 => '#657b83',
38
+ :base0 => '#839496',
39
+ :base1 => '#93a1a1',
40
+ :base2 => '#eee8d5',
41
+ :base3 => '#fdf6e3',
42
+ :yellow => '#b58900',
43
+ :orange => '#cb4b16',
44
+ :red => '#dc322f',
45
+ :magenta => '#d33682',
46
+ :violet => '#6c71c4',
47
+ :blue => '#268bd2',
48
+ :cyan => '#2aa198',
49
+ :green => '#859900',
50
+ }
51
+
52
+ # Constructor. Inits to white (#FFFFFF) by default, or accepts any params
53
+ # supported by #parse.
54
+ def initialize(*args)
55
+ @r = 255
56
+ @g = 255
57
+ @b = 255
58
+ @a = 255
59
+
60
+ if args.size.between?(3,4)
61
+ self.r = args[0]
62
+ self.g = args[1]
63
+ self.b = args[2]
64
+ self.a = args[3] if args[3]
65
+ else
66
+ set(*args)
67
+ end
68
+ end
69
+
70
+ # All-purpose setter - pass in another Color, '#000000', rgb vals... whatever
71
+ def set(*args)
72
+ val = Color.parse(*args)
73
+ unless val.nil?
74
+ self.r = val.r
75
+ self.g = val.g
76
+ self.b = val.b
77
+ self.a = val.a
78
+ end
79
+ self
80
+ end
81
+
82
+ # Test for equality, accepts string vals as well, eg Color.new('aaa') == '#AAAAAA' => true
83
+ def ==(val)
84
+ val = Color.parse(val)
85
+ return false if val.nil?
86
+ return r == val.r && g == val.g && b == val.b && a == val.a
87
+ end
88
+
89
+ # Setters for individual channels - take 0-255 or '00'-'FF' values
90
+ def r=(val); @r = from_hex(val); end
91
+ def g=(val); @g = from_hex(val); end
92
+ def b=(val); @b = from_hex(val); end
93
+ def a=(val); @a = from_hex(val); end
94
+
95
+ # Attempt to read in a string and parse it into values
96
+ def self.parse(*args)
97
+ case args.size
98
+
99
+ when 0 then
100
+ return nil
101
+
102
+ when 1 then
103
+ val = args[0]
104
+
105
+ # Trivial parse... :-)
106
+ return val if val.is_a?(Color)
107
+
108
+ # Single value, assume grayscale
109
+ return Color.new(val, val, val) if val.is_a?(Numeric)
110
+
111
+ # Assume string
112
+ str = val.to_s.upcase
113
+ str = str[/[0-9A-F]{3,8}/] || ''
114
+ case str.size
115
+ when 3, 4 then
116
+ r, g, b, a = str.scan(/[0-9A-F]/)
117
+ when 6,8 then
118
+ r, g, b, a = str.scan(/[0-9A-F]{2}/)
119
+ else
120
+ return nil
121
+ end
122
+
123
+ return Color.new(r,g,b,a || 255)
124
+
125
+ when 3,4 then
126
+ return Color.new(*args)
127
+
128
+ end
129
+ nil
130
+ end
131
+
132
+ def inspect
133
+ to_s(true)
134
+ end
135
+
136
+ def to_s(add_hash = true)
137
+ trans? ? to_rgba(add_hash) : to_rgb(add_hash)
138
+ end
139
+
140
+ def to_rgb(add_hash = true)
141
+ (add_hash ? '#' : '') + to_hex(r) + to_hex(g) + to_hex(b)
142
+ end
143
+
144
+ def to_rgba(add_hash = true)
145
+ to_rgb(add_hash) + to_hex(a)
146
+ end
147
+
148
+ def opaque?
149
+ @a == 255
150
+ end
151
+
152
+ def trans?
153
+ @a != 255
154
+ end
155
+
156
+ def grayscale?
157
+ @r == @g && @g == @b
158
+ end
159
+
160
+ # Lighten color towards white. 0.0 is a no-op, 1.0 will return #FFFFFF
161
+ def lighten(amt = BRIGHTNESS_DEFAULT)
162
+ return self if amt <= 0
163
+ return WHITE if amt >= 1.0
164
+ val = Color.new(self)
165
+ val.r += ((255-val.r) * amt).to_i
166
+ val.g += ((255-val.g) * amt).to_i
167
+ val.b += ((255-val.b) * amt).to_i
168
+ val
169
+ end
170
+
171
+ # In place version of #lighten
172
+ def lighten!(amt = BRIGHTNESS_DEFAULT)
173
+ set(lighten(amt))
174
+ self
175
+ end
176
+
177
+ # Darken a color towards full black. 0.0 is a no-op, 1.0 will return #000000
178
+ def darken(amt = BRIGHTNESS_DEFAULT)
179
+ return self if amt <= 0
180
+ return BLACK if amt >= 1.0
181
+ val = Color.new(self)
182
+ val.r -= (val.r * amt).to_i
183
+ val.g -= (val.g * amt).to_i
184
+ val.b -= (val.b * amt).to_i
185
+ val
186
+ end
187
+
188
+ # In place version of #darken
189
+ def darken!(amt = BRIGHTNESS_DEFAULT)
190
+ set(darken(amt))
191
+ self
192
+ end
193
+
194
+ # Convert to grayscale, using perception-based weighting
195
+ def grayscale
196
+ val = Color.new(self)
197
+ val.r = val.g = val.b = (0.2126 * val.r + 0.7152 * val.g + 0.0722 * val.b)
198
+ val
199
+ end
200
+
201
+ # In place version of #grayscale
202
+ def grayscale!
203
+ set(grayscale)
204
+ self
205
+ end
206
+
207
+ # Blend to a color amt % towards another color value, eg
208
+ # red.blend(blue, 0.5) will be purple, white.blend(black, 0.5) will be gray, etc.
209
+ def blend(other, amt)
210
+ other = Color.parse(other)
211
+ return Color.new(self) if amt <= 0 || other.nil?
212
+ return Color.new(other) if amt >= 1.0
213
+ val = Color.new(self)
214
+ val.r += ((other.r - val.r)*amt).to_i
215
+ val.g += ((other.g - val.g)*amt).to_i
216
+ val.b += ((other.b - val.b)*amt).to_i
217
+ val
218
+ end
219
+
220
+ # In place version of #blend
221
+ def blend!(other, amt)
222
+ set(blend(other, amt))
223
+ self
224
+ end
225
+
226
+ # Class-level version for explicit blends of two values, useful with constants
227
+ def self.blend(col1, col2, amt)
228
+ col1 = Color.parse(col1)
229
+ col2 = Color.parse(col2)
230
+ col1.blend(col2, amt)
231
+ end
232
+
233
+ protected
234
+
235
+ # Convert int to string hex, eg 255 => 'FF'
236
+ def to_hex(val)
237
+ HEXVAL[val / 16] + HEXVAL[val % 16]
238
+ end
239
+
240
+ # Convert int or string to int, eg 80 => 80, 'FF' => 255, '7' => 119
241
+ def from_hex(val)
242
+ if val.is_a?(String)
243
+ # Double up if single char form
244
+ val = val + val if val.size == 1
245
+ # Convert to integer
246
+ val = val.hex
247
+ end
248
+ # Clamp
249
+ val = 0 if val < 0
250
+ val = 255 if val > 255
251
+ val
252
+ end
253
+
254
+ public
255
+
256
+ # Some constants for general use
257
+ WHITE = Color.new(255,255,255).freeze
258
+ BLACK = Color.new(0,0,0).freeze
259
+
260
+ end
261
+
262
+ # "Global" method for creating Color objects, eg:
263
+ # new_color = rgb(params[:new_color])
264
+ # style="border: 1px solid <%= rgb(10,50,80).lighten %>"
265
+ def rgb(*args)
266
+ Color.parse(*args)
267
+ end
268
+
269
+
@@ -0,0 +1,69 @@
1
+ require 'digest/md5'
2
+ module Log
3
+ def self.fingerprint(obj)
4
+ return obj.fingerprint if obj.respond_to?(:fingerprint)
5
+
6
+ case obj
7
+ when nil
8
+ "nil"
9
+ when TrueClass
10
+ "true"
11
+ when FalseClass
12
+ "false"
13
+ when Symbol
14
+ ":" << obj.to_s
15
+ when String
16
+ if obj.length > 100
17
+ digest = Digest::MD5.hexdigest(obj)
18
+ "'" << obj.slice(0,30) << "<...#{obj.length} - #{digest[0..4]}...>" << obj.slice(-10,30)<< "'"
19
+ else
20
+ "'" << obj << "'"
21
+ end
22
+ when ConcurrentStream
23
+ name = obj.inspect + " " + obj.object_id.to_s
24
+ name += " #{obj.filename}" if obj.filename
25
+ name
26
+ when IO
27
+ (obj.respond_to?(:filename) and obj.filename ) ? "<IO:" + (obj.filename || obj.inspect + rand(100000)) + ">" : obj.inspect + " " + obj.object_id.to_s
28
+ when File
29
+ "<File:" + obj.path + ">"
30
+ when Array
31
+ if (length = obj.length) > 10
32
+ "[#{length}--" << (obj.values_at(0,1, length / 2, -2, -1).collect{|e| fingerprint(e)} * ",") << "]"
33
+ else
34
+ "[" << (obj.collect{|e| fingerprint(e) } * ", ") << "]"
35
+ end
36
+ when Hash
37
+ if obj.length > 10
38
+ "H:{"<< fingerprint(obj.keys) << ";" << fingerprint(obj.values) << "}"
39
+ else
40
+ new = "{"
41
+ obj.each do |k,v|
42
+ new << fingerprint(k) << '=>' << fingerprint(v) << ' '
43
+ end
44
+ if new.length > 1
45
+ new[-1] = "}"
46
+ else
47
+ new << '}'
48
+ end
49
+ new
50
+ end
51
+ when Float
52
+ if obj.abs > 10
53
+ "%.1f" % obj
54
+ elsif obj.abs > 1
55
+ "%.3f" % obj
56
+ else
57
+ "%.6f" % obj
58
+ end
59
+ when Thread
60
+ if obj["name"]
61
+ obj["name"]
62
+ else
63
+ obj.inspect
64
+ end
65
+ else
66
+ obj.to_s
67
+ end
68
+ end
69
+ end