From 87ffc3662fc023d33e3baaf2fadb09ce4bece5b5 Mon Sep 17 00:00:00 2001 From: Sebastian Martinez Date: Sat, 21 May 2011 13:14:55 -0300 Subject: [PATCH 1/3] New #update_columns method. --- activerecord/lib/active_record/persistence.rb | 17 ++++++ activerecord/test/cases/persistence_test.rb | 57 +++++++++++++++++++ ...ctive_record_validations_callbacks.textile | 2 + 3 files changed, 76 insertions(+) diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index a23597be28..bd5d8de69c 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -211,6 +211,23 @@ module ActiveRecord end end + # Updates the attributes from the passed-in hash, without calling save. + # + # * Validation is skipped. + # * Callbacks are skipped. + # * updated_at/updated_on column is not updated if that column is available. + # + # Raises an +ActiveRecordError+ when called on new objects, or when at least + # one of the attributes is marked as readonly. + def update_columns(attributes) + raise ActiveRecordError, "can not update on a new record object" unless persisted? + attributes.each_key {|key| raise ActiveRecordError, "#{key.to_s} is marked as readonly" if self.class.readonly_attributes.include?(key.to_s) } + attributes.each do |k,v| + raw_write_attribute(k,v) + end + self.class.where(self.class.primary_key => id).update_all(attributes) == 1 + end + # Initializes +attribute+ to zero if +nil+ and adds the value passed as +by+ (default is 1). # The increment is performed directly on the underlying attribute, no setter is invoked. # Only makes sense for number-based attributes. Returns +self+. diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb index b7b77b24af..77a5458b2a 100644 --- a/activerecord/test/cases/persistence_test.rb +++ b/activerecord/test/cases/persistence_test.rb @@ -461,6 +461,63 @@ class PersistencesTest < ActiveRecord::TestCase assert_equal 'super_title', t.title end + def test_update_columns + topic = Topic.find(1) + topic.update_columns({ "approved" => true, title: "Sebastian Topic" }) + assert topic.approved? + assert_equal "Sebastian Topic", topic.title + topic.reload + assert topic.approved? + assert_equal "Sebastian Topic", topic.title + end + + def test_update_columns_should_raise_exception_if_new_record + topic = Topic.new + assert_raises(ActiveRecord::ActiveRecordError) { topic.update_columns({ approved: false }) } + end + + def test_update_columns_should_not_leave_the_object_dirty + topic = Topic.find(1) + topic.update_attributes({ "content" => "Have a nice day", :author_name => "Jose" }) + + topic.reload + topic.update_columns({ content: "You too", "author_name" => "Sebastian" }) + assert_equal [], topic.changed + + topic.reload + topic.update_columns({ content: "Have a nice day", author_name: "Jose" }) + assert_equal [], topic.changed + end + + def test_update_columns_with_one_readonly_attribute + minivan = Minivan.find('m1') + prev_color = minivan.color + prev_name = minivan.name + assert_raises(ActiveRecord::ActiveRecordError) { minivan.update_columns({ name: "My old minivan", color: 'black' }) } + assert_equal prev_color, minivan.color + assert_equal prev_name, minivan.name + + minivan.reload + assert_equal prev_color, minivan.color + assert_equal prev_name, minivan.name + end + + def test_update_columns_should_not_modify_updated_at + developer = Developer.find(1) + prev_month = Time.now.prev_month + + developer.update_column(:updated_at, prev_month) + assert_equal prev_month, developer.updated_at + + developer.update_columns({ salary: 80000 }) + assert_equal prev_month, developer.updated_at + assert_equal 80000, developer.salary + + developer.reload + assert_equal prev_month.to_i, developer.updated_at.to_i + assert_equal 80000, developer.salary + end + def test_update_attributes topic = Topic.find(1) assert !topic.approved? diff --git a/guides/source/active_record_validations_callbacks.textile b/guides/source/active_record_validations_callbacks.textile index f49d91fd3c..673894c93a 100644 --- a/guides/source/active_record_validations_callbacks.textile +++ b/guides/source/active_record_validations_callbacks.textile @@ -86,6 +86,7 @@ The following methods skip validations, and will save the object to the database * +update_all+ * +update_attribute+ * +update_column+ +* +update_columns+ * +update_counters+ Note that +save+ also has the ability to skip validations if passed +:validate => false+ as argument. This technique should be used with caution. @@ -1082,6 +1083,7 @@ Just as with validations, it is also possible to skip callbacks. These methods s * +toggle+ * +touch+ * +update_column+ +* +update_columns+ * +update_all+ * +update_counters+ From da84ccb6f04080d0f18f9bce35f773f193b1770b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Tue, 24 Jul 2012 19:13:18 -0300 Subject: [PATCH 2/3] Use update_columns to implemente the update_column --- activerecord/lib/active_record/persistence.rb | 35 ++++++++--------- activerecord/test/cases/persistence_test.rb | 38 ++++++++++++++++++- 2 files changed, 54 insertions(+), 19 deletions(-) diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index bd5d8de69c..e34dc11606 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -167,22 +167,6 @@ module ActiveRecord became end - # Updates a single attribute of an object, without calling save. - # - # * Validation is skipped. - # * Callbacks are skipped. - # * updated_at/updated_on column is not updated if that column is available. - # - # Raises an +ActiveRecordError+ when called on new objects, or when the +name+ - # attribute is marked as readonly. - def update_column(name, value) - name = name.to_s - verify_readonly_attribute(name) - raise ActiveRecordError, "can not update on a new record object" unless persisted? - raw_write_attribute(name, value) - self.class.where(self.class.primary_key => id).update_all(name => value) == 1 - end - # Updates the attributes of the model from the passed-in hash and saves the # record, all wrapped in a transaction. If the object is invalid, the saving # will fail and false will be returned. @@ -211,6 +195,18 @@ module ActiveRecord end end + # Updates a single attribute of an object, without calling save. + # + # * Validation is skipped. + # * Callbacks are skipped. + # * updated_at/updated_on column is not updated if that column is available. + # + # Raises an +ActiveRecordError+ when called on new objects, or when the +name+ + # attribute is marked as readonly. + def update_column(name, value) + update_columns(name => value) + end + # Updates the attributes from the passed-in hash, without calling save. # # * Validation is skipped. @@ -221,10 +217,15 @@ module ActiveRecord # one of the attributes is marked as readonly. def update_columns(attributes) raise ActiveRecordError, "can not update on a new record object" unless persisted? - attributes.each_key {|key| raise ActiveRecordError, "#{key.to_s} is marked as readonly" if self.class.readonly_attributes.include?(key.to_s) } + + attributes.each_key do |key| + raise ActiveRecordError, "#{key.to_s} is marked as readonly" if self.class.readonly_attributes.include?(key.to_s) + end + attributes.each do |k,v| raw_write_attribute(k,v) end + self.class.where(self.class.primary_key => id).update_all(attributes) == 1 end diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb index 77a5458b2a..1f6c224a11 100644 --- a/activerecord/test/cases/persistence_test.rb +++ b/activerecord/test/cases/persistence_test.rb @@ -471,6 +471,17 @@ class PersistencesTest < ActiveRecord::TestCase assert_equal "Sebastian Topic", topic.title end + def test_update_columns_should_not_use_setter_method + dev = Developer.find(1) + dev.instance_eval { def salary=(value); write_attribute(:salary, value * 2); end } + + dev.update_columns(salary: 80000) + assert_equal 80000, dev.salary + + dev.reload + assert_equal 80000, dev.salary + end + def test_update_columns_should_raise_exception_if_new_record topic = Topic.new assert_raises(ActiveRecord::ActiveRecordError) { topic.update_columns({ approved: false }) } @@ -489,6 +500,14 @@ class PersistencesTest < ActiveRecord::TestCase assert_equal [], topic.changed end + def test_update_columns_with_model_having_primary_key_other_than_id + minivan = Minivan.find('m1') + new_name = 'sebavan' + + minivan.update_columns(name: new_name) + assert_equal new_name, minivan.name + end + def test_update_columns_with_one_readonly_attribute minivan = Minivan.find('m1') prev_color = minivan.color @@ -506,10 +525,10 @@ class PersistencesTest < ActiveRecord::TestCase developer = Developer.find(1) prev_month = Time.now.prev_month - developer.update_column(:updated_at, prev_month) + developer.update_columns(updated_at: prev_month) assert_equal prev_month, developer.updated_at - developer.update_columns({ salary: 80000 }) + developer.update_columns(salary: 80000) assert_equal prev_month, developer.updated_at assert_equal 80000, developer.salary @@ -518,6 +537,21 @@ class PersistencesTest < ActiveRecord::TestCase assert_equal 80000, developer.salary end + def test_update_columns_with_one_changed_and_one_updated + t = Topic.order('id').limit(1).first + author_name = t.author_name + t.author_name = 'John' + t.update_columns(title: 'super_title') + assert_equal 'John', t.author_name + assert_equal 'super_title', t.title + assert t.changed?, "topic should have changed" + assert t.author_name_changed?, "author_name should have changed" + + t.reload + assert_equal author_name, t.author_name + assert_equal 'super_title', t.title + end + def test_update_attributes topic = Topic.find(1) assert !topic.approved? From 2d9e5a26a10e5f83f9b3ac465bd45b2e15a0bc2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Tue, 24 Jul 2012 20:20:03 -0300 Subject: [PATCH 3/3] Deprecate update_column in favor of update_columns. Closes #1190 --- activerecord/CHANGELOG.md | 12 ++++ .../associations/has_one_association.rb | 2 +- activerecord/lib/active_record/persistence.rb | 11 +++- .../belongs_to_associations_test.rb | 4 +- ...s_and_belongs_to_many_associations_test.rb | 2 +- .../has_many_associations_test.rb | 6 +- .../has_many_through_associations_test.rb | 4 +- .../has_one_through_associations_test.rb | 4 +- .../cases/associations/join_model_test.rb | 12 ++-- activerecord/test/cases/associations_test.rb | 4 +- activerecord/test/cases/base_test.rb | 4 +- activerecord/test/cases/calculations_test.rb | 4 +- activerecord/test/cases/dirty_test.rb | 2 +- activerecord/test/cases/persistence_test.rb | 55 +++++++++++++------ activerecord/test/cases/timestamp_test.rb | 6 +- 15 files changed, 85 insertions(+), 47 deletions(-) diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 08aa3aae34..bdd9edf27b 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,5 +1,17 @@ ## Rails 4.0.0 (unreleased) ## +* Deprecate `update_column` method in favor of `update_columns`. + + *Rafael Mendonça França* + +* Added an `update_columns` method. This new method updates the given attributes on an object, + without calling save, hence skipping validations and callbacks. + Example: + + User.first.update_columns({:name => "sebastian", :age => 25}) # => true + + *Sebastian Martinez + Rafael Mendonça França* + * Removed `:finder_sql` and `:counter_sql` collection association options. Please use scopes instead. diff --git a/activerecord/lib/active_record/associations/has_one_association.rb b/activerecord/lib/active_record/associations/has_one_association.rb index f0d1120c68..7086dfa34c 100644 --- a/activerecord/lib/active_record/associations/has_one_association.rb +++ b/activerecord/lib/active_record/associations/has_one_association.rb @@ -36,7 +36,7 @@ module ActiveRecord when :destroy target.destroy when :nullify - target.update_column(reflection.foreign_key, nil) + target.update_columns(reflection.foreign_key => nil) end end end diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index e34dc11606..2830d651ba 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -204,6 +204,11 @@ module ActiveRecord # Raises an +ActiveRecordError+ when called on new objects, or when the +name+ # attribute is marked as readonly. def update_column(name, value) + msg = "update_column is deprecated and will be removed in 4.1. Please use update_columns. " \ + "E.g. update_columns(foo: 'bar')" + + ActiveSupport::Deprecation.warn(msg) + update_columns(name => value) end @@ -243,7 +248,7 @@ module ActiveRecord # Saving is not subjected to validation checks. Returns +true+ if the # record could be saved. def increment!(attribute, by = 1) - increment(attribute, by).update_column(attribute, self[attribute]) + increment(attribute, by).update_columns(attribute => self[attribute]) end # Initializes +attribute+ to zero if +nil+ and subtracts the value passed as +by+ (default is 1). @@ -260,7 +265,7 @@ module ActiveRecord # Saving is not subjected to validation checks. Returns +true+ if the # record could be saved. def decrement!(attribute, by = 1) - decrement(attribute, by).update_column(attribute, self[attribute]) + decrement(attribute, by).update_columns(attribute => self[attribute]) end # Assigns to +attribute+ the boolean opposite of attribute?. So @@ -277,7 +282,7 @@ module ActiveRecord # Saving is not subjected to validation checks. Returns +true+ if the # record could be saved. def toggle!(attribute) - toggle(attribute).update_column(attribute, self[attribute]) + toggle(attribute).update_columns(attribute => self[attribute]) end # Reloads the attributes of this object from the database. diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb index 2dd8c78eab..5825a23b33 100644 --- a/activerecord/test/cases/associations/belongs_to_associations_test.rb +++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb @@ -298,12 +298,12 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase assert_equal 1, Topic.find(topic.id)[:replies_count] end - def test_belongs_to_counter_when_update_column + def test_belongs_to_counter_when_update_columns topic = Topic.create!(:title => "37s") topic.replies.create!(:title => "re: 37s", :content => "rails") assert_equal 1, Topic.find(topic.id)[:replies_count] - topic.update_column(:content, "rails is wonderfull") + topic.update_columns(content: "rails is wonderfull") assert_equal 1, Topic.find(topic.id)[:replies_count] end diff --git a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb index 90e5467f13..29aa0defd0 100644 --- a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb @@ -582,7 +582,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase project = SpecialProject.create("name" => "Special Project") assert developer.save developer.projects << project - developer.update_column("name", "Bruza") + developer.update_columns("name" => "Bruza") assert_equal 1, Developer.connection.select_value(<<-end_sql).to_i SELECT count(*) FROM developers_projects WHERE project_id = #{project.id} diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index 4b7a2db096..f7e7691e8e 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -634,7 +634,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase def test_deleting_updates_counter_cache_with_dependent_delete_all post = posts(:welcome) - post.update_column(:taggings_with_delete_all_count, post.taggings_count) + post.update_columns(taggings_with_delete_all_count: post.taggings_count) assert_difference "post.reload.taggings_with_delete_all_count", -1 do post.taggings_with_delete_all.delete(post.taggings_with_delete_all.first) @@ -643,7 +643,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase def test_deleting_updates_counter_cache_with_dependent_destroy post = posts(:welcome) - post.update_column(:taggings_with_destroy_count, post.taggings_count) + post.update_columns(taggings_with_destroy_count: post.taggings_count) assert_difference "post.reload.taggings_with_destroy_count", -1 do post.taggings_with_destroy.delete(post.taggings_with_destroy.first) @@ -813,7 +813,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase firm = Firm.first # break the vanilla firm_id foreign key assert_equal 2, firm.clients.count - firm.clients.first.update_column(:firm_id, nil) + firm.clients.first.update_columns(firm_id: nil) assert_equal 1, firm.clients(true).count assert_equal 1, firm.clients_using_primary_key_with_delete_all.count old_record = firm.clients_using_primary_key_with_delete_all.first diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb index 805e9eac37..94848f1044 100644 --- a/activerecord/test/cases/associations/has_many_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb @@ -327,7 +327,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase def test_update_counter_caches_on_delete_with_dependent_destroy post = posts(:welcome) tag = post.tags.create!(:name => 'doomed') - post.update_column(:tags_with_destroy_count, post.tags.count) + post.update_columns(tags_with_destroy_count: post.tags.count) assert_difference ['post.reload.taggings_count', 'post.reload.tags_with_destroy_count'], -1 do posts(:welcome).tags_with_destroy.delete(tag) @@ -337,7 +337,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase def test_update_counter_caches_on_delete_with_dependent_nullify post = posts(:welcome) tag = post.tags.create!(:name => 'doomed') - post.update_column(:tags_with_nullify_count, post.tags.count) + post.update_columns(tags_with_nullify_count: post.tags.count) assert_no_difference 'post.reload.taggings_count' do assert_difference 'post.reload.tags_with_nullify_count', -1 do diff --git a/activerecord/test/cases/associations/has_one_through_associations_test.rb b/activerecord/test/cases/associations/has_one_through_associations_test.rb index 94b9639e57..79056c09a7 100644 --- a/activerecord/test/cases/associations/has_one_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_one_through_associations_test.rb @@ -90,12 +90,12 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase def test_has_one_through_with_conditions_eager_loading # conditions on the through table assert_equal clubs(:moustache_club), Member.scoped(:includes => :favourite_club).find(@member.id).favourite_club - memberships(:membership_of_favourite_club).update_column(:favourite, false) + memberships(:membership_of_favourite_club).update_columns(favourite: false) assert_equal nil, Member.scoped(:includes => :favourite_club).find(@member.id).reload.favourite_club # conditions on the source table assert_equal clubs(:moustache_club), Member.scoped(:includes => :hairy_club).find(@member.id).hairy_club - clubs(:moustache_club).update_column(:name, "Association of Clean-Shaven Persons") + clubs(:moustache_club).update_columns(name: "Association of Clean-Shaven Persons") assert_equal nil, Member.scoped(:includes => :hairy_club).find(@member.id).reload.hairy_club end diff --git a/activerecord/test/cases/associations/join_model_test.rb b/activerecord/test/cases/associations/join_model_test.rb index 783b83631c..678b412712 100644 --- a/activerecord/test/cases/associations/join_model_test.rb +++ b/activerecord/test/cases/associations/join_model_test.rb @@ -175,7 +175,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase def test_delete_polymorphic_has_many_with_delete_all assert_equal 1, posts(:welcome).taggings.count - posts(:welcome).taggings.first.update_column :taggable_type, 'PostWithHasManyDeleteAll' + posts(:welcome).taggings.first.update_columns taggable_type: 'PostWithHasManyDeleteAll' post = find_post_with_dependency(1, :has_many, :taggings, :delete_all) old_count = Tagging.count @@ -186,7 +186,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase def test_delete_polymorphic_has_many_with_destroy assert_equal 1, posts(:welcome).taggings.count - posts(:welcome).taggings.first.update_column :taggable_type, 'PostWithHasManyDestroy' + posts(:welcome).taggings.first.update_columns taggable_type: 'PostWithHasManyDestroy' post = find_post_with_dependency(1, :has_many, :taggings, :destroy) old_count = Tagging.count @@ -197,7 +197,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase def test_delete_polymorphic_has_many_with_nullify assert_equal 1, posts(:welcome).taggings.count - posts(:welcome).taggings.first.update_column :taggable_type, 'PostWithHasManyNullify' + posts(:welcome).taggings.first.update_columns taggable_type: 'PostWithHasManyNullify' post = find_post_with_dependency(1, :has_many, :taggings, :nullify) old_count = Tagging.count @@ -208,7 +208,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase def test_delete_polymorphic_has_one_with_destroy assert posts(:welcome).tagging - posts(:welcome).tagging.update_column :taggable_type, 'PostWithHasOneDestroy' + posts(:welcome).tagging.update_columns taggable_type: 'PostWithHasOneDestroy' post = find_post_with_dependency(1, :has_one, :tagging, :destroy) old_count = Tagging.count @@ -219,7 +219,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase def test_delete_polymorphic_has_one_with_nullify assert posts(:welcome).tagging - posts(:welcome).tagging.update_column :taggable_type, 'PostWithHasOneNullify' + posts(:welcome).tagging.update_columns taggable_type: 'PostWithHasOneNullify' post = find_post_with_dependency(1, :has_one, :tagging, :nullify) old_count = Tagging.count @@ -734,7 +734,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase # create dynamic Post models to allow different dependency options def find_post_with_dependency(post_id, association, association_name, dependency) class_name = "PostWith#{association.to_s.classify}#{dependency.to_s.classify}" - Post.find(post_id).update_column :type, class_name + Post.find(post_id).update_columns type: class_name klass = Object.const_set(class_name, Class.new(ActiveRecord::Base)) klass.table_name = 'posts' klass.send(association, association_name, :as => :taggable, :dependent => dependency) diff --git a/activerecord/test/cases/associations_test.rb b/activerecord/test/cases/associations_test.rb index 1d0550afaf..1298710c75 100644 --- a/activerecord/test/cases/associations_test.rb +++ b/activerecord/test/cases/associations_test.rb @@ -67,7 +67,7 @@ class AssociationsTest < ActiveRecord::TestCase ship = Ship.create!(:name => "The good ship Dollypop") part = ship.parts.create!(:name => "Mast") part.mark_for_destruction - ShipPart.find(part.id).update_column(:name, 'Deck') + ShipPart.find(part.id).update_columns(name: 'Deck') ship.parts.send(:load_target) assert_equal 'Deck', ship.parts[0].name end @@ -176,7 +176,7 @@ class AssociationProxyTest < ActiveRecord::TestCase david = developers(:david) assert !david.projects.loaded? - david.update_column(:created_at, Time.now) + david.update_columns(created_at: Time.now) assert !david.projects.loaded? end diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index e4ba1c62c9..a117568c85 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -609,7 +609,7 @@ class BasicsTest < ActiveRecord::TestCase assert_equal 'value', weird.send('a$b') assert_equal 'value', weird.read_attribute('a$b') - weird.update_column('a$b', 'value2') + weird.update_columns('a$b' => 'value2') weird.reload assert_equal 'value2', weird.send('a$b') assert_equal 'value2', weird.read_attribute('a$b') @@ -1896,7 +1896,7 @@ class BasicsTest < ActiveRecord::TestCase def test_cache_key_format_for_existing_record_with_nil_updated_at dev = Developer.first - dev.update_column(:updated_at, nil) + dev.update_columns(updated_at: nil) assert_match(/\/#{dev.id}$/, dev.cache_key) end diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb index 43c034703d..9a718756d0 100644 --- a/activerecord/test/cases/calculations_test.rb +++ b/activerecord/test/cases/calculations_test.rb @@ -331,8 +331,8 @@ class CalculationsTest < ActiveRecord::TestCase def test_should_count_scoped_select_with_options Account.update_all("credit_limit = NULL") - Account.last.update_column('credit_limit', 49) - Account.first.update_column('credit_limit', 51) + Account.last.update_columns('credit_limit' => 49) + Account.first.update_columns('credit_limit' => 51) assert_equal 1, Account.scoped(:select => "credit_limit").where('credit_limit >= 50').count end diff --git a/activerecord/test/cases/dirty_test.rb b/activerecord/test/cases/dirty_test.rb index 97ffc068af..3f26e10dda 100644 --- a/activerecord/test/cases/dirty_test.rb +++ b/activerecord/test/cases/dirty_test.rb @@ -424,7 +424,7 @@ class DirtyTest < ActiveRecord::TestCase with_partial_updates(Topic) do Topic.create!(:author_name => 'Bill', :content => {:a => "a"}) topic = Topic.select('id, author_name').first - topic.update_column :author_name, 'John' + topic.update_columns author_name: 'John' topic = Topic.first assert_not_nil topic.content end diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb index 1f6c224a11..1fd108fb9d 100644 --- a/activerecord/test/cases/persistence_test.rb +++ b/activerecord/test/cases/persistence_test.rb @@ -377,22 +377,21 @@ class PersistencesTest < ActiveRecord::TestCase def test_update_column topic = Topic.find(1) - topic.update_column("approved", true) + assert_deprecated "update_column is deprecated and will be removed in 4.1. Please use update_columns. E.g. update_columns(foo: 'bar')" do + topic.update_column("approved", true) + end assert topic.approved? topic.reload assert topic.approved? - - topic.update_column(:approved, false) - assert !topic.approved? - topic.reload - assert !topic.approved? end def test_update_column_should_not_use_setter_method dev = Developer.find(1) dev.instance_eval { def salary=(value); write_attribute(:salary, value * 2); end } - dev.update_column(:salary, 80000) + assert_deprecated "update_column is deprecated and will be removed in 4.1. Please use update_columns. E.g. update_columns(foo: 'bar')" do + dev.update_column(:salary, 80000) + end assert_equal 80000, dev.salary dev.reload @@ -401,19 +400,29 @@ class PersistencesTest < ActiveRecord::TestCase def test_update_column_should_raise_exception_if_new_record topic = Topic.new - assert_raises(ActiveRecord::ActiveRecordError) { topic.update_column("approved", false) } + assert_raises(ActiveRecord::ActiveRecordError) do + assert_deprecated "update_column is deprecated and will be removed in 4.1. Please use update_columns. E.g. update_columns(foo: 'bar')" do + topic.update_column("approved", false) + end + end end def test_update_column_should_not_leave_the_object_dirty topic = Topic.find(1) - topic.update_column("content", "Have a nice day") + assert_deprecated "update_column is deprecated and will be removed in 4.1. Please use update_columns. E.g. update_columns(foo: 'bar')" do + topic.update_column("content", "Have a nice day") + end topic.reload - topic.update_column(:content, "You too") + assert_deprecated "update_column is deprecated and will be removed in 4.1. Please use update_columns. E.g. update_columns(foo: 'bar')" do + topic.update_column(:content, "You too") + end assert_equal [], topic.changed topic.reload - topic.update_column("content", "Have a nice day") + assert_deprecated "update_column is deprecated and will be removed in 4.1. Please use update_columns. E.g. update_columns(foo: 'bar')" do + topic.update_column("content", "Have a nice day") + end assert_equal [], topic.changed end @@ -421,14 +430,20 @@ class PersistencesTest < ActiveRecord::TestCase minivan = Minivan.find('m1') new_name = 'sebavan' - minivan.update_column(:name, new_name) + assert_deprecated "update_column is deprecated and will be removed in 4.1. Please use update_columns. E.g. update_columns(foo: 'bar')" do + minivan.update_column(:name, new_name) + end assert_equal new_name, minivan.name end def test_update_column_for_readonly_attribute minivan = Minivan.find('m1') prev_color = minivan.color - assert_raises(ActiveRecord::ActiveRecordError) { minivan.update_column(:color, 'black') } + assert_raises(ActiveRecord::ActiveRecordError) do + assert_deprecated "update_column is deprecated and will be removed in 4.1. Please use update_columns. E.g. update_columns(foo: 'bar')" do + minivan.update_column(:color, 'black') + end + end assert_equal prev_color, minivan.color end @@ -436,10 +451,14 @@ class PersistencesTest < ActiveRecord::TestCase developer = Developer.find(1) prev_month = Time.now.prev_month - developer.update_column(:updated_at, prev_month) + assert_deprecated "update_column is deprecated and will be removed in 4.1. Please use update_columns. E.g. update_columns(foo: 'bar')" do + developer.update_column(:updated_at, prev_month) + end assert_equal prev_month, developer.updated_at - developer.update_column(:salary, 80001) + assert_deprecated "update_column is deprecated and will be removed in 4.1. Please use update_columns. E.g. update_columns(foo: 'bar')" do + developer.update_column(:salary, 80001) + end assert_equal prev_month, developer.updated_at developer.reload @@ -448,9 +467,11 @@ class PersistencesTest < ActiveRecord::TestCase def test_update_column_with_one_changed_and_one_updated t = Topic.order('id').limit(1).first - title, author_name = t.title, t.author_name + author_name = t.author_name t.author_name = 'John' - t.update_column(:title, 'super_title') + assert_deprecated "update_column is deprecated and will be removed in 4.1. Please use update_columns. E.g. update_columns(foo: 'bar')" do + t.update_column(:title, 'super_title') + end assert_equal 'John', t.author_name assert_equal 'super_title', t.title assert t.changed?, "topic should have changed" diff --git a/activerecord/test/cases/timestamp_test.rb b/activerecord/test/cases/timestamp_test.rb index 7df6993b30..7444dc5de1 100644 --- a/activerecord/test/cases/timestamp_test.rb +++ b/activerecord/test/cases/timestamp_test.rb @@ -11,7 +11,7 @@ class TimestampTest < ActiveRecord::TestCase def setup @developer = Developer.first - @developer.update_column(:updated_at, Time.now.prev_month) + @developer.update_columns(updated_at: Time.now.prev_month) @previously_updated_at = @developer.updated_at end @@ -133,7 +133,7 @@ class TimestampTest < ActiveRecord::TestCase pet = Pet.first owner = pet.owner - owner.update_column(:happy_at, 3.days.ago) + owner.update_columns(happy_at: 3.days.ago) previously_owner_updated_at = owner.updated_at pet.name = "I'm a parrot" @@ -153,7 +153,7 @@ class TimestampTest < ActiveRecord::TestCase owner = pet.owner time = 3.days.ago - owner.update_column(:updated_at, time) + owner.update_columns(updated_at: time) toy.touch owner.reload