~michaelforrest/use-case-mapper/trunk

« back to all changes in this revision

Viewing changes to vendor/rails/activerecord/test/cases/nested_attributes_test.rb

  • Committer: Michael Forrest
  • Date: 2010-10-15 16:28:50 UTC
  • Revision ID: michael.forrest@canonical.com-20101015162850-tj2vchanv0kr0dun
refrozeĀ gems

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
require "cases/helper"
 
2
require "models/pirate"
 
3
require "models/ship"
 
4
require "models/bird"
 
5
require "models/parrot"
 
6
require "models/treasure"
 
7
 
 
8
module AssertRaiseWithMessage
 
9
  def assert_raise_with_message(expected_exception, expected_message)
 
10
    begin
 
11
      error_raised = false
 
12
      yield
 
13
    rescue expected_exception => error
 
14
      error_raised = true
 
15
      actual_message = error.message
 
16
    end
 
17
    assert error_raised
 
18
    assert_equal expected_message, actual_message
 
19
  end
 
20
end
 
21
 
 
22
class TestNestedAttributesInGeneral < ActiveRecord::TestCase
 
23
  include AssertRaiseWithMessage
 
24
 
 
25
  def teardown
 
26
    Pirate.accepts_nested_attributes_for :ship, :allow_destroy => true, :reject_if => proc { |attributes| attributes.empty? }
 
27
  end
 
28
 
 
29
  def test_base_should_have_an_empty_nested_attributes_options
 
30
    assert_equal Hash.new, ActiveRecord::Base.nested_attributes_options
 
31
  end
 
32
 
 
33
  def test_should_add_a_proc_to_nested_attributes_options
 
34
    [:parrots, :birds].each do |name|
 
35
      assert_instance_of Proc, Pirate.nested_attributes_options[name][:reject_if]
 
36
    end
 
37
  end
 
38
 
 
39
  def test_should_raise_an_ArgumentError_for_non_existing_associations
 
40
    assert_raise_with_message ArgumentError, "No association found for name `honesty'. Has it been defined yet?" do
 
41
      Pirate.accepts_nested_attributes_for :honesty
 
42
    end
 
43
  end
 
44
 
 
45
  def test_should_disable_allow_destroy_by_default
 
46
    Pirate.accepts_nested_attributes_for :ship
 
47
 
 
48
    pirate = Pirate.create!(:catchphrase => "Don' botharrr talkin' like one, savvy?")
 
49
    ship = pirate.create_ship(:name => 'Nights Dirty Lightning')
 
50
 
 
51
    assert_no_difference('Ship.count') do
 
52
      pirate.update_attributes(:ship_attributes => { '_destroy' => true })
 
53
    end
 
54
  end
 
55
 
 
56
  def test_a_model_should_respond_to_underscore_destroy_and_return_if_it_is_marked_for_destruction
 
57
    ship = Ship.create!(:name => 'Nights Dirty Lightning')
 
58
    assert !ship._destroy
 
59
    ship.mark_for_destruction
 
60
    assert ship._destroy
 
61
  end
 
62
 
 
63
  def test_underscore_delete_is_deprecated
 
64
    ActiveSupport::Deprecation.expects(:warn)
 
65
    ship = Ship.create!(:name => 'Nights Dirty Lightning')
 
66
    ship._delete
 
67
  end
 
68
 
 
69
  def test_reject_if_method_without_arguments
 
70
    Pirate.accepts_nested_attributes_for :ship, :reject_if => :new_record?
 
71
 
 
72
    pirate = Pirate.new(:catchphrase => "Stop wastin' me time")
 
73
    pirate.ship_attributes = { :name => 'Black Pearl' }
 
74
    assert_no_difference('Ship.count') { pirate.save! }
 
75
  end
 
76
 
 
77
  def test_reject_if_method_with_arguments
 
78
    Pirate.accepts_nested_attributes_for :ship, :reject_if => :reject_empty_ships_on_create
 
79
 
 
80
    pirate = Pirate.new(:catchphrase => "Stop wastin' me time")
 
81
    pirate.ship_attributes = { :name => 'Red Pearl', :_reject_me_if_new => true }
 
82
    assert_no_difference('Ship.count') { pirate.save! }
 
83
 
 
84
    # pirate.reject_empty_ships_on_create returns false for saved records
 
85
    pirate.ship_attributes = { :name => 'Red Pearl', :_reject_me_if_new => true }
 
