chrono_model 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.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +2 -1
  3. data/lib/chrono_model/time_machine.rb +1 -1
  4. data/lib/chrono_model/time_machine/history_model.rb +4 -0
  5. data/lib/chrono_model/version.rb +1 -1
  6. data/spec/chrono_model/adapter/base_spec.rb +157 -0
  7. data/spec/chrono_model/adapter/ddl_spec.rb +243 -0
  8. data/spec/chrono_model/adapter/indexes_spec.rb +72 -0
  9. data/spec/chrono_model/adapter/migrations_spec.rb +312 -0
  10. data/spec/chrono_model/history_models_spec.rb +32 -0
  11. data/spec/chrono_model/time_machine/as_of_spec.rb +188 -0
  12. data/spec/chrono_model/time_machine/changes_spec.rb +50 -0
  13. data/spec/chrono_model/{adapter → time_machine}/counter_cache_race_spec.rb +2 -2
  14. data/spec/chrono_model/time_machine/default_scope_spec.rb +37 -0
  15. data/spec/chrono_model/time_machine/history_spec.rb +104 -0
  16. data/spec/chrono_model/time_machine/keep_cool_spec.rb +27 -0
  17. data/spec/chrono_model/time_machine/manipulations_spec.rb +84 -0
  18. data/spec/chrono_model/time_machine/model_identification_spec.rb +46 -0
  19. data/spec/chrono_model/time_machine/sequence_spec.rb +74 -0
  20. data/spec/chrono_model/time_machine/sti_spec.rb +100 -0
  21. data/spec/chrono_model/{time_query_spec.rb → time_machine/time_query_spec.rb} +22 -5
  22. data/spec/chrono_model/time_machine/timeline_spec.rb +63 -0
  23. data/spec/chrono_model/time_machine/timestamps_spec.rb +43 -0
  24. data/spec/chrono_model/time_machine/transactions_spec.rb +69 -0
  25. data/spec/support/adapter/helpers.rb +53 -0
  26. data/spec/support/adapter/structure.rb +44 -0
  27. data/spec/support/time_machine/helpers.rb +47 -0
  28. data/spec/support/time_machine/structure.rb +111 -0
  29. metadata +48 -14
  30. data/spec/chrono_model/adapter/sti_bug_spec.rb +0 -49
  31. data/spec/chrono_model/adapter_spec.rb +0 -788
  32. data/spec/chrono_model/time_machine_spec.rb +0 -749
  33. data/spec/support/helpers.rb +0 -198
