mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Merge pull request #31513 from fatkodima/relation-touch_all
Add `touch_all` method to `ActiveRecord::Relation`
This commit is contained in:
commit
7bdfc63cc2
4 changed files with 101 additions and 11 deletions
|
@ -1,3 +1,11 @@
|
|||
* Add `touch_all` method to `ActiveRecord::Relation`.
|
||||
|
||||
Example:
|
||||
|
||||
Person.where(name: "David").touch_all(time: Time.new(2020, 5, 16, 0, 0, 0))
|
||||
|
||||
*fatkodima*, *duggiefresh*
|
||||
|
||||
* Add `ActiveRecord::Base.base_class?` predicate.
|
||||
|
||||
*Bogdan Gusiev*
|
||||
|
|
|
@ -369,6 +369,43 @@ module ActiveRecord
|
|||
@klass.connection.update stmt, "#{@klass} Update All"
|
||||
end
|
||||
|
||||
# Touches all records in the current relation without instantiating records first with the updated_at/on attributes
|
||||
# set to the current time or the time specified.
|
||||
# This method can be passed attribute names and an optional time argument.
|
||||
# If attribute names are passed, they are updated along with updated_at/on attributes.
|
||||
# If no time argument is passed, the current time is used as default.
|
||||
#
|
||||
# === Examples
|
||||
#
|
||||
# # Touch all records
|
||||
# Person.all.touch
|
||||
# # => "UPDATE \"people\" SET \"updated_at\" = '2018-01-04 22:55:23.132670'"
|
||||
#
|
||||
# # Touch multiple records with a custom attribute
|
||||
# Person.all.touch(:created_at)
|
||||
# # => "UPDATE \"people\" SET \"updated_at\" = '2018-01-04 22:55:23.132670', \"created_at\" = '2018-01-04 22:55:23.132670'"
|
||||
#
|
||||
# # Touch multiple records with a specified time
|
||||
# Person.all.touch(time: Time.new(2020, 5, 16, 0, 0, 0))
|
||||
# # => "UPDATE \"people\" SET \"updated_at\" = '2020-05-16 00:00:00'"
|
||||
#
|
||||
# # Touch records with scope
|
||||
# Person.where(name: 'David').touch
|
||||
# # => "UPDATE \"people\" SET \"updated_at\" = '2018-01-04 22:55:23.132670' WHERE \"people\".\"name\" = 'David'"
|
||||
def touch_all(*names, time: nil)
|
||||
attributes = Array(names) + klass.timestamp_attributes_for_update_in_model
|
||||
time ||= klass.current_time_from_proper_timezone
|
||||
updates = {}
|
||||
attributes.each { |column| updates[column] = time }
|
||||
|
||||
if klass.locking_enabled?
|
||||
quoted_locking_column = connection.quote_column_name(klass.locking_column)
|
||||
updates = sanitize_sql_for_assignment(updates) + ", #{quoted_locking_column} = COALESCE(#{quoted_locking_column}, 0) + 1"
|
||||
end
|
||||
|
||||
update_all(updates)
|
||||
end
|
||||
|
||||
# Destroys the records by instantiating each
|
||||
# record and calling its {#destroy}[rdoc-ref:Persistence#destroy] method.
|
||||
# Each object's callbacks are executed (including <tt>:dependent</tt> association options).
|
||||
|
|
|
@ -53,15 +53,19 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
module ClassMethods # :nodoc:
|
||||
def timestamp_attributes_for_update_in_model
|
||||
timestamp_attributes_for_update.select { |c| column_names.include?(c) }
|
||||
end
|
||||
|
||||
def current_time_from_proper_timezone
|
||||
default_timezone == :utc ? Time.now.utc : Time.now
|
||||
end
|
||||
|
||||
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
|
||||
|
@ -73,10 +77,6 @@ module ActiveRecord
|
|||
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
|
||||
|
@ -116,7 +116,7 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
def timestamp_attributes_for_update_in_model
|
||||
self.class.send(:timestamp_attributes_for_update_in_model)
|
||||
self.class.timestamp_attributes_for_update_in_model
|
||||
end
|
||||
|
||||
def all_timestamp_attributes_in_model
|
||||
|
@ -124,7 +124,7 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
def current_time_from_proper_timezone
|
||||
self.class.send(:current_time_from_proper_timezone)
|
||||
self.class.current_time_from_proper_timezone
|
||||
end
|
||||
|
||||
def max_updated_column_timestamp(timestamp_names = timestamp_attributes_for_update_in_model)
|
||||
|
|
|
@ -9,6 +9,7 @@ require "models/comment"
|
|||
require "models/author"
|
||||
require "models/entrant"
|
||||
require "models/developer"
|
||||
require "models/person"
|
||||
require "models/computer"
|
||||
require "models/reply"
|
||||
require "models/company"
|
||||
|
@ -25,7 +26,7 @@ require "models/edge"
|
|||
require "models/subscriber"
|
||||
|
||||
class RelationTest < ActiveRecord::TestCase
|
||||
fixtures :authors, :author_addresses, :topics, :entrants, :developers, :companies, :developers_projects, :accounts, :categories, :categorizations, :categories_posts, :posts, :comments, :tags, :taggings, :cars, :minivans
|
||||
fixtures :authors, :author_addresses, :topics, :entrants, :developers, :people, :companies, :developers_projects, :accounts, :categories, :categorizations, :categories_posts, :posts, :comments, :tags, :taggings, :cars, :minivans
|
||||
|
||||
class TopicWithCallbacks < ActiveRecord::Base
|
||||
self.table_name = :topics
|
||||
|
@ -1525,6 +1526,50 @@ class RelationTest < ActiveRecord::TestCase
|
|||
assert_equal posts(:welcome), comments(:greetings).post
|
||||
end
|
||||
|
||||
def test_touch_all_updates_records_timestamps
|
||||
david = developers(:david)
|
||||
david_previously_updated_at = david.updated_at
|
||||
jamis = developers(:jamis)
|
||||
jamis_previously_updated_at = jamis.updated_at
|
||||
Developer.where(name: "David").touch_all
|
||||
|
||||
assert_not_equal david_previously_updated_at, david.reload.updated_at
|
||||
assert_equal jamis_previously_updated_at, jamis.reload.updated_at
|
||||
end
|
||||
|
||||
def test_touch_all_with_custom_timestamp
|
||||
developer = developers(:david)
|
||||
previously_created_at = developer.created_at
|
||||
previously_updated_at = developer.updated_at
|
||||
Developer.where(name: "David").touch_all(:created_at)
|
||||
developer = developer.reload
|
||||
|
||||
assert_not_equal previously_created_at, developer.created_at
|
||||
assert_not_equal previously_updated_at, developer.updated_at
|
||||
end
|
||||
|
||||
def test_touch_all_with_given_time
|
||||
developer = developers(:david)
|
||||
previously_created_at = developer.created_at
|
||||
previously_updated_at = developer.updated_at
|
||||
new_time = Time.utc(2015, 2, 16, 4, 54, 0)
|
||||
Developer.where(name: "David").touch_all(:created_at, time: new_time)
|
||||
developer = developer.reload
|
||||
|
||||
assert_not_equal previously_created_at, developer.created_at
|
||||
assert_not_equal previously_updated_at, developer.updated_at
|
||||
assert_equal new_time, developer.created_at
|
||||
assert_equal new_time, developer.updated_at
|
||||
end
|
||||
|
||||
def test_touch_all_updates_locking_column
|
||||
person = people(:david)
|
||||
|
||||
assert_difference -> { person.reload.lock_version }, +1 do
|
||||
Person.where(first_name: "David").touch_all
|
||||
end
|
||||
end
|
||||
|
||||
def test_update_on_relation
|
||||
topic1 = TopicWithCallbacks.create! title: "arel", author_name: nil
|
||||
topic2 = TopicWithCallbacks.create! title: "activerecord", author_name: nil
|
||||
|
|
Loading…
Reference in a new issue