Merged updates from trunk into stable branch
[feedcatcher.git] / vendor / rails / activerecord / test / cases / dirty_test.rb
1 require 'cases/helper'
2 require 'models/topic' # For booleans
3 require 'models/pirate' # For timestamps
4 require 'models/parrot'
5 require 'models/person' # For optimistic locking
6
7 class Pirate # Just reopening it, not defining it
8 attr_accessor :detected_changes_in_after_update # Boolean for if changes are detected
9 attr_accessor :changes_detected_in_after_update # Actual changes
10
11 after_update :check_changes
12
13 private
14 # after_save/update in sweepers, observers, and the model itself
15 # can end up checking dirty status and acting on the results
16 def check_changes
17 if self.changed?
18 self.detected_changes_in_after_update = true
19 self.changes_detected_in_after_update = self.changes
20 end
21 end
22 end
23
24 class NumericData < ActiveRecord::Base
25 self.table_name = 'numeric_data'
26 end
27
28 class DirtyTest < ActiveRecord::TestCase
29 def test_attribute_changes
30 # New record - no changes.
31 pirate = Pirate.new
32 assert !pirate.catchphrase_changed?
33 assert_nil pirate.catchphrase_change
34
35 # Change catchphrase.
36 pirate.catchphrase = 'arrr'
37 assert pirate.catchphrase_changed?
38 assert_nil pirate.catchphrase_was
39 assert_equal [nil, 'arrr'], pirate.catchphrase_change
40
41 # Saved - no changes.
42 pirate.save!
43 assert !pirate.catchphrase_changed?
44 assert_nil pirate.catchphrase_change
45
46 # Same value - no changes.
47 pirate.catchphrase = 'arrr'
48 assert !pirate.catchphrase_changed?
49 assert_nil pirate.catchphrase_change
50 end
51
52 def test_aliased_attribute_changes
53 # the actual attribute here is name, title is an
54 # alias setup via alias_attribute
55 parrot = Parrot.new
56 assert !parrot.title_changed?
57 assert_nil parrot.title_change
58
59 parrot.name = 'Sam'
60 assert parrot.title_changed?
61 assert_nil parrot.title_was
62 assert_equal parrot.name_change, parrot.title_change
63 end
64
65 def test_nullable_number_not_marked_as_changed_if_new_value_is_blank
66 pirate = Pirate.new
67
68 ["", nil].each do |value|
69 pirate.parrot_id = value
70 assert !pirate.parrot_id_changed?
71 assert_nil pirate.parrot_id_change
72 end
73 end
74
75 def test_nullable_decimal_not_marked_as_changed_if_new_value_is_blank
76 numeric_data = NumericData.new
77
78 ["", nil].each do |value|
79 numeric_data.bank_balance = value
80 assert !numeric_data.bank_balance_changed?
81 assert_nil numeric_data.bank_balance_change
82 end
83 end
84
85 def test_nullable_float_not_marked_as_changed_if_new_value_is_blank
86 numeric_data = NumericData.new
87
88 ["", nil].each do |value|
89 numeric_data.temperature = value
90 assert !numeric_data.temperature_changed?
91 assert_nil numeric_data.temperature_change
92 end
93 end
94
95 def test_nullable_integer_zero_to_string_zero_not_marked_as_changed
96 pirate = Pirate.new
97 pirate.parrot_id = 0
98 pirate.catchphrase = 'arrr'
99 assert pirate.save!
100
101 assert !pirate.changed?
102
103 pirate.parrot_id = '0'
104 assert !pirate.changed?
105 end
106
107 def test_zero_to_blank_marked_as_changed
108 pirate = Pirate.new
109 pirate.catchphrase = "Yarrrr, me hearties"
110 pirate.parrot_id = 1
111 pirate.save
112
113 # check the change from 1 to ''
114 pirate = Pirate.find_by_catchphrase("Yarrrr, me hearties")
115 pirate.parrot_id = ''
116 assert pirate.parrot_id_changed?
117 assert_equal([1, nil], pirate.parrot_id_change)
118 pirate.save
119
120 # check the change from nil to 0
121 pirate = Pirate.find_by_catchphrase("Yarrrr, me hearties")
122 pirate.parrot_id = 0
123 assert pirate.parrot_id_changed?
124 assert_equal([nil, 0], pirate.parrot_id_change)
125 pirate.save
126
127 # check the change from 0 to ''
128 pirate = Pirate.find_by_catchphrase("Yarrrr, me hearties")
129 pirate.parrot_id = ''
130 assert pirate.parrot_id_changed?
131 assert_equal([0, nil], pirate.parrot_id_change)
132 end
133
134 def test_object_should_be_changed_if_any_attribute_is_changed
135 pirate = Pirate.new
136 assert !pirate.changed?
137 assert_equal [], pirate.changed
138 assert_equal Hash.new, pirate.changes
139
140 pirate.catchphrase = 'arrr'
141 assert pirate.changed?
142 assert_nil pirate.catchphrase_was
143 assert_equal %w(catchphrase), pirate.changed
144 assert_equal({'catchphrase' => [nil, 'arrr']}, pirate.changes)
145
146 pirate.save
147 assert !pirate.changed?
148 assert_equal [], pirate.changed
149 assert_equal Hash.new, pirate.changes
150 end
151
152 def test_attribute_will_change!
153 pirate = Pirate.create!(:catchphrase => 'arr')
154
155 pirate.catchphrase << ' matey'
156 assert !pirate.catchphrase_changed?
157
158 assert pirate.catchphrase_will_change!
159 assert pirate.catchphrase_changed?
160 assert_equal ['arr matey', 'arr matey'], pirate.catchphrase_change
161
162 pirate.catchphrase << '!'
163 assert pirate.catchphrase_changed?
164 assert_equal ['arr matey', 'arr matey!'], pirate.catchphrase_change
165 end
166
167 def test_association_assignment_changes_foreign_key
168 pirate = Pirate.create!(:catchphrase => 'jarl')
169 pirate.parrot = Parrot.create!(:name => 'Lorre')
170 assert pirate.changed?
171 assert_equal %w(parrot_id), pirate.changed
172 end
173
174 def test_attribute_should_be_compared_with_type_cast
175 topic = Topic.new
176 assert topic.approved?
177 assert !topic.approved_changed?
178
179 # Coming from web form.
180 params = {:topic => {:approved => 1}}
181 # In the controller.
182 topic.attributes = params[:topic]
183 assert topic.approved?
184 assert !topic.approved_changed?
185 end
186
187 def test_partial_update
188 pirate = Pirate.new(:catchphrase => 'foo')
189 old_updated_on = 1.hour.ago.beginning_of_day
190
191 with_partial_updates Pirate, false do
192 assert_queries(2) { 2.times { pirate.save! } }
193 Pirate.update_all({ :updated_on => old_updated_on }, :id => pirate.id)
194 end
195
196 with_partial_updates Pirate, true do
197 assert_queries(0) { 2.times { pirate.save! } }
198 assert_equal old_updated_on, pirate.reload.updated_on
199
200 assert_queries(1) { pirate.catchphrase = 'bar'; pirate.save! }
201 assert_not_equal old_updated_on, pirate.reload.updated_on
202 end
203 end
204
205 def test_partial_update_with_optimistic_locking
206 person = Person.new(:first_name => 'foo')
207 old_lock_version = 1
208
209 with_partial_updates Person, false do
210 assert_queries(2) { 2.times { person.save! } }
211 Person.update_all({ :first_name => 'baz' }, :id => person.id)
212 end
213
214 with_partial_updates Person, true do
215 assert_queries(0) { 2.times { person.save! } }
216 assert_equal old_lock_version, person.reload.lock_version
217
218 assert_queries(1) { person.first_name = 'bar'; person.save! }
219 assert_not_equal old_lock_version, person.reload.lock_version
220 end
221 end
222
223 def test_changed_attributes_should_be_preserved_if_save_failure
224 pirate = Pirate.new
225 pirate.parrot_id = 1
226 assert !pirate.save
227 check_pirate_after_save_failure(pirate)
228
229 pirate = Pirate.new
230 pirate.parrot_id = 1
231 assert_raise(ActiveRecord::RecordInvalid) { pirate.save! }
232 check_pirate_after_save_failure(pirate)
233 end
234
235 def test_reload_should_clear_changed_attributes
236 pirate = Pirate.create!(:catchphrase => "shiver me timbers")
237 pirate.catchphrase = "*hic*"
238 assert pirate.changed?
239 pirate.reload
240 assert !pirate.changed?
241 end
242
243 def test_reverted_changes_are_not_dirty
244 phrase = "shiver me timbers"
245 pirate = Pirate.create!(:catchphrase => phrase)
246 pirate.catchphrase = "*hic*"
247 assert pirate.changed?
248 pirate.catchphrase = phrase
249 assert !pirate.changed?
250 end
251
252 def test_reverted_changes_are_not_dirty_after_multiple_changes
253 phrase = "shiver me timbers"
254 pirate = Pirate.create!(:catchphrase => phrase)
255 10.times do |i|
256 pirate.catchphrase = "*hic*" * i
257 assert pirate.changed?
258 end
259 assert pirate.changed?
260 pirate.catchphrase = phrase
261 assert !pirate.changed?
262 end
263
264
265 def test_reverted_changes_are_not_dirty_going_from_nil_to_value_and_back
266 pirate = Pirate.create!(:catchphrase => "Yar!")
267
268 pirate.parrot_id = 1
269 assert pirate.changed?
270 assert pirate.parrot_id_changed?
271 assert !pirate.catchphrase_changed?
272
273 pirate.parrot_id = nil
274 assert !pirate.changed?
275 assert !pirate.parrot_id_changed?
276 assert !pirate.catchphrase_changed?
277 end
278
279 def test_save_should_store_serialized_attributes_even_with_partial_updates
280 with_partial_updates(Topic) do
281 topic = Topic.create!(:content => {:a => "a"})
282 topic.content[:b] = "b"
283 #assert topic.changed? # Known bug, will fail
284 topic.save!
285 assert_equal "b", topic.content[:b]
286 topic.reload
287 assert_equal "b", topic.content[:b]
288 end
289 end
290
291 private
292 def with_partial_updates(klass, on = true)
293 old = klass.partial_updates?
294 klass.partial_updates = on
295 yield
296 ensure
297 klass.partial_updates = old
298 end
299
300 def check_pirate_after_save_failure(pirate)
301 assert pirate.changed?
302 assert pirate.parrot_id_changed?
303 assert_equal %w(parrot_id), pirate.changed
304 assert_nil pirate.parrot_id_was
305 end
306 end