86
    assert_difference('Ship.count') { pirate.save! }
 
87
  end
 
88
 
 
89
  def test_reject_if_with_indifferent_keys
 
90
    Pirate.accepts_nested_attributes_for :ship, :reject_if => proc {|attributes| attributes[:name].blank? }
 
91
 
 
92
    pirate = Pirate.new(:catchphrase => "Stop wastin' me time")
 
93
    pirate.ship_attributes = { :name => 'Hello Pearl' }
 
94
    assert_difference('Ship.count') { pirate.save! }
 
95
  end
 
96
end
 
97
 
 
98
class TestNestedAttributesOnAHasOneAssociation < ActiveRecord::TestCase
 
99
  include AssertRaiseWithMessage
 
100
 
 
101
  def setup
 
102
    @pirate = Pirate.create!(:catchphrase => "Don' botharrr talkin' like one, savvy?")
 
103
    @ship = @pirate.create_ship(:name => 'Nights Dirty Lightning')
 
104
  end
 
105
 
 
106
  def test_should_raise_argument_error_if_trying_to_build_polymorphic_belongs_to
 
107
    assert_raise_with_message ArgumentError, "Cannot build association looter. Are you trying to build a polymorphic one-to-one association?" do
 
108
      Treasure.new(:name => 'pearl', :looter_attributes => {:catchphrase => "Arrr"})
 
109
    end
 
110
  end
 
111
 
 
112
  def test_should_define_an_attribute_writer_method_for_the_association
 
113
    assert_respond_to @pirate, :ship_attributes=
 
114
  end
 
115
 
 
116
  def test_should_build_a_new_record_if_there_is_no_id
 
117
    @ship.destroy
 
118
    @pirate.reload.ship_attributes = { :name => 'Davy Jones Gold Dagger' }
 
119
 
 
120
    assert @pirate.ship.new_record?
 
121
    assert_equal 'Davy Jones Gold Dagger', @pirate.ship.name
 
122
  end
 
123
 
 
124
  def test_should_not_build_a_new_record_if_there_is_no_id_and_destroy_is_truthy
 
125
    @ship.destroy
 
126
    @pirate.reload.ship_attributes = { :name => 'Davy Jones Gold Dagger', :_destroy => '1' }
 
127
 
 
128
    assert_nil @pirate.ship
 
129
  end
 
130
 
 
131
  def test_should_not_build_a_new_record_if_a_reject_if_proc_returns_false
 
132
    @ship.destroy
 
133
    @pirate.reload.ship_attributes = {}
 
134
 
 
135
    assert_nil @pirate.ship
 
136
  end
 
137
 
 
138
  def test_should_replace_an_existing_record_if_there_is_no_id
 
139
    @pirate.reload.ship_attributes = { :name => 'Davy Jones Gold Dagger' }
 
140
 
 
141
    assert @pirate.ship.new_record?
 
142
    assert_equal 'Davy Jones Gold Dagger', @pirate.ship.name
 
143
    assert_equal 'Nights Dirty Lightning', @ship.name
 
144
  end
 
145
 
 
146
  def test_should_not_replace_an_existing_record_if_there_is_no_id_and_destroy_is_truthy
 
147
    @pirate.reload.ship_attributes = { :name => 'Davy Jones Gold Dagger', :_destroy => '1' }
 
148
 
 
149
    assert_equal @ship, @pirate.ship
 
150
    assert_equal 'Nights Dirty Lightning', @pirate.ship.name
 
151
  end
 
152
 
 
153
  def test_should_modify_an_existing_record_if_there_is_a_matching_id
 
154
    @pirate.reload.ship_attributes = { :id => @ship.id, :name => 'Davy Jones Gold Dagger' }
 
155
 
 
156
    assert_equal @ship, @pirate.ship
 
157
    assert_equal 'Davy Jones Gold Dagger', @pirate.ship.name
 
158
  end
 
159
 
 
160
  def test_should_take_a_hash_with_string_keys_and_update_the_associated_model
 
161
    @pirate.reload.ship_attributes = { 'id' => @ship.id, 'name' => 'Davy Jones Gold Dagger' }
 
162
 
 
163
    assert_equal @ship, @pirate.ship
 
164
    assert_equal 'Davy Jones Gold Dagger', @pirate.ship.name
 
165
  end
 
