From 50bdb924ba26999a468ec4844917cefec39ba08c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Sat, 25 Aug 2012 22:54:55 -0300 Subject: Revert "Remove update_attribute." This reverts commit a7f4b0a1231bf3c65db2ad4066da78c3da5ffb01. Conflicts: activerecord/lib/active_record/associations/has_one_association.rb activerecord/lib/active_record/persistence.rb activerecord/test/cases/base_test.rb activerecord/test/cases/dirty_test.rb activerecord/test/cases/timestamp_test.rb --- activerecord/lib/active_record/migration.rb | 4 +-- activerecord/lib/active_record/persistence.rb | 22 ++++++++++++--- activerecord/test/cases/dirty_test.rb | 12 +++++++- activerecord/test/cases/persistence_test.rb | 40 +++++++++++++++++++++++++++ 4 files changed, 71 insertions(+), 7 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index 703265c334..c1d57855a9 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -236,7 +236,7 @@ module ActiveRecord # add_column :people, :salary, :integer # Person.reset_column_information # Person.all.each do |p| - # p.update_column :salary, SalaryCalculator.compute(p) + # p.update_attribute :salary, SalaryCalculator.compute(p) # end # end # end @@ -256,7 +256,7 @@ module ActiveRecord # ... # say_with_time "Updating salaries..." do # Person.all.each do |p| - # p.update_column :salary, SalaryCalculator.compute(p) + # p.update_attribute :salary, SalaryCalculator.compute(p) # end # end # ... diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index 6b4b9bd103..c64e3e6ccf 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -1,4 +1,3 @@ - module ActiveRecord # = Active Record Persistence module Persistence @@ -166,6 +165,21 @@ module ActiveRecord became end + # Updates a single attribute and saves the record. + # This is especially useful for boolean flags on existing records. Also note that + # + # * Validation is skipped. + # * Callbacks are invoked. + # * updated_at/updated_on column is updated if that column is available. + # * Updates all the attributes that are dirty in this object. + # + def update_attribute(name, value) + name = name.to_s + verify_readonly_attribute(name) + send("#{name}=", value) + save(:validate => false) + 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. @@ -242,7 +256,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_columns(attribute => self[attribute]) + increment(attribute, by).update_attribute(attribute, self[attribute]) end # Initializes +attribute+ to zero if +nil+ and subtracts the value passed as +by+ (default is 1). @@ -259,7 +273,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_columns(attribute => self[attribute]) + decrement(attribute, by).update_attribute(attribute, self[attribute]) end # Assigns to +attribute+ the boolean opposite of attribute?. So @@ -276,7 +290,7 @@ module ActiveRecord # Saving is not subjected to validation checks. Returns +true+ if the # record could be saved. def toggle!(attribute) - toggle(attribute).update_columns(attribute => self[attribute]) + toggle(attribute).update_attribute(attribute, self[attribute]) end # Reloads the attributes of this object from the database. diff --git a/activerecord/test/cases/dirty_test.rb b/activerecord/test/cases/dirty_test.rb index 92677b9926..9a2a5a4e3c 100644 --- a/activerecord/test/cases/dirty_test.rb +++ b/activerecord/test/cases/dirty_test.rb @@ -509,6 +509,16 @@ class DirtyTest < ActiveRecord::TestCase assert_not_nil pirate.previous_changes['updated_on'][1] assert !pirate.previous_changes.key?('parrot_id') assert !pirate.previous_changes.key?('created_on') + + pirate = Pirate.find_by_catchphrase("Ahoy!") + pirate.update_attribute(:catchphrase, "Ninjas suck!") + + assert_equal 2, pirate.previous_changes.size + assert_equal ["Ahoy!", "Ninjas suck!"], pirate.previous_changes['catchphrase'] + assert_not_nil pirate.previous_changes['updated_on'][0] + assert_not_nil pirate.previous_changes['updated_on'][1] + assert !pirate.previous_changes.key?('parrot_id') + assert !pirate.previous_changes.key?('created_on') end if ActiveRecord::Base.connection.supports_migrations? @@ -534,7 +544,7 @@ class DirtyTest < ActiveRecord::TestCase pirate = target.create(:created_on => created_on) pirate.reload # Here mysql truncate the usec value to 0 - + pirate.created_on = created_on assert !pirate.created_on_changed? end diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb index 72b8219782..4ffa4836e0 100644 --- a/activerecord/test/cases/persistence_test.rb +++ b/activerecord/test/cases/persistence_test.rb @@ -371,10 +371,50 @@ class PersistencesTest < ActiveRecord::TestCase assert_raise(ActiveSupport::FrozenObjectError) { client.name = "something else" } end + def test_update_attribute + assert !Topic.find(1).approved? + Topic.find(1).update_attribute("approved", true) + assert Topic.find(1).approved? + + Topic.find(1).update_attribute(:approved, false) + assert !Topic.find(1).approved? + end + def test_update_attribute_does_not_choke_on_nil assert Topic.find(1).update_attributes(nil) end + def test_update_attribute_for_readonly_attribute + minivan = Minivan.find('m1') + assert_raises(ActiveRecord::ActiveRecordError) { minivan.update_attribute(:color, 'black') } + end + + def test_update_attribute_with_one_updated + t = Topic.first + t.update_attribute(:title, 'super_title') + assert_equal 'super_title', t.title + assert !t.changed?, "topic should not have changed" + assert !t.title_changed?, "title should not have changed" + assert_nil t.title_change, 'title change should be nil' + + t.reload + assert_equal 'super_title', t.title + end + + def test_update_attribute_for_updated_at_on + developer = Developer.find(1) + prev_month = Time.now.prev_month + + developer.update_attribute(:updated_at, prev_month) + assert_equal prev_month, developer.updated_at + + developer.update_attribute(:salary, 80001) + assert_not_equal prev_month, developer.updated_at + + developer.reload + assert_not_equal prev_month, developer.updated_at + end + def test_update_column topic = Topic.find(1) topic.update_column("approved", true) -- cgit v1.2.3