From f4aac2f5f54789752056851cee062aa206ceea2e Mon Sep 17 00:00:00 2001 From: Diego Carrion Date: Mon, 19 Apr 2010 20:57:25 -0300 Subject: [PATCH 1/2] add touch support for has_one associations --- activerecord/lib/active_record/associations.rb | 24 ++++++++++++------------ activerecord/test/cases/timestamp_test.rb | 22 +++++++++++++++++++--- activerecord/test/models/pet.rb | 1 + activerecord/test/schema/schema.rb | 2 ++ 4 files changed, 34 insertions(+), 15 deletions(-) diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index d94cc03..e27c802 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1000,6 +1000,9 @@ module ActiveRecord # If false, don't validate the associated object when saving the parent object. +false+ by default. # [:autosave] # If true, always save the associated object or destroy it if marked for destruction, when saving the parent object. Off by default. + # [:touch] + # If true, the associated object will be touched (the updated_at/on attributes set to now) when this record is either saved or + # destroyed. If you specify a symbol, that attribute will be updated with the current time instead of the updated_at/on attribute. # [:inverse_of] # Specifies the name of the belongs_to association on the associated object that is the inverse of this has_one # association. Does not work in combination with :through or :as options. @@ -1025,6 +1028,7 @@ module ActiveRecord association_constructor_method(:create, reflection, HasOneAssociation) configure_dependency_for_has_one(reflection) end + add_touch_callbacks(reflection, options[:touch]) end # Specifies a one-to-one association with another class. This method should only be used @@ -1133,7 +1137,7 @@ module ActiveRecord end add_counter_cache_callbacks(reflection) if options[:counter_cache] - add_touch_callbacks(reflection, options[:touch]) if options[:touch] + add_touch_callbacks(reflection, options[:touch]) configure_dependency_for_belongs_to(reflection) end @@ -1465,18 +1469,14 @@ module ActiveRecord end def add_touch_callbacks(reflection, touch_attribute) - method_name = "belongs_to_touch_after_save_or_destroy_for_#{reflection.name}".to_sym - define_method(method_name) do - association = send(reflection.name) - - if touch_attribute == true - association.touch unless association.nil? - else - association.touch(touch_attribute) unless association.nil? + if touch_attribute + method_name = "belongs_to_touch_after_save_or_destroy_for_#{reflection.name}".to_sym + define_method(method_name) do + send(reflection.name).touch touch_attribute end + after_save(method_name) + after_destroy(method_name) end - after_save(method_name) - after_destroy(method_name) end # Creates before_destroy callback methods that nullify, delete or destroy @@ -1625,7 +1625,7 @@ module ActiveRecord @@valid_keys_for_has_one_association = [ :class_name, :foreign_key, :remote, :select, :conditions, :order, :include, :dependent, :counter_cache, :extend, :as, :readonly, - :validate, :primary_key, :inverse_of + :validate, :primary_key, :inverse_of, :touch ] def create_has_one_reflection(association_id, options) diff --git a/activerecord/test/cases/timestamp_test.rb b/activerecord/test/cases/timestamp_test.rb index 24b237a..21b1894 100644 --- a/activerecord/test/cases/timestamp_test.rb +++ b/activerecord/test/cases/timestamp_test.rb @@ -2,9 +2,10 @@ require 'cases/helper' require 'models/developer' require 'models/owner' require 'models/pet' +require 'models/toy' class TimestampTest < ActiveRecord::TestCase - fixtures :developers, :owners, :pets + fixtures :developers, :owners, :pets, :toys def setup @developer = Developer.first @@ -36,7 +37,22 @@ class TimestampTest < ActiveRecord::TestCase assert previously_created_at != @developer.created_at end - + + def test_saving_a_record_with_a_has_one_that_specifies_touching_the_children_should_update_the_children_updated_at + pet = Pet.first + previously_children_updated_at = pet.toy.updated_at + pet.update_attribute :name, "Clash" + assert previously_children_updated_at != pet.toy.updated_at + end + + def test_saving_a_record_with_a_has_one_that_specifies_touching_a_specific_attribute_from_the_children_should_update_that_attribute + Pet.has_one :toy, :touch => :created_at + pet = Pet.first + previously_children_created_at = pet.toy.created_at + pet.update_attribute :name, "Clash" + assert previously_children_created_at != pet.toy.created_at + end + def test_saving_a_record_with_a_belongs_to_that_specifies_touching_the_parent_should_update_the_parent_updated_at pet = Pet.first owner = pet.owner @@ -72,4 +88,4 @@ class TimestampTest < ActiveRecord::TestCase ensure Pet.belongs_to :owner, :touch => true end -end \ No newline at end of file +end diff --git a/activerecord/test/models/pet.rb b/activerecord/test/models/pet.rb index a8bf94d..d303331 100644 --- a/activerecord/test/models/pet.rb +++ b/activerecord/test/models/pet.rb @@ -2,4 +2,5 @@ class Pet < ActiveRecord::Base set_primary_key :pet_id belongs_to :owner, :touch => true has_many :toys + has_one :toy, :touch => true end diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index 7a0cf55..701942f 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -493,6 +493,8 @@ ActiveRecord::Schema.define do create_table :toys, :primary_key => :toy_id ,:force => true do |t| t.string :name t.integer :pet_id, :integer + t.datetime :created_at + t.datetime :updated_at end create_table :traffic_lights, :force => true do |t| -- 1.6.5.2 From 84228edf640f83903604a0a4046df2c9cb1cae14 Mon Sep 17 00:00:00 2001 From: Diego Carrion Date: Mon, 19 Apr 2010 21:09:13 -0300 Subject: [PATCH 2/2] added tests for touch behavior when association doesnt exists --- activerecord/lib/active_record/associations.rb | 2 +- activerecord/test/cases/timestamp_test.rb | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletions(-) diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index e27c802..b4b3ef1 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1472,7 +1472,7 @@ module ActiveRecord if touch_attribute method_name = "belongs_to_touch_after_save_or_destroy_for_#{reflection.name}".to_sym define_method(method_name) do - send(reflection.name).touch touch_attribute + send(reflection.name).try :touch, touch_attribute end after_save(method_name) after_destroy(method_name) diff --git a/activerecord/test/cases/timestamp_test.rb b/activerecord/test/cases/timestamp_test.rb index 21b1894..e9101cb 100644 --- a/activerecord/test/cases/timestamp_test.rb +++ b/activerecord/test/cases/timestamp_test.rb @@ -53,6 +53,12 @@ class TimestampTest < ActiveRecord::TestCase assert previously_children_created_at != pet.toy.created_at end + def test_saving_a_record_with_a_has_one_that_specifies_touching_a_specific_attribute_dont_fail_when_the_association_doesnt_exists + assert_nothing_raised do + Pet.create!.update_attribute :name, "Royal" + end + end + def test_saving_a_record_with_a_belongs_to_that_specifies_touching_the_parent_should_update_the_parent_updated_at pet = Pet.first owner = pet.owner -- 1.6.5.2