166
 
 
167
  def test_should_modify_an_existing_record_if_there_is_a_matching_composite_id
 
168
    @ship.stubs(:id).returns('ABC1X')
 
169
    @pirate.ship_attributes = { :id => @ship.id, :name => 'Davy Jones Gold Dagger' }
 
170
 
 
171
    assert_equal 'Davy Jones Gold Dagger', @pirate.ship.name
 
172
  end
 
173
 
 
174
  def test_should_destroy_an_existing_record_if_there_is_a_matching_id_and_destroy_is_truthy
 
175
    @pirate.ship.destroy
 
176
    [1, '1', true, 'true'].each do |truth|
 
177
      @pirate.reload.create_ship(:name => 'Mister Pablo')
 
178
      assert_difference('Ship.count', -1) do
 
179
        @pirate.update_attribute(:ship_attributes, { :id => @pirate.ship.id, :_destroy => truth })
 
180
      end
 
181
    end
 
182
  end
 
183
 
 
184
  def test_should_not_destroy_an_existing_record_if_destroy_is_not_truthy
 
185
    [nil, '0', 0, 'false', false].each do |not_truth|
 
186
      assert_no_difference('Ship.count') do
 
187
        @pirate.update_attribute(:ship_attributes, { :id => @pirate.ship.id, :_destroy => not_truth })
 
188
      end
 
189
    end
 
190
  end
 
191
 
 
192
  def test_should_not_destroy_an_existing_record_if_allow_destroy_is_false
 
193
    Pirate.accepts_nested_attributes_for :ship, :allow_destroy => false, :reject_if => proc { |attributes| attributes.empty? }
 
194
 
 
195
    assert_no_difference('Ship.count') do
 
196
      @pirate.update_attribute(:ship_attributes, { :id => @pirate.ship.id, :_destroy => '1' })
 
197
    end
 
198
 
 
199
    Pirate.accepts_nested_attributes_for :ship, :allow_destroy => true, :reject_if => proc { |attributes| attributes.empty? }
 
200
  end
 
201
 
 
202
  def test_should_also_work_with_a_HashWithIndifferentAccess
 
203
    @pirate.ship_attributes = HashWithIndifferentAccess.new(:id => @ship.id, :name => 'Davy Jones Gold Dagger')
 
204
 
 
205
    assert !@pirate.ship.new_record?
 
206
    assert_equal 'Davy Jones Gold Dagger', @pirate.ship.name
 
207
  end
 
208
 
 
209
  def test_should_work_with_update_attributes_as_well
 
210
    @pirate.update_attributes({ :catchphrase => 'Arr', :ship_attributes => { :id => @ship.id, :name => 'Mister Pablo' } })
 
211
    @pirate.reload
 
212
 
 
213
    assert_equal 'Arr', @pirate.catchphrase
 
214
    assert_equal 'Mister Pablo', @pirate.ship.name
 
215
  end
 
216
 
 
217
  def test_should_not_destroy_the_associated_model_until_the_parent_is_saved
 
218
    assert_no_difference('Ship.count') do
 
219
      @pirate.attributes = { :ship_attributes => { :id => @ship.id, :_destroy => '1' } }
 
220
    end
 
221
    assert_difference('Ship.count', -1) do
 
222
      @pirate.save
 
223
    end
 
224
  end
 
225
 
 
226
  def test_should_automatically_enable_autosave_on_the_association
 
227
    assert Pirate.reflect_on_association(:ship).options[:autosave]
 
228
  end
 
229
end
 
230
 
 
231
class TestNestedAttributesOnABelongsToAssociation < ActiveRecord::TestCase
 
232
  def setup
 
233
    @ship = Ship.new(:name => 'Nights Dirty Lightning')
 
234
    @pirate = @ship.build_pirate(:catchphrase => 'Aye')
 
235
    @ship.save!
 
236
  end
 
237
 
 
238
  def test_should_define_an_attribute_writer_method_for_the_association
 
239
    assert_respond_to @ship, :pirate_attributes=
 
240
  end
 
241
 
 
242
  def test_should_build_a_new_record_if_there_is_no_id
 
243
    @pirate.destroy
 
244
    @ship.reload.pirate_attributes = { :catchphrase => 'Arr' }
 
245
 
 
246
    assert @ship.pirate.new_record?
 
247
    assert_equal 'Arr', @ship.pirate.catchphrase
 
248
  end
 
