hoodoo 1.2.0 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- MjI0N2Q4YmRmMTIyYzA5NDhiZTdmYjY3YjBiNDg2ZWI4ZTQ5ODU2MQ==
4
+ OWM1YWFkNDAxZGQ1MGUyZTNmNTIyZjk3MGYxMzNmYjRlYzZiYmRmYw==
5
5
  data.tar.gz: !binary |-
6
- M2U5ZjY5OTEyZWI4Y2NiN2M5M2VjM2MyNzYyZWQyNGQ3YzdmZThkZQ==
6
+ YjRmZDVjYjk1YzBkMjU2ODczMGU3YTJjYzZhMjdjYTE1ZDgyYzE1NA==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- Mjc4MGE2ZjMyMDNkNGMzYTViNzM5YmEyOTQ1M2M1ZTgyZGYwNjA4N2M1ZThl
10
- ODNkOTZjNTcwNjQyNmY2OGRmMmRlOTE5ZTEyZTljYTg3MTI5MjZlNmIzYzY2
11
- NTUwZWQ0ZmVjZDgxNzRkYzRmZWQ0YTAwM2U2MzE2NWNiMTNmYjI=
9
+ ZTRiOGNkNjYzOTE3YjJkYWE0N2M4ZjdkZTllM2JhMTkwMTFjMTM1NWJjYTU5
10
+ YTVkMThjYjZkMmU3ZGJiMTk0NzM1YmRlNWZiZTk4ZTgxYTQwMGNlMTE2YjAy
11
+ ZTc4OTNiNGY5Mjg3MDc0NGI1MDg5OTg2MmU5YzFlNzczZTM1NTA=
12
12
  data.tar.gz: !binary |-
13
- MjY2N2QwNjViZjhiYjM1NDBkNDczMGVkNDVlOTczYzVlOTRlYzM2NGQ4ODEx
14
- NDFkNDI4YjI1NmYzZTVlNmNmYjdiMDhlZWU1NDk1YTRhNjg1YzE0YjNmZDY2
15
- ZmM5NWM5MTc5OTEyOGQzN2I4MTUwMDE2NTRlNGQ5NTQ3MWVjYjY=
13
+ ZmFiZTNiZDZhZGI4NzA0NzAyYWM4ZTI3YTAxM2E5OTRlYzNlN2NkZWM4ZDUx
14
+ Y2NkNmJmZTA4Njg3N2EyNmQwMmY3YThmOGM4NWFiNmI3MTRmMDEzNWQ4MmFj
15
+ ZjI3NTJmNzVmYTgxOGNkYWEwOTA3OWExNGFlZDkzYzQzMDFmZmI=
@@ -189,6 +189,10 @@ module Hoodoo
189
189
  # #
190
190
  # TABLES_TO_CONVERT = [ :table_name, :another_table_name, ... ]
191
191
  #
192
+ # # This will come in handy later.
193
+ # #
194
+ # SQL_DATE_MAXIMUM = ActiveRecord::Base.connection.quoted_date( Hoodoo::ActiveRecord::ManuallyDated::DATE_MAXIMUM )
195
+ #
192
196
  # def up
193
197
  #
194
198
  # # If you have any uniqueness constraints on this table, you'll need to
@@ -211,9 +215,17 @@ module Hoodoo
211
215
  # add_column table, :effective_end, :datetime, :null => true # (initially, but see below)
212
216
  # add_column table, :uuid, :string, :limit => 32
213
217
  #
214
- # add_index table, [ :effective_start, :effective_end ], :name => "index_#{ table }_start_end"
215
- # add_index table, [ :uuid, :effective_start, :effective_end ], :name => "index_#{ table }_id_start_end"
216
- # add_index table, [ :uuid, :effective_end ], :unique => true, :name => "index_#{ table }_id_end"
218
+ # add_index table, [ :effective_start, :effective_end ], :name => "index_#{ table }_start_end"
219
+ # add_index table, [ :uuid, :effective_start, :effective_end ], :name => "index_#{ table }_uuid_start_end"
220
+ #
221
+ # # We can't allow duplicate UUIDs. Here's how to correctly scope based on
222
+ # # any 'contemporary' record, given its known fixed 'effective_end'.
223
+ # #
224
+ # ActiveRecord::Migration.add_index table,
225
+ # :uuid,
226
+ # :unique => true,
227
+ # :name => "index_#{ table }_uuid_end_unique",
228
+ # :where => "(effective_end = '#{ SQL_DATE_MAXIMUM }')"
217
229
  #
