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
|
-
|
4
|
+
OWM1YWFkNDAxZGQ1MGUyZTNmNTIyZjk3MGYxMzNmYjRlYzZiYmRmYw==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
YjRmZDVjYjk1YzBkMjU2ODczMGU3YTJjYzZhMjdjYTE1ZDgyYzE1NA==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
ZTRiOGNkNjYzOTE3YjJkYWE0N2M4ZjdkZTllM2JhMTkwMTFjMTM1NWJjYTU5
|
10
|
+
YTVkMThjYjZkMmU3ZGJiMTk0NzM1YmRlNWZiZTk4ZTgxYTQwMGNlMTE2YjAy
|
11
|
+
ZTc4OTNiNGY5Mjg3MDc0NGI1MDg5OTg2MmU5YzFlNzczZTM1NTA=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
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 ],
|
215
|
-
# add_index table, [ :uuid, :effective_start, :effective_end ],
|
216
|
-
#
|
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
|
245
|
-
# #
|
246
|
-
# #
|
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
|
254
|
-
# #
|
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
|
-
# #
|
258
|
-
# #
|
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
|
-
|
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
|
573
|
-
|
588
|
+
new_record = nil
|
589
|
+
retried_operation = false
|
574
590
|
|
575
591
|
begin
|
576
|
-
begin
|
577
592
|
|
578
|
-
|
579
|
-
|
580
|
-
|
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
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
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
|
-
#
|
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
|
-
|
638
|
-
|
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
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
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
|
-
|
646
|
-
|
659
|
+
else
|
660
|
+
raise exception
|
647
661
|
|
648
|
-
|
662
|
+
end
|
649
663
|
|
650
|
-
|
651
|
-
end while ( retry_operation ) # "begin"..."end while"
|
664
|
+
end # "begin"..."rescue"..."end"
|
652
665
|
|
653
666
|
return new_record
|
654
667
|
end
|
data/lib/hoodoo/version.rb
CHANGED
@@ -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
|