aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord
diff options
context:
space:
mode:
authorRafael Mendonça França <rafaelmfranca@gmail.com>2014-06-27 15:14:47 -0300
committerRafael Mendonça França <rafaelmfranca@gmail.com>2014-06-27 15:17:47 -0300
commiteb6e3e34d766cd5d75258b1f4617c993f3347741 (patch)
treec8834197fe8292f8a79de898f922f7665e2436a9 /activerecord
parenta87573d9e6610c72a93d4cc778e7703ca6d964e2 (diff)
parentc7802dccba69517083000261477f7e1d410e1cca (diff)
downloadrails-eb6e3e34d766cd5d75258b1f4617c993f3347741.tar.gz
rails-eb6e3e34d766cd5d75258b1f4617c993f3347741.tar.bz2
rails-eb6e3e34d766cd5d75258b1f4617c993f3347741.zip
Merge pull request #15788 from sgrif/sg-mutable-strings
Detect in-place modifications on Strings
Diffstat (limited to 'activerecord')
-rw-r--r--activerecord/CHANGELOG.md22
-rw-r--r--activerecord/lib/active_record/type/string.rb17
-rw-r--r--activerecord/test/cases/dirty_test.rb8
-rw-r--r--activerecord/test/cases/type/string_test.rb36
-rw-r--r--activerecord/test/cases/types_test.rb7
5 files changed, 77 insertions, 13 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 8eabeee304..4af510603e 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,3 +1,25 @@
+* Detect in-place modifications on String attributes.
+
+ Before this change user have to mark the attribute as changed to it be persisted
+ in the database. Now it is not required anymore.
+
+ Before:
+
+ user = User.first
+ user.name << ' Griffin'
+ user.name_will_change!
+ user.save
+ user.reload.name # => "Sean Griffin"
+
+ After:
+
+ user = User.first
+ user.name << ' Griffin'
+ user.save
+ user.reload.name # => "Sean Griffin"
+
+ *Sean Griffin*
+
* Add `ActiveRecord::Base#validate!` that raises `RecordInvalid` if the record
is invalid.
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 @@ module ActiveRecord
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 @@ class DirtyTest < ActiveRecord::TestCase
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 961aae88cb..47cf775cb6 100644
--- a/activerecord/test/cases/types_test.rb
+++ b/activerecord/test/cases/types_test.rb
@@ -35,13 +35,6 @@ module ActiveRecord
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)