218
230
  # # If there's any data in the table already, it can't have any historic
219
231
  # # entries. So, we want to set the UUID to the 'id' field's old value,
@@ -241,21 +253,21 @@ module Hoodoo
241
253
  #
242
254
  # end
243
255
  #
244
- # # Now add back any indices dropped earlier, but add them as two composite
245
- # # indices each - one with just :effective_end added, the other with both
246
- # # :effective_start and :effective_end added.
247
- # #
248
- # # For example, suppose you had declared this index somewhere:
256
+ # # Now add back any indices dropped earlier, but add them back as a
257
+ # # conditional index as shown earlier for the "uuid" column. For example,
258
+ # # suppose you had declared this index somewhere:
249
259
  # #
250
260
  # # add_index :accounts, :account_number, :unique => true
251
261
  # #
252
262
  # # You need to have done "remove_index :accounts, :account_number" earlier;
253
- # # then now add the two new equivalents. You may well find you have to give
254
- # # them custom names to avoid hitting index name length limits in your
255
- # # database if ActiveRecord is allowed to generate a name automatically:
263
+ # # then now add the new equivalent. You may well find you have to give it a
264
+ # # custom name to avoid hitting index name length limits in your database:
256
265
  # #
257
- # # add_index :accounts, [ :account_number, :effective_start, :effective_end ], :name => 'index_accounts_an_es_ee'
258
- # # add_index :accounts, [ :account_number, :effective_end ], :unique => true, :name => 'index_accounts_an_ee'
266
+ # # ActiveRecord::Migration.add_index :accounts,
267
+ # # :account_number,
268
+ # # :unique => true,
269
+ # # :name => "index_#{ table }_account_number_end_unique",
270
+ # # :where => "(effective_end = '#{ SQL_DATE_MAXIMUM }')"
259
271
  # #
260
272
  # # You might want to perform more detailed analysis on your index
261
273
  # # requirements once manual dating is enabled, but the above is a good rule
@@ -396,7 +408,11 @@ module Hoodoo
396
408
  def manual_dating_enabled
397
409
  self.nz_co_loyalty_hoodoo_manually_dated = true
398
410
 
399
- before_save do
411
+ # This is the 'tightest'/innermost callback available for creation.
412
+ # Intentionally have nothing for updates/deletes as the high level
413
+ # API here must be used; we don't want to introduce any more magic.
414
+
415
+ before_create do
400
416
  now = Time.now.utc.round( SECONDS_DECIMAL_PLACES )
401
417
 
402
418
  self.created_at ||= now
@@ -569,86 +585,83 @@ module Hoodoo
569
585
  attributes: context.request.body,
570
586
  scope: all() )
571
587
 
572
- new_record = nil
573
- retry_operation = false
588
+ new_record = nil
589
+ retried_operation = false
574
590
 
575
591
  begin
576
- begin
577
592
 
578
- # 'requires_new' => exceptions in nested transactions will cause
579
- # rollback; see the comment documentation for the Writer module's
580
- # "persist_in" method for details.
593
+ # 'requires_new' => exceptions in nested transactions will cause
594
+ # rollback; see the comment documentation for the Writer module's
595
+ # "persist_in" method for details.
596
+ #
597
+ self.transaction( :requires_new => true ) do
598
+
599
+ lock_scope = scope.acquisition_scope( ident ).lock( true )
600
+ self.connection.execute( lock_scope.to_sql )
601
+
602
+ original = scope.manually_dated_contemporary().acquire( ident )
603
+ break if original.nil?
604
+
605
+ # The only way this can fail is by throwing an exception.
581
606
  #
