Skip to content

Commit 16456ea

Browse files
committed
Allow record_timestamps override on {in,up}sert_all
This provides the option to override the record_timestamps value provided by the model when bulk inserting/upserting records, which controls if timestamp columns are set automatically.
1 parent c5c4315 commit 16456ea

File tree

3 files changed

+70
-14
lines changed

3 files changed

+70
-14
lines changed

activerecord/lib/active_record/insert_all.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@ class InsertAll # :nodoc:
77
attr_reader :model, :connection, :inserts, :keys
88
attr_reader :on_duplicate, :returning, :unique_by, :update_sql
99

10-
def initialize(model, inserts, on_duplicate:, returning: nil, unique_by: nil)
10+
def initialize(model, inserts, on_duplicate:, returning: nil, unique_by: nil, record_timestamps: nil)
1111
raise ArgumentError, "Empty list of attributes passed" if inserts.blank?
1212

1313
@model, @connection, @inserts, @keys = model, model.connection, inserts, inserts.first.keys.map(&:to_s)
1414
@on_duplicate, @returning, @unique_by = on_duplicate, returning, unique_by
15-
@record_timestamps = model.record_timestamps
15+
@record_timestamps = record_timestamps.nil? ? model.record_timestamps : record_timestamps
1616

1717
disallow_raw_sql!(returning)
1818
disallow_raw_sql!(on_duplicate)

activerecord/lib/active_record/persistence.rb

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,8 @@ def create!(attributes = nil, &block)
6363
# go through Active Record's type casting and serialization.
6464
#
6565
# See <tt>ActiveRecord::Persistence#insert_all</tt> for documentation.
66-
def insert(attributes, returning: nil, unique_by: nil)
67-
insert_all([ attributes ], returning: returning, unique_by: unique_by)
66+
def insert(attributes, returning: nil, unique_by: nil, record_timestamps: nil)
67+
insert_all([ attributes ], returning: returning, unique_by: unique_by, record_timestamps: record_timestamps)
6868
end
6969

7070
# Inserts multiple records into the database in a single SQL INSERT
@@ -131,8 +131,8 @@ def insert(attributes, returning: nil, unique_by: nil)
131131
# { id: 1, title: "Rework" },
132132
# { id: 2, title: "Eloquent Ruby" }
133133
# ])
134-
def insert_all(attributes, returning: nil, unique_by: nil)
135-
InsertAll.new(self, attributes, on_duplicate: :skip, returning: returning, unique_by: unique_by).execute
134+
def insert_all(attributes, returning: nil, unique_by: nil, record_timestamps: nil)
135+
InsertAll.new(self, attributes, on_duplicate: :skip, returning: returning, unique_by: unique_by, record_timestamps: record_timestamps).execute
136136
end
137137

138138
# Inserts a single record into the database in a single SQL INSERT
@@ -141,8 +141,8 @@ def insert_all(attributes, returning: nil, unique_by: nil)
141141
# go through Active Record's type casting and serialization.
142142
#
143143
# See <tt>ActiveRecord::Persistence#insert_all!</tt> for more.
144-
def insert!(attributes, returning: nil)
145-
insert_all!([ attributes ], returning: returning)
144+
def insert!(attributes, returning: nil, record_timestamps: nil)
145+
insert_all!([ attributes ], returning: returning, record_timestamps: record_timestamps)
146146
end
147147

148148
# Inserts multiple records into the database in a single SQL INSERT
@@ -188,8 +188,8 @@ def insert!(attributes, returning: nil)
188188
# { id: 1, title: "Rework", author: "David" },
189189
# { id: 1, title: "Eloquent Ruby", author: "Russ" }
190190
# ])
191-
def insert_all!(attributes, returning: nil)
192-
InsertAll.new(self, attributes, on_duplicate: :raise, returning: returning).execute
191+
def insert_all!(attributes, returning: nil, record_timestamps: nil)
192+
InsertAll.new(self, attributes, on_duplicate: :raise, returning: returning, record_timestamps: record_timestamps).execute
193193
end
194194

195195
# Updates or inserts (upserts) a single record into the database in a
@@ -198,8 +198,8 @@ def insert_all!(attributes, returning: nil)
198198
# go through Active Record's type casting and serialization.
199199
#
200200
# See <tt>ActiveRecord::Persistence#upsert_all</tt> for documentation.
201-
def upsert(attributes, on_duplicate: :update, returning: nil, unique_by: nil)
202-
upsert_all([ attributes ], on_duplicate: on_duplicate, returning: returning, unique_by: unique_by)
201+
def upsert(attributes, on_duplicate: :update, returning: nil, unique_by: nil, record_timestamps: nil)
202+
upsert_all([ attributes ], on_duplicate: on_duplicate, returning: returning, unique_by: unique_by, record_timestamps: record_timestamps)
203203
end
204204

