require "cases/helper"
require "models/pirate"
require "models/bird"
class NestedAttributesWithCallbacksTest < ActiveRecord::TestCase
Pirate.has_many(:birds_with_add_load,
:class_name => "Bird",
:before_add => proc { |p,b|
@@add_callback_called << b
p.birds_with_add_load.to_a
})
Pirate.has_many(:birds_with_add,
:class_name => "Bird",
:before_add => proc { |p,b| @@add_callback_called << b })
Pirate.accepts_nested_attributes_for(:birds_with_add_load,
:birds_with_add,
:allow_destroy => true)
def setup
@@add_callback_called = []
@pirate = Pirate.new.tap do |pirate|
pirate.catchphrase = "Don't call me!"
pirate.birds_attributes = [{:name => 'Bird1'},{:name => 'Bird2'}]
pirate.save!
end
@birds = @pirate.birds.to_a
end
def bird_to_update
@birds[0]
end
def bird_to_destroy
@birds[1]
end
def existing_birds_attributes
@birds.map do |bird|
bird.attributes.slice("id","name")
end
end
def new_birds
@pirate.birds_with_add.to_a - @birds
end
def new_bird_attributes
[{'name' => "New Bird"}]
end
def destroy_bird_attributes
[{'id' => bird_to_destroy.id.to_s, "_destroy" => true}]
end
def update_new_and_destroy_bird_attributes
[{'id' => @birds[0].id.to_s, 'name' => 'New Name'},
{'name' => "New Bird"},
{'id' => bird_to_destroy.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_not @pirate.birds_with_add.loaded?
@pirate.birds_with_add_attributes = new_bird_attributes
assert_new_bird_with_callback_called
end
test ":before_add called for new bird when loaded" do
@pirate.birds_with_add.load_target
@pirate.birds_with_add_attributes = new_bird_attributes
assert_new_bird_with_callback_called
end
def assert_new_bird_with_callback_called
assert_equal(1, new_birds.size)
assert_equal(new_birds, @@add_callback_called)
end
test ":before_add not called for identical assignment when not loaded" do
assert_not @pirate.birds_with_add.loaded?
@pirate.birds_with_add_attributes = existing_birds_attributes
assert_callbacks_not_called
end
test ":before_add not called for identical assignment when loaded" do
@pirate.birds_with_add.load_target
@pirate.birds_with_add_attributes = existing_birds_attributes
assert_callbacks_not_called
end
test ":before_add not called for destroy assignment when not loaded" do
assert_not @pirate.birds_with_add.loaded?
@pirate.birds_with_add_attributes = destroy_bird_attributes
assert_callbacks_not_called
end
test ":before_add not called for deletion assignment when loaded" do
@pirate.birds_with_add.load_target
@pirate.birds_with_add_attributes = destroy_bird_attributes
assert_callbacks_not_called
end
def assert_callbacks_not_called
assert_empty new_birds
assert_empty @@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_not @pirate.birds_with_add.loaded?
@pirate.birds_with_add_attributes = update_new_and_destroy_bird_attributes
assert_assignment_affects_records_in_target(:birds_with_add)
end
test "Assignment updates records in target when loaded" do
@pirate.birds_with_add.load_target
@pirate.birds_with_add_attributes = update_new_and_destroy_bird_attributes
assert_assignment_affects_records_in_target(:birds_with_add)
end
test("Assignment updates records in target when not loaded" +
" and callback loads target") do
assert_not @pirate.birds_with_add_load.loaded?
@pirate.birds_with_add_load_attributes = update_new_and_destroy_bird_attributes
assert_assignment_affects_records_in_target(:birds_with_add_load)
end
test("Assignment updates records in target when loaded" +
" and callback loads target") do
@pirate.birds_with_add_load.load_target
@pirate.birds_with_add_load_attributes = update_new_and_destroy_bird_attributes
assert_assignment_affects_records_in_target(:birds_with_add_load)
end
def assert_assignment_affects_records_in_target(association_name)
association = @pirate.send(association_name)
assert association.detect {|b| b == bird_to_update }.name_changed?,
'Update record not updated'
assert association.detect {|b| b == bird_to_destroy }.marked_for_destruction?,
'Destroy record not marked for destruction'
end
end