582
- self.transaction( :requires_new => true ) do
583
-
584
- lock_scope = scope.acquisition_scope( ident ).lock( true )
585
- self.connection.execute( lock_scope.to_sql )
586
-
587
- original = scope.manually_dated_contemporary().acquire( ident )
588
- break if original.nil?
589
-
590
- # The only way this can fail is by throwing an exception.
591
- #
592
- original.update_column( :effective_end, Time.now.utc.round( SECONDS_DECIMAL_PLACES ) )
593
-
594
- # When you 'dup' a live model, ActiveRecord clears the 'created_at'
595
- # and 'updated_at' values, and the 'id' column - even if you set
596
- # the "primary_key=..." value on the model to something else. Put
597
- # it all back together again.
598
- #
599
- # Duplicate, apply attributes, then overwrite anything that is
600
- # vital for dating so that the inbound attributes hash can't cause
601
- # any inconsistencies.
602
- #
603
- new_record = original.dup
604
- new_record.assign_attributes( attributes )
605
-
606
- new_record.id = nil
607
- new_record.uuid = original.uuid
608
- new_record.created_at = original.created_at
609
- new_record.updated_at = original.effective_end # (sic.)
610
- new_record.effective_start = original.effective_end # (sic.)
611
- new_record.effective_end = DATE_MAXIMUM
612
-
613
- # Save with validation but no exceptions. The caller examines the
614
- # returned object to see if there were any validation errors.
615
- #
616
- new_record.save()
617
-
618
- # Must roll back if the new record didn't save, to undo the
619
- # 'effective_end' column update on 'original' earlier.
620
- #
621
- raise ::ActiveRecord::Rollback if new_record.errors.present?
622
- end
623
-
624
- retry_operation = false
625
-
626
- rescue ::ActiveRecord::StatementInvalid => exception
627
-
628
- # By observation, PostgreSQL can start worrying about deadlocks
629
- # with the above. TODO: I don't know why; I can't see how it can
630
- # possibly end up trying to do the things the logs imply given
631
- # that the locking is definitely working and blocking anything
632
- # other than one transaction at a time from working on a set of
633
- # rows scoped by a particular resource UUID.
607
+ original.update_column( :effective_end, Time.now.utc.round( SECONDS_DECIMAL_PLACES ) )
608
+
609
+ # When you 'dup' a live model, ActiveRecord clears the 'created_at'
610
+ # and 'updated_at' values, and the 'id' column - even if you set
611
+ # the "primary_key=..." value on the model to something else. Put
612
+ # it all back together again.
634
613
  #
635
- # In such a case, retry. But only do so once; then give up.
614
+ # Duplicate, apply attributes, then overwrite anything that is
615
+ # vital for dating so that the inbound attributes hash can't cause
616
+ # any inconsistencies.
636
617
  #
637
- if retry_operation == false && exception.message.downcase.include?( 'deadlock' )
638
- retry_operation = true
618
+ new_record = original.dup
619
+ new_record.assign_attributes( attributes )
620
+
621
+ new_record.id = nil
622
+ new_record.uuid = original.uuid
623
+ new_record.created_at = original.created_at
624
+ new_record.updated_at = original.effective_end # (sic.)
625
+ new_record.effective_start = original.effective_end # (sic.)
626
+ new_record.effective_end = DATE_MAXIMUM
627
+
628
+ # Save with validation but no exceptions. The caller examines the
629
+ # returned object to see if there were any validation errors.
630
+ #
631
+ new_record.save()
639
632
 
640
- # Give other Threads time to run, maximising chance of deadlock
641
- # being resolved before retry.
642
- #
643
- sleep 0.1
633
+ # Must roll back if the new record didn't save, to undo the
634
+ # 'effective_end' column update on 'original' earlier.
635
+ #
636
+ raise ::ActiveRecord::Rollback if new_record.errors.present?
637
+ end
638
+
639
+ rescue ::ActiveRecord::StatementInvalid => exception
640
+
641
+ # By observation, PostgreSQL can start worrying about deadlocks
642
+ # with the above. Leading theory is that it's "half way through"
643
+ # inserting the new row when someone else comes along and waits
644
+ # on the lock, but that new waiting thread has also ended up
645
+ # capturing a lock on the half-inserted row (since inserting
646
+ # involves lots of internal steps and locks).
647
+ #
648
+ # In such a case, retry. But only do so once; then give up.
649
+ #
650
+ if retried_operation == false && exception.message.downcase.include?( 'deadlock' )
651
+ retried_operation = true
652
+
653
+ # Give other Threads time to run, maximising chance of deadlock
654
+ # being resolved before retry.
655
+ #
656
+ sleep( 0.1 )
657
+ retry
644
658
 
645
- else
646
- raise exception
659
+ else
660
+ raise exception
647
661
 
648
- end
662
+ end
649
663
 
650
- end # "begin"..."rescue"..."end"
651
- end while ( retry_operation ) # "begin"..."end while"
664
+ end # "begin"..."rescue"..."end"
652
665
 
653
666
  return new_record
654
667
  end