205205
# Updates or inserts (upserts) multiple records into the database in a
@@ -261,8 +261,8 @@ def upsert(attributes, on_duplicate: :update, returning: nil, unique_by: nil)
261261
# ], unique_by: :isbn)
262262
#
263263
# Book.find_by(isbn: "1").title # => "Eloquent Ruby"
264-
def upsert_all(attributes, on_duplicate: :update, returning: nil, unique_by: nil)
265-
InsertAll.new(self, attributes, on_duplicate: on_duplicate, returning: returning, unique_by: unique_by).execute
264+
def upsert_all(attributes, on_duplicate: :update, returning: nil, unique_by: nil, record_timestamps: nil)
265+
InsertAll.new(self, attributes, on_duplicate: on_duplicate, returning: returning, unique_by: unique_by, record_timestamps: record_timestamps).execute
266266
end
267267

268268
# Given an attributes hash, +instantiate+ returns a new instance of

activerecord/test/cases/insert_all_test.rb

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,18 @@ def test_upsert_all_implicitly_sets_timestamps_on_create_when_model_record_times
406406
end
407407
end
408408

409+
def test_upsert_all_does_not_implicitly_set_timestamps_on_create_when_model_record_timestamps_is_true_but_overridden
410+
with_record_timestamps(Ship, true) do
411+
Ship.upsert_all [{ id: 101, name: "RSS Boaty McBoatface" }], record_timestamps: false
412+
413+
ship = Ship.find(101)
414+
assert_nil ship.created_at
415+
assert_nil ship.created_on
416+
assert_nil ship.updated_at
417+
assert_nil ship.updated_on
418+
end
419+
end
420+
409421
def test_upsert_all_does_not_implicitly_set_timestamps_on_create_when_model_record_timestamps_is_false
410422
with_record_timestamps(Ship, false) do
411423
Ship.upsert_all [{ id: 101, name: "RSS Boaty McBoatface" }]
@@ -418,6 +430,18 @@ def test_upsert_all_does_not_implicitly_set_timestamps_on_create_when_model_reco
418430
end
419431
end
420432

433+
def test_upsert_all_implicitly_sets_timestamps_on_create_when_model_record_timestamps_is_false_but_overridden
434+
with_record_timestamps(Ship, false) do
435+
Ship.upsert_all [{ id: 101, name: "RSS Boaty McBoatface" }], record_timestamps: true
436+
437+
ship = Ship.find(101)
438+
assert_equal Time.now.year, ship.created_at.year
439+
assert_equal Time.now.year, ship.created_on.year
440+
assert_equal Time.now.year, ship.updated_at.year
441+
assert_equal Time.now.year, ship.updated_on.year
442+
end
443+
end
444+
421445
def test_upsert_all_implicitly_sets_timestamps_on_update_when_model_record_timestamps_is_true
422446
skip unless supports_insert_on_duplicate_update?
423447

@@ -434,6 +458,22 @@ def test_upsert_all_implicitly_sets_timestamps_on_update_when_model_record_times
434458
end
435459
end
436460

461+
def test_upsert_all_does_not_implicitly_set_timestamps_on_update_when_model_record_timestamps_is_true_but_overridden
462+
skip unless supports_insert_on_duplicate_update?
463+
464+
with_record_timestamps(Ship, true) do
465+
travel_to(Date.new(2016, 4, 17)) { Ship.create! id: 101, name: "RSS Boaty McBoatface" }
466+
467+
Ship.upsert_all [{ id: 101, name: "RSS Sir David Attenborough" }], record_timestamps: false
468+
469+
ship = Ship.find(101)
470+
assert_equal 2016, ship.created_at.year
471+
assert_equal 2016, ship.created_on.year
472+
assert_equal 2016, ship.updated_at.year
473+
assert_equal 2016, ship.updated_on.year
474+
end
475+
end
476+
437477
def test_upsert_all_does_not_implicitly_set_timestamps_on_update_when_model_record_timestamps_is_false
438478
skip unless supports_insert_on_duplicate_update?
439479

@@ -450,6 +490,22 @@ def test_upsert_all_does_not_implicitly_set_timestamps_on_update_when_model_reco
450490
end
451491
end
452492

493+
def test_upsert_all_implicitly_sets_timestamps_on_update_when_model_record_timestamps_is_false_but_overridden
494+
skip unless supports_insert_on_duplicate_update?
495+
496+
with_record_timestamps(Ship, false) do
497+
Ship.create! id: 101, name: "RSS Boaty McBoatface"
498+
499+
Ship.upsert_all [{ id: 101, name: "RSS Sir David Attenborough" }], record_timestamps: true
500+
501+
ship = Ship.find(101)
502+
assert_nil ship.created_at
503+
assert_nil ship.created_on
504+
assert_equal Time.now.year, ship.updated_at.year
505+
assert_equal Time.now.year, ship.updated_on.year
506+
end
507+
end
508+
453509
def test_insert_all_raises_on_unknown_attribute
454510
assert_raise ActiveRecord::UnknownAttributeError do
455511
Book.insert_all! [{ unknown_attribute: "Test" }]

0 commit comments

Comments
 (0)