style_train 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,143 @@
1
+ # Taken from RAILS, see RAIL'S LICENSE for usage
2
+
3
+ # This class has dubious semantics and we only have it so that
4
+ # people can write params[:key] instead of params['key']
5
+ # and they get the same value for both keys.
6
+ unless defined?(HashWithIndifferentAccess)
7
+ class Hash
8
+ def with_indifferent_access
9
+ hash = HashWithIndifferentAccess.new(self)
10
+ hash.default = self.default
11
+ hash
12
+ end
13
+ end
14
+
15
+ class HashWithIndifferentAccess < Hash
16
+ def initialize(constructor = {})
17
+ if constructor.is_a?(Hash)
18
+ super()
19
+ update(constructor)
20
+ else
21
+ super(constructor)
22
+ end
23
+ end
24
+
25
+ def default(key = nil)
26
+ if key.is_a?(Symbol) && include?(key = key.to_s)
27
+ self[key]
28
+ else
29
+ super
30
+ end
31
+ end
32
+
33
+ alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
34
+ alias_method :regular_update, :update unless method_defined?(:regular_update)
35
+
36
+ # Assigns a new value to the hash:
37
+ #
38
+ # hash = HashWithIndifferentAccess.new
39
+ # hash[:key] = "value"
40
+ #
41
+ def []=(key, value)
42
+ regular_writer(convert_key(key), convert_value(value))
43
+ end
44
+
45
+ # Updates the instantized hash with values from the second:
46
+ #
47
+ # hash_1 = HashWithIndifferentAccess.new
48
+ # hash_1[:key] = "value"
49
+ #
50
+ # hash_2 = HashWithIndifferentAccess.new
51
+ # hash_2[:key] = "New Value!"
52
+ #
53
+ # hash_1.update(hash_2) # => {"key"=>"New Value!"}
54
+ #
55
+ def update(other_hash)
56
+ other_hash.each_pair { |key, value| regular_writer(convert_key(key), convert_value(value)) }
57
+ self
58
+ end
59
+
60
+ alias_method :merge!, :update
61
+
62
+ # Checks the hash for a key matching the argument passed in:
63
+ #
64
+ # hash = HashWithIndifferentAccess.new
65
+ # hash["key"] = "value"
66
+ # hash.key? :key # => true
67
+ # hash.key? "key" # => true
68
+ #
69
+ def key?(key)
70
+ super(convert_key(key))
71
+ end
72
+
73
+ alias_method :include?, :key?
74
+ alias_method :has_key?, :key?
75
+ alias_method :member?, :key?
76
+
77
+ # Fetches the value for the specified key, same as doing hash[key]
78
+ def fetch(key, *extras)
79
+ super(convert_key(key), *extras)
80
+ end
81
+
82
+ # Returns an array of the values at the specified indices:
83
+ #
84
+ # hash = HashWithIndifferentAccess.new
85
+ # hash[:a] = "x"
86
+ # hash[:b] = "y"
87
+ # hash.values_at("a", "b") # => ["x", "y"]
88
+ #
89
+ def values_at(*indices)
90
+ indices.collect {|key| self[convert_key(key)]}
91
+ end
92
+
93
+ # Returns an exact copy of the hash.
94
+ def dup
95
+ HashWithIndifferentAccess.new(self)
96
+ end
97
+
98
+ # Merges the instantized and the specified hashes together, giving precedence to the values from the second hash
99
+ # Does not overwrite the existing hash.
100
+ def merge(hash)
101
+ self.dup.update(hash)
102
+ end
103
+
104
+ # Performs the opposite of merge, with the keys and values from the first hash taking precedence over the second.
105
+ # This overloaded definition prevents returning a regular hash, if reverse_merge is called on a HashWithDifferentAccess.
106
+ def reverse_merge(other_hash)
107
+ super other_hash.with_indifferent_access
108
+ end
109
+
110
+ # Removes a specified key from the hash.
111
+ def delete(key)
112
+ super(convert_key(key))
113
+ end
114
+
115
+ def stringify_keys!; self end
116
+ def symbolize_keys!; self end
117
+ def to_options!; self end
118
+
119
+ # Convert to a Hash with String keys.
120
+ def to_hash
121
+ Hash.new(default).merge(self)
122
+ end
123
+
124
+ protected
125
+ def convert_key(key)
126
+ key.kind_of?(Symbol) ? key.to_s : key
127
+ end
128
+
129
+ def convert_value(value)
130
+ case value
131
+ when Hash
132
+ value.with_indifferent_access
133
+ when Array
134
+ value.collect { |e| e.is_a?(Hash) ? e.with_indifferent_access : e }
135
+ else
136
+ value
137
+ end
138
+ end
139
+ end
140
+ end
141
+
142
+ Gnash = HashWithIndifferentAccess unless defined?( Gnash ) # because Gnash is easier to write, thanks Merb!
143
+
@@ -0,0 +1,23 @@
1
+ class Fixnum
2
+ def px
3
+ "#{self}px"
4
+ end
5
+
6
+ def em
7
+ "#{self}em"
8
+ end
9
+
10
+ def percent
11
+ "#{self}%"
12
+ end
13
+
14
+ def pt
15
+ "#{self}pt"
16
+ end
17
+ end
18
+
19
+ class Float
20
+ def em
21
+ "#{self}em"
22
+ end
23
+ end
@@ -0,0 +1,24 @@
1
+ # From ActiveSupport
2
+
3
+ unless String.instance_methods.include?( 'constantize' )
4
+ class String
5
+ # Constantize tries to find a declared constant with the name specified
6
+ # in the string. It raises a NameError when the name is not in CamelCase
7
+ # or is not initialized.
8
+ #
9
+ # @example
10
+ # "Module".constantize #=> Module
11
+ # "Class".constantize #=> Class
12
+ def constantize
13
+ unless /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/ =~ self
14
+ raise NameError, "#{self.inspect} is not a valid constant name!"
15
+ end
16
+
17
+ Object.module_eval("::#{$1}", __FILE__, __LINE__)
18
+ end
19
+
20
+ def dasherize
21
+ self.gsub('_', '-') || self
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,22 @@
1
+ $LOAD_PATH.unshift( File.dirname(__FILE__) )
2
+
3
+ # ruby libs
4
+ require 'singleton'
5
+
6
+ # Support stuff
7
+ require 'style_train/support/gnash'
8
+ require 'style_train/support/string'
9
+ require 'style_train/support/numbers'
10
+
11
+ # The color load!
12
+ require 'style_train/color_types/color_type'
13
+ require 'style_train/color_types/rgb_color'
14
+ require 'style_train/color_types/hsl_color'
15
+ require 'style_train/color_types/keyword_color'
16
+ require 'style_train/color_types/hex_color'
17
+ require "style_train/color"
18
+ # require 'lib/palette'
19
+
20
+ require "style_train/sheet"
21
+ # require 'set'
22
+ # require 'style'
@@ -0,0 +1,224 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ Color = StyleTrain::Color unless defined?(Color)
4
+ HexColor = StyleTrain::HexColor unless defined?( HexColor )
5
+ KeywordColor = StyleTrain::KeywordColor unless defined?( KeywordColor )
6
+ RGBcolor = StyleTrain::RGBcolor unless defined?( RGBcolor )
7
+
8
+ describe Color do
9
+ def build_color_type( color_class, args )
10
+ color_class.new(:color => args)
11
+ end
12
+
13
+ describe 'alpha' do
14
+ it 'should be delegated' do
15
+ color = Color.new(:black, :alpha => 0.5)
16
+ color.alpha.should == 0.5
17
+ end
18
+
19
+ it 'setter should be delegated' do
20
+ color = Color.new(:black)
21
+ color.alpha = 0.5
22
+ color.delegate.alpha.should == 0.5
23
+ end
24
+ end
25
+
26
+ describe 'initialization' do
27
+ describe 'with an existing color' do
28
+ it 'should build a delegate of the existing type' do
29
+ Color.new(Color.new(:white)).delegate.should == build_color_type(KeywordColor, :white)
30
+ Color.new(Color.new('#FFF')).delegate.should == build_color_type(HexColor, '#FFF')
31
+ Color.new(Color.new([255,255,255])).delegate.should == build_color_type(RGBcolor, [255,255,255])
32
+ end
33
+
34
+ it 'should set the background if the argument color has a background' do
35
+ Color.new(Color.new(:white), :background => :gray).background.should == Color.new(:gray)
36
+ Color.new(Color.new(:white)).background_set.should_not be_true
37
+ end
38
+
39
+ it 'after the new color is created changes to the delegate should not change the original' do
40
+ original = Color.new(:white)
41
+ new_color = Color.new(original, :background => :black, :alpha => 0.5)
42
+ original.alpha.should == 1.0
43
+ end
44
+
45
+ it 'should set the alpha' do
46
+ color = Color.new(Color.new(:white), :alpha => 0.5)
47
+ color.alpha.should == 0.5
48
+ end
49
+ end
50
+
51
+ describe 'assigns a delegate' do
52
+ describe 'with a keyword argument' do
53
+ it 'should build an html keyword color' do
54
+ Color.new("gray").delegate.should == build_color_type(KeywordColor, 'gray')
55
+ end
56
+
57
+ it 'should build a svg keyword color' do
58
+ Color.new(:linen).delegate.should == build_color_type(KeywordColor, :linen)
59
+ end
60
+ end
61
+
62
+ describe 'with a hex string argument' do
63
+ it '(3-digit no #) should build a hex color' do
64
+ Color.new('333').delegate.should == build_color_type(HexColor, '333')
65
+ end
66
+
67
+ it '(3-digit with #) should build a hex color' do
68
+ Color.new('#333').delegate.should == build_color_type(HexColor, '#333')
69
+ end
70
+
71
+ it '(6-digit no #) should build a hex color' do
72
+ Color.new('333333').delegate.should == build_color_type(HexColor, '333333')
73
+ end
74
+
75
+ it '(6-digit with #) should build a hex color' do
76
+ Color.new('#333333').delegate.should == build_color_type(HexColor, '#333333')
77
+ end
78
+ end
79
+
80
+ describe 'with an rgb array argument' do
81
+ it '(with a byte array) should build a rgb color' do
82
+ Color.new([127,127,127]).delegate.should == build_color_type(RGBcolor, [127,127,127])
83
+ end
84
+
85
+ it '(with a percentage array) should build a rgb color' do
86
+ Color.new(['50%','50%','50%']).delegate.should == build_color_type(RGBcolor, [127,127,127])
87
+ end
88
+ end
89
+
90
+ describe 'hsl colors'
91
+ end
92
+
93
+ describe 'options' do
94
+ it 'should pass on the alpha argument' do
95
+ color = Color.new(:black, :alpha => 0.5)
96
+ color.alpha.should == 0.5
97
+ end
98
+ end
99
+ end
100
+
101
+ describe 'comparison' do
102
+ before do
103
+ @color = Color.new(:black)
104
+ @color_type = KeywordColor.new(:color => :black)
105
+ @color_type_too = RGBcolor.new(:color => [0,0,0])
106
+ @color_too = Color.new(:black)
107
+ end
108
+
109
+ describe '=~' do
110
+ describe 'with another Color object' do
111
+ it 'should not be true if the delegates don\'t match' do
112
+ (@color =~ Color.new(:white)).should_not be_true
113
+ end
114
+
115
+ it 'should be indifferent to background' do
116
+ (@color =~ Color.new(:black, :background => :yellow)).should be_true
117
+ end
118
+
119
+ it 'should be true if background and delegate match' do
120
+ (@color =~ @color_too).should be_true
121
+ end
122
+ end
123
+
124
+ describe 'with a ColorType object' do
125
+ it 'should be true if the delegate matches the ColorType' do
126
+ (@color =~ @color_type).should be_true
127
+ (@color =~ @color_type_too).should be_true
128
+ end
129
+ end
130
+ end
131
+
132
+ describe '==' do
133
+ describe 'with another Color object' do
134
+ it 'should not be true if the delegates don\'t match' do
135
+ (@color == Color.new(:white)).should_not be_true
136
+ end
137
+
138
+ it 'should be indifferent to background' do
139
+ (@color == Color.new(:black, :background => :yellow)).should_not be_true
140
+ end
141
+
142
+ it 'should be true if background and delegate match' do
143
+ (@color == @color_too).should be_true
144
+ end
145
+ end
146
+
147
+ describe 'with a ColorType object' do
148
+ it 'should be false' do
149
+ (@color == @color_type).should be_false
150
+ end
151
+ end
152
+ end
153
+ end
154
+
155
+ describe 'background' do
156
+ before do
157
+ @color = Color.new(:black, :alpha => 0.5)
158
+ end
159
+
160
+ it 'should have a white opaque background by default' do
161
+ @color.background.should =~ Color.new(:white)
162
+ end
163
+
164
+ it 'should be settable' do
165
+ @color.background = :black
166
+ @color.background.should =~ Color.new(:black)
167
+ end
168
+
169
+ it 'should ignore alpha' do
170
+ @color.background = {:color => :black, :alpha => 0.5 }
171
+ @color.background.delegate.should == Color.new(:black, :alpha => 1.0).delegate
172
+ end
173
+
174
+ it 'should be initalized into the instance' do
175
+ color = Color.new(:black, :background => :yellow)
176
+ color.background.should =~ Color.new(:yellow)
177
+ end
178
+
179
+ it 'should set a flag when it is set not by default' do
180
+ color = Color.new(:black)
181
+ color.background
182
+ color.background_set.should_not == true
183
+
184
+ color = Color.new(:black, :background => :yellow)
185
+ color.background_set.should == true
186
+ end
187
+ end
188
+
189
+ describe 'rendering' do
190
+ it 'should render via the delegates default method by default' do
191
+ Color.new(:white).render.should == build_color_type(KeywordColor, :white).render_as_given
192
+ Color.new(['100%', '100%', '100%']).render.should == build_color_type(RGBcolor, ['100%', '100%', '100%']).render_as_given
193
+ Color.new([255,255,255]).render.should == build_color_type(RGBcolor, [255,255,255]).render_as_given
194
+ Color.new('#FFF').render.should == build_color_type(HexColor, '#FFF').render_as_given
195
+ end
196
+
197
+ it 'should render rgba if it has transparency' do
198
+ Color.new(:white, :alpha => 0.5).render.should =~ /rgba/
199
+ Color.new(['100%', '100%', '100%'], :alpha => 0.5).render.should =~ /rgba/
200
+ Color.new([255,255,255], :alpha => 0.5).render.should =~ /rgba/
201
+ Color.new('#FFF', :alpha => 0.5).render.should =~ /rgba/
202
+ end
203
+
204
+ describe 'ie' do
205
+ it 'should render as a hex color' do
206
+ Color.new(:white).render(:ie).should == build_color_type(HexColor, '#ffffff').render_as_given
207
+ Color.new(['100%', '100%', '100%']).render(:ie).should == build_color_type(HexColor, '#ffffff').render_as_given
208
+ Color.new([255,255,255]).render(:ie).should == build_color_type(HexColor, '#ffffff').render_as_given
209
+ Color.new('#FFF').render(:ie).should == build_color_type(HexColor, '#ffffff').render_as_given
210
+ end
211
+
212
+ it 'should render delegate layered on the background if there is transparency and as a hex color' do
213
+ Color.new(:black, :alpha => 0.5).render(:ie).should == build_color_type( HexColor, '#808080' ).render
214
+ Color.new(['0%', '0%', '0%'], :alpha => 0.5).render(:ie).should == build_color_type( HexColor, '#808080' ).render
215
+ Color.new([255,255,255], :alpha => 0.5, :background => :black).render(:ie).should == build_color_type( HexColor, '#808080' ).render
216
+ Color.new('#FFF', :alpha => 0.5, :background => :black).render(:ie).should == build_color_type( HexColor, '#808080' ).render
217
+ end
218
+ end
219
+
220
+ it 'should alias #to_s to render' do
221
+ Color.new(:black, :alpha => 0.5).to_s.should == Color.new(:black, :alpha => 0.5).render
222
+ end
223
+ end
224
+ end
@@ -0,0 +1,370 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ ColorType = StyleTrain::ColorType unless defined?( ColorType )
4
+ HexColor = StyleTrain::HexColor unless defined?( HexColor )
5
+ KeywordColor = StyleTrain::KeywordColor unless defined?( KeywordColor )
6
+ RGBcolor = StyleTrain::RGBcolor unless defined?( RGBcolor )
7
+
8
+ describe ColorType do
9
+ describe 'class methods' do
10
+ describe 'percentages' do
11
+ it 'should return false if string does not contain a percentage' do
12
+ ColorType.percentage( '255' ).should == false
13
+ end
14
+
15
+ it 'should return the number' do
16
+ ColorType.percentage( '33%' ).should == 33
17
+ end
18
+
19
+ it 'should return decimal values of percentages' do
20
+ ColorType.percentage( '33.5%' ).should == 33.5
21
+ end
22
+
23
+ it 'should raise an argument error if the percentage is out of range' do
24
+ lambda { ColorType.percentage( '133%' ) }.should raise_error( ArgumentError )
25
+ end
26
+ end
27
+
28
+ describe 'byte numbers' do
29
+ it 'should return the number' do
30
+ ColorType.byte( '33' ).should == 33
31
+ ColorType.byte( '0' ).should == 0
32
+ end
33
+
34
+ it 'should raise an argument error if the number is out of range' do
35
+ lambda { ColorType.byte( '256' ) }.should raise_error( ArgumentError )
36
+ end
37
+ end
38
+
39
+ describe 'conversion' do
40
+ it 'should convert from byte numbers to percentages' do
41
+ ColorType.byte_to_percentage( 255 ).should == 100
42
+ ColorType.byte_to_percentage( 127 ).should == 50
43
+ ColorType.byte_to_percentage( 64 ).should == 25
44
+ ColorType.byte_to_percentage( 0 ).should == 0
45
+ lambda{ ColorType.byte_to_percentage(256) }.should raise_error( ColorType::ByteNumberError )
46
+ lambda{ ColorType.byte_to_percentage(-5) }.should raise_error( ColorType::ByteNumberError )
47
+ lambda{ ColorType.byte_to_percentage(122.6) }.should_not raise_error
48
+ end
49
+
50
+ it 'should convert from percentage to byte' do
51
+ ColorType.percent_to_byte( 100 ).should == 255
52
+ ColorType.percent_to_byte( 50 ).should == 127
53
+ ColorType.percent_to_byte( 25 ).should == 64
54
+ ColorType.percent_to_byte( 0 ).should == 0
55
+ lambda{ ColorType.percent_to_byte(101) }.should raise_error( ColorType::PercentageError )
56
+ lambda{ ColorType.percent_to_byte(-5) }.should raise_error( ColorType::PercentageError )
57
+ lambda{ ColorType.percent_to_byte(22.6) }.should_not raise_error
58
+ end
59
+
60
+ it 'custom argument errors should raise custom messages' do
61
+ begin
62
+ ColorType.percent_to_byte(101)
63
+ rescue Exception => e
64
+ e.message.match(/must be between 0 and/)
65
+ end
66
+
67
+ begin
68
+ ColorType.byte_to_percentage(256)
69
+ rescue Exception => e
70
+ e.message.match(/must be between 0 and/)
71
+ end
72
+ end
73
+ end
74
+
75
+ describe '#is_color?' do
76
+ it 'should recognize any of the color types' do
77
+ ColorType.is_color?(KeywordColor.new(:color => :linen)).should be_true
78
+ ColorType.is_color?(RGBcolor.new(:color => [0,0,0])).should be_true
79
+ end
80
+
81
+ it 'should be false if not a color type' do
82
+ ColorType.is_color?( nil ).should be_false
83
+ ColorType.is_color?( {} ).should be_false
84
+ ColorType.is_color?( [] ).should be_false
85
+ end
86
+ end
87
+
88
+ describe '#make' do
89
+ it 'returns nil if not the right type' do
90
+ HexColor.make(:color => :lightyellow).should be_nil
91
+ end
92
+
93
+ it 'returns an instance on the color type if arguments are correct' do
94
+ KeywordColor.make(:color => :lightyellow).is_a?(KeywordColor).should be_true
95
+ end
96
+ end
97
+ end
98
+
99
+ describe 'instance methods' do
100
+ describe 'conversion' do
101
+ describe 'from rgb' do
102
+ before do
103
+ @color = RGBcolor.new(:color => [0,0,0])
104
+ end
105
+
106
+ it 'to keyword works' do
107
+ new_color = @color.to(:keyword)
108
+ new_color.class.should == KeywordColor
109
+ new_color.keyword.should == :black
110
+ end
111
+
112
+ it 'to hex works' do
113
+ new_color = @color.to(:hex)
114
+ new_color.class.should == HexColor
115
+ new_color.hex_6.should == 0x000000
116
+ end
117
+ end
118
+
119
+ describe 'from hex' do
120
+ before do
121
+ @color = HexColor.new(:color => '#000')
122
+ end
123
+
124
+ it 'to keyword works' do
125
+ new_color = @color.to(:keyword)
126
+ new_color.class.should == KeywordColor
127
+ new_color.keyword.should == :black
128
+ end
129
+
130
+ it 'to rgb works' do
131
+ new_color = @color.to(:rgb)
132
+ new_color.class.should == RGBcolor
133
+ new_color.red.should == 0
134
+ new_color.green.should == 0
135
+ new_color.blue.should == 0
136
+ end
137
+ end
138
+
139
+ describe 'from keyword' do
140
+ before do
141
+ @color = KeywordColor.new(:color => :black)
142
+ end
143
+
144
+ it 'to rgb works' do
145
+ new_color = @color.to(:rgb)
146
+ new_color.class.should == RGBcolor
147
+ new_color.red.should == 0
148
+ new_color.green.should == 0
149
+ new_color.blue.should == 0
150
+ end
151
+
152
+ it 'to hex works' do
153
+ new_color = @color.to(:hex)
154
+ new_color.class.should == HexColor
155
+ new_color.hex_6.should == 0x000000
156
+ end
157
+ end
158
+ end
159
+
160
+ describe 'alpha' do
161
+ before do
162
+ @color = KeywordColor.new(:color => :black)
163
+ end
164
+
165
+ it 'should be 1.0 by default' do
166
+ @color.alpha.should == 1.0
167
+ end
168
+
169
+ it 'should be 1.0 if alpha is entered as 1' do
170
+ @color.alpha = 1
171
+ @color.alpha.should == 1.0
172
+ end
173
+
174
+ it 'should be be 0.0 if set to 0' do
175
+ @color.alpha = 0
176
+ @color.alpha.should == 0.0
177
+ end
178
+
179
+ it 'should be a decimal value between zero and one if set that way' do
180
+ @color.alpha = 0.5
181
+ @color.alpha.should == 0.5
182
+ end
183
+
184
+ it 'should be set via the initializer' do
185
+ color = KeywordColor.new(:color => :black, :alpha => 0.5)
186
+ color.alpha.should == 0.5
187
+ end
188
+
189
+ it 'should raise an error if out of range' do
190
+ lambda{ @color.alpha = 1.5 }.should raise_error
191
+ end
192
+
193
+ describe '#alpha?' do
194
+ it '#should be false if alpha value is 1.0' do
195
+ @color.alpha?.should == false
196
+ end
197
+
198
+ it 'should be true if alpha is greater than 0 and less than 1' do
199
+ @color.alpha = 0.5
200
+ @color.alpha?.should be_true
201
+ end
202
+ end
203
+ end
204
+
205
+ describe 'comparisons' do
206
+ before do
207
+ @hex = HexColor.new(:color => '#000')
208
+ @keyword = KeywordColor.new(:color => :black)
209
+ end
210
+
211
+ describe '=~' do
212
+ it 'should be true if the r, g, b values are the same' do
213
+ (@hex =~ @keyword).should == true
214
+ end
215
+
216
+ it 'should be true even if the alpha values are different' do
217
+ @hex.alpha = 0.5
218
+ (@hex =~ @keyword).should == true
219
+ end
220
+
221
+ it 'should compare with the delagate of a color' do
222
+ (@hex =~ StyleTrain::Color.new('#000')).should == true
223
+ (@hex =~ StyleTrain::Color.new('#fff')).should == false
224
+ end
225
+ end
226
+
227
+ describe '==' do
228
+ it 'should be true if the colors are =~ and also have the same alpha' do
229
+ (@hex == @keyword).should == true
230
+ end
231
+
232
+ it 'should be false if the alphas are different' do
233
+ @hex.alpha = 0.5
234
+ (@hex == @keyword).should == false
235
+ end
236
+
237
+ it 'should be false if the r, g, b values are different' do
238
+ color = KeywordColor.new(:color => :maroon)
239
+ (color == @keyword).should == false
240
+ end
241
+ end
242
+
243
+ describe '===' do
244
+ before do
245
+ @key2 = KeywordColor.new(:color => :black)
246
+ end
247
+
248
+ it 'should be true if the colors are == and also have the same class' do
249
+ (@key2 === @keyword).should == true
250
+ end
251
+
252
+ it 'should be false if the classes are different' do
253
+ (@keyword === @hex).should == false
254
+ end
255
+
256
+ it 'should be false if the alphas are different' do
257
+ @key2.alpha = 0.5
258
+ (@key2 == @keyword).should == false
259
+ end
260
+
261
+ it 'should be false if the r, g, b values are different' do
262
+ color = KeywordColor.new(:color => :maroon)
263
+ (color == @keyword).should == false
264
+ end
265
+ end
266
+ end
267
+
268
+ describe 'mixing' do
269
+ before do
270
+ @rgb = RGBcolor.new(:color => [20,40,60], :alpha => 0.5)
271
+ @hex = HexColor.new(:color => '#666')
272
+ end
273
+
274
+ describe 'averaging' do
275
+ it 'should average the r, g, and b values' do
276
+ color = @rgb.mix(@hex)
277
+ color.r.should == ((102+20)/2.0).round
278
+ color.g.should == ((102+40)/2.0).round
279
+ color.b.should == ((102+60)/2.0).round
280
+ end
281
+
282
+ it 'should average the alpha' do
283
+ color = @rgb.mix(@hex)
284
+ color.alpha.should == 0.75
285
+ end
286
+
287
+ it 'should return the original color type' do
288
+ color = @rgb.mix(@hex)
289
+ color.class.should == RGBcolor
290
+
291
+ color = @hex.mix(@rgb)
292
+ color.class.should == HexColor
293
+ end
294
+ end
295
+
296
+ describe 'layering' do
297
+ before do
298
+ @white = HexColor.new(:color => '#FFF')
299
+ @shadow = HexColor.new(:color => '#000', :alpha => 0.5)
300
+ end
301
+
302
+ it 'should blend on the class method' do
303
+ ColorType.blend(255, 0, 0.5).should == 128
304
+ ColorType.blend(128, 0, 0.5).should == 64
305
+ ColorType.blend(255, 0, 0.25).should == 64
306
+ end
307
+
308
+ describe 'r, g, b' do
309
+ describe 'opaque top layer' do
310
+ it 'should have the r, g, b values of the top layer' do
311
+ color = @rgb.layer(@hex)
312
+ color.r.should == 102
313
+ color.g.should == 102
314
+ color.b.should == 102
315
+ end
316
+ end
317
+
318
+ describe 'opaque bottom layer' do
319
+ before do
320
+ @color = @white.layer(@shadow)
321
+ end
322
+
323
+ it 'should mix the r, g, and b in proportion to the top layer\'s alpha' do
324
+ @color.r.should == 128
325
+ @color.g.should == 128
326
+ @color.b.should == 128
327
+
328
+ red = RGBcolor.new(:color => [153,0,0], :alpha => 0.25)
329
+ color = @white.layer(red)
330
+ color.r.should == (153*0.25 + 255*0.75).round
331
+ color.g.should == (0*0.25 + 255*0.75).round
332
+ color.b.should == (0*0.25 + 255*0.75).round
333
+ end
334
+ end
335
+ end
336
+
337
+ describe 'alpha blending' do
338
+ it 'should be 1.0 if bottom layer is opaque' do
339
+ color = @white.layer(@shadow)
340
+ color.alpha.should == 1.0
341
+ end
342
+
343
+ it 'should be 1.0 if the top layer is opaque' do
344
+ color = @shadow.layer(@white)
345
+ color.alpha.should == 1.0
346
+ end
347
+
348
+ it 'should have an alpha greater than or equal to the composites' do
349
+ color = @rgb.layer(@shadow)
350
+ (color.alpha >= @rgb.alpha).should be_true
351
+ (color.alpha >= @shadow.alpha).should be_true
352
+ end
353
+
354
+ it 'should calculate the blending of two alphas properly' do
355
+ color = @rgb.layer(@shadow)
356
+ color.alpha.should == 0.75 # 0.5 for the base color, plus 0.5 of the remaining transparency
357
+ end
358
+ end
359
+ end
360
+
361
+ describe 'stepping'
362
+
363
+ end
364
+
365
+ describe 'rendering' do
366
+ # todo: do color specs first, to work out where the blending of background should happen
367
+ end
368
+ end
369
+
370
+ end