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