1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

Mutation tracker should be cleared before continuing around callbacks

`changes_applied` should be called before continuing around callback
chain. Otherwise the mutation tracker returns old value for methods like
`changed`? or `id_in_database` in around callbacks. Also methods depend
on `id_in_database`, like `update_column`, are not working in
`around_create` callbacks.

```
class Foo < ActiveRecord::Base
  around_create :around_create_callback

  def around_create_callback
    ...
    yield
    p id_in_database # => nil
    update_column(:generated_column, generate_value) # silently fails
  end

  ...
end
```
This commit is contained in:
Yuya Tanaka 2018-08-22 20:00:19 +09:00
parent 1353610ff2
commit 34f075fe56
2 changed files with 26 additions and 5 deletions

View file

@ -16,9 +16,6 @@ module ActiveRecord
class_attribute :partial_writes, instance_writer: false, default: true
after_create { changes_applied }
after_update { changes_applied }
# Attribute methods for "changed in last call to save?"
attribute_method_affix(prefix: "saved_change_to_", suffix: "?")
attribute_method_prefix("saved_change_to_")
@ -168,11 +165,15 @@ module ActiveRecord
end
def _update_record(*)
partial_writes? ? super(keys_for_partial_write) : super
affected_rows = partial_writes? ? super(keys_for_partial_write) : super
changes_applied
affected_rows
end
def _create_record(*)
partial_writes? ? super(keys_for_partial_write) : super
id = partial_writes? ? super(keys_for_partial_write) : super
changes_applied
id
end
def keys_for_partial_write

View file

@ -879,6 +879,26 @@ class DirtyTest < ActiveRecord::TestCase
raise "changed? should be false" if changed?
raise "has_changes_to_save? should be false" if has_changes_to_save?
raise "saved_changes? should be true" unless saved_changes?
raise "id_in_database should not be nil" if id_in_database.nil?
end
end
person = klass.create!(first_name: "Sean")
assert_not_predicate person, :changed?
end
test "changed? in around callbacks after yield returns false" do
klass = Class.new(ActiveRecord::Base) do
self.table_name = "people"
around_create :check_around
def check_around
yield
raise "changed? should be false" if changed?
raise "has_changes_to_save? should be false" if has_changes_to_save?
raise "saved_changes? should be true" unless saved_changes?
raise "id_in_database should not be nil" if id_in_database.nil?
end
end