From b177427d977b21cb18d2eca688539565e8970324 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Tue, 3 Jan 2017 01:40:04 +0900 Subject: Fix update counters of multiple records with touch: true Currently does not work the following example in the doc: ```ruby # For the Posts with id of 10 and 15, increment the comment_count by 1 # and update the updated_at value for each counter. Post.update_counters [10, 15], comment_count: 1, touch: true # Executes the following SQL: # UPDATE posts # SET comment_count = COALESCE(comment_count, 0) + 1, # `updated_at` = '2016-10-13T09:59:23-05:00' # WHERE id IN (10, 15) ``` --- activerecord/lib/active_record/counter_cache.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/counter_cache.rb b/activerecord/lib/active_record/counter_cache.rb index c654d4703a..f4ee008a12 100644 --- a/activerecord/lib/active_record/counter_cache.rb +++ b/activerecord/lib/active_record/counter_cache.rb @@ -105,7 +105,7 @@ module ActiveRecord end if touch - object = find(id) + object = find(id.is_a?(Array) ? id.first : id) updates << object.class.send(:sanitize_sql_for_assignment, touch_updates(object, touch)) end -- cgit v1.2.3 From 77ff9a0adbd1318b45203a76c64561b115245603 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Tue, 3 Jan 2017 04:34:43 +0900 Subject: Push `current_time_from_proper_timezone` and timestamp attributes methods up to class method Actually these methods don't need instantiation. --- activerecord/lib/active_record/timestamp.rb | 55 ++++++++++++++++++----------- 1 file changed, 35 insertions(+), 20 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/timestamp.rb b/activerecord/lib/active_record/timestamp.rb index 030fc35e2f..09d8d1cdd4 100644 --- a/activerecord/lib/active_record/timestamp.rb +++ b/activerecord/lib/active_record/timestamp.rb @@ -52,14 +52,41 @@ module ActiveRecord clear_timestamp_attributes end + class_methods do + private + def timestamp_attributes_for_create_in_model + timestamp_attributes_for_create.select { |c| column_names.include?(c) } + end + + def timestamp_attributes_for_update_in_model + timestamp_attributes_for_update.select { |c| column_names.include?(c) } + end + + def all_timestamp_attributes_in_model + timestamp_attributes_for_create_in_model + timestamp_attributes_for_update_in_model + end + + def timestamp_attributes_for_create + ["created_at", "created_on"] + end + + def timestamp_attributes_for_update + ["updated_at", "updated_on"] + end + + def current_time_from_proper_timezone + default_timezone == :utc ? Time.now.utc : Time.now + end + end + private def _create_record if record_timestamps current_time = current_time_from_proper_timezone - all_timestamp_attributes.each do |column| - if has_attribute?(column) && !attribute_present?(column) + all_timestamp_attributes_in_model.each do |column| + if !attribute_present?(column) write_attribute(column, current_time) end end @@ -85,30 +112,22 @@ module ActiveRecord end def timestamp_attributes_for_create_in_model - timestamp_attributes_for_create.select { |c| self.class.column_names.include?(c) } + self.class.send(:timestamp_attributes_for_create_in_model) end def timestamp_attributes_for_update_in_model - timestamp_attributes_for_update.select { |c| self.class.column_names.include?(c) } + self.class.send(:timestamp_attributes_for_update_in_model) end def all_timestamp_attributes_in_model - timestamp_attributes_for_create_in_model + timestamp_attributes_for_update_in_model - end - - def timestamp_attributes_for_update - ["updated_at", "updated_on"] - end - - def timestamp_attributes_for_create - ["created_at", "created_on"] + self.class.send(:all_timestamp_attributes_in_model) end - def all_timestamp_attributes - timestamp_attributes_for_create + timestamp_attributes_for_update + def current_time_from_proper_timezone + self.class.send(:current_time_from_proper_timezone) end - def max_updated_column_timestamp(timestamp_names = timestamp_attributes_for_update) + def max_updated_column_timestamp(timestamp_names = self.class.send(:timestamp_attributes_for_update)) timestamp_names .map { |attr| self[attr] } .compact @@ -116,10 +135,6 @@ module ActiveRecord .max end - def current_time_from_proper_timezone - self.class.default_timezone == :utc ? Time.now.utc : Time.now - end - # Clear attributes and changed_attributes def clear_timestamp_attributes all_timestamp_attributes_in_model.each do |attribute_name| -- cgit v1.2.3 From 059a476c570bc3451df855b99724e74bc2cba604 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Tue, 3 Jan 2017 04:42:56 +0900 Subject: Counter cache touching don't need object finding anymore `current_time_from_proper_timezone` and timestamp attributes methods was pushed up to class method. --- activerecord/lib/active_record/counter_cache.rb | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/counter_cache.rb b/activerecord/lib/active_record/counter_cache.rb index f4ee008a12..93b9371206 100644 --- a/activerecord/lib/active_record/counter_cache.rb +++ b/activerecord/lib/active_record/counter_cache.rb @@ -46,7 +46,7 @@ module ActiveRecord counter_name = reflection.counter_cache_column updates = { counter_name.to_sym => object.send(counter_association).count(:all) } - updates.merge!(touch_updates(object, touch)) if touch + updates.merge!(touch_updates(touch)) if touch unscoped.where(primary_key => object.id).update_all(updates) end @@ -105,8 +105,7 @@ module ActiveRecord end if touch - object = find(id.is_a?(Array) ? id.first : id) - updates << object.class.send(:sanitize_sql_for_assignment, touch_updates(object, touch)) + updates << sanitize_sql_for_assignment(touch_updates(touch)) end unscoped.where(primary_key => id).update_all updates.join(", ") @@ -165,9 +164,9 @@ module ActiveRecord end private - def touch_updates(object, touch) - touch = object.send(:timestamp_attributes_for_update_in_model) if touch == true - touch_time = object.send(:current_time_from_proper_timezone) + def touch_updates(touch) + touch = timestamp_attributes_for_update_in_model if touch == true + touch_time = current_time_from_proper_timezone Array(touch).map { |column| [ column, touch_time ] }.to_h end end -- cgit v1.2.3