mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Added :touch option to belongs_to associations that will touch the parent record when the current record is saved or destroyed [DHH]
This commit is contained in:
parent
fdb61f02c5
commit
abb899c54e
6 changed files with 110 additions and 29 deletions
|
@ -1,6 +1,8 @@
|
|||
*Edge*
|
||||
|
||||
* Added ActiveRecord::Base#touch to update the updated_at/on attributes with the current time [DHH]
|
||||
* Added :touch option to belongs_to associations that will touch the parent record when the current record is saved or destroyed [DHH]
|
||||
|
||||
* Added ActiveRecord::Base#touch to update the updated_at/on attributes (or another specified timestamp) with the current time [DHH]
|
||||
|
||||
|
||||
*2.3.2 [Final] (March 15, 2009)*
|
||||
|
|
|
@ -981,6 +981,9 @@ module ActiveRecord
|
|||
# If false, don't validate the associated objects 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.
|
||||
#
|
||||
# Option examples:
|
||||
# belongs_to :firm, :foreign_key => "client_of"
|
||||
|
@ -990,6 +993,8 @@ module ActiveRecord
|
|||
# belongs_to :attachable, :polymorphic => true
|
||||
# belongs_to :project, :readonly => true
|
||||
# belongs_to :post, :counter_cache => true
|
||||
# belongs_to :company, :touch => true
|
||||
# belongs_to :company, :touch => :employees_last_updated_at
|
||||
def belongs_to(association_id, options = {})
|
||||
reflection = create_belongs_to_reflection(association_id, options)
|
||||
|
||||
|
@ -1001,28 +1006,8 @@ module ActiveRecord
|
|||
association_constructor_method(:create, reflection, BelongsToAssociation)
|
||||
end
|
||||
|
||||
# Create the callbacks to update counter cache
|
||||
if options[:counter_cache]
|
||||
cache_column = reflection.counter_cache_column
|
||||
|
||||
method_name = "belongs_to_counter_cache_after_create_for_#{reflection.name}".to_sym
|
||||
define_method(method_name) do
|
||||
association = send(reflection.name)
|
||||
association.class.increment_counter(cache_column, send(reflection.primary_key_name)) unless association.nil?
|
||||
end
|
||||
after_create method_name
|
||||
|
||||
method_name = "belongs_to_counter_cache_before_destroy_for_#{reflection.name}".to_sym
|
||||
define_method(method_name) do
|
||||
association = send(reflection.name)
|
||||
association.class.decrement_counter(cache_column, send(reflection.primary_key_name)) unless association.nil?
|
||||
end
|
||||
before_destroy method_name
|
||||
|
||||
module_eval(
|
||||
"#{reflection.class_name}.send(:attr_readonly,\"#{cache_column}\".intern) if defined?(#{reflection.class_name}) && #{reflection.class_name}.respond_to?(:attr_readonly)"
|
||||
)
|
||||
end
|
||||
add_counter_cache_callbacks(reflection) if options[:counter_cache]
|
||||
add_touch_callbacks(reflection, options[:touch]) if options[:touch]
|
||||
|
||||
configure_dependency_for_belongs_to(reflection)
|
||||
end
|
||||
|
@ -1329,6 +1314,43 @@ module ActiveRecord
|
|||
end
|
||||
end
|
||||
|
||||
def add_counter_cache_callbacks(reflection)
|
||||
cache_column = reflection.counter_cache_column
|
||||
|
||||
method_name = "belongs_to_counter_cache_after_create_for_#{reflection.name}".to_sym
|
||||
define_method(method_name) do
|
||||
association = send(reflection.name)
|
||||
association.class.increment_counter(cache_column, send(reflection.primary_key_name)) unless association.nil?
|
||||
end
|
||||
after_create(method_name)
|
||||
|
||||
method_name = "belongs_to_counter_cache_before_destroy_for_#{reflection.name}".to_sym
|
||||
define_method(method_name) do
|
||||
association = send(reflection.name)
|
||||
association.class.decrement_counter(cache_column, send(reflection.primary_key_name)) unless association.nil?
|
||||
end
|
||||
before_destroy(method_name)
|
||||
|
||||
module_eval(
|
||||
"#{reflection.class_name}.send(:attr_readonly,\"#{cache_column}\".intern) if defined?(#{reflection.class_name}) && #{reflection.class_name}.respond_to?(:attr_readonly)"
|
||||
)
|
||||
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?
|
||||
end
|
||||
end
|
||||
after_save(method_name)
|
||||
after_destroy(method_name)
|
||||
end
|
||||
|
||||
def find_with_associations(options = {})
|
||||
catch :invalid_query do
|
||||
join_dependency = JoinDependency.new(self, merge_includes(scope(:find, :include), options[:include]), options[:joins])
|
||||
|
@ -1499,7 +1521,7 @@ module ActiveRecord
|
|||
@@valid_keys_for_belongs_to_association = [
|
||||
:class_name, :foreign_key, :foreign_type, :remote, :select, :conditions,
|
||||
:include, :dependent, :counter_cache, :extend, :polymorphic, :readonly,
|
||||
:validate
|
||||
:validate, :touch
|
||||
]
|
||||
|
||||
def create_belongs_to_reflection(association_id, options)
|
||||
|
|
|
@ -18,11 +18,21 @@ module ActiveRecord
|
|||
|
||||
# Saves the record with the updated_at/on attributes set to the current time.
|
||||
# If the save fails because of validation errors, an ActiveRecord::RecordInvalid exception is raised.
|
||||
def touch
|
||||
# If an attribute name is passed, that attribute is used for the touch instead of the updated_at/on attributes.
|
||||
#
|
||||
# Examples:
|
||||
#
|
||||
# product.touch # updates updated_at
|
||||
# product.touch(:designed_at) # updates the designed_at attribute
|
||||
def touch(attribute = nil)
|
||||
current_time = current_time_from_proper_timezone
|
||||
|
||||
write_attribute('updated_at', current_time) if respond_to?(:updated_at)
|
||||
write_attribute('updated_on', current_time) if respond_to?(:updated_on)
|
||||
if attribute
|
||||
write_attribute(attribute, current_time)
|
||||
else
|
||||
write_attribute('updated_at', current_time) if respond_to?(:updated_at)
|
||||
write_attribute('updated_on', current_time) if respond_to?(:updated_on)
|
||||
end
|
||||
|
||||
save!
|
||||
end
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
require 'cases/helper'
|
||||
require 'models/developer'
|
||||
require 'models/owner'
|
||||
require 'models/pet'
|
||||
|
||||
class TimestampTest < ActiveRecord::TestCase
|
||||
fixtures :developers
|
||||
fixtures :developers, :owners, :pets
|
||||
|
||||
def setup
|
||||
@developer = Developer.first
|
||||
|
@ -27,4 +29,47 @@ class TimestampTest < ActiveRecord::TestCase
|
|||
|
||||
assert @previously_updated_at != @developer.updated_at
|
||||
end
|
||||
|
||||
def test_touching_a_different_attribute
|
||||
previously_created_at = @developer.created_at
|
||||
@developer.touch(:created_at)
|
||||
|
||||
assert previously_created_at != @developer.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
|
||||
previously_owner_updated_at = owner.updated_at
|
||||
|
||||
pet.name = "Fluffy the Third"
|
||||
pet.save
|
||||
|
||||
assert previously_owner_updated_at != pet.owner.updated_at
|
||||
end
|
||||
|
||||
def test_destroying_a_record_with_a_belongs_to_that_specifies_touching_the_parent_should_update_the_parent_updated_at
|
||||
pet = Pet.first
|
||||
owner = pet.owner
|
||||
previously_owner_updated_at = owner.updated_at
|
||||
|
||||
pet.destroy
|
||||
|
||||
assert previously_owner_updated_at != pet.owner.updated_at
|
||||
end
|
||||
|
||||
def test_saving_a_record_with_a_belongs_to_that_specifies_touching_a_specific_attribute_the_parent_should_update_that_attribute
|
||||
Pet.belongs_to :owner, :touch => :happy_at
|
||||
|
||||
pet = Pet.first
|
||||
owner = pet.owner
|
||||
previously_owner_happy_at = owner.happy_at
|
||||
|
||||
pet.name = "Fluffy the Third"
|
||||
pet.save
|
||||
|
||||
assert previously_owner_happy_at != pet.owner.happy_at
|
||||
ensure
|
||||
Pet.belongs_to :owner, :touch => true
|
||||
end
|
||||
end
|
|
@ -1,5 +1,5 @@
|
|||
class Pet < ActiveRecord::Base
|
||||
set_primary_key :pet_id
|
||||
belongs_to :owner
|
||||
belongs_to :owner, :touch => true
|
||||
has_many :toys
|
||||
end
|
||||
|
|
|
@ -281,6 +281,8 @@ ActiveRecord::Schema.define do
|
|||
|
||||
create_table :owners, :primary_key => :owner_id ,:force => true do |t|
|
||||
t.string :name
|
||||
t.column :updated_at, :datetime
|
||||
t.column :happy_at, :datetime
|
||||
end
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue