colorable 0.0.8 → 0.1.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.
@@ -5,56 +5,42 @@ module Colorable
5
5
  include Enumerable
6
6
  extend Forwardable
7
7
 
8
- def initialize(colorset=nil)
8
+ # Create a X11 colorset object.
9
+ #
10
+ # options::keys should be:
11
+ # +order+:: :NAME(default), :RGB, :HSB or :HEX can be specified.
12
+ # +dir+:: set :+(default) to increment order, :- to decrement order.
13
+ # +colorset+:: set original colorset other than X11 if any.
14
+ def initialize(opt={})
15
+ opt = { order: :name, dir: :+ }.merge(opt)
9
16
  @pos = 0
10
- @colorset =
11
- colorset || COLORNAMES.map { |name, rgb| Colorable::Color.new(name) }
17
+ @colorset = opt[:colorset] ? opt[:colorset] : build_colorset(opt)
12
18
  end
13
19
 
14
20
  def_delegators :@colorset, :size, :first, :last, :to_a
15
21
 
16
- # +Colorset[:order]+ create a ordered colorset by passing a order key.
17
- def self.[](order, dir=:+, colorset=nil)
18
- rgb = [:red, :green, :blue]
19
- hsb = [:hue, :sat, :bright]
20
- blk =
21
- case order
22
- when :red, :blue, :green
23
- ->color{ color.rgb.move_to_top rgb.index(order) }
24
- when :hue, :sat, :bright
25
- ->color{ color.hsb.move_to_top hsb.index(order) }
26
- else
27
- ->color{ color.send order }
28
- end
29
-
30
- case dir
31
- when :+
32
- new(colorset).sort_by(&blk)
33
- when :-
34
- new(colorset).sort_by(&blk).reverse
35
- else
36
- raise ArgumentError, "it must ':+' or ':-'"
37
- end
38
- end
39
-
40
22
  def each(&blk)
41
23
  @colorset.each(&blk)
42
24
  end
43
25
 
26
+ # Returns a top color object in colorset
44
27
  def at(pos=0)
45
28
  @colorset[(@pos+pos)%size]
46
29
  end
47
30
 
31
+ # Returns a next color object in colorset
48
32
  def next(n=1)
49
33
  @pos = (@pos+n)%size
50
34
  at
51
35
  end
52
36
 
37
+ # Returns a previous color object in colorset
53
38
  def prev(n=1)
54
39
  @pos = (@pos-n)%size
55
40
  at
56
41
  end
57
42
 
43
+ # Rewind a cursor to the top
58
44
  def rewind
59
45
  @pos = 0
60
46
  at
@@ -65,17 +51,60 @@ module Colorable
65
51
  (@pos+idx)%size if idx
66
52
  end
67
53
 
54
+ # Returns a sorted colorset defined by passed block
68
55
  def sort_by(&blk)
69
- self.class.new @colorset.sort_by(&blk)
56
+ self.class.new colorset: @colorset.sort_by(&blk)
70
57
  end
71
58
 
59
+ # Returns a reversed colorset
72
60
  def reverse
73
- self.class.new @colorset.reverse
61
+ self.class.new colorset: @colorset.reverse
74
62
  end
75
63
 
76
64
  def to_s
77
- "#<%s %d/%d pos='%s:%s'>" % [:Colorset, @pos, size, at.name, at]
65
+ "#<%s %d/%d pos='%s/%s/%s'>" % [:Colorset, @pos, size, at.name, at.rgb, at.hsb]
78
66
  end
79
67
  alias :inspect :to_s
68
+
69
+ private
70
+ def build_colorset(opt)
71
+ rgb_part = [:red, :green, :blue]
72
+ hsb_part = [:hue, :sat, :bright]
73
+
74
+ order = opt[:order].downcase.intern
75
+
76
+ mode =
77
+ case order
78
+ when :name then :NAME
79
+ when :rgb, *rgb_part then :RGB
80
+ when :hsb, :hsv, *hsb_part then :HSB
81
+ else
82
+ raise ArgumentError, "Invalid order option given"
83
+ end
84
+
85
+ colorset = COLORNAMES.map do |name, _|
86
+ Colorable::Color.new(name).tap {|c| c.mode = mode }
87
+ end
88
+
89
+ order_cond =
90
+ case order
91
+ when *rgb_part
92
+ ->color{ color.rgb.move_to_top rgb_part.index(order) }
93
+ when *hsb_part
94
+ ->color{ color.hsb.move_to_top hsb_part.index(order) }
95
+ when :name, :rgb, :hsb, :hsv
96
+ ->color{ color.send order }
97
+ end
98
+
99
+ case opt[:dir].intern
100
+ when :+
101
+ colorset.sort_by(&order_cond)
102
+ when :-
103
+ colorset.sort_by(&order_cond).reverse
104
+ else
105
+ raise ArgumentError, "Invalid dir option given"
106
+ end
107
+ end
108
+
80
109
  end