249
 
 
250
  def test_should_not_build_a_new_record_if_there_is_no_id_and_destroy_is_truthy
 
251
    @pirate.destroy
 
252
    @ship.reload.pirate_attributes = { :catchphrase => 'Arr', :_destroy => '1' }
 
253
 
 
254
    assert_nil @ship.pirate
 
255
  end
 
256
 
 
257
  def test_should_not_build_a_new_record_if_a_reject_if_proc_returns_false
 
258
    @pirate.destroy
 
259
    @ship.reload.pirate_attributes = {}
 
260
 
 
261
    assert_nil @ship.pirate
 
262
  end
 
263
 
 
264
  def test_should_replace_an_existing_record_if_there_is_no_id
 
265
    @ship.reload.pirate_attributes = { :catchphrase => 'Arr' }
 
266
 
 
267
    assert @ship.pirate.new_record?
 
268
    assert_equal 'Arr', @ship.pirate.catchphrase
 
269
    assert_equal 'Aye', @pirate.catchphrase
 
270
  end
 
271
 
 
272
  def test_should_not_replace_an_existing_record_if_there_is_no_id_and_destroy_is_truthy
 
273
    @ship.reload.pirate_attributes = { :catchphrase => 'Arr', :_destroy => '1' }
 
274
 
 
275
    assert_equal @pirate, @ship.pirate
 
276
    assert_equal 'Aye', @ship.pirate.catchphrase
 
277
  end
 
278
 
 
279
  def test_should_modify_an_existing_record_if_there_is_a_matching_id
 
280
    @ship.reload.pirate_attributes = { :id => @pirate.id, :catchphrase => 'Arr' }
 
281
 
 
282
    assert_equal @pirate, @ship.pirate
 
283
    assert_equal 'Arr', @ship.pirate.catchphrase
 
284
  end
 
285
 
 
286
  def test_should_take_a_hash_with_string_keys_and_update_the_associated_model
 
287
    @ship.reload.pirate_attributes = { 'id' => @pirate.id, 'catchphrase' => 'Arr' }
 
288
 
 
289
    assert_equal @pirate, @ship.pirate
 
290
    assert_equal 'Arr', @ship.pirate.catchphrase
 
291
  end
 
292
 
 
293
  def test_should_modify_an_existing_record_if_there_is_a_matching_composite_id
 
294
    @pirate.stubs(:id).returns('ABC1X')
 
295
    @ship.pirate_attributes = { :id => @pirate.id, :catchphrase => 'Arr' }
 
296
 
 
297
    assert_equal 'Arr', @ship.pirate.catchphrase
 
298
  end
 
299
 
 
300
  def test_should_destroy_an_existing_record_if_there_is_a_matching_id_and_destroy_is_truthy
 
301
    @ship.pirate.destroy
 
302
    [1, '1', true, 'true'].each do |truth|
 
303
      @ship.reload.create_pirate(:catchphrase => 'Arr')
 
304
      assert_difference('Pirate.count', -1) do
 
305
        @ship.update_attribute(:pirate_attributes, { :id => @ship.pirate.id, :_destroy => truth })
 
306
      end
 
307
    end
 
308
  end
 
309
 
 
310
  def test_should_not_destroy_an_existing_record_if_destroy_is_not_truthy
 
311
    [nil, '0', 0, 'false', false].each do |not_truth|
 
312
      assert_no_difference('Pirate.count') do
 
313
        @ship.update_attribute(:pirate_attributes, { :id => @ship.pirate.id, :_destroy => not_truth })
 
314
      end
 
315
    end
 
316
  end
 
317
 
 
318
  def test_should_not_destroy_an_existing_record_if_allow_destroy_is_false
 
319
    Ship.accepts_nested_attributes_for :pirate, :allow_destroy => false, :reject_if => proc { |attributes| attributes.empty? }
 
320
 
 
321
    assert_no_difference('Pirate.count') do
 
322
      @ship.update_attribute(:pirate_attributes, { :id => @ship.pirate.id, :_destroy => '1' })
 
323
    end
 
324
 
 
325
    Ship.accepts_nested_attributes_for :pirate, :allow_destroy => true, :reject_if => proc { |attributes| attributes.empty? }
 
326
  end
 
327
 
 
328
  def test_should_work_with_update_attributes_as_well
 
329
    @ship.update_attributes({ :name => 'Mister Pablo', :pirate_attributes => { :catchphrase => 'Arr' } })
 