@@ -12,6 +12,6 @@ module Hoodoo
12
12
  # The Hoodoo gem version. If this changes, ensure that the date in
13
13
  # "hoodoo.gemspec" is correct and run "bundle install" (or "update").
14
14
  #
15
- VERSION = '1.2.0'
15
+ VERSION = '1.2.1'
16
16
 
17
17
  end
@@ -16,11 +16,35 @@ describe Hoodoo::ActiveRecord::ManuallyDated do
16
16
  t.string :id, :null => false, :length => 32
17
17
 
18
18
  t.text :data
19
+ t.text :unique
19
20
 
20
21
  t.timestamps
21
22
  t.datetime :effective_start, :null => false
22
23
  t.datetime :effective_end, :null => false
23
24
  end
25
+
26
+ # Documentation recommends these constraints in migrations, so ensure
27
+ # everything works when they're present.
28
+
29
+ ActiveRecord::Migration.add_index :r_spec_model_manual_date_tests, [ :effective_start, :effective_end ], :name => "index_rspec_mmdt_start_end"
30
+ ActiveRecord::Migration.add_index :r_spec_model_manual_date_tests, [ :uuid, :effective_start, :effective_end ], :name => "index_rspec_mmdt_id_start_end"
31
+
32
+ sql_date_maximum = ActiveRecord::Base.connection.quoted_date( Hoodoo::ActiveRecord::ManuallyDated::DATE_MAXIMUM )
33
+
34
+ ActiveRecord::Migration.add_index :r_spec_model_manual_date_tests,
35
+ :uuid,
36
+ :unique => true,
37
+ :name => "index_rspec_mmdt_id_end",
38
+ :where => "(effective_end = '#{ sql_date_maximum }')"
39
+
40
+ # Documentation gives something similar to this as an example of how
41
+ # to enforce a previously-simple uniqness constraint on one column.
42
+
43
+ ActiveRecord::Migration.add_index :r_spec_model_manual_date_tests,
44
+ :unique,
45
+ :unique => true,
46
+ :name => "index_rspec_mmdt_unique_ee",
47
+ :where => "(effective_end = '#{ sql_date_maximum }')"
24
48
  end
25
49
 
26
50
  class RSpecModelManualDateTest < ActiveRecord::Base
@@ -448,7 +472,7 @@ describe Hoodoo::ActiveRecord::ManuallyDated do
448
472
  }.to change( RSpecModelManualDateTest, :count ).by( 1 )
449
473
 
450
474
  expect( result ).to_not be_nil
451
- expect( result.errors ).to be_empty
475
+ expect( result.errors.messages ).to be_empty
452
476
  expect( result.persisted? ).to eq( true )
453
477
  end
454
478
 
@@ -593,6 +617,7 @@ describe Hoodoo::ActiveRecord::ManuallyDated do
593
617
  )
594
618
 
595
619
  expect( result ).to_not be_nil
620
+ expect( result.errors.messages ).to be_empty
596
621
  expect( result.persisted? ).to eq( true )
597
622
  end
598
623
 
@@ -618,6 +643,212 @@ describe Hoodoo::ActiveRecord::ManuallyDated do
618
643
  end
619
644
  end
620
645
 