81
110
  end
@@ -1,20 +1,15 @@
1
1
  module Colorable
2
2
  module Converter
3
- class NoNameError < StandardError; end
4
-
5
3
  def name2rgb(name)
6
- COLORNAMES.assoc(name)[1]
7
- rescue
8
- raise NoNameError, "'#{name}' not exist"
4
+ COLORNAMES.assoc(name).tap{|c, rgb| break rgb if c }
9
5
  end
10
6
 
11
7
  def rgb2name(rgb)
12
- validate_rgb(rgb)
13
- COLORNAMES.rassoc(rgb).tap { |c, _| break c if c }
8
+ COLORNAMES.rassoc(rgb)
9
+ .tap { |c, _| break c if c }
14
10
  end
15
11
 
16
12
  def rgb2hsb(rgb)
17
- validate_rgb(rgb)
18
13
  r, g, b = rgb.map(&:to_f)
19
14
  hue = Math.atan2(Math.sqrt(3)*(g-b), 2*r-g-b).to_degree
20
15
 
@@ -28,7 +23,6 @@ module Colorable
28
23
 
29
24
  class NotImplemented < StandardError; end
30
25
  def rgb2hsl(rgb)
31
- validate_rgb(rgb)
32
26
  raise NotImplemented, 'Not Implemented Yet'
33
27
  r, g, b = rgb.map(&:to_f)
34
28
  hue = Math.atan2(Math.sqrt(3)*(g-b), 2*r-g-b).to_degree
@@ -41,7 +35,6 @@ module Colorable
41
35
  end
42
36
 
43
37
  def hsb2rgb(hsb)
44
- validate_hsb(hsb)
45
38
  hue, sat, bright = hsb
46
39
  norm = ->range{ hue.norm(range, 0..255) }
47
40
  rgb_h =
@@ -51,7 +44,7 @@ module Colorable
51
44
  when 120..180 then [0, 255, norm[120..180]]
52
45
  when 180..240 then [0, 255-norm[180..240], 255]
53
46
  when 240..300 then [norm[240..300], 0, 255]
54
- when 300..360 then [255, 0, 255-norm[300.360]]
47
+ when 300..360 then [255, 0, 255-norm[300..360]]
55
48
  end
56
49
  rgb_s = rgb_h.map { |val| val + (255-val) * (1-sat/100.0) }
57
50
  rgb_s.map { |val| (val * bright/100.0).round }
@@ -59,7 +52,6 @@ module Colorable
59
52
  alias :hsv2rgb :hsb2rgb
60
53
 
61
54
  def rgb2hex(rgb)
62
- validate_rgb(rgb)
63
55
  hex = rgb.map do |val|
64
56
  val.to_s(16).tap { |h| break "0#{h}" if h.size==1 }
65
57
  end
@@ -67,35 +59,8 @@ module Colorable
67
59
  end
68
60
 
69
61
  def hex2rgb(hex)
70
- validate_hex(hex)
71
62
  _, *hex = hex.unpack('A1A2A2A2')
72
63
  hex.map { |val| val.to_i(16) }
73
64
  end
