chrono_model 1.2.2 → 2.0.0

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 (95) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +19 -20
  3. data/README.md +62 -40
  4. data/lib/active_record/connection_adapters/chronomodel_adapter.rb +17 -11
  5. data/lib/active_record/tasks/chronomodel_database_tasks.rb +64 -23
  6. data/lib/chrono_model/adapter/ddl.rb +168 -153
  7. data/lib/chrono_model/adapter/indexes.rb +99 -94
  8. data/lib/chrono_model/adapter/migrations.rb +81 -104
  9. data/lib/chrono_model/adapter/migrations_modules/legacy.rb +41 -0
  10. data/lib/chrono_model/adapter/migrations_modules/stable.rb +41 -0
  11. data/lib/chrono_model/adapter/tsrange.rb +20 -5
  12. data/lib/chrono_model/adapter/upgrade.rb +89 -91
  13. data/lib/chrono_model/adapter.rb +64 -31
  14. data/lib/chrono_model/chrono.rb +17 -0
  15. data/lib/chrono_model/conversions.rb +15 -9
  16. data/lib/chrono_model/db_console.rb +9 -0
  17. data/lib/chrono_model/json.rb +9 -6
  18. data/lib/chrono_model/patches/as_of_time_holder.rb +2 -2
  19. data/lib/chrono_model/patches/as_of_time_relation.rb +2 -2
  20. data/lib/chrono_model/patches/association.rb +15 -12
  21. data/lib/chrono_model/patches/batches.rb +17 -0
  22. data/lib/chrono_model/patches/db_console.rb +20 -4
  23. data/lib/chrono_model/patches/join_node.rb +4 -4
  24. data/lib/chrono_model/patches/preloader.rb +41 -11
  25. data/lib/chrono_model/patches/relation.rb +53 -8
  26. data/lib/chrono_model/patches.rb +3 -1
  27. data/lib/chrono_model/railtie.rb +29 -24
  28. data/lib/chrono_model/time_gate.rb +3 -3
  29. data/lib/chrono_model/time_machine/history_model.rb +65 -31
  30. data/lib/chrono_model/time_machine/time_query.rb +65 -49
  31. data/lib/chrono_model/time_machine/timeline.rb +52 -28
  32. data/lib/chrono_model/time_machine.rb +66 -25
  33. data/lib/chrono_model/utilities.rb +3 -3
  34. data/lib/chrono_model/version.rb +3 -1
  35. data/lib/chrono_model.rb +31 -36
  36. metadata +39 -136
  37. data/.gitignore +0 -21
  38. data/.rspec +0 -2
  39. data/.travis.yml +0 -41
  40. data/Gemfile +0 -4
  41. data/README.sql +0 -161
  42. data/Rakefile +0 -25
  43. data/chrono_model.gemspec +0 -33
  44. data/gemfiles/rails_5.0.gemfile +0 -6
  45. data/gemfiles/rails_5.1.gemfile +0 -6
  46. data/gemfiles/rails_5.2.gemfile +0 -6
  47. data/spec/aruba/dbconsole_spec.rb +0 -25
  48. data/spec/aruba/fixtures/database_with_default_username_and_password.yml +0 -14
  49. data/spec/aruba/fixtures/database_without_username_and_password.yml +0 -11
  50. data/spec/aruba/fixtures/empty_structure.sql +0 -27
  51. data/spec/aruba/fixtures/migrations/56/20160812190335_create_impressions.rb +0 -10
  52. data/spec/aruba/fixtures/migrations/56/20171115195229_add_temporal_extension_to_impressions.rb +0 -10
  53. data/spec/aruba/fixtures/railsapp/config/application.rb +0 -17
  54. data/spec/aruba/fixtures/railsapp/config/boot.rb +0 -5
  55. data/spec/aruba/fixtures/railsapp/config/environments/development.rb +0 -38
  56. data/spec/aruba/migrations_spec.rb +0 -48
  57. data/spec/aruba/rake_task_spec.rb +0 -71
  58. data/spec/chrono_model/adapter/base_spec.rb +0 -157
  59. data/spec/chrono_model/adapter/ddl_spec.rb +0 -243
  60. data/spec/chrono_model/adapter/indexes_spec.rb +0 -72
  61. data/spec/chrono_model/adapter/migrations_spec.rb +0 -312
  62. data/spec/chrono_model/conversions_spec.rb +0 -43
  63. data/spec/chrono_model/history_models_spec.rb +0 -32
  64. data/spec/chrono_model/json_ops_spec.rb +0 -59
  65. data/spec/chrono_model/time_machine/as_of_spec.rb +0 -188
  66. data/spec/chrono_model/time_machine/changes_spec.rb +0 -50
  67. data/spec/chrono_model/time_machine/counter_cache_race_spec.rb +0 -46
  68. data/spec/chrono_model/time_machine/default_scope_spec.rb +0 -37
  69. data/spec/chrono_model/time_machine/history_spec.rb +0 -104
  70. data/spec/chrono_model/time_machine/keep_cool_spec.rb +0 -27
  71. data/spec/chrono_model/time_machine/manipulations_spec.rb +0 -84
  72. data/spec/chrono_model/time_machine/model_identification_spec.rb +0 -46
  73. data/spec/chrono_model/time_machine/sequence_spec.rb +0 -74
  74. data/spec/chrono_model/time_machine/sti_spec.rb +0 -100
  75. data/spec/chrono_model/time_machine/time_query_spec.rb +0 -261
  76. data/spec/chrono_model/time_machine/timeline_spec.rb +0 -63
  77. data/spec/chrono_model/time_machine/timestamps_spec.rb +0 -43
  78. data/spec/chrono_model/time_machine/transactions_spec.rb +0 -69
  79. data/spec/config.travis.yml +0 -5
  80. data/spec/config.yml.example +0 -9
  81. data/spec/spec_helper.rb +0 -33
  82. data/spec/support/adapter/helpers.rb +0 -53
  83. data/spec/support/adapter/structure.rb +0 -44
  84. data/spec/support/aruba.rb +0 -44
  85. data/spec/support/connection.rb +0 -70
  86. data/spec/support/matchers/base.rb +0 -56
  87. data/spec/support/matchers/column.rb +0 -99
  88. data/spec/support/matchers/function.rb +0 -79
  89. data/spec/support/matchers/index.rb +0 -69
  90. data/spec/support/matchers/schema.rb +0 -39
  91. data/spec/support/matchers/table.rb +0 -275
  92. data/spec/support/time_machine/helpers.rb +0 -47
  93. data/spec/support/time_machine/structure.rb +0 -111
  94. data/sql/json_ops.sql +0 -56
  95. data/sql/uninstall-json_ops.sql +0 -24
