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
@@ -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