330
    @ship.reload
 
331
 
 
332
    assert_equal 'Mister Pablo', @ship.name
 
333
    assert_equal 'Arr', @ship.pirate.catchphrase
 
334
  end
 
335
 
 
336
  def test_should_not_destroy_the_associated_model_until_the_parent_is_saved
 
337
    assert_no_difference('Pirate.count') do
 
338
      @ship.attributes = { :pirate_attributes => { :id => @ship.pirate.id, '_destroy' => true } }
 
339
    end
 
340
    assert_difference('Pirate.count', -1) { @ship.save }
 
341
  end
 
342
 
 
343
  def test_should_automatically_enable_autosave_on_the_association
 
344
    assert Ship.reflect_on_association(:pirate).options[:autosave]
 
345
  end
 
346
end
 
347
 
 
348
module NestedAttributesOnACollectionAssociationTests
 
349
  include AssertRaiseWithMessage
 
350
 
 
351
  def test_should_define_an_attribute_writer_method_for_the_association
 
352
    assert_respond_to @pirate, association_setter
 
353
  end
 
354
 
 
355
  def test_should_take_a_hash_with_string_keys_and_assign_the_attributes_to_the_associated_models
 
356
    @alternate_params[association_getter].stringify_keys!
 
357
    @pirate.update_attributes @alternate_params
 
358
    assert_equal ['Grace OMalley', 'Privateers Greed'], [@child_1.reload.name, @child_2.reload.name]
 
359
  end
 
360
 
 
361
  def test_should_take_an_array_and_assign_the_attributes_to_the_associated_models
 
362
    @pirate.send(association_setter, @alternate_params[association_getter].values)
 
363
    @pirate.save
 
364
    assert_equal ['Grace OMalley', 'Privateers Greed'], [@child_1.reload.name, @child_2.reload.name]
 
365
  end
 
366
 
 
367
  def test_should_also_work_with_a_HashWithIndifferentAccess
 
368
    @pirate.send(association_setter, HashWithIndifferentAccess.new('foo' => HashWithIndifferentAccess.new(:id => @child_1.id, :name => 'Grace OMalley')))
 
369
    @pirate.save
 
370
    assert_equal 'Grace OMalley', @child_1.reload.name
 
371
  end
 
372
 
 
373
  def test_should_take_a_hash_and_assign_the_attributes_to_the_associated_models
 
374
    @pirate.attributes = @alternate_params
 
375
    assert_equal 'Grace OMalley', @pirate.send(@association_name).first.name
 
376
    assert_equal 'Privateers Greed', @pirate.send(@association_name).last.name
 
377
  end
 
378
 
 
379
  def test_should_take_a_hash_with_composite_id_keys_and_assign_the_attributes_to_the_associated_models
 
380
    @child_1.stubs(:id).returns('ABC1X')
 
381
    @child_2.stubs(:id).returns('ABC2X')
 
382
 
 
383
    @pirate.attributes = {
 
384
      association_getter => [
 
385
        { :id => @child_1.id, :name => 'Grace OMalley' },
 
386
        { :id => @child_2.id, :name => 'Privateers Greed' }
 
387
      ]
 
388
    }
 
389
 
 
390
    assert_equal ['Grace OMalley', 'Privateers Greed'], [@child_1.name, @child_2.name]
 
391
  end
 
392
 
 
393
  def test_should_automatically_build_new_associated_models_for_each_entry_in_a_hash_where_the_id_is_missing
 
394
    @pirate.send(@association_name).destroy_all
 
395
    @pirate.reload.attributes = {
 
396
      association_getter => { 'foo' => { :name => 'Grace OMalley' }, 'bar' => { :name => 'Privateers Greed' }}
 
397
    }
 
398
 
 
399
    assert @pirate.send(@association_name).first.new_record?
 
400
    assert_equal 'Grace OMalley', @pirate.send(@association_name).first.name
 
401
 
 
402
    assert @pirate.send(@association_name).last.new_record?
 
403
    assert_equal 'Privateers Greed', @pirate.send(@association_name).last.name
 
404
  end
 
405
 
 
406
  def test_should_not_assign_destroy_key_to_a_record
 
407
    assert_nothing_raised ActiveRecord::UnknownAttributeError do
 
408
      @pirate.send(association_setter, { 'foo' => { '_destroy' => '0' }})
 
409
    end
 