@@ -1,312 +0,0 @@
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
@@ -1,43 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe ChronoModel::Conversions do
4
-
5
- describe 'string_to_utc_time' do
6
- subject { described_class.string_to_utc_time(string) }
7
-
8
- context 'given a valid UTC time string' do
9
- let(:string) { '2017-02-06 09:46:31.129626' }
10
-
11
- it { is_expected.to be_a(Time) }
12
- it { expect(subject.year).to eq 2017 }
13
- it { expect(subject.month).to eq 2 }
14
- it { expect(subject.day).to eq 6 }
15
- it { expect(subject.hour).to eq 9 }
16
- it { expect(subject.min).to eq 46 }
17
- it { expect(subject.sec).to eq 31 }
18
- it { expect(subject.usec).to eq 129626 } # Ref Issue #32
19
- end
20
-
21
- context 'given a valid UTC string without least significant zeros' do
22
- let(:string) { '2017-02-06 09:46:31.129' }
23
-
24
- it { is_expected.to be_a(Time) }
25
- it { expect(subject.usec).to eq 129000 } # Ref Issue #32
26
- end
27
-
28
- context 'given an invalid UTC time string' do
29
- let(:string) { 'foobar' }
30
-
31
- it { is_expected.to be(nil) }
32
- end
33
- end
34
-
35
- describe 'time_to_utc_string' do
36
- subject { described_class.time_to_utc_string(time) }
37
-
38
- let(:time) { Time.utc(1981, 4, 11, 2, 42, 10, 123456) }
39
-
40
- it { is_expected.to eq '1981-04-11 02:42:10.123456' }
41
- end
42
-
43
- end
@@ -1,32 +0,0 @@
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
@@ -1,59 +0,0 @@
1
- ##########################################################
2
- ### DEPRECATED: JSON operators are an hack and there is no
3
- ### reason not to use jsonb other than migrating your data
4
- ##########################################################
5
- if ENV['HAVE_PLPYTHON'] == '1'
6
-
7
- require 'spec_helper'
8
- require 'support/helpers'
9
-
10
- require 'chrono_model/json'
11
-
12
- describe 'JSON equality operator' do
13
- include ChronoTest::Helpers::Adapter
14
-
15
- table 'json_test'
16
-
17
- before :all do
18
- ChronoModel::Json.create
19
- end
20
-
21
- after :all do
22
- ChronoModel::Json.drop
23
- end
24
-
25
- it { expect(adapter.select_value(%[ SELECT '{"a":1}'::json = '{"a":1}'::json ])).to eq true }
26
- it { expect(adapter.select_value(%[ SELECT '{"a":1}'::json = '{"a" : 1}'::json ])).to eq true }
27
- it { expect(adapter.select_value(%[ SELECT '{"a":1}'::json = '{"a":2}'::json ])).to eq false }
28
- it { expect(adapter.select_value(%[ SELECT '{"a":1,"b":2}'::json = '{"b":2,"a":1}'::json ])).to eq true }
29
- it { expect(adapter.select_value(%[ SELECT '{"a":1,"b":2,"x":{"c":4,"d":5}}'::json = '{"b":2, "x": { "d": 5, "c": 4}, "a":1}'::json ])).to eq true }
30
-
31
- context 'on a temporal table' do
32
- before :all do
33
- adapter.create_table table, :temporal => true do |t|
34
- t.json 'data'
35
- end
36
-
37
- adapter.execute %[
38
- INSERT INTO #{table} ( data ) VALUES ( '{"a":1,"b":2}' )
39
- ]
40
- end
41
-
42
- after :all do
43
- adapter.drop_table table
44
- end
45
-
46
- it { expect {
47
- adapter.execute "UPDATE #{table} SET data = NULL"
48
- }.to_not raise_error }
49
-
50
- it { expect {
51
- adapter.execute %[UPDATE #{table} SET data = '{"x":1,"y":2}']
52
-
53
- }.to_not raise_error }
54
- end
55
-
56
- end
57
-
58
-
59
- end
@@ -1,188 +0,0 @@
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
@@ -1,50 +0,0 @@
1
- require 'spec_helper'
2
- require 'support/time_machine/structure'
3
-
4
- describe ChronoModel::TimeMachine do
5
- include ChronoTest::TimeMachine::Helpers
6
-
7
- describe '#last_changes' do
8
- context 'on plain records' do
9
- context 'having history' do
10
- subject { $t.bar.last_changes }
11
- it { is_expected.to eq('name' => ['bar bar', 'new bar']) }
12
- end
13
-
14
- context 'without history' do
15
- let(:record) { Bar.create!(:name => 'foreveralone') }
16
- subject { record.last_changes }
17
- it { is_expected.to be_nil }
18
- after { record.destroy.history.delete_all } # UGLY
19
- end
20
- end
21
-
22
- context 'on history records' do
23
- context 'at the beginning of the timeline' do
24
- subject { $t.bar.history.first.last_changes }
25
- it { is_expected.to be_nil }
26
- end
27
-
28
- context 'in the middle of the timeline' do
29
- subject { $t.bar.history.second.last_changes }
30
- it { is_expected.to eq('name' => ['bar', 'foo bar']) }
31
- end
32
- end
33
- end
34
-
35
- describe '#changes_against' do
36
- context 'can compare records against history' do
37
- it { expect($t.bar.changes_against($t.bar.history.first)).to eq('name' => ['bar', 'new bar']) }
38
- it { expect($t.bar.changes_against($t.bar.history.second)).to eq('name' => ['foo bar', 'new bar']) }
39
- it { expect($t.bar.changes_against($t.bar.history.third)).to eq('name' => ['bar bar', 'new bar']) }
40
- it { expect($t.bar.changes_against($t.bar.history.last)).to eq({}) }
41
- end
42
-
43
- context 'can compare history against history' do
44
- it { expect($t.bar.history.first. changes_against($t.bar.history.third)).to eq('name' => ['bar bar', 'bar']) }
45
- it { expect($t.bar.history.second.changes_against($t.bar.history.third)).to eq('name' => ['bar bar', 'foo bar']) }
46
- it { expect($t.bar.history.third. changes_against($t.bar.history.third)).to eq({}) }
47
- end
48
- end
49
-
50
- end