74
-
75
- private
76
- def validate_rgb(rgb)
77
- if rgb.all? { |val| val.between?(0, 255) }
78
- rgb
79
- else
80
- raise ArgumentError, "'#{rgb}' is invalid for a RGB value"
81
- end
82
- end
83
-
84
- def validate_hsb(hsb)
85
- h, *sb = hsb
86
- if h.between?(0, 360) && sb.all? { |val| val.between?(0, 100) }
87
- hsb
88
- else
89
- raise ArgumentError, "'#{hsb}' is invalid for a HSB value"
90
- end
91
- end
92
-
93
- def validate_hex(hex)
94
- if hex.match(/^#[0-9A-F]{6}$/i)
95
- hex.upcase
96
- else
97
- raise ArgumentError, "'#{hex}' is invalid for a HEX value"
98
- end
99
- end
100
65
  end
101
66
  end
@@ -16,9 +16,11 @@ class Array
16
16
  def same?(&blk)
17
17
  self.uniq(&blk).size==1
18
18
  end
19
+ end
19
20
 
20
- def move_to_top(idx)
21
- arr = self.dup
22
- arr.insert 0, arr.delete_at(idx)
23
- end
21
+ class Pattern < Array
22
+ def ===(other)
23
+ zip(other).all? { |a, b| a === b }
24
+ end
24
25
  end
26
+
@@ -1,3 +1,3 @@
1
1
  module Colorable
2
- VERSION = "0.0.8"
2
+ VERSION = "0.1.0"
3
3
  end
@@ -0,0 +1,356 @@
1
+ require_relative 'spec_helper'
2
+
3
+ include Colorable
4
+ describe RGB do
5
+ describe ".new" do
6
+ context "without arguments" do
7
+ subject { RGB.new }
8
+ its(:to_a) { should eql [0, 0, 0] }
9
+ its(:rgb) { should eql [0, 0, 0] }
10
+ its(:red) { should eql 0 }
11
+ its(:green) { should eql 0 }
12
+ its(:blue) { should eql 0 }
13
+ end
14
+
15
+ context "with arguments" do
16
+ subject { RGB.new 100, 255, 255 }
17
+ its(:to_a) { should eql [100, 255, 255] }
18
+ its(:rgb) { should eql [100, 255, 255] }
19
+ its(:red) { should eql 100 }
20
+ its(:green) { should eql 255 }
21
+ its(:blue) { should eql 255 }
22
+ end
23
+
24
+ context "pass out of RGB range" do
25
+ it "raise ArgumentError" do
26
+ expect { RGB.new(0, 0, 256) }.to raise_error ArgumentError
27
+ expect { RGB.new(-10, 0, 255) }.to raise_error ArgumentError
28
+ expect { RGB.new(256, 256, 256) }.to raise_error ArgumentError
29
+ end
30
+ end
31
+ end
32
+
33
+ describe "#+" do
34
+ context "pass an array of numbers" do
35
+ before(:all) { @rgb = RGB.new(100, 100, 100) }
36
+ subject { @rgb + [0, 50, 100] }
37
+ its(:rgb) { should eql [100, 150, 200] }
38
+ it "keep original same" do @rgb.rgb.should eql [100, 100, 100] end
39
+ end
40
+
41
+ context "when '+' makes out of RGB range or rack of numbers" do
42
+ it "raise ArgumentError" do
43
+ expect { RGB.new(100, 100, 100) + [0, 50, 200] }.to raise_error ArgumentError
44
+ expect { RGB.new(100, 100, 100) + [0, -150, 0] }.to raise_error ArgumentError
45
+ expect { RGB.new(100, 100, 100) + [0, 150] }.to raise_error ArgumentError
46
+ end
47
+ end
48
+
49
+ context "pass a Fixnum" do
50
+ before(:all) { @rgb = RGB.new(100, 100, 100) }
51
+ it { (@rgb + 50).rgb.should eql [150, 150, 150] }
52
+ it "raise ArgumentError" do
53
+ expect { @rgb + 160 }.to raise_error ArgumentError
54
+ end
55
+ end
56
+
57
+ context "coerce make Fixnum#+ accept rgb object" do
58
+ it { (10 + RGB.new(100, 100, 100)).rgb.should eql [110, 110, 110] }
59
+ end
60
+ end
61
+
62
+ describe "#-" do
63
+ context "pass an array of numbers" do
64
+ before(:all) { @rgb = RGB.new(100, 100, 100) }
65
+ subject { @rgb - [0, 50, 100] }
66
+ its(:rgb) { should eql [100, 50, 0] }
67
+ it "keep original same" do @rgb.rgb.should eql [100, 100, 100] end
68
+ end
69
+
70
+ context "when '-' makes out of RGB range" do
71
+ it "raise ArgumentError" do
72
+ expect { RGB.new(100, 100, 100) - [0, 50, 200] }.to raise_error ArgumentError
73
+ expect { RGB.new(100, 100, 100) - [0, -250, 0] }.to raise_error ArgumentError
74
+ end
75
+ end
76
+
77
+ context "pass a Fixnum" do
78
+ before(:all) { @rgb = RGB.new(100, 100, 100) }
79
+ it { (@rgb - 50).rgb.should eql [50, 50, 50] }
80
+ it "raise ArgumentError" do
81
+ expect { @rgb - 160 }.to raise_error ArgumentError
82
+ end
83
+ end
84
+ end
85
+
86
+ describe "#to_s" do
87
+ it { RGB.new(0, 50, 100).to_s.should eql "rgb(0,50,100)"}
88
+ end
89
+
90
+ describe "#to_name" do
91
+ it { RGB.new(240, 248, 255).to_name.should eql 'Alice Blue' }
92
+ end
93
+
94
+ describe "#to_hsb" do
95
+ it { RGB.new(240, 248, 255).to_hsb.should eql [208, 6, 100] }
96
+ end
97
+
98
+ describe "#to_hex" do
99
+ it { RGB.new(240, 248, 255).to_hex.should eql '#F0F8FF' }
100
+ end
101
+ end
102
+
103
+ describe HSB do
104
+ describe ".new" do
105
+ context "without arguments" do
106
+ subject { HSB.new }
107
+ its(:to_a) { should eql [0, 0, 0] }
108
+ its(:hsb) { should eql [0, 0, 0] }
109
+ its(:hue) { should eql 0 }
110
+ its(:sat) { should eql 0 }
111
+ its(:bright) { should eql 0 }
112
+ end
113
+
114
+ context "with arguments" do
115
+ subject { HSB.new 300, 70, 90 }
116
+ its(:to_a) { should eql [300, 70, 90] }
117
+ its(:hsb) { should eql [300, 70, 90] }
118
+ its(:hue) { should eql 300 }
119
+ its(:sat) { should eql 70 }
120
+ its(:bright) { should eql 90 }
121
+ end
122
+
123
+ context "pass out of HSB range" do
124
+ it "raise ArgumentError" do
125
+ expect { HSB.new(0, 0, 120) }.to raise_error ArgumentError
126
+ expect { HSB.new(360, 0, 80) }.to raise_error ArgumentError
127
+ expect { HSB.new(0, -10, 0) }.to raise_error ArgumentError
128
+ end
129
+ end
130
+ end
131
+
132
+ describe "#+" do
133
+ context "pass an array of numbers" do
134
+ before(:all) { @hsb = HSB.new(300, 70, 90) }
135
+ subject { @hsb + [0, 5, 10] }
136
+ its(:hsb) { should eql [300, 75, 100] }
137
+ it "keep original same" do @hsb.hsb.should eql [300, 70, 90] end
138
+ end
139
+
140
+ context "when '+' makes out of HSB range or rack of numbers" do
141
+ it "raise ArgumentError" do
142
+ expect { HSB.new(300, 70, 90) + [0, 50, 0] }.to raise_error ArgumentError
143
+ expect { HSB.new(300, 70, 90) + [0, 0, -100] }.to raise_error ArgumentError
144
+ expect { HSB.new(300, 70, 90) + [0, 5] }.to raise_error ArgumentError
145
+ expect { HSB.new(300, 70, 90) + 5 }.to raise_error ArgumentError
146
+ end
147
+ end
148
+ end
149
+
150
+ describe "#-" do
151
+ context "pass an array of numbers" do
152
+ before(:all) { @hsb = HSB.new(300, 70, 90) }
153
+ subject { @hsb - [0, 5, 10] }
154
+ its(:hsb) { should eql [300, 65, 80] }
155
+ it "keep original same" do @hsb.hsb.should eql [300, 70, 90] end
156
+ end
157
+
158
+ context "when '-' makes out of HSB range" do
159
+ it "raise ArgumentError" do
160
+ expect { HSB.new(300, 7, 90) - [305, 0, 10] }.to raise_error ArgumentError
161
+ expect { HSB.new(300, 70, 90) - [0, -35, 0] }.to raise_error ArgumentError
162
+ end
163
+ end
164
+ end
165
+
166
+ describe "#to_s" do
167
+ it { HSB.new(0, 50, 100).to_s.should eql "hsb(0,50,100)"}
168
+ end
169
+
170
+ describe "#to_name" do
171
+ it { HSB.new(208, 6, 100).to_name.should eql 'Alice Blue' }
172
+ end
173
+
174
+ describe "#to_rgb" do
175
+ it { HSB.new(208, 6, 100).to_rgb.should eql [240, 248, 255] }
176
+ end
177
+
178
+ describe "#to_hex" do
179
+ it { HSB.new(208, 6, 100).to_hex.should eql '#F0F8FF' }
180
+ end
181
+ end
182
+
183
+ describe NAME do
184
+ describe ".new" do
185
+ context "with valid name" do
186
+ subject { NAME.new :alice_blue }
187
+ its(:to_s) { should eql 'Alice Blue' }
188
+ its(:name) { should eql 'Alice Blue' }
189
+ its(:sym) { should eql :alice_blue }
190
+ end
191
+
192
+ context "with invalid name" do
193
+ it "raise ArgumentError" do
194
+ expect { NAME.new :abc_color }.to raise_error ArgumentError
195
+ end
196
+ end
197
+ end
198
+
199
+ describe "dark?" do
200
+ it { NAME.new(:navy).dark?.should be_true }
201
+ it { NAME.new(:alice_blue).dark?.should be_false }
202
+ end
203
+
204
+ describe "#+" do
205
+ context "pass an irregal argument" do
206
+ it "raise ArgumentError" do
207
+ expect { NAME.new(:alice_blue) + [0, 150, 100] }.to raise_error ArgumentError
208
+ end
209
+ end
210
+
211
+ context "pass a Fixnum" do
212
+ before(:all) { @name = NAME.new(:alice_blue) }
213
+ it { (@name + 1).name.should eql 'Antique White' }
214
+ it { (@name + 2).name.should eql 'Aqua' }
215
+ end
216
+
217
+ context "coerce make Fixnum#+ accept name object" do
218
+ it { (1 + NAME.new(:alice_blue)).to_s.should eql 'Antique White' }
219
+ end
220
+ end
221
+
222
+ describe "#-" do
223
+ context "pass an irregal argument" do
224
+ it "raise ArgumentError" do
225
+ expect { NAME.new(:alice_blue) - [0, 150, 100] }.to raise_error ArgumentError
226
+ end
227
+ end
228
+
229
+ context "pass a Fixnum" do
230
+ before(:all) { @name = NAME.new(:alice_blue) }
231
+ it { (@name - 1).name.should eql 'Yellow Green' }
232
+ it { (@name - 2).name.should eql 'Yellow' }
233
+ end
234
+
235
+ context "coerce make Fixnum#+ accept name object" do
236
+ it { (1 - NAME.new(:alice_blue)).to_s.should eql 'Yellow Green' }
237
+ end
238
+ end
239
+
240
+ describe "#to_rgb" do
241
+ it { NAME.new('Alice Blue').to_rgb.should eql [240, 248, 255] }
242
+ end
243
+
244
+ describe "#to_hsb" do
245
+ it { NAME.new('Alice Blue').to_hsb.should eql [208, 6, 100] }
246
+ end
247
+
248
+ describe "#to_hex" do
249
+ it { NAME.new('Alice Blue').to_hex.should eql '#F0F8FF' }
250
+ end
251
+ end
252
+
253
+ describe HEX do
254
+ describe ".new" do
255
+ context "without arguments" do
256
+ subject { HEX.new }
257
+ its(:to_s) { should eql '#FFFFFF' }
258
+ its(:hex) { should eql '#FFFFFF' }
259
+ its(:to_a) { should eql ['FF', 'FF', 'FF'] }
260
+ end
261
+
262
+ context "with arguments" do
263
+ it { HEX.new('#FFFF00').to_s.should eql '#FFFF00' }
264
+ it { HEX.new('#ffff00').to_s.should eql '#FFFF00' }
265
+ it { HEX.new('FFFF00').to_s.should eql '#FFFF00' }
266
+ it { HEX.new('#ff0').to_s.should eql '#FFFF00' }
267
+ it { HEX.new(['FF', 'FF', '00']).to_s.should eql '#FFFF00' }
268
+ end
269
+
270
+ context "pass out of HEX range" do
271
+ it "raise ArgumentError" do
272
+ expect { HEX.new('#FFGG00') }.to raise_error ArgumentError
273
+ expect { HEX.new('aaxxxx') }.to raise_error ArgumentError
274
+ expect { HEX.new(['FF', 'FF', 00]) }.to raise_error ArgumentError
275
+ end
276
+ end
277
+ end
278
+
279
+ describe "#+" do
280
+ context "pass a HEX string" do
281
+ before(:all) { @hex = HEX.new('#FF0000') }
282
+ it { (@hex + '#00FFCC').hex.should eql '#FFFFCC'}
283
+ it { (@hex + '#00ffcc').hex.should eql '#FFFFCC'}
284
+ it { (@hex + '00FFCC').hex.should eql '#FFFFCC'}
285
+ it { (@hex + '#0FC').hex.should eql '#FFFFCC'}
286
+ it "keep original" do @hex.hex.should eql '#FF0000' end
287
+ end
288
+
289
+ context "out of HEX range" do
290
+ it "raise ArgumentError" do
291
+ expect { HEX.new('#FF0000') + '#010000' }.to raise_error ArgumentError
292
+ expect { HEX.new('#FF0000') + '#00ffgg' }.to raise_error ArgumentError
293
+ expect { HEX.new('#000000') + [0, 50, 100] }.to raise_error ArgumentError
294
+ end
295
+ end
296
+
297
+ context "pass a Fixnum" do
298
+ before(:all) { @hex = HEX.new('#000000') }
299
+ it { (@hex + 255).hex.should eql '#FFFFFF' }
300
+ it "raise ArgumentError" do
301
+ expect { @hex + 260 }.to raise_error ArgumentError
302
+ end
303
+ end
304
+
305
+ context "coerce make Fixnum#+ accept hex object" do
306
+ it { (255 + HEX.new('#000000')).hex.should eql '#FFFFFF' }
307
+ end
308
+ end
309
+
310
+ describe "#-" do
311
+ context "pass a HEX string" do
312
+ before(:all) { @hex = HEX.new('#00FFFF') }
313
+ it { (@hex - '#00FFCC').hex.should eql '#000033'}
314
+ it { (@hex - '#00ffcc').hex.should eql '#000033'}
315
+ it { (@hex - '00FFCC').hex.should eql '#000033'}
316
+ it { (@hex - '#0FC').hex.should eql '#000033'}
317
+ it "keep original" do @hex.hex.should eql '#00FFFF' end
318
+ end
319
+
320
+ context "out of HEX range" do
321
+ it "raise ArgumentError" do
322
+ expect { HEX.new('#FF0000') - '#000100' }.to raise_error ArgumentError
323
+ expect { HEX.new('#FF0000') - '#0000cc' }.to raise_error ArgumentError
324
+ expect { HEX.new('#000000') - [0, 50, 100] }.to raise_error ArgumentError
325
+ end
326
+ end
327
+
328
+ context "pass a Fixnum" do
329
+ before(:all) { @hex = HEX.new('#FFFFFF') }
330
+ it { (@hex - 255).hex.should eql '#000000' }
331
+ it "raise ArgumentError" do
332
+ expect { @hex + 260 }.to raise_error ArgumentError
333
+ end
334
+ end
335
+
336
+ context "coerce make Fixnum#+ accept hex object" do
337
+ it { (255 - HEX.new('#FFFFFF')).hex.should eql '#000000' }
338
+ end
339
+ end
340
+
341
+ describe "#to_s" do
342
+ it { HEX.new('#F0F8FF').to_s.should eql '#F0F8FF' }
343
+ end
344
+
345
+ describe "#to_rgb" do
346
+ it { HEX.new('#F0F8FF').to_rgb.should eql [240, 248, 255] }
347
+ end
348
+
349
+ describe "#to_hsb" do
350
+ it { HEX.new('#F0F8FF').to_hsb.should eql [208, 6, 100] }
351
+ end
352
+
353
+ describe "#to_name" do
354
+ it { HEX.new('#F0F8FF').to_name.should eql 'Alice Blue' }
355
+ end
356
+ end