1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
|
# frozen_string_literal: true
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_predicate @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_predicate @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_predicate @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_predicate @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_predicate @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.public_send(association_name)
assert_predicate association.detect { |b| b == bird_to_update }, :name_changed?,
"Update record not updated"
assert_predicate association.detect { |b| b == bird_to_destroy }, :marked_for_destruction?,
"Destroy record not marked for destruction"
end
end
|