@@ -66,6 +66,13 @@ private class ParamKeySaveOperation < ValueColumnModel::SaveOperation
6666end
6767
6868private class UpsertUserOperation < User ::SaveOperation
69+ include QuerySpy
70+
71+ upsert_lookup_columns :name , :nickname
72+ upsert_unique_on :name , :nickname
73+ end
74+
75+ private class UpsertWithoutUniqueKeys < User ::SaveOperation
6976 upsert_lookup_columns :name , :nickname
7077end
7178
@@ -307,6 +314,147 @@ describe "Avram::SaveOperation" do
307314 end
308315 end
309316
317+ describe " .upsert" do
318+ it " should only proc one query" do
319+ UpsertUserOperation .times_called = 0
320+ some_time = Time .utc(2016 , 2 , 15 , 10 , 20 , 30 )
321+
322+ updates = [
323+ {
324+ name: " Name 1" ,
325+ nickname: " Nickname 1" ,
326+ age: 42 ,
327+ joined_at: some_time,
328+ created_at: some_time,
329+ updated_at: some_time,
330+ },
331+ {
332+ name: " Name 2" ,
333+ nickname: " Nickname 2" ,
334+ age: 42 ,
335+ joined_at: some_time,
336+ created_at: some_time,
337+ updated_at: some_time,
338+ },
339+ ]
340+
341+ UpsertUserOperation .upsert(updates)
342+ UpsertUserOperation .times_called.should eq 1
343+ end
344+
345+ context " when a record already exists" do
346+ before_each do
347+ UserFactory .create do |u |
348+ u.name(" Name 1" )
349+ u.nickname(" Nickname 1" )
350+ u.age(42 )
351+ u.year_born(1960 )
352+ u.joined_at(Time .utc)
353+ end
354+ end
355+
356+ it " allows manual passing of updated_at, but ignores created_at" do
357+ some_time = Time .utc(2016 , 2 , 15 , 10 , 20 , 30 )
358+
359+ update = {
360+ name: " Name 1" ,
361+ nickname: " Nickname 1" ,
362+ age: 42 ,
363+ joined_at: some_time,
364+ created_at: some_time,
365+ updated_at: some_time,
366+ }
367+
368+ records = UpsertUserOperation .upsert([update])
369+ records.first.created_at.should_not eq some_time
370+ records.first.updated_at.should eq some_time
371+ end
372+
373+ it " should create one, and update the other record" do
374+ update = {
375+ name: " Name 1" ,
376+ nickname: " Nickname 1" ,
377+ year_born: nil ,
378+ age: 42 ,
379+ joined_at: Time .utc,
380+ }
381+
382+ insert = {
383+ name: " Name 2" ,
384+ nickname: " Nickname 2" ,
385+ year_born: 1980 _i16 ,
386+ age: 64 ,
387+ joined_at: Time .utc,
388+ }
389+
390+ records = UpsertUserOperation .upsert([update, insert])
391+
392+ records.first.id.should_not eq nil
393+ records.last.id.should_not eq nil
394+ records.first.year_born.should eq nil
395+ records.last.year_born.should eq 1980 _i16
396+ end
397+ end
398+
399+ context " when no records exist" do
400+ it " allows manual passing of id" do
401+ insert = {
402+ id: 42 _i64 ,
403+ name: " Name 1" ,
404+ nickname: " Nickname 1" ,
405+ age: 42 ,
406+ joined_at: Time .utc,
407+ }
408+
409+ records = UpsertUserOperation .upsert([insert])
410+ records.first.id.should eq 42 _i64
411+ end
412+
413+ it " allows manual passing of updated_at and created_at" do
414+ some_time = Time .utc(2016 , 2 , 15 , 10 , 20 , 30 )
415+
416+ insert = {
417+ name: " Name 1" ,
418+ nickname: " Nickname 1" ,
419+ age: 42 ,
420+ joined_at: some_time,
421+ created_at: some_time,
422+ updated_at: some_time,
423+ }
424+
425+ records = UpsertUserOperation .upsert([insert])
426+ records.first.id.should_not eq nil
427+ records.first.created_at.should eq some_time
428+ records.first.updated_at.should eq some_time
429+ end
430+ end
431+
432+ context " when the tuple values are passed in different orders" do
433+ it " should upsert records" do
434+ record_args = [
435+ {
436+ name: " Name 1" ,
437+ nickname: " Nickname 1" ,
438+ year_born: nil ,
439+ age: 42 ,
440+ joined_at: Time .utc,
441+ },
442+ {
443+ nickname: " Nickname 2" ,
444+ name: " Name 2" ,
445+ age: 42 ,
446+ joined_at: Time .utc,
447+ year_born: nil ,
448+ },
449+ ]
450+
451+ records = UpsertUserOperation .upsert(record_args)
452+ records.last.nickname.should eq " Nickname 2"
453+ records.last.name.should eq " Name 2"
454+ end
455+ end
456+ end
457+
310458 describe " #errors" do
311459 it " includes errors for all operation attributes" do
312460 operation = SaveUser .new
0 commit comments