diff --git a/docs/release-notes/mongoid-7.5.txt b/docs/release-notes/mongoid-7.5.txt index 60cd6062e2..ace57a437b 100644 --- a/docs/release-notes/mongoid-7.5.txt +++ b/docs/release-notes/mongoid-7.5.txt @@ -117,3 +117,46 @@ Embedded associations (``embeds_one`` and ``embeds_many``): +---------------------------------------+---------------------------------------+ | Parent :after_save | Parent :after_save | +---------------------------------------+---------------------------------------+ + + +``Changeable`` Module Behavior Made Compatible With ``ActiveModel::Ditry`` +-------------------------------------------------------------------------- + +When updating documents, it is now possible to get updated attribute values +in ``after_*`` callbacks. This is following with ActiveRecord/ActiveModel +behavior. + +.. code-block:: ruby + + class Cat + include Mongoid::Document + + field :age, type: Integer + + after_save do + p self + p attribute_was(:age) + end + end + + a = Cat.create! + a.age = 2 + a.save! + +Mongoid 7.5 output: + +.. code-block:: ruby + + # + 2 + + +Mongoid 7.4 output: + +.. code-block:: ruby + + # + nil + +Notice that in 7.4 ``attribute_was(:age)`` returns an old attribute value, +while in 7.5 ``attribute_was(:age)`` returns the new values, \ No newline at end of file diff --git a/lib/mongoid/association/referenced/counter_cache.rb b/lib/mongoid/association/referenced/counter_cache.rb index 010c0b516b..0efc9e9f96 100644 --- a/lib/mongoid/association/referenced/counter_cache.rb +++ b/lib/mongoid/association/referenced/counter_cache.rb @@ -100,8 +100,8 @@ def self.define_callbacks!(association) if record = __send__(name) foreign_key = association.foreign_key - if attribute_changed?(foreign_key) - original, current = attribute_change(foreign_key) + if send("#{foreign_key}_previously_changed?") + original, current = send("#{foreign_key}_previous_change") unless original.nil? record.class.with(persistence_context) do |_class| diff --git a/lib/mongoid/association/referenced/syncable.rb b/lib/mongoid/association/referenced/syncable.rb index 294e9f33ef..b20a03c853 100644 --- a/lib/mongoid/association/referenced/syncable.rb +++ b/lib/mongoid/association/referenced/syncable.rb @@ -67,8 +67,8 @@ def remove_inverse_keys(association) # # @return [ Object ] The updated values. def update_inverse_keys(association) - if changes.has_key?(association.foreign_key) - old, new = changes[association.foreign_key] + if previous_changes.has_key?(association.foreign_key) + old, new = previous_changes[association.foreign_key] adds, subs = new - (old || []), (old || []) - new # If we are autosaving we don't want a duplicate to get added - the diff --git a/lib/mongoid/association/relatable.rb b/lib/mongoid/association/relatable.rb index 964451cfdb..b88b96239e 100644 --- a/lib/mongoid/association/relatable.rb +++ b/lib/mongoid/association/relatable.rb @@ -242,7 +242,7 @@ def inverse_type_setter # # @return [ String ] The foreign key check. def foreign_key_check - @foreign_key_check ||= "#{foreign_key}_changed?" if (stores_foreign_key? && foreign_key) + @foreign_key_check ||= "#{foreign_key}_previously_changed?" if (stores_foreign_key? && foreign_key) end # Create an association proxy object using the owner and target. diff --git a/lib/mongoid/persistable/creatable.rb b/lib/mongoid/persistable/creatable.rb index c4b938db31..9238d38086 100644 --- a/lib/mongoid/persistable/creatable.rb +++ b/lib/mongoid/persistable/creatable.rb @@ -102,19 +102,20 @@ def post_process_insert def prepare_insert(options = {}) return self if performing_validations?(options) && invalid?(options[:context] || :create) - result = run_callbacks(:save, with_children: false) do + run_callbacks(:save, with_children: false) do run_callbacks(:create, with_children: false) do run_callbacks(:persist_parent, with_children: false) do _mongoid_run_child_callbacks(:save) do _mongoid_run_child_callbacks(:create) do - yield(self) + result = yield(self) post_process_insert + post_process_persist(result, options) end end end end end - post_process_persist(result, options) and self + self end module ClassMethods diff --git a/lib/mongoid/persistable/updatable.rb b/lib/mongoid/persistable/updatable.rb index a76db681e0..02a1a01244 100644 --- a/lib/mongoid/persistable/updatable.rb +++ b/lib/mongoid/persistable/updatable.rb @@ -96,19 +96,19 @@ def prepare_update(options = {}) return false if performing_validations?(options) && invalid?(options[:context] || :update) process_flagged_destroys - result = run_callbacks(:save, with_children: false) do + run_callbacks(:save, with_children: false) do run_callbacks(:update, with_children: false) do run_callbacks(:persist_parent, with_children: false) do _mongoid_run_child_callbacks(:save) do _mongoid_run_child_callbacks(:update) do - yield(self) + result = yield(self) + post_process_persist(result, options) true end end end end end - post_process_persist(result, options) and result end # Update the document in the database. diff --git a/spec/integration/callbacks_spec.rb b/spec/integration/callbacks_spec.rb index a0ef0d6a87..9d325239ed 100644 --- a/spec/integration/callbacks_spec.rb +++ b/spec/integration/callbacks_spec.rb @@ -217,8 +217,6 @@ let!(:obj) { Emission.create!(frequency: 1) } it 'is set to the new value' do - pending 'MONGOID-5104' - obj.frequency = 2 obj.save! diff --git a/spec/mongoid/association/referenced/belongs_to_spec.rb b/spec/mongoid/association/referenced/belongs_to_spec.rb index fc44759a7d..4b1e0c107f 100644 --- a/spec/mongoid/association/referenced/belongs_to_spec.rb +++ b/spec/mongoid/association/referenced/belongs_to_spec.rb @@ -2019,8 +2019,8 @@ class BelongingObject; include Mongoid::Document; end describe '#foreign_key_check' do - it 'returns the foreign_key followed by "_changed?"' do - expect(association.foreign_key_check).to eq('owner_object_id_changed?') + it 'returns the foreign_key followed by "_previously_changed?"' do + expect(association.foreign_key_check).to eq('owner_object_id_previously_changed?') end end diff --git a/spec/mongoid/association/referenced/has_and_belongs_to_many_spec.rb b/spec/mongoid/association/referenced/has_and_belongs_to_many_spec.rb index ce1ce8d4ba..eccb828343 100644 --- a/spec/mongoid/association/referenced/has_and_belongs_to_many_spec.rb +++ b/spec/mongoid/association/referenced/has_and_belongs_to_many_spec.rb @@ -1008,8 +1008,8 @@ class OtherHasManyRightObject; end describe '#foreign_key_check' do - it 'returns the foreign_key followed by "_changed?"' do - expect(association.foreign_key_check).to eq('has_many_right_object_ids_changed?') + it 'returns the foreign_key followed by "_previously_changed?"' do + expect(association.foreign_key_check).to eq('has_many_right_object_ids_previously_changed?') end end diff --git a/spec/mongoid/attributes/nested_spec.rb b/spec/mongoid/attributes/nested_spec.rb index 539b43b376..9e325133a1 100644 --- a/spec/mongoid/attributes/nested_spec.rb +++ b/spec/mongoid/attributes/nested_spec.rb @@ -4395,13 +4395,6 @@ class BandWithAllowDestroyedRecords < Band context "when nesting multiple levels and parent is timestamped" do - around do |example| - original_relations = Location.relations - Location.embedded_in :address, touch: true - example.run - Location.relations = original_relations - end - after do Address.reset_callbacks(:save) end diff --git a/spec/mongoid/changeable_spec.rb b/spec/mongoid/changeable_spec.rb index 0cd864a4ab..39422f3c6e 100644 --- a/spec/mongoid/changeable_spec.rb +++ b/spec/mongoid/changeable_spec.rb @@ -1649,21 +1649,26 @@ before do Acolyte.set_callback(:save, :after, if: :callback_test?) do |doc| - doc[:changed_in_callback] = doc.changes.dup + doc[:changed_in_after_callback] = doc.changes.dup + end + + Acolyte.set_callback(:save, :before, if: :callback_test?) do |doc| + doc[:changed_in_before_callback] = doc.changes.dup end end after do Acolyte._save_callbacks.select do |callback| - callback.kind == :after + [:before, :after].include?(callback.kind) end.each do |callback| Acolyte._save_callbacks.delete(callback) end end - it "retains the changes until after all callbacks" do + it "does not retain the changes until after all callbacks" do acolyte.update_attribute(:status, "testing") - expect(acolyte.changed_in_callback).to eq({ "status" => [ nil, "testing" ] }) + expect(acolyte.changed_in_before_callback).to eq({"status"=>[nil, "testing"]}) + expect(acolyte.changed_in_after_callback).to eq({ }) end end @@ -1675,21 +1680,26 @@ before do Acolyte.set_callback(:save, :after, if: :callback_test?) do |doc| - doc[:changed_in_callback] = doc.changes.dup + doc[:changed_after_in_callback] = doc.changes.dup + end + + Acolyte.set_callback(:save, :before, if: :callback_test?) do |doc| + doc[:changed_before_in_callback] = doc.changes.dup end end after do Acolyte._save_callbacks.select do |callback| - callback.kind == :after + [:before, :after].include?(callback.kind) end.each do |callback| Acolyte._save_callbacks.delete(callback) end end - it "retains the changes until after all callbacks" do + it "does not retain the changes until after all callbacks" do acolyte.save - expect(acolyte.changed_in_callback["name"]).to eq([ nil, "callback-test" ]) + expect(acolyte.changed_before_in_callback["name"]).to eq([ nil, "callback-test" ]) + expect(acolyte.changed_after_in_callback["name"]).to be_nil end end end