From c7802dccba69517083000261477f7e1d410e1cca Mon Sep 17 00:00:00 2001 From: Sean Griffin Date: Tue, 17 Jun 2014 18:51:38 -0600 Subject: [PATCH] Detect in-place modifications on Strings --- activerecord/lib/active_record/type/string.rb | 17 ++++++++- activerecord/test/cases/dirty_test.rb | 8 ++--- activerecord/test/cases/type/string_test.rb | 36 +++++++++++++++++++ activerecord/test/cases/types_test.rb | 7 ---- 4 files changed, 55 insertions(+), 13 deletions(-) create mode 100644 activerecord/test/cases/type/string_test.rb diff --git a/activerecord/lib/active_record/type/string.rb b/activerecord/lib/active_record/type/string.rb index 3b1554bd5a..14b03dcb2d 100644 --- a/activerecord/lib/active_record/type/string.rb +++ b/activerecord/lib/active_record/type/string.rb @@ -9,13 +9,28 @@ def text? true end + def changed_in_place?(raw_old_value, new_value) + if new_value.is_a?(::String) + raw_old_value != new_value + end + end + + def type_cast_for_database(value) + if value.is_a?(::String) + ::String.new(value) + else + super + end + end + private def cast_value(value) case value when true then "1" when false then "0" - else value.to_s + # String.new is slightly faster than dup + else ::String.new(value.to_s) end end end diff --git a/activerecord/test/cases/dirty_test.rb b/activerecord/test/cases/dirty_test.rb index 5d6601a881..ea73c561e9 100644 --- a/activerecord/test/cases/dirty_test.rb +++ b/activerecord/test/cases/dirty_test.rb @@ -309,16 +309,14 @@ def test_object_should_be_changed_if_any_attribute_is_changed def test_attribute_will_change! pirate = Pirate.create!(:catchphrase => 'arr') - pirate.catchphrase << ' matey' assert !pirate.catchphrase_changed? - assert pirate.catchphrase_will_change! assert pirate.catchphrase_changed? - assert_equal ['arr matey', 'arr matey'], pirate.catchphrase_change + assert_equal ['arr', 'arr'], pirate.catchphrase_change - pirate.catchphrase << '!' + pirate.catchphrase << ' matey!' assert pirate.catchphrase_changed? - assert_equal ['arr matey', 'arr matey!'], pirate.catchphrase_change + assert_equal ['arr', 'arr matey!'], pirate.catchphrase_change end def test_association_assignment_changes_foreign_key diff --git a/activerecord/test/cases/type/string_test.rb b/activerecord/test/cases/type/string_test.rb new file mode 100644 index 0000000000..420177ed49 --- /dev/null +++ b/activerecord/test/cases/type/string_test.rb @@ -0,0 +1,36 @@ +require 'cases/helper' + +module ActiveRecord + class StringTypeTest < ActiveRecord::TestCase + test "type casting" do + type = Type::String.new + assert_equal "1", type.type_cast_from_user(true) + assert_equal "0", type.type_cast_from_user(false) + assert_equal "123", type.type_cast_from_user(123) + end + + test "values are duped coming out" do + s = "foo" + type = Type::String.new + assert_not_same s, type.type_cast_from_user(s) + assert_not_same s, type.type_cast_from_database(s) + end + + test "string mutations are detected" do + klass = Class.new(Base) + klass.table_name = 'authors' + + author = klass.create!(name: 'Sean') + assert_not author.changed? + + author.name << ' Griffin' + assert author.name_changed? + + author.save! + author.reload + + assert_equal 'Sean Griffin', author.name + assert_not author.changed? + end + end +end diff --git a/activerecord/test/cases/types_test.rb b/activerecord/test/cases/types_test.rb index 731f8cfba3..c62e22c902 100644 --- a/activerecord/test/cases/types_test.rb +++ b/activerecord/test/cases/types_test.rb @@ -35,13 +35,6 @@ def test_type_cast_boolean assert_equal false, type.type_cast_from_user('SOMETHING RANDOM') end - def test_type_cast_string - type = Type::String.new - assert_equal "1", type.type_cast_from_user(true) - assert_equal "0", type.type_cast_from_user(false) - assert_equal "123", type.type_cast_from_user(123) - end - def test_type_cast_integer type = Type::Integer.new assert_equal 1, type.type_cast_from_user(1)