scout-gear 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,169 @@
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({green: "#00cd00" , red: "#cd0000" , yellow: "#ffd700" })
13
+ end
14
+
15
+ def self.diverging_colors=(colors)
16
+ @diverging_colors = colors
17
+ end
18
+
19
+ def self.diverging_colors
20
+ @diverging_colors ||=<<~EOF.split("\n")
21
+ #a6cee3
22
+ #1f78b4
23
+ #b2df8a
24
+ #33a02c
25
+ #fb9a99
26
+ #e31a1c
27
+ #fdbf6f
28
+ #ff7f00
29
+ #cab2d6
30
+ #6a3d9a
31
+ #ffff99
32
+ #b15928
33
+ EOF
34
+ end
35
+
36
+
37
+ def self.from_name(color)
38
+ return color if color =~ /^#?[0-9A-F]+$/i
39
+ return colors[color.to_s] if colors.include?(color.to_s)
40
+
41
+ case color.to_s
42
+ when "white"
43
+ '#000'
44
+ when "black"
45
+ '#fff'
46
+ when 'green'
47
+ colors["green3"]
48
+ when 'red'
49
+ colors["red3"]
50
+ when 'yellow'
51
+ colors["gold1"]
52
+ when 'blue'
53
+ colors["RoyalBlue"]
54
+ else
55
+ colors[color.to_s] || color
56
+ end
57
+ end
58
+
59
+ def self.continuous(array, start = "#40324F", eend = "#EABD5D", percent = false)
60
+ start_color = Color.new from_name(start)
61
+ end_color = Color.new from_name(eend)
62
+
63
+ if percent
64
+ array = array.collect{|v| n = v.to_f; n = n > 100 ? 100 : n; n < 0.001 ? 0.001 : n}
65
+ else
66
+ array = array.collect{|v| n = v.to_f; }
67
+ end
68
+ max = array.max
69
+ min = array.min
70
+ range = max - min
71
+ array.collect do |v|
72
+ ratio = (v.to_f-min) / range
73
+ start_color.blend end_color, ratio
74
+ end
75
+ end
76
+
77
+ def self.gradient(array, value, start = :green, eend = :red, percent = false)
78
+ index = array.index value
79
+ colors = continuous(array, start, eend, percent)
80
+ colors[index]
81
+ end
82
+
83
+ def self.rank_gradient(array, value, start = :green, eend = :red, percent = false)
84
+ index = array.index value
85
+ sorted = array.sort
86
+ array = array.collect{|e| sorted.index e}
87
+ colors = continuous(array, start, eend, percent)
88
+ colors[index]
89
+ end
90
+
91
+
92
+ def self.distinct(array)
93
+ colors = diverging_colors.collect{|c| Color.new c }
94
+
95
+ num = array.uniq.length
96
+ times = num / 12
97
+
98
+ all_colors = colors.dup
99
+ factor = 0.3 / times
100
+ times.times do
101
+ all_colors.concat colors.collect{|n| n.darken(factor) }
102
+ end
103
+
104
+ value_color = Hash[*array.uniq.zip(all_colors).flatten]
105
+
106
+ value_color.values_at *array
107
+ end
108
+
109
+ def self.tsv(tsv, options = {})
110
+ values = tsv.values.flatten
111
+ if Numeric === values.first or (values.first.to_f != 0 and values[0] != "0" and values[0] != "0.0")
112
+ value_colors = IndiferentHash.process_to_hash(values){continuous(values)}
113
+ else
114
+ value_colors = IndiferentHash.process_to_hash(values){distinct(values)}
115
+ end
116
+
117
+ if tsv.type == :single
118
+ Hash[*tsv.keys.zip(value_colors.values_at(*values)).flatten]
119
+ else
120
+ Hash[*tsv.keys.zip(values.collect{|vs| value_colors.values_at(*vs)}).flatten]
121
+ end
122
+ end
123
+ end
124
+
125
+ module Log
126
+ extend Term::ANSIColor
127
+
128
+ class << self
129
+ attr_accessor :nocolor
130
+ end
131
+
132
+ self.nocolor = ENV["RBBT_NOCOLOR"] == 'true'
133
+
134
+ WHITE, DARK, GREEN, YELLOW, RED = Color::SOLARIZED.values_at :base0, :base00, :green, :yellow, :magenta
135
+
136
+ SEVERITY_COLOR = [reset, cyan, green, magenta, blue, yellow, red] #.collect{|e| "\033[#{e}"}
137
+ HIGHLIGHT = "\033[1m"
138
+
139
+ def self.uncolor(str)
140
+ "" << Term::ANSIColor.uncolor(str)
141
+ end
142
+
143
+ def self.reset_color
144
+ reset
145
+ end
146
+
147
+ def self.color(severity, str = nil, reset = false)
148
+ return str.dup || "" if nocolor
149
+ color = reset ? Term::ANSIColor.reset : ""
150
+ color << SEVERITY_COLOR[severity] if Integer === severity
151
+ color << Term::ANSIColor.send(severity) if Symbol === severity and Term::ANSIColor.respond_to? severity
152
+ if str.nil?
153
+ color
154
+ else
155
+ color + str.to_s + self.color(0)
156
+ end
157
+ end
158
+
159
+ def self.highlight(str = nil)
160
+ if str.nil?
161
+ return "" if nocolor
162
+ HIGHLIGHT
163
+ else
164
+ return str if nocolor
165
+ HIGHLIGHT + str + color(0)
166
+ end
167
+ end
168
+
169
+ 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,59 @@
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 IO
23
+ (obj.respond_to?(:filename) and obj.filename ) ? "<IO:" + (obj.filename || obj.inspect + rand(100000)) + ">" : obj.inspect
24
+ when File
25
+ "<File:" + obj.path + ">"
26
+ when Array
27
+ if (length = obj.length) > 10
28
+ "[#{length}--" << (obj.values_at(0,1, length / 2, -2, -1).collect{|e| fingerprint(e)} * ",") << "]"
29
+ else
30
+ "[" << (obj.collect{|e| fingerprint(e) } * ", ") << "]"
31
+ end
32
+ when Hash
33
+ if obj.length > 10
34
+ "H:{"<< fingerprint(obj.keys) << ";" << fingerprint(obj.values) << "}"
35
+ else
36
+ new = "{"
37
+ obj.each do |k,v|
38
+ new << fingerprint(k) << '=>' << fingerprint(v) << ' '
39
+ end
40
+ if new.length > 1
41
+ new[-1] = "}"
42
+ else
43
+ new << '}'
44
+ end
45
+ new
46
+ end
47
+ when Float
48
+ if obj.abs > 10
49
+ "%.1f" % obj
50
+ elsif obj.abs > 1
51
+ "%.3f" % obj
52
+ else
53
+ "%.6f" % obj
54
+ end
55
+ else
56
+ obj.to_s
57
+ end
58
+ end
59
+ end