646
+ # Rapid updates within configured date resolution might not be resolvable
647
+ # as individual history items via API, but they should still exist and
648
+ # things like uuid/start/end uniqueness constraint columns ought to still
649
+ # function without false positives.
650
+ #
651
+ context 'rapid updates' do
652
+ it 'does not hit uniqueness constraint violations during very rapid update attempts' do
653
+ overall_before = RSpecModelManualDateTest.count
654
+ historic_before = RSpecModelManualDateTest.manually_dated_historic.count
655
+ contemporary_before = RSpecModelManualDateTest.manually_dated_contemporary.count
656
+ update_count = 20
657
+
658
+ record = RSpecModelManualDateTest.new( {
659
+ :data => Hoodoo::UUID.generate(),
660
+ :created_at => Time.now - 1.year
661
+ } )
662
+
663
+ record.save!
664
+
665
+ 1.upto( update_count ) do
666
+ result = RSpecModelManualDateTest.manually_dated_update_in(
667
+ @context,
668
+ ident: record.uuid,
669
+ attributes: { 'data' => Hoodoo::UUID.generate() }
670
+ )
671
+
672
+ expect( result ).to_not be_nil
673
+ expect( result.errors.messages ).to be_empty
674
+ expect( result.persisted? ).to eq( true )
675
+ end
676
+
677
+ overall_after = RSpecModelManualDateTest.count
678
+ historic_after = RSpecModelManualDateTest.manually_dated_historic.count
679
+ contemporary_after = RSpecModelManualDateTest.manually_dated_contemporary.count
680
+
681
+ expect( overall_after - overall_before ).to eq( update_count + 1 )
682
+ expect( historic_after - historic_before ).to eq( update_count )
683
+ expect( contemporary_after - contemporary_before ).to eq( 1 )
684
+
685
+ expect( RSpecModelManualDateTest.where( :uuid => record.uuid ).count ).to eq( update_count + 1 )
686
+ expect( RSpecModelManualDateTest.manually_dated_historic.where( :uuid => record.uuid ).count ).to eq( update_count )
687
+ expect( RSpecModelManualDateTest.manually_dated_contemporary.where( :uuid => record.uuid ).count ).to eq( 1 )
688
+ end
689
+ end
690
+
691
+ context 'uniqueness' do
692
+ before :all do
693
+ @unique = Hoodoo::UUID.generate()
694
+ end
695
+
696
+ it 'allows duplications within same resource instance history' do
697
+
698
+ # First generate a unique record
699
+
700
+ record = RSpecModelManualDateTest.new( {
701
+ :data => Hoodoo::UUID.generate(),
702
+ :unique => @unique,
703
+ :created_at => Time.now - 1.year
704
+ } )
705
+
706
+ record.save!
707
+
708
+ # Now update it
709
+
710
+ result = RSpecModelManualDateTest.manually_dated_update_in(
711
+ @context,
712
+ ident: record.uuid,
713
+ attributes: { 'data' => Hoodoo::UUID.generate() }
714
+ )
715
+
716
+ # We should end up with two records; one historic and one contemporary
717
+
718
+ expect( result ).to_not be_nil
719
+ expect( result.errors.messages ).to be_empty
720
+ expect( result.persisted? ).to eq( true )
721
+ expect( RSpecModelManualDateTest.where( :unique => @unique ).count ).to eq( 2 )
722
+ expect( RSpecModelManualDateTest.manually_dated_historic.where( :unique => @unique ).count ).to eq( 1 )
723
+ expect( RSpecModelManualDateTest.manually_dated_contemporary.where( :unique => @unique ).count ).to eq( 1 )
724
+
725
+ end
726
+
727
+ it 'prohibits duplicates across different resource instances' do
728
+
729
+ # First generate a unique record
730
+
731
+ record = RSpecModelManualDateTest.new( {
732
+ :data => Hoodoo::UUID.generate(),
733
+ :unique => @unique,
734
+ :created_at => Time.now - 1.year
735
+ } )
736
+
737
+ record.save!
738
+
739
+ # Now make another one; this should be prohibited.
740
+
741
+ another_record = RSpecModelManualDateTest.new( {
742
+ :data => Hoodoo::UUID.generate(),
743
+ :unique => @unique
744
+ } )
745
+
746
+ expect {
747
+ another_record.save!
748
+ }.to raise_error( ::ActiveRecord::RecordNotUnique )
749
+
750
+ expect( RSpecModelManualDateTest.where( :unique => @unique ).count ).to eq( 1 )
751
+
752
+ end
753
+
754
+ it 'prohibits duplications when there is resource history and a contemporary record' do
755
+
756
+ # A fair bit of cut-and-paste here.. First generate, then update
757
+ # one record.
758
+
759
+ record = RSpecModelManualDateTest.new( {
760
+ :data => Hoodoo::UUID.generate(),
761
+ :unique => @unique,
762
+ :created_at => Time.now - 1.year
763
+ } )
764
+
765
+ record.save!
766
+
767
+ result = RSpecModelManualDateTest.manually_dated_update_in(
768
+ @context,
769
+ ident: record.uuid,
770
+ attributes: { 'data' => Hoodoo::UUID.generate() }
771
+ )
772
+
773
+ # We should end up with two records; one historic and one contemporary
774
+
775
+ expect( result ).to_not be_nil
776
+ expect( result.errors.messages ).to be_empty
777
+ expect( result.persisted? ).to eq( true )
778
+ expect( RSpecModelManualDateTest.where( :unique => @unique ).count ).to eq( 2 )
779
+ expect( RSpecModelManualDateTest.manually_dated_historic.where( :unique => @unique ).count ).to eq( 1 )
780
+ expect( RSpecModelManualDateTest.manually_dated_contemporary.where( :unique => @unique ).count ).to eq( 1 )
781
+
782
+ # We should be unable to make a new instance that duplicates the unique
783
+ # value.
784
+
785
+ another_record = RSpecModelManualDateTest.new( {
786
+ :data => Hoodoo::UUID.generate(),
787
+ :unique => @unique
788
+ } )
789
+
790
+ expect {
791
+ another_record.save!
792
+ }.to raise_error( ::ActiveRecord::RecordNotUnique )
793
+
794
+ expect( RSpecModelManualDateTest.where( :unique => @unique ).count ).to eq( 2 )
795
+
796
+ end
797
+
798
+ it 'allows duplications when an old resource instance has been deleted' do
799
+
800
+ # First generate a unique record
801
+
802
+ record = RSpecModelManualDateTest.new( {
803
+ :data => Hoodoo::UUID.generate(),
804
+ :unique => @unique,
805
+ :created_at => Time.now - 1.year
806
+ } )
807
+
808
+ record.save!
809
+
810
+ # Now delete it
811
+
812
+ result = RSpecModelManualDateTest.manually_dated_destruction_in(
813
+ @context,
814
+ ident: record.uuid
815
+ )
816
+
817
+ # We should end up with one historic record
818
+
819
+ expect( result ).to_not be_nil
820
+ expect( RSpecModelManualDateTest.where( :unique => @unique ).count ).to eq( 1 )
821
+ expect( RSpecModelManualDateTest.manually_dated_historic.where( :unique => @unique ).count ).to eq( 1 )
822
+ expect( RSpecModelManualDateTest.manually_dated_contemporary.where( :unique => @unique ).count ).to eq( 0 )
823
+
824
+ # For safety/test reliability, sleep to make sure we are beyond the
825
+ # limits of configured date accuracy.
826
+
827
+ sleep( ( 0.1 ** Hoodoo::ActiveRecord::ManuallyDated::SECONDS_DECIMAL_PLACES ) * 2 )
828
+
829
+ # We should be able to make a new instance with the unique value now
830
+
831
+ another_record = RSpecModelManualDateTest.new( {
832
+ :data => Hoodoo::UUID.generate(),
833
+ :unique => @unique
834
+ } )
835
+
836
+ another_record.save!
837
+
838
+ # We should end up with two records; one old UUID historic and one new
839
+ # UUID contemporary
840
+
841
+ expect( another_record ).to_not be_nil
842
+ expect( another_record.errors.messages ).to be_empty
843
+ expect( another_record.persisted? ).to eq( true )
844
+ expect( RSpecModelManualDateTest.where( :unique => @unique ).count ).to eq( 2 )
845
+ expect( RSpecModelManualDateTest.manually_dated_historic.where( :unique => @unique ).count ).to eq( 1 )
846
+ expect( RSpecModelManualDateTest.manually_dated_contemporary.where( :unique => @unique ).count ).to eq( 1 )
847
+ expect( RSpecModelManualDateTest.manually_dated_historic.where( :unique => @unique, :uuid => record.uuid ).count ).to eq( 1 )
848
+ expect( RSpecModelManualDateTest.manually_dated_contemporary.where( :unique => @unique, :uuid => another_record.uuid ).count ).to eq( 1 )
849
+ end
850
+ end
851
+
621
852
  context 'concurrent' do
622
853
  before :all do
623
854
  DatabaseCleaner.strategy = :truncation
@@ -702,7 +933,7 @@ describe Hoodoo::ActiveRecord::ManuallyDated do
702
933
 
703
934
  results.each do | result |
704
935
  expect( result ).to be_a( RSpecModelManualDateTest )
705
- expect( result.errors ).to be_empty
936
+ expect( result.errors.messages ).to be_empty
706
937
  expect( result.persisted? ).to eq( true )
707
938
 
708
939
  result.reload
@@ -766,7 +997,7 @@ describe Hoodoo::ActiveRecord::ManuallyDated do
766
997
  success.reload
767
998
 
768
999
  expect( success ).to be_a( RSpecModelManualDateTest )
769
- expect( success.errors ).to be_empty
1000
+ expect( success.errors.messages ).to be_empty
770
1001
  expect( success.persisted? ).to eq( true )
771
1002
  expect( success.effective_end ).to_not eq( @eot )
772
1003
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hoodoo
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Loyalty New Zealand