diff options
author | Sean Griffin <sean@seantheprogrammer.com> | 2015-09-28 17:12:28 -0400 |
---|---|---|
committer | Sean Griffin <sean@seantheprogrammer.com> | 2015-10-02 08:03:11 -0400 |
commit | 07723c23a7dc570beae73c074ad37227e3e8a06e (patch) | |
tree | 442e264cad0f2c99c8402d3a7ceea73beb2dadbc /activerecord/test/cases | |
parent | 9db73a2591e43d1851411727d6594a72efa35663 (diff) | |
download | rails-07723c23a7dc570beae73c074ad37227e3e8a06e.tar.gz rails-07723c23a7dc570beae73c074ad37227e3e8a06e.tar.bz2 rails-07723c23a7dc570beae73c074ad37227e3e8a06e.zip |
Further encapsulate dirty checking on `Attribute`
We can skip the allocation of a full `AttributeSet` by changing the
semantics of how we structure things. Instead of comparing two separate
`AttributeSet` objects, and `Attribute` is now a singly linked list of
every change that has happened to it. Since the attribute objects are
immutable, to apply the changes we simply need to copy the head of the
list.
It's worth noting that this causes one subtle change in the behavior of
AR. When a record is saved successfully, the `before_type_cast` version
of everything will be what was sent to the database. I honestly think
these semantics make more sense, as we could have just as easily had the
DB do `RETURNING *` and updated the record with those if we had things
like timestamps implemented at the DB layer.
This brings our performance closer to 4.2, but we're still not quite
there.
Diffstat (limited to 'activerecord/test/cases')
-rw-r--r-- | activerecord/test/cases/attribute_methods_test.rb | 3 | ||||
-rw-r--r-- | activerecord/test/cases/attribute_test.rb | 39 |
2 files changed, 38 insertions, 4 deletions
diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb index b67201d94d..8e5723c8e5 100644 --- a/activerecord/test/cases/attribute_methods_test.rb +++ b/activerecord/test/cases/attribute_methods_test.rb @@ -542,9 +542,6 @@ class AttributeMethodsTest < ActiveRecord::TestCase developer.save! - assert_equal "50000", developer.salary_before_type_cast - assert_equal 1337, developer.name_before_type_cast - assert_equal 50000, developer.salary assert_equal "1337", developer.name end diff --git a/activerecord/test/cases/attribute_test.rb b/activerecord/test/cases/attribute_test.rb index 0f216b7a13..a24a4fc6a4 100644 --- a/activerecord/test/cases/attribute_test.rb +++ b/activerecord/test/cases/attribute_test.rb @@ -182,12 +182,49 @@ module ActiveRecord assert attribute.has_been_read? end + test "an attribute is not changed if it hasn't been assigned or mutated" do + attribute = Attribute.from_database(:foo, 1, Type::Value.new) + + refute attribute.changed? + end + + test "an attribute is changed if it's been assigned a new value" do + attribute = Attribute.from_database(:foo, 1, Type::Value.new) + changed = attribute.with_value_from_user(2) + + assert changed.changed? + end + + test "an attribute is not changed if it's assigned the same value" do + attribute = Attribute.from_database(:foo, 1, Type::Value.new) + unchanged = attribute.with_value_from_user(1) + + refute unchanged.changed? + end + test "an attribute can not be mutated if it has not been read, and skips expensive calculations" do type_which_raises_from_all_methods = Object.new attribute = Attribute.from_database(:foo, "bar", type_which_raises_from_all_methods) - assert_not attribute.changed_in_place_from?("bar") + assert_not attribute.changed_in_place? + end + + test "an attribute is changed if it has been mutated" do + attribute = Attribute.from_database(:foo, "bar", Type::String.new) + attribute.value << "!" + + assert attribute.changed_in_place? + assert attribute.changed? + end + + test "an attribute can forget its changes" do + attribute = Attribute.from_database(:foo, "bar", Type::String.new) + changed = attribute.with_value_from_user("foo") + forgotten = changed.forgetting_assignment + + assert changed.changed? # sanity check + refute forgotten.changed? end test "with_value_from_user validates the value" do |