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
|
-
|
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
|