410
  end
 
411
 
 
412
  def test_should_ignore_new_associated_records_with_truthy_destroy_attribute
 
413
    @pirate.send(@association_name).destroy_all
 
414
    @pirate.reload.attributes = {
 
415
      association_getter => {
 
416
        'foo' => { :name => 'Grace OMalley' },
 
417
        'bar' => { :name => 'Privateers Greed', '_destroy' => '1' }
 
418
      }
 
419
    }
 
420
 
 
421
    assert_equal 1, @pirate.send(@association_name).length
 
422
    assert_equal 'Grace OMalley', @pirate.send(@association_name).first.name
 
423
  end
 
424
 
 
425
  def test_should_ignore_new_associated_records_if_a_reject_if_proc_returns_false
 
426
    @alternate_params[association_getter]['baz'] = {}
 
427
    assert_no_difference("@pirate.send(@association_name).length") do
 
428
      @pirate.attributes = @alternate_params
 
429
    end
 
430
  end
 
431
 
 
432
  def test_should_sort_the_hash_by_the_keys_before_building_new_associated_models
 
433
    attributes = ActiveSupport::OrderedHash.new
 
434
    attributes['123726353'] = { :name => 'Grace OMalley' }
 
435
    attributes['2'] = { :name => 'Privateers Greed' } # 2 is lower then 123726353
 
436
    @pirate.send(association_setter, attributes)
 
437
 
 
438
    assert_equal ['Posideons Killer', 'Killer bandita Dionne', 'Privateers Greed', 'Grace OMalley'].to_set, @pirate.send(@association_name).map(&:name).to_set
 
439
  end
 
440
 
 
441
  def test_should_raise_an_argument_error_if_something_else_than_a_hash_is_passed
 
442
    assert_nothing_raised(ArgumentError) { @pirate.send(association_setter, {}) }
 
443
    assert_nothing_raised(ArgumentError) { @pirate.send(association_setter, ActiveSupport::OrderedHash.new) }
 
444
 
 
445
    assert_raise_with_message ArgumentError, 'Hash or Array expected, got String ("foo")' do
 
446
      @pirate.send(association_setter, "foo")
 
447
    end
 
448
  end
 
449
 
 
450
  def test_should_work_with_update_attributes_as_well
 
451
    @pirate.update_attributes(:catchphrase => 'Arr',
 
452
      association_getter => { 'foo' => { :id => @child_1.id, :name => 'Grace OMalley' }})
 
453
 
 
454
    assert_equal 'Grace OMalley', @child_1.reload.name
 
455
  end
 
456
 
 
457
  def test_should_update_existing_records_and_add_new_ones_that_have_no_id
 
458
    @alternate_params[association_getter]['baz'] = { :name => 'Buccaneers Servant' }
 
459
    assert_difference('@pirate.send(@association_name).count', +1) do
 
460
      @pirate.update_attributes @alternate_params
 
461
    end
 
462
    assert_equal ['Grace OMalley', 'Privateers Greed', 'Buccaneers Servant'].to_set, @pirate.reload.send(@association_name).map(&:name).to_set
 
463
  end
 
464
 
 
465
  def test_should_be_possible_to_destroy_a_record
 
466
    ['1', 1, 'true', true].each do |true_variable|
 
467
      record = @pirate.reload.send(@association_name).create!(:name => 'Grace OMalley')
 
468
      @pirate.send(association_setter,
 
469
        @alternate_params[association_getter].merge('baz' => { :id => record.id, '_destroy' => true_variable })
 
470
      )
 
471
 
 
472
      assert_difference('@pirate.send(@association_name).count', -1) do
 
473
        @pirate.save
 
474
      end
 
475
    end
 
476
  end
 
477
 
 
478
  def test_should_not_destroy_the_associated_model_with_a_non_truthy_argument
 
479
    [nil, '', '0', 0, 'false', false].each do |false_variable|
 
480
      @alternate_params[association_getter]['foo']['_destroy'] = false_variable
 
481
      assert_no_difference('@pirate.send(@association_name).count') do
 
482
        @pirate.update_attributes(@alternate_params)
 
483
      end
 
484
    end
 
485
  end
 
486
 
 
487
  def test_should_not_destroy_the_associated_model_until_the_parent_is_saved
 
488
    assert_no_difference('@pirate.send(@association_name).count') do
 
