forked from rails/rails
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Issue rails#1: :before_add callback is called when nested attributes …
…assignment assigns to existing record if the association is not yet loaded Issue rails#2: Nested Attributes assignment does not affect the record in the association target when callback triggers loading of the association Complete rewrite of tests
- Loading branch information
1 parent
36f7732
commit 1eced51
Showing
2 changed files
with
154 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
145 changes: 145 additions & 0 deletions
145
activerecord/test/cases/nested_attributes_with_callbacks_test.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
require "cases/helper" | ||
require "models/pirate" | ||
require "models/bird" | ||
|
||
class TestNestedAttributesWithCallbacksInterferingWithAssignment < ActiveRecord::TestCase | ||
Pirate.has_many(:birds_with_interfering_callback, | ||
:class_name => "Bird", | ||
:before_add => proc { |p,b| | ||
@@add_callback_called << b | ||
p.birds_with_interfering_callback.to_a | ||
}) | ||
Pirate.has_many(:birds_with_callback, | ||
:class_name => "Bird", | ||
:before_add => proc { |p,b| @@add_callback_called << b }) | ||
|
||
Pirate.accepts_nested_attributes_for(:birds_with_interfering_callback, | ||
:birds_with_callback, | ||
:allow_destroy => true) | ||
|
||
def setup | ||
@@add_callback_called = [] | ||
@expect_callbacks_for = [] | ||
@pirate_with_two_birds = Pirate.new.tap do |pirate| | ||
pirate.catchphrase = "Don't call me!" | ||
pirate.birds_attributes = [{:name => 'Bird1'},{:name => 'Bird2'}] | ||
pirate.save! | ||
end | ||
@birds = @pirate_with_two_birds.birds.to_a | ||
@birds_attributes = @birds.map do |bird| | ||
bird.attributes.slice("id","name") | ||
end | ||
end | ||
|
||
def new_birds | ||
@pirate_with_two_birds.birds_with_callback.to_a - @birds | ||
end | ||
|
||
def new_bird_attributes | ||
[{'name' => "New Bird"}] | ||
end | ||
|
||
def bird2_deletion_attributes | ||
[{'id' => @birds[1].id.to_s, "_destroy" => true}] | ||
end | ||
|
||
def b1_update_new_b_b2_destroy_attributes | ||
[{'id' => @birds[0].id.to_s, 'name' => 'New Name'}, | ||
{'name' => "New Bird"}, | ||
{'id' => @birds[1].id.to_s, "_destroy" => true}] | ||
end | ||
|
||
# Characterizing when :before_add callback is called | ||
test ":before_add called for new bird when not loaded" do | ||
assert_birds_with_callback_not_loaded | ||
assert_callback_called_for_new_bird_assignment | ||
end | ||
|
||
test ":before_add called for new bird when loaded" do | ||
@pirate_with_two_birds.birds_with_callback.load_target | ||
assert_callback_called_for_new_bird_assignment | ||
end | ||
|
||
def assert_callback_called_for_new_bird_assignment | ||
@pirate_with_two_birds.birds_with_callback_attributes = new_bird_attributes | ||
assert_equal(1,new_birds.size) | ||
assert_callback_called_for_new_birds | ||
end | ||
|
||
test ":before_add not called for identical assignment when not loaded" do | ||
assert_birds_with_callback_not_loaded | ||
assert_callback_not_called_for_identical_assignment | ||
end | ||
|
||
test ":before_add not called for identical assignment when loaded" do | ||
@pirate_with_two_birds.birds_with_callback.load_target | ||
assert_callback_not_called_for_identical_assignment | ||
end | ||
|
||
def assert_callback_not_called_for_identical_assignment | ||
@pirate_with_two_birds.birds_with_callback_attributes = @birds_attributes | ||
assert_equal([],new_birds) | ||
assert_callback_called_for_new_birds | ||
end | ||
|
||
test ":before_add not called for deletion assignment when not loaded" do | ||
assert_birds_with_callback_not_loaded | ||
assert_callback_not_called_for_deletion_assignment | ||
end | ||
|
||
test ":before_add not called for deletion assignment when loaded" do | ||
@pirate_with_two_birds.birds_with_callback.load_target | ||
assert_callback_not_called_for_deletion_assignment | ||
end | ||
|
||
def assert_callback_not_called_for_deletion_assignment | ||
@pirate_with_two_birds.birds_with_callback_attributes = | ||
bird2_deletion_attributes | ||
assert_callback_called_for_new_birds | ||
end | ||
|
||
def assert_birds_with_callback_not_loaded | ||
assert_equal(false,@pirate_with_two_birds.birds_with_callback.loaded?) | ||
end | ||
|
||
def assert_callback_called_for_new_birds | ||
assert_equal(new_birds,@@add_callback_called) | ||
end | ||
|
||
# Ensuring that the records in the association target are updated, | ||
# whether the association is loaded before or not | ||
test "Assignment updates records in target when not loaded" do | ||
assert_equal(false,@pirate_with_two_birds.birds_with_callback.loaded?) | ||
assert_assignment_affects_records_in_target(:birds_with_callback) | ||
end | ||
|
||
test "Assignment updates records in target when loaded" do | ||
@pirate_with_two_birds.birds_with_callback.load_target | ||
assert_assignment_affects_records_in_target(:birds_with_callback) | ||
end | ||
|
||
test("Assignment updates records in target when not loaded" + | ||
" and callback loads target") do | ||
assert_equal(false, | ||
@pirate_with_two_birds.birds_with_interfering_callback.loaded?) | ||
assert_assignment_affects_records_in_target( | ||
:birds_with_interfering_callback) | ||
end | ||
|
||
test("Assignment updates records in target when loaded" + | ||
" and callback loads target") do | ||
@pirate_with_two_birds.birds_with_interfering_callback.load_target | ||
assert_assignment_affects_records_in_target( | ||
:birds_with_interfering_callback) | ||
end | ||
|
||
def assert_assignment_affects_records_in_target(association_name) | ||
@pirate_with_two_birds.send("#{association_name}_attributes=", | ||
b1_update_new_b_b2_destroy_attributes) | ||
association = @pirate_with_two_birds.send(association_name) | ||
birds_in_target = @birds.map { |b| association.detect { |b_in_t| b_in_t == b }} | ||
assert_equal(true,birds_in_target[0].name_changed?,'First record not changed') | ||
assert_equal(true,birds_in_target[1].marked_for_destruction?, | ||
'Second record not marked for destruction') | ||
end | ||
end |