@@ -0,0 +1,72 @@
1
+ require 'spec_helper'
2
+ require 'support/adapter/structure'
3
+
4
+ describe ChronoModel::Adapter do
5
+ include ChronoTest::Adapter::Helpers
6
+ include ChronoTest::Adapter::Structure
7
+
8
+ before :all do
9
+ adapter.create_table :meetings do |t|
10
+ t.string :name
11
+ t.tsrange :interval
12
+ end
13
+ end
14
+
15
+ after :all do
16
+ adapter.drop_table :meetings
17
+ end
18
+
19
+ describe '.add_temporal_indexes' do
20
+ before do
21
+ adapter.add_temporal_indexes :meetings, :interval
22
+ end
23
+
24
+ it { expect(adapter.indexes(:meetings).map(&:name)).to eq [
25
+ 'index_meetings_temporal_on_interval',
26
+ 'index_meetings_temporal_on_lower_interval',
27
+ 'index_meetings_temporal_on_upper_interval'
28
+ ] }
29
+
30
+ after do
31
+ adapter.remove_temporal_indexes :meetings, :interval
32
+ end
33
+ end
34
+
35
+ describe '.remove_temporal_indexes' do
36
+ before :all do
37
+ adapter.add_temporal_indexes :meetings, :interval
38
+ end
39
+
40
+ before do
41
+ adapter.remove_temporal_indexes :meetings, :interval
42
+ end
43
+
44
+ it { expect(adapter.indexes(:meetings)).to be_empty }
45
+ end
46
+
47
+ describe '.add_timeline_consistency_constraint' do
48
+ before do
49
+ adapter.add_timeline_consistency_constraint(:meetings, :interval)
50
+ end
51
+
52
+ it { expect(adapter.indexes(:meetings).map(&:name)).to eq [
53
+ 'meetings_timeline_consistency'
54
+ ] }
55
+
56
+ after do
57
+ adapter.remove_timeline_consistency_constraint(:meetings)
58
+ end
59
+ end
60
+
61
+ describe '.remove_timeline_consistency_constraint' do
62
+ before :all do
63
+ adapter.add_timeline_consistency_constraint :meetings, :interval
64
+ end
65
+
66
+ before do
67
+ adapter.remove_timeline_consistency_constraint(:meetings)
68
+ end
69
+
70
+ it { expect(adapter.indexes(:meetings)).to be_empty }
71
+ end
72
+ end
@@ -0,0 +1,312 @@
1
+ require 'spec_helper'
2
+ require 'support/adapter/structure'
3
+
4
+ shared_examples_for 'temporal table' do
5
+ it { expect(adapter.is_chrono?(subject)).to be(true) }
6
+
7
+ it { is_expected.to_not have_public_backing }
8
+
9
+ it { is_expected.to have_temporal_backing }
10
+ it { is_expected.to have_history_backing }
11
+ it { is_expected.to have_history_extra_columns }
12
+ it { is_expected.to have_history_functions }
13
+ it { is_expected.to have_public_interface }
14
+
15
+ it { is_expected.to have_columns(columns) }
16
+ it { is_expected.to have_temporal_columns(columns) }
17
+ it { is_expected.to have_history_columns(columns) }
18
+ end
19
+
20
+ shared_examples_for 'plain table' do
21
+ it { expect(adapter.is_chrono?(subject)).to be(false) }
22
+
23
+ it { is_expected.to have_public_backing }
24
+
25
+ it { is_expected.to_not have_temporal_backing }
26
+ it { is_expected.to_not have_history_backing }
27
+ it { is_expected.to_not have_history_functions }
28
+ it { is_expected.to_not have_public_interface }
29
+
30
+ it { is_expected.to have_columns(columns) }
31
+ end
32
+
33
+ describe ChronoModel::Adapter do
34
+ include ChronoTest::Adapter::Helpers
35
+ include ChronoTest::Adapter::Structure
36
+
37
+ describe '.create_table' do
38
+ with_temporal_table do
39
+ it_should_behave_like 'temporal table'
40
+ end
41
+
42
+ with_plain_table do
43
+ it_should_behave_like 'plain table'
44
+ end
45
+ end
46
+
47
+ describe '.rename_table' do
48
+ renamed = 'foo_table'
49
+ subject { renamed }
50
+
51
+ context 'temporal: true' do
52
+ before :all do
53
+ adapter.create_table table, :temporal => true, &columns
54
+ adapter.add_index table, :test
55
+ adapter.add_index table, [:foo, :bar]
56
+
57
+ adapter.rename_table table, renamed
58
+ end
59
+ after(:all) { adapter.drop_table(renamed) }
60
+
61
+ it_should_behave_like 'temporal table'
62
+
63
+ it 'renames indexes' do
64
+ new_index_names = adapter.indexes(renamed).map(&:name)
65
+ expected_index_names = [[:test], [:foo, :bar]].map do |idx_cols|
66
+ "index_#{renamed}_on_#{idx_cols.join('_and_')}"
67
+ end
68
+ expect(new_index_names.to_set).to eq expected_index_names.to_set
69
+ end
70
+ end
71
+
72
+ context 'temporal: false' do
73
+ before :all do
74
+ adapter.create_table table, :temporal => false, &columns
75
+
76
+ adapter.rename_table table, renamed
77
+ end
78
+ after(:all) { adapter.drop_table(renamed) }
79
+
80
+ it_should_behave_like 'plain table'
81
+ end
82
+ end
83
+
84
+ describe '.change_table' do
85
+ with_temporal_table do
86
+ before :all do
87
+ adapter.change_table table, :temporal => false
88
+ end
89
+
90
+ it_should_behave_like 'plain table'
91
+ end
92
+
93
+ with_plain_table do
94
+ before :all do
95
+ adapter.add_index table, :foo
96
+ adapter.add_index table, :bar, :unique => true
97
+
98
+ adapter.change_table table, :temporal => true
99
+ end
100
+
101
+ it_should_behave_like 'temporal table'
102
+
103
+ let(:history_indexes) do
104
+ adapter.on_schema(ChronoModel::Adapter::HISTORY_SCHEMA) do
105
+ adapter.indexes(table)
106
+ end
107
+ end
108
+
109
+ it "copies plain index to history" do
110
+ expect(history_indexes.find {|i| i.columns == ['foo']}).to be_present
111
+ end
112
+
113
+ it "copies unique index to history without uniqueness constraint" do
114
+ expect(history_indexes.find {|i| i.columns == ['bar'] && i.unique == false}).to be_present
115
+ end
116
+ end
117
+ end
118
+
119
+ describe '.drop_table' do
120
+ before :all do
121
+ adapter.create_table table, :temporal => true, &columns
122
+
123
+ adapter.drop_table table
124
+ end
125
+
126
+ it { is_expected.to_not have_public_backing }
127
+ it { is_expected.to_not have_temporal_backing }
128
+ it { is_expected.to_not have_history_backing }
129
+ it { is_expected.to_not have_history_functions }
130
+ it { is_expected.to_not have_public_interface }
131
+ end
132
+
133
+ describe '.add_index' do
134
+ with_temporal_table do
135
+ before :all do
136
+ adapter.add_index table, [:foo, :bar], :name => 'foobar_index'
137
+ adapter.add_index table, [:test], :name => 'test_index'
138
+ end
139
+
140
+ it { is_expected.to have_temporal_index 'foobar_index', %w( foo bar ) }
141
+ it { is_expected.to have_history_index 'foobar_index', %w( foo bar ) }
142
+ it { is_expected.to have_temporal_index 'test_index', %w( test ) }
143
+ it { is_expected.to have_history_index 'test_index', %w( test ) }
144
+
145
+ it { is_expected.to_not have_index 'foobar_index', %w( foo bar ) }
146
+ it { is_expected.to_not have_index 'test_index', %w( test ) }
147
+ end
148
+
149
+ with_plain_table do
150
+ before :all do
151
+ adapter.add_index table, [:foo, :bar], :name => 'foobar_index'
152
+ adapter.add_index table, [:test], :name => 'test_index'
153
+ end
154
+
155
+ it { is_expected.to_not have_temporal_index 'foobar_index', %w( foo bar ) }
156
+ it { is_expected.to_not have_history_index 'foobar_index', %w( foo bar ) }
157
+ it { is_expected.to_not have_temporal_index 'test_index', %w( test ) }
158
+ it { is_expected.to_not have_history_index 'test_index', %w( test ) }
159
+
160
+ it { is_expected.to have_index 'foobar_index', %w( foo bar ) }
161
+ it { is_expected.to have_index 'test_index', %w( test ) }
162
+ end
163
+ end
164
+
165
+ describe '.remove_index' do
166
+ with_temporal_table do
167
+ before :all do
168
+ adapter.add_index table, [:foo, :bar], :name => 'foobar_index'
169
+ adapter.add_index table, [:test], :name => 'test_index'
170
+
171
+ adapter.remove_index table, :name => 'test_index'
172
+ end
173
+
174
+ it { is_expected.to_not have_temporal_index 'test_index', %w( test ) }
175
+ it { is_expected.to_not have_history_index 'test_index', %w( test ) }
176
+ it { is_expected.to_not have_index 'test_index', %w( test ) }
177
+ end
178
+
179
+ with_plain_table do
180
+ before :all do
181
+ adapter.add_index table, [:foo, :bar], :name => 'foobar_index'
182
+ adapter.add_index table, [:test], :name => 'test_index'
183
+
184
+ adapter.remove_index table, :name => 'test_index'
185
+ end
186
+
187
+ it { is_expected.to_not have_temporal_index 'test_index', %w( test ) }
188
+ it { is_expected.to_not have_history_index 'test_index', %w( test ) }
189
+ it { is_expected.to_not have_index 'test_index', %w( test ) }
190
+ end
191
+ end
192
+
193
+ describe '.add_column' do
194
+ let(:extra_columns) { [['foobarbaz', 'integer']] }
195
+
196
+ with_temporal_table do
197
+ before :all do
198
+ adapter.add_column table, :foobarbaz, :integer
199
+ end
200
+
201
+ it { is_expected.to have_columns(extra_columns) }
202
+ it { is_expected.to have_temporal_columns(extra_columns) }
203
+ it { is_expected.to have_history_columns(extra_columns) }
204
+ end
205
+
206
+ with_plain_table do
207
+ before :all do
208
+ adapter.add_column table, :foobarbaz, :integer
209
+ end
210
+
211
+ it { is_expected.to have_columns(extra_columns) }
212
+ end
213
+ end
214
+
215
+ describe '.remove_column' do
216
+ let(:resulting_columns) { columns.reject {|c,_| c == 'foo'} }
217
+
218
+ with_temporal_table do
219
+ before :all do
220
+ adapter.remove_column table, :foo
221
+ end
222
+
223
+ it { is_expected.to have_columns(resulting_columns) }
224
+ it { is_expected.to have_temporal_columns(resulting_columns) }
225
+ it { is_expected.to have_history_columns(resulting_columns) }
226
+
227
+ it { is_expected.to_not have_columns([['foo', 'integer']]) }
228
+ it { is_expected.to_not have_temporal_columns([['foo', 'integer']]) }
229
+ it { is_expected.to_not have_history_columns([['foo', 'integer']]) }
230
+ end
231
+
232
+ with_plain_table do
233
+ before :all do
234
+ adapter.remove_column table, :foo
235
+ end
236
+
237
+ it { is_expected.to have_columns(resulting_columns) }
238
+ it { is_expected.to_not have_columns([['foo', 'integer']]) }
239
+ end
240
+ end
241
+
242
+ describe '.rename_column' do
243
+ with_temporal_table do
244
+ before :all do
245
+ adapter.rename_column table, :foo, :taratapiatapioca
246
+ end
247
+
248
+ it { is_expected.to_not have_columns([['foo', 'integer']]) }
249
+ it { is_expected.to_not have_temporal_columns([['foo', 'integer']]) }
250
+ it { is_expected.to_not have_history_columns([['foo', 'integer']]) }
251
+
252
+ it { is_expected.to have_columns([['taratapiatapioca', 'integer']]) }
253
+ it { is_expected.to have_temporal_columns([['taratapiatapioca', 'integer']]) }
254
+ it { is_expected.to have_history_columns([['taratapiatapioca', 'integer']]) }
255
+ end
256
+
257
+ with_plain_table do
258
+ before :all do
259
+ adapter.rename_column table, :foo, :taratapiatapioca
260
+ end
261
+
262
+ it { is_expected.to_not have_columns([['foo', 'integer']]) }
263
+ it { is_expected.to have_columns([['taratapiatapioca', 'integer']]) }
264
+ end
265
+ end
266
+
267
+ describe '.change_column' do
268
+ with_temporal_table do
269
+ before :all do
270
+ adapter.change_column table, :foo, :float
271
+ end
272
+
273
+ it { is_expected.to_not have_columns([['foo', 'integer']]) }
274
+ it { is_expected.to_not have_temporal_columns([['foo', 'integer']]) }
275
+ it { is_expected.to_not have_history_columns([['foo', 'integer']]) }
276
+
277
+ it { is_expected.to have_columns([['foo', 'double precision']]) }
278
+ it { is_expected.to have_temporal_columns([['foo', 'double precision']]) }
279
+ it { is_expected.to have_history_columns([['foo', 'double precision']]) }
280
+ end
281
+
282
+ with_plain_table do
283
+ before(:all) do
284
+ adapter.change_column table, :foo, :float
285
+ end
286
+
287
+ it { is_expected.to_not have_columns([['foo', 'integer']]) }
288
+ it { is_expected.to have_columns([['foo', 'double precision']]) }
289
+ end
290
+ end
291
+
292
+ describe '.remove_column' do
293
+ with_temporal_table do
294
+ before :all do
295
+ adapter.remove_column table, :foo
296
+ end
297
+
298
+ it { is_expected.to_not have_columns([['foo', 'integer']]) }
299
+ it { is_expected.to_not have_temporal_columns([['foo', 'integer']]) }
300
+ it { is_expected.to_not have_history_columns([['foo', 'integer']]) }
301
+ end
302
+
303
+ with_plain_table do
304
+ before :all do
305
+ adapter.remove_column table, :foo
306
+ end
307
+
308
+ it { is_expected.to_not have_columns([['foo', 'integer']]) }
309
+ end
310
+ end
311
+
312
+ end
@@ -0,0 +1,32 @@
1
+ require 'spec_helper'
2
+ require 'support/time_machine/structure'
3
+
4
+ describe ChronoModel do
5
+ describe '.history_models' do
6
+ subject { ChronoModel.history_models }
7
+
8
+ it 'tracks recorded history models' do
9
+ expected = {}
10
+
11
+ # support/time_machine/structure
12
+ expected['foos'] = Foo::History if defined?(Foo::History)
13
+ expected['bars'] = Bar::History if defined?(Bar::History)
14
+ expected['sub_bars'] = SubBar::History if defined?(SubBar::History)
15
+
16
+ # default_scope_spec
17
+ expected['defoos'] = Defoo::History if defined?(Defoo::History)
18
+
19
+ # counter_cache_race_spec
20
+ expected['sections'] = Section::History if defined?(Section::History)
21
+ expected['articles'] = Article::History if defined?(Article::History)
22
+
23
+ # sti_spec
24
+ expected['animals'] = Animal::History if defined?(Animal::History)
25
+ expected['elements'] = Element::History if defined?(Element::History)
26
+
27
+ is_expected.to eq(expected)
28
+ end
29
+
30
+ it { expect(subject.size).to be > 0 }
31
+ end
32
+ end
@@ -0,0 +1,188 @@
1
+ require 'spec_helper'
2
+ require 'support/time_machine/structure'
3
+
4
+ describe ChronoModel::TimeMachine do
5
+ include ChronoTest::TimeMachine::Helpers
6
+
7
+ describe '.as_of' do
8
+ it { expect(Foo.as_of(1.month.ago)).to eq [] }
9
+
10
+ it { expect(Foo.as_of($t.foos[0].ts[0])).to eq [$t.foo, $t.foos[0]] }
11
+ it { expect(Foo.as_of($t.foos[1].ts[0])).to eq [$t.foo, $t.foos[0], $t.foos[1]] }
12
+ it { expect(Foo.as_of(Time.now )).to eq [$t.foo, $t.foos[0], $t.foos[1]] }
13
+
14
+ it { expect(Bar.as_of($t.foos[1].ts[0])).to eq [$t.bar] }
15
+
16
+ it { expect(Bar.as_of($t.bars[0].ts[0])).to eq [$t.bar, $t.bars[0]] }
17
+ it { expect(Bar.as_of($t.bars[1].ts[0])).to eq [$t.bar, $t.bars[0], $t.bars[1]] }
18
+ it { expect(Bar.as_of(Time.now )).to eq [$t.bar, $t.bars[0], $t.bars[1]] }
19
+
20
+ it { expect(Foo.as_of($t.foos[0].ts[0]).first).to be_a(Foo) }
21
+ it { expect(Bar.as_of($t.foos[0].ts[0]).first).to be_a(Bar) }
22
+
23
+ # Associations
24
+ context do
25
+ subject { $t.foos[0].id }
26
+
27
+ it { expect(Foo.as_of($t.foos[0].ts[0]).find(subject).bars).to eq [] }
28
+ it { expect(Foo.as_of($t.foos[1].ts[0]).find(subject).bars).to eq [] }
29
+ it { expect(Foo.as_of($t.bars[0].ts[0]).find(subject).bars).to eq [$t.bars[0]] }
30
+ it { expect(Foo.as_of($t.bars[1].ts[0]).find(subject).bars).to eq [$t.bars[0]] }
31
+ it { expect(Foo.as_of(Time.now ).find(subject).bars).to eq [$t.bars[0]] }
32
+
33
+ it { expect(Foo.as_of($t.bars[0].ts[0]).find(subject).bars.first).to be_a(Bar) }
34
+ end
35
+
36
+ context do
37
+ subject { $t.foos[1].id }
38
+
39
+ it { expect { Foo.as_of($t.foos[0].ts[0]).find(subject) }.to raise_error(ActiveRecord::RecordNotFound) }
40
+ it { expect { Foo.as_of($t.foos[1].ts[0]).find(subject) }.to_not raise_error }
41
+
42
+ it { expect(Foo.as_of($t.bars[0].ts[0]).find(subject).bars).to eq [] }
43
+ it { expect(Foo.as_of($t.bars[1].ts[0]).find(subject).bars).to eq [$t.bars[1]] }
44
+ it { expect(Foo.as_of(Time.now ).find(subject).bars).to eq [$t.bars[1]] }
45
+ end
46
+ end
47
+
48
+
49
+ describe '#as_of' do
50
+ describe 'accepts a Time instance' do
51
+ it { expect($t.foo.as_of(Time.now).name).to eq 'new foo' }
52
+ it { expect($t.bar.as_of(Time.now).name).to eq 'new bar' }
53
+ end
54
+
55
+
56
+ describe 'ignores time zones' do
57
+ it { expect($t.foo.as_of(Time.now.in_time_zone('America/Havana')).name).to eq 'new foo' }
58
+ it { expect($t.bar.as_of(Time.now.in_time_zone('America/Havana')).name).to eq 'new bar' }
59
+ end
60
+
61
+
62
+ describe 'returns records as they were before' do
63
+ it { expect($t.foo.as_of($t.foo.ts[0]).name).to eq 'foo' }
64
+ it { expect($t.foo.as_of($t.foo.ts[1]).name).to eq 'foo bar' }
65
+ it { expect($t.foo.as_of($t.foo.ts[2]).name).to eq 'new foo' }
66
+
67
+ it { expect($t.bar.as_of($t.bar.ts[0]).name).to eq 'bar' }
68
+ it { expect($t.bar.as_of($t.bar.ts[1]).name).to eq 'foo bar' }
69
+ it { expect($t.bar.as_of($t.bar.ts[2]).name).to eq 'bar bar' }
70
+ it { expect($t.bar.as_of($t.bar.ts[3]).name).to eq 'new bar' }
71
+ end
72
+
73
+
74
+ describe 'takes care of associated records' do
75
+ it { expect($t.foo.as_of($t.foo.ts[0]).bars).to eq [] }
76
+ it { expect($t.foo.as_of($t.foo.ts[1]).bars).to eq [] }
77
+ it { expect($t.foo.as_of($t.foo.ts[2]).bars).to eq [$t.bar] }
78
+
79
+ it { expect($t.foo.as_of($t.foo.ts[2]).bars.first.name).to eq 'foo bar' }
80
+
81
+ it { expect($t.foo.as_of($t.bar.ts[0]).bars).to eq [$t.bar] }
82
+ it { expect($t.foo.as_of($t.bar.ts[1]).bars).to eq [$t.bar] }
83
+ it { expect($t.foo.as_of($t.bar.ts[2]).bars).to eq [$t.bar] }
84
+ it { expect($t.foo.as_of($t.bar.ts[3]).bars).to eq [$t.bar] }
85
+
86
+ it { expect($t.foo.as_of($t.bar.ts[0]).bars.first.name).to eq 'bar' }
87
+ it { expect($t.foo.as_of($t.bar.ts[1]).bars.first.name).to eq 'foo bar' }
88
+ it { expect($t.foo.as_of($t.bar.ts[2]).bars.first.name).to eq 'bar bar' }
89
+ it { expect($t.foo.as_of($t.bar.ts[3]).bars.first.name).to eq 'new bar' }
90
+
91
+
92
+ it { expect($t.bar.as_of($t.bar.ts[0]).foo).to eq $t.foo }
93
+ it { expect($t.bar.as_of($t.bar.ts[1]).foo).to eq $t.foo }
94
+ it { expect($t.bar.as_of($t.bar.ts[2]).foo).to eq $t.foo }
95
+ it { expect($t.bar.as_of($t.bar.ts[3]).foo).to eq $t.foo }
96
+
97
+ it { expect($t.bar.as_of($t.bar.ts[0]).foo.name).to eq 'foo bar' }
98
+ it { expect($t.bar.as_of($t.bar.ts[1]).foo.name).to eq 'foo bar' }
99
+ it { expect($t.bar.as_of($t.bar.ts[2]).foo.name).to eq 'new foo' }
100
+ it { expect($t.bar.as_of($t.bar.ts[3]).foo.name).to eq 'new foo' }
101
+ end
102
+
103
+
104
+ describe 'supports historical queries with includes()' do
105
+ it { expect(Foo.as_of($t.foo.ts[0]).includes(:bars).first.bars).to eq [] }
106
+ it { expect(Foo.as_of($t.foo.ts[1]).includes(:bars).first.bars).to eq [] }
107
+ it { expect(Foo.as_of($t.foo.ts[2]).includes(:bars).first.bars).to eq [$t.bar] }
108
+
109
+ it { expect(Foo.as_of($t.bar.ts[0]).includes(:bars).first.bars.first.name).to eq 'bar' }
110
+ it { expect(Foo.as_of($t.bar.ts[1]).includes(:bars).first.bars.first.name).to eq 'foo bar' }
111
+ it { expect(Foo.as_of($t.bar.ts[2]).includes(:bars).first.bars.first.name).to eq 'bar bar' }
112
+ it { expect(Foo.as_of($t.bar.ts[3]).includes(:bars).first.bars.first.name).to eq 'new bar' }
113
+
114
+
115
+ it { expect(Foo.as_of($t.foo.ts[0]).includes(bars: :sub_bars).first.bars).to eq [] }
116
+ it { expect(Foo.as_of($t.foo.ts[1]).includes(bars: :sub_bars).first.bars).to eq [] }
117
+ it { expect(Foo.as_of($t.foo.ts[2]).includes(bars: :sub_bars).first.bars).to eq [$t.bar] }
118
+
119
+ it { expect(Foo.as_of($t.bar.ts[0]).includes(bars: :sub_bars).first.bars.first.name).to eq 'bar' }
120
+ it { expect(Foo.as_of($t.bar.ts[1]).includes(bars: :sub_bars).first.bars.first.name).to eq 'foo bar' }
121
+ it { expect(Foo.as_of($t.bar.ts[2]).includes(bars: :sub_bars).first.bars.first.name).to eq 'bar bar' }
122
+ it { expect(Foo.as_of($t.bar.ts[3]).includes(bars: :sub_bars).first.bars.first.name).to eq 'new bar' }
123
+
124
+ it { expect(Bar.as_of($t.bar.ts[0]).includes(:foo).first.foo).to eq $t.foo }
125
+ it { expect(Bar.as_of($t.bar.ts[1]).includes(:foo).first.foo).to eq $t.foo }
126
+ it { expect(Bar.as_of($t.bar.ts[2]).includes(:foo).first.foo).to eq $t.foo }
127
+ it { expect(Bar.as_of($t.bar.ts[3]).includes(:foo).first.foo).to eq $t.foo }
128
+
129
+ it { expect(Bar.as_of($t.bar.ts[0]).includes(:foo).first.foo.name).to eq 'foo bar' }
130
+ it { expect(Bar.as_of($t.bar.ts[1]).includes(:foo).first.foo.name).to eq 'foo bar' }
131
+ it { expect(Bar.as_of($t.bar.ts[2]).includes(:foo).first.foo.name).to eq 'new foo' }
132
+ it { expect(Bar.as_of($t.bar.ts[3]).includes(:foo).first.foo.name).to eq 'new foo' }
133
+
134
+
135
+ it { expect(Bar.as_of($t.bar.ts[0]).includes(foo: :sub_bars).first.foo).to eq $t.foo }
136
+ it { expect(Bar.as_of($t.bar.ts[1]).includes(foo: :sub_bars).first.foo).to eq $t.foo }
137
+ it { expect(Bar.as_of($t.bar.ts[2]).includes(foo: :sub_bars).first.foo).to eq $t.foo }
138
+ it { expect(Bar.as_of($t.bar.ts[3]).includes(foo: :sub_bars).first.foo).to eq $t.foo }
139
+
140
+ it { expect(Bar.as_of($t.bar.ts[0]).includes(foo: :sub_bars).first.foo.name).to eq 'foo bar' }
141
+ it { expect(Bar.as_of($t.bar.ts[1]).includes(foo: :sub_bars).first.foo.name).to eq 'foo bar' }
142
+ it { expect(Bar.as_of($t.bar.ts[2]).includes(foo: :sub_bars).first.foo.name).to eq 'new foo' }
143
+ it { expect(Bar.as_of($t.bar.ts[3]).includes(foo: :sub_bars).first.foo.name).to eq 'new foo' }
144
+
145
+ it { expect(Foo.as_of($t.foo.ts[0]).includes(:bars, :sub_bars).first.sub_bars.count).to eq 0 }
146
+ it { expect(Foo.as_of($t.foo.ts[1]).includes(:bars, :sub_bars).first.sub_bars.count).to eq 0 }
147
+ it { expect(Foo.as_of($t.foo.ts[2]).includes(:bars, :sub_bars).first.sub_bars.count).to eq 1 }
148
+
149
+ it { expect(Foo.as_of($t.foo.ts[0]).includes(:bars, :sub_bars).first.sub_bars.first).to be nil }
150
+ it { expect(Foo.as_of($t.foo.ts[1]).includes(:bars, :sub_bars).first.sub_bars.first).to be nil }
151
+
152
+ it { expect(Foo.as_of($t.subbar.ts[0]).includes(:bars, :sub_bars).first.sub_bars.first.name).to eq 'sub-bar' }
153
+ it { expect(Foo.as_of($t.subbar.ts[1]).includes(:bars, :sub_bars).first.sub_bars.first.name).to eq 'bar sub-bar' }
154
+ it { expect(Foo.as_of($t.subbar.ts[2]).includes(:bars, :sub_bars).first.sub_bars.first.name).to eq 'sub-bar sub-bar' }
155
+ it { expect(Foo.as_of($t.subbar.ts[3]).includes(:bars, :sub_bars).first.sub_bars.first.name).to eq 'new sub-bar' }
156
+ end
157
+
158
+
159
+ it 'does not raise RecordNotFound when no history records are found' do
160
+ expect { $t.foo.as_of(1.minute.ago) }.to_not raise_error
161
+
162
+ expect($t.foo.as_of(1.minute.ago)).to be(nil)
163
+ end
164
+
165
+
166
+ it 'raises ActiveRecord::RecordNotFound in the bang variant' do
167
+ expect { $t.foo.as_of!(1.minute.ago) }.to raise_error(ActiveRecord::RecordNotFound)
168
+ end
169
+
170
+
171
+ describe 'proxies from non-temporal models to temporal ones' do
172
+ it { expect($t.baz.as_of($t.bar.ts[0]).name).to eq 'baz' }
173
+ it { expect($t.baz.as_of($t.bar.ts[1]).name).to eq 'baz' }
174
+ it { expect($t.baz.as_of($t.bar.ts[2]).name).to eq 'baz' }
175
+ it { expect($t.baz.as_of($t.bar.ts[3]).name).to eq 'baz' }
176
+
177
+ it { expect($t.baz.as_of($t.bar.ts[0]).bar.name).to eq 'bar' }
178
+ it { expect($t.baz.as_of($t.bar.ts[1]).bar.name).to eq 'foo bar' }
179
+ it { expect($t.baz.as_of($t.bar.ts[2]).bar.name).to eq 'bar bar' }
180
+ it { expect($t.baz.as_of($t.bar.ts[3]).bar.name).to eq 'new bar' }
181
+
182
+ it { expect($t.baz.as_of($t.bar.ts[0]).bar.foo.name).to eq 'foo bar' }
183
+ it { expect($t.baz.as_of($t.bar.ts[1]).bar.foo.name).to eq 'foo bar' }
184
+ it { expect($t.baz.as_of($t.bar.ts[2]).bar.foo.name).to eq 'new foo' }
185
+ it { expect($t.baz.as_of($t.bar.ts[3]).bar.foo.name).to eq 'new foo' }
186
+ end
187
+ end
188
+ end