489
      @pirate.send(association_setter, @alternate_params[association_getter].merge('baz' => { :id => @child_1.id, '_destroy' => true }))
 
490
    end
 
491
    assert_difference('@pirate.send(@association_name).count', -1) { @pirate.save }
 
492
  end
 
493
 
 
494
  def test_should_automatically_enable_autosave_on_the_association
 
495
    assert Pirate.reflect_on_association(@association_name).options[:autosave]
 
496
  end
 
497
 
 
498
  private
 
499
 
 
500
  def association_setter
 
501
    @association_setter ||= "#{@association_name}_attributes=".to_sym
 
502
  end
 
503
 
 
504
  def association_getter
 
505
    @association_getter ||= "#{@association_name}_attributes".to_sym
 
506
  end
 
507
end
 
508
 
 
509
class TestNestedAttributesOnAHasManyAssociation < ActiveRecord::TestCase
 
510
  def setup
 
511
    @association_type = :has_many
 
512
    @association_name = :birds
 
513
 
 
514
    @pirate = Pirate.create!(:catchphrase => "Don' botharrr talkin' like one, savvy?")
 
515
    @pirate.birds.create!(:name => 'Posideons Killer')
 
516
    @pirate.birds.create!(:name => 'Killer bandita Dionne')
 
517
 
 
518
    @child_1, @child_2 = @pirate.birds
 
519
 
 
520
    @alternate_params = {
 
521
      :birds_attributes => {
 
522
        'foo' => { :id => @child_1.id, :name => 'Grace OMalley' },
 
523
        'bar' => { :id => @child_2.id, :name => 'Privateers Greed' }
 
524
      }
 
525
    }
 
526
  end
 
527
 
 
528
  include NestedAttributesOnACollectionAssociationTests
 
529
end
 
530
 
 
531
class TestNestedAttributesOnAHasAndBelongsToManyAssociation < ActiveRecord::TestCase
 
532
  def setup
 
533
    @association_type = :has_and_belongs_to_many
 
534
    @association_name = :parrots
 
535
 
 
536
    @pirate = Pirate.create!(:catchphrase => "Don' botharrr talkin' like one, savvy?")
 
537
    @pirate.parrots.create!(:name => 'Posideons Killer')
 
538
    @pirate.parrots.create!(:name => 'Killer bandita Dionne')
 
539
 
 
540
    @child_1, @child_2 = @pirate.parrots
 
541
 
 
542
    @alternate_params = {
 
543
      :parrots_attributes => {
 
544
        'foo' => { :id => @child_1.id, :name => 'Grace OMalley' },
 
545
        'bar' => { :id => @child_2.id, :name => 'Privateers Greed' }
 
546
      }
 
547
    }
 
548
  end
 
549
 
 
550
  include NestedAttributesOnACollectionAssociationTests
 
551
end
 
552
 
 
553
class TestNestedAttributesLimit < ActiveRecord::TestCase
 
554
  def setup
 
555
    Pirate.accepts_nested_attributes_for :parrots, :limit => 2
 
556
 
 
557
    @pirate = Pirate.create!(:catchphrase => "Don' botharrr talkin' like one, savvy?")
 
558
  end
 
559
 
 
560
  def teardown
 
561
    Pirate.accepts_nested_attributes_for :parrots, :allow_destroy => true, :reject_if => proc { |attributes| attributes.empty? }
 
562
  end
 
563
 
 
564
  def test_limit_with_less_records
 
565
    @pirate.attributes = { :parrots_attributes => { 'foo' => { :name => 'Big Big Love' } } }
 
566
    assert_difference('Parrot.count') { @pirate.save! }
 
567
  end
 
568
 
 
569
  def test_limit_with_number_exact_records
 
570
    @pirate.attributes = { :parrots_attributes => { 'foo' => { :name => 'Lovely Day' }, 'bar' => { :name => 'Blown Away' } } }
 
571
    assert_difference('Parrot.count', 2) { @pirate.save! }
 
572
  end
 
573
 
 
574
  def test_limit_with_exceeding_records
 
575
    assert_raises(ActiveRecord::NestedAttributes::TooManyRecords) do
 
576
      @pirate.attributes = { :parrots_attributes => { 'foo' => { :name => 'Lovely Day' },
 
577
                                                      'bar' => { :name => 'Blown Away' },
 
578
                                                      'car' => { :name => 'The Happening' }} }
 
579
    end
 
580
  end
 
581
end