enumerate_by 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.rdoc +101 -0
- data/LICENSE +20 -0
- data/README.rdoc +117 -0
- data/Rakefile +88 -0
- data/init.rb +1 -0
- data/lib/enumerate_by.rb +366 -0
- data/lib/enumerate_by/extensions/associations.rb +109 -0
- data/lib/enumerate_by/extensions/base_conditions.rb +130 -0
- data/lib/enumerate_by/extensions/serializer.rb +117 -0
- data/lib/enumerate_by/extensions/xml_serializer.rb +41 -0
- data/test/app_root/app/models/car.rb +4 -0
- data/test/app_root/app/models/color.rb +3 -0
- data/test/app_root/db/migrate/001_create_colors.rb +12 -0
- data/test/app_root/db/migrate/002_create_cars.rb +13 -0
- data/test/factory.rb +48 -0
- data/test/test_helper.rb +28 -0
- data/test/unit/assocations_test.rb +70 -0
- data/test/unit/base_conditions_test.rb +46 -0
- data/test/unit/enumerate_by_test.rb +350 -0
- data/test/unit/json_serializer_test.rb +18 -0
- data/test/unit/serializer_test.rb +166 -0
- data/test/unit/xml_serializer_test.rb +71 -0
- metadata +87 -0
data/test/test_helper.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# Load the plugin testing framework
|
2
|
+
$:.unshift("#{File.dirname(__FILE__)}/../../plugin_test_helper/lib")
|
3
|
+
require 'rubygems'
|
4
|
+
require 'plugin_test_helper'
|
5
|
+
|
6
|
+
# Run the migrations
|
7
|
+
ActiveRecord::Migrator.migrate("#{Rails.root}/db/migrate")
|
8
|
+
|
9
|
+
# Mixin the factory helper
|
10
|
+
require File.expand_path("#{File.dirname(__FILE__)}/factory")
|
11
|
+
Test::Unit::TestCase.class_eval do
|
12
|
+
include Factory
|
13
|
+
end
|
14
|
+
|
15
|
+
# Add query counter
|
16
|
+
ActiveRecord::Base.connection.class.class_eval do
|
17
|
+
IGNORED_SQL = [/^PRAGMA/, /^SELECT currval/, /^SELECT CAST/, /^SELECT @@IDENTITY/, /^SELECT @@ROWCOUNT/, /^SAVEPOINT/, /^ROLLBACK TO SAVEPOINT/, /^RELEASE SAVEPOINT/, /SHOW FIELDS/]
|
18
|
+
|
19
|
+
def execute_with_query_record(sql, name = nil, &block)
|
20
|
+
$queries_executed ||= []
|
21
|
+
$queries_executed << sql unless IGNORED_SQL.any? { |r| sql =~ r }
|
22
|
+
execute_without_query_record(sql, name, &block)
|
23
|
+
end
|
24
|
+
|
25
|
+
alias_method_chain :execute, :query_record
|
26
|
+
end
|
27
|
+
|
28
|
+
EnumerateBy.perform_caching = false
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../test_helper'
|
2
|
+
|
3
|
+
class ModelWithBelongsToAssociationTest < ActiveRecord::TestCase
|
4
|
+
def setup
|
5
|
+
@red = create_color(:name => 'red')
|
6
|
+
@green = create_color(:name => 'green')
|
7
|
+
@car = create_car(:name => 'Ford Mustang', :color => nil)
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_should_find_association_from_id
|
11
|
+
@car.color_id = @red.id
|
12
|
+
assert_equal @red, @car.color
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_should_find_association_from_enumerator
|
16
|
+
@car.color = 'green'
|
17
|
+
assert_equal @green, @car.color
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_should_find_assocation_from_record
|
21
|
+
@car.color = @green
|
22
|
+
assert_equal @green, @car.color
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_should_use_nil_if_enumeration_does_not_exist
|
26
|
+
@car.color = 'blue'
|
27
|
+
assert_nil @car.color
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_should_track_associations
|
31
|
+
expected = {'color_id' => 'color'}
|
32
|
+
assert_equal expected, Car.enumeration_associations
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class ModelWithEnumerationScopesTest < ActiveRecord::TestCase
|
37
|
+
def setup
|
38
|
+
@red = create_color(:name => 'red')
|
39
|
+
@blue = create_color(:name => 'blue')
|
40
|
+
@red_car = create_car(:name => 'Ford Mustang', :color => @red)
|
41
|
+
@blue_car = create_car(:name => 'Ford Mustang', :color => @blue)
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_should_have_inclusion_scope_for_single_enumerator
|
45
|
+
assert_equal [@red_car], Car.with_color('red')
|
46
|
+
assert_equal [@blue_car], Car.with_color('blue')
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_should_have_inclusion_scope_for_multiple_enumerators
|
50
|
+
assert_equal [@red_car, @blue_car], Car.with_color('red', 'blue')
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_should_have_exclusion_scope_for_single_enumerator
|
54
|
+
assert_equal [@blue_car], Car.without_color('red')
|
55
|
+
assert_equal [@red_car], Car.without_color('blue')
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_should_have_exclusion_scope_for_multiple_enumerators
|
59
|
+
assert_equal [], Car.without_colors('red', 'blue')
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
class ModelWithPolymorphicBelongsToAssociationTest < ActiveRecord::TestCase
|
64
|
+
def test_should_not_create_named_scopes
|
65
|
+
assert !Car.respond_to?(:with_feature)
|
66
|
+
assert !Car.respond_to?(:with_features)
|
67
|
+
assert !Car.respond_to?(:without_feature)
|
68
|
+
assert !Car.respond_to?(:without_features)
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../test_helper'
|
2
|
+
|
3
|
+
class EnumerationWithFinderConditionsTest < ActiveRecord::TestCase
|
4
|
+
def setup
|
5
|
+
@red = create_color(:name => 'red')
|
6
|
+
@blue = create_color(:name => 'blue')
|
7
|
+
@red_car = create_car(:name => 'Ford Mustang', :color => @red)
|
8
|
+
@blue_car = create_car(:name => 'Ford Mustang', :color => @blue)
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_should_replace_enumerations_in_dynamic_finders
|
12
|
+
assert_equal @red_car, Car.find_by_color('red')
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_should_replace_multiple_enumerations_in_dynamic_finders
|
16
|
+
assert_equal [@red_car, @blue_car], Car.find_all_by_color(%w(red blue))
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_should_replace_enumerations_in_find_conditions
|
20
|
+
assert_equal @red_car, Car.first(:conditions => {:color => 'red'})
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_should_replace_multiple_enumerations_in_find_conditions
|
24
|
+
assert_equal [@red_car, @blue_car], Car.all(:conditions => {:color => %w(red blue)})
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class EnumerationWithFinderUpdatesTest < ActiveRecord::TestCase
|
29
|
+
def setup
|
30
|
+
@red = create_color(:name => 'red')
|
31
|
+
@blue = create_color(:name => 'blue')
|
32
|
+
@red_car = create_car(:name => 'Ford Mustang', :color => @red)
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_should_replace_enumerations_in_update_conditions
|
36
|
+
Car.update_all({:color => 'blue'}, :name => 'Ford Mustang')
|
37
|
+
@red_car.reload
|
38
|
+
assert_equal @blue, @red_car.color
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_should_not_replace_multiple_enumerations_in_update_conditions
|
42
|
+
Car.update_all({:color => %w(red blue)}, :name => 'Ford Mustang')
|
43
|
+
@red_car.reload
|
44
|
+
assert_equal @red, @red_car.color
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,350 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../test_helper'
|
2
|
+
|
3
|
+
class EnumerateByTest < ActiveRecord::TestCase
|
4
|
+
def test_should_raise_exception_if_invalid_option_specified
|
5
|
+
assert_raise(ArgumentError) { Color.enumerate_by(:id, :invalid => true) }
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
class ModelWithoutEnumerationTest < ActiveRecord::TestCase
|
10
|
+
def test_should_not_be_an_enumeration
|
11
|
+
assert !Car.enumeration?
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_should_be_able_to_bootstrap
|
15
|
+
Car.class_eval do
|
16
|
+
extend EnumerateBy::Bootstrapped
|
17
|
+
end
|
18
|
+
|
19
|
+
Car.bootstrap(
|
20
|
+
{:id => 1, :name => 'Ford Mustang'},
|
21
|
+
{:id => 2, :name => 'Ford Explorer'}
|
22
|
+
)
|
23
|
+
|
24
|
+
assert_equal 2, Car.count
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class EnumerationByDefaultTest < ActiveRecord::TestCase
|
29
|
+
def setup
|
30
|
+
@color = Color.new
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_should_not_have_an_id
|
34
|
+
assert @color.id.blank?
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_should_not_have_a_name
|
38
|
+
assert @color.name.blank?
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_should_not_have_an_enumerator
|
42
|
+
assert @color.enumerator.blank?
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_should_have_empty_stringify
|
46
|
+
assert_equal '', @color.to_s
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
class EnumerationTest < ActiveRecord::TestCase
|
51
|
+
def test_should_be_an_enumeration
|
52
|
+
assert Color.enumeration?
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_should_have_an_enumerator_attribute
|
56
|
+
assert_equal :name, Color.enumerator_attribute
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_should_perform_caching
|
60
|
+
assert Color.perform_enumerator_caching
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_should_have_a_cache_store
|
64
|
+
assert_instance_of ActiveSupport::Cache::MemoryStore, Color.enumerator_cache_store
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_should_require_a_name
|
68
|
+
color = new_color(:name => nil)
|
69
|
+
assert !color.valid?
|
70
|
+
assert color.errors.invalid?(:name)
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_should_require_a_unique_name
|
74
|
+
color = create_color(:name => 'red')
|
75
|
+
|
76
|
+
second_color = new_color(:name => 'red')
|
77
|
+
assert !second_color.valid?
|
78
|
+
assert second_color.errors.invalid?(:name)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
class EnumerationWithRecordsTest < ActiveRecord::TestCase
|
83
|
+
def setup
|
84
|
+
@red = create_color(:name => 'red')
|
85
|
+
@green = create_color(:name => 'green')
|
86
|
+
end
|
87
|
+
|
88
|
+
def test_should_index_by_enumerator
|
89
|
+
assert_equal @red, Color['red']
|
90
|
+
end
|
91
|
+
|
92
|
+
def test_should_allow_finding_by_enumerator
|
93
|
+
assert_equal @red, Color.find_by_enumerator('red')
|
94
|
+
end
|
95
|
+
|
96
|
+
def test_should_allow_finding_by_enumerator_with_nil
|
97
|
+
assert_nil Color.find_by_enumerator(nil)
|
98
|
+
end
|
99
|
+
|
100
|
+
def test_should_find_nothing_if_finding_by_unknown_enumerator
|
101
|
+
assert_nil Color.find_by_enumerator('invalid')
|
102
|
+
end
|
103
|
+
|
104
|
+
def test_should_raise_exception_for_invalid_index
|
105
|
+
assert_raise(ActiveRecord::RecordNotFound) {Color['white']}
|
106
|
+
end
|
107
|
+
|
108
|
+
def test_should_allow_finding_all_by_enumerator
|
109
|
+
assert_equal [@red, @green], Color.find_all_by_enumerator(['red', 'green'])
|
110
|
+
end
|
111
|
+
|
112
|
+
def test_should_allow_finding_all_by_enumerator_with_nil
|
113
|
+
assert_equal [], Color.find_all_by_enumerator(nil)
|
114
|
+
end
|
115
|
+
|
116
|
+
def test_should_find_nothing_if_finding_all_by_unknown_enumerator
|
117
|
+
assert_equal [], Color.find_all_by_enumerator('invalid')
|
118
|
+
end
|
119
|
+
|
120
|
+
def test_should_raise_exception_find_finding_all_by_unknown_enumerator!
|
121
|
+
assert_raise(ActiveRecord::RecordNotFound) { Color.find_all_by_enumerator!('invalid') }
|
122
|
+
end
|
123
|
+
|
124
|
+
def test_should_raise_exception_if_only_some_found_when_finding_all_by_enumerator!
|
125
|
+
assert_raise(ActiveRecord::RecordNotFound) { Color.find_all_by_enumerator!(['red', 'invalid']) }
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
class EnumerationAfterBeingCreatedTest < ActiveRecord::TestCase
|
130
|
+
def setup
|
131
|
+
@red = create_color(:name => 'red')
|
132
|
+
@green = create_color(:name => 'green')
|
133
|
+
@blue = create_color(:name => 'blue')
|
134
|
+
end
|
135
|
+
|
136
|
+
def test_should_have_an_enumerator
|
137
|
+
assert_equal 'red', @red.enumerator
|
138
|
+
end
|
139
|
+
|
140
|
+
def test_should_allow_equality_with_enumerator
|
141
|
+
assert @red == 'red'
|
142
|
+
end
|
143
|
+
|
144
|
+
def test_should_allow_equality_with_record
|
145
|
+
assert @red == @red
|
146
|
+
end
|
147
|
+
|
148
|
+
def test_should_allow_equality_with_strings
|
149
|
+
assert 'red' == @red
|
150
|
+
end
|
151
|
+
|
152
|
+
def test_should_raise_exception_on_quality_with_invalid_enumerator
|
153
|
+
assert_raise(ActiveRecord::RecordNotFound) {@red == 'invalid'}
|
154
|
+
end
|
155
|
+
|
156
|
+
def test_should_be_found_in_a_list_of_valid_names
|
157
|
+
assert @red.in?('red')
|
158
|
+
end
|
159
|
+
|
160
|
+
def test_should_not_be_found_in_a_list_of_invalid_names
|
161
|
+
assert !@red.in?('blue', 'green')
|
162
|
+
end
|
163
|
+
|
164
|
+
def test_should_stringify_enumerator
|
165
|
+
assert_equal 'red', @red.to_s
|
166
|
+
assert_equal 'red', @red.to_str
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
class EnumerationWithCachingTest < ActiveRecord::TestCase
|
171
|
+
def setup
|
172
|
+
@red = create_color(:name => 'red')
|
173
|
+
|
174
|
+
EnumerateBy.perform_caching = true
|
175
|
+
end
|
176
|
+
|
177
|
+
def test_should_perform_enumerator_caching
|
178
|
+
assert Color.perform_enumerator_caching
|
179
|
+
end
|
180
|
+
|
181
|
+
def test_should_cache_all_finder_queries
|
182
|
+
assert_queries(1) { Color.find(@red.id) }
|
183
|
+
assert_queries(0) { Color.find(@red.id) }
|
184
|
+
|
185
|
+
assert_queries(1) { Color.all }
|
186
|
+
assert_queries(0) { Color.all }
|
187
|
+
end
|
188
|
+
|
189
|
+
def test_should_cache_exist_queries
|
190
|
+
assert_queries(1) { Color.exists?(:name => 'red') }
|
191
|
+
assert_queries(0) { Color.exists?(:name => 'red') }
|
192
|
+
end
|
193
|
+
|
194
|
+
def test_should_cache_calculate_queries
|
195
|
+
assert_queries(1) { Color.minimum('id') }
|
196
|
+
assert_queries(0) { Color.minimum('id') }
|
197
|
+
end
|
198
|
+
|
199
|
+
def test_should_skip_cache_when_in_uncached_block
|
200
|
+
Color.uncached do
|
201
|
+
assert_queries(1) { Color.find(@red.id) }
|
202
|
+
assert_queries(1) { Color.find(@red.id) }
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
def teardown
|
207
|
+
EnumerateBy.perform_caching = false
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
class EnumerationWithoutCachingTest < ActiveRecord::TestCase
|
212
|
+
def setup
|
213
|
+
@red = create_color(:name => 'red')
|
214
|
+
|
215
|
+
EnumerateBy.perform_caching = true
|
216
|
+
@original_perform_caching = Color.perform_enumerator_caching
|
217
|
+
Color.perform_enumerator_caching = false
|
218
|
+
end
|
219
|
+
|
220
|
+
def test_should_not_cache_finder_queries
|
221
|
+
assert_queries(1) { Color.find(@red.id) }
|
222
|
+
assert_queries(1) { Color.find(@red.id) }
|
223
|
+
|
224
|
+
assert_queries(1) { Color.all }
|
225
|
+
assert_queries(1) { Color.all }
|
226
|
+
end
|
227
|
+
|
228
|
+
def test_should_not_cache_exist_queries
|
229
|
+
assert_queries(1) { Color.exists?(:name => 'red') }
|
230
|
+
assert_queries(1) { Color.exists?(:name => 'red') }
|
231
|
+
end
|
232
|
+
|
233
|
+
def test_should_not_cache_calculate_queries
|
234
|
+
assert_queries(1) { Color.minimum('id') }
|
235
|
+
assert_queries(1) { Color.minimum('id') }
|
236
|
+
end
|
237
|
+
|
238
|
+
def teardown
|
239
|
+
EnumerateBy.perform_caching = false
|
240
|
+
Color.perform_enumerator_caching = @original_perform_caching
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
class EnumerationBootstrappedTest < ActiveRecord::TestCase
|
245
|
+
def setup
|
246
|
+
@red, @green = Color.bootstrap(
|
247
|
+
{:id => 1, :name => 'red'},
|
248
|
+
{:id => 2, :name => 'green'}
|
249
|
+
)
|
250
|
+
end
|
251
|
+
|
252
|
+
def test_should_not_raise_exception_if_id_not_specified
|
253
|
+
assert_nothing_raised { Color.bootstrap({:name => 'red'}, {:name => 'green'}) }
|
254
|
+
assert_equal 2, Color.count
|
255
|
+
end
|
256
|
+
|
257
|
+
def test_should_raise_exception_if_validation_fails
|
258
|
+
assert_raise(ActiveRecord::RecordInvalid) { Color.bootstrap({:id => 1, :name => nil}, {:id => 2, :name => 'green'}) }
|
259
|
+
end
|
260
|
+
|
261
|
+
def test_should_flatten_bootstrap_records
|
262
|
+
Color.bootstrap(
|
263
|
+
[{:id => 1, :name => 'red'}],
|
264
|
+
[{:id => 2, :name => 'green'}]
|
265
|
+
)
|
266
|
+
assert_equal 2, Color.count
|
267
|
+
end
|
268
|
+
|
269
|
+
def test_should_create_records
|
270
|
+
assert_equal @red, Color.find(1)
|
271
|
+
assert_equal 'red', @red.name
|
272
|
+
|
273
|
+
assert_equal @green, Color.find(2)
|
274
|
+
assert_equal 'green', @green.name
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
class EnumerationBootstrappedWithExistingRecordsTest < ActiveRecord::TestCase
|
279
|
+
def setup
|
280
|
+
@red = create_color(:name => 'RED')
|
281
|
+
@green = create_color(:name => 'GREEN')
|
282
|
+
|
283
|
+
Color.bootstrap(
|
284
|
+
{:id => @red.id, :name => 'red'},
|
285
|
+
{:id => @green.id, :name => 'green'}
|
286
|
+
)
|
287
|
+
|
288
|
+
@red.reload
|
289
|
+
@green.reload
|
290
|
+
end
|
291
|
+
|
292
|
+
def test_should_synchronize_all_attributes
|
293
|
+
assert_equal 'red', @red.name
|
294
|
+
assert_equal 'green', @green.name
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
class EnumerationBootstrappedWithDefaultsTest < ActiveRecord::TestCase
|
299
|
+
def setup
|
300
|
+
@red = create_color(:name => 'RED', :html => '#f00')
|
301
|
+
@green = create_color(:name => 'GREEN')
|
302
|
+
|
303
|
+
Color.bootstrap(
|
304
|
+
{:id => @red.id, :name => 'red', :defaults => {:html => '#ff0000'}},
|
305
|
+
{:id => @green.id, :name => 'green', :defaults => {:html => '#00ff00'}}
|
306
|
+
)
|
307
|
+
|
308
|
+
@red.reload
|
309
|
+
@green.reload
|
310
|
+
end
|
311
|
+
|
312
|
+
def test_should_update_all_non_default_attributes
|
313
|
+
assert_equal 'red', @red.name
|
314
|
+
assert_equal 'green', @green.name
|
315
|
+
end
|
316
|
+
|
317
|
+
def test_should_not_update_default_attributes_if_defined
|
318
|
+
assert_equal '#f00', @red.html
|
319
|
+
end
|
320
|
+
|
321
|
+
def test_should_update_default_attributes_if_not_defined
|
322
|
+
assert_equal '#00ff00', @green.html
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
class EnumerationFastBootstrappedTest < ActiveRecord::TestCase
|
327
|
+
def test_should_not_run_validations
|
328
|
+
assert_raise(ActiveRecord::StatementInvalid) { Color.fast_bootstrap({:id => 1, :name => nil}) }
|
329
|
+
end
|
330
|
+
|
331
|
+
def test_should_still_record_timestamps_after_bootstrap
|
332
|
+
Color.fast_bootstrap({:id => 1, :name => 'red'})
|
333
|
+
assert Color.record_timestamps
|
334
|
+
end
|
335
|
+
|
336
|
+
def test_should_still_run_validations_after_bootstrap
|
337
|
+
Color.fast_bootstrap({:id => 1, :name => 'red'})
|
338
|
+
|
339
|
+
color = Color.new
|
340
|
+
assert !color.save
|
341
|
+
assert_raise(ActiveRecord::RecordInvalid) { color.save! }
|
342
|
+
end
|
343
|
+
|
344
|
+
def test_should_still_track_changed_attributes_after_bootstrap
|
345
|
+
Color.fast_bootstrap({:id => 1, :name => 'red'})
|
346
|
+
|
347
|
+
color = Color.new(:name => 'red')
|
348
|
+
assert color.changed?
|
349
|
+
end
|
350
|
+
end
|