hoodoo 1.2.0 → 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
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