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
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chrono_model
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marcello Barnaba
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2019-04-13 00:00:00.000000000 Z
12
+ date: 2019-04-14 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activerecord
@@ -252,25 +252,42 @@ files:
252
252
  - spec/aruba/fixtures/railsapp/config/environments/development.rb
253
253
  - spec/aruba/migrations_spec.rb
254
254
  - spec/aruba/rake_task_spec.rb
255
- - spec/chrono_model/adapter/counter_cache_race_spec.rb
256
- - spec/chrono_model/adapter/sti_bug_spec.rb
257
- - spec/chrono_model/adapter_spec.rb
255
+ - spec/chrono_model/adapter/base_spec.rb
256
+ - spec/chrono_model/adapter/ddl_spec.rb
257
+ - spec/chrono_model/adapter/indexes_spec.rb
258
+ - spec/chrono_model/adapter/migrations_spec.rb
258
259
  - spec/chrono_model/conversions_spec.rb
260
+ - spec/chrono_model/history_models_spec.rb
259
261
  - spec/chrono_model/json_ops_spec.rb
260
- - spec/chrono_model/time_machine_spec.rb
261
- - spec/chrono_model/time_query_spec.rb
262
+ - spec/chrono_model/time_machine/as_of_spec.rb
263
+ - spec/chrono_model/time_machine/changes_spec.rb
264
+ - spec/chrono_model/time_machine/counter_cache_race_spec.rb
265
+ - spec/chrono_model/time_machine/default_scope_spec.rb
266
+ - spec/chrono_model/time_machine/history_spec.rb
267
+ - spec/chrono_model/time_machine/keep_cool_spec.rb
268
+ - spec/chrono_model/time_machine/manipulations_spec.rb
269
+ - spec/chrono_model/time_machine/model_identification_spec.rb
270
+ - spec/chrono_model/time_machine/sequence_spec.rb
271
+ - spec/chrono_model/time_machine/sti_spec.rb
272
+ - spec/chrono_model/time_machine/time_query_spec.rb
273
+ - spec/chrono_model/time_machine/timeline_spec.rb
274
+ - spec/chrono_model/time_machine/timestamps_spec.rb
275
+ - spec/chrono_model/time_machine/transactions_spec.rb
262
276
  - spec/config.travis.yml
263
277
  - spec/config.yml.example
264
278
  - spec/spec_helper.rb
279
+ - spec/support/adapter/helpers.rb
280
+ - spec/support/adapter/structure.rb
265
281
  - spec/support/aruba.rb
266
282
  - spec/support/connection.rb
267
- - spec/support/helpers.rb
268
283
  - spec/support/matchers/base.rb
269
284
  - spec/support/matchers/column.rb
270
285
  - spec/support/matchers/function.rb
271
286
  - spec/support/matchers/index.rb
272
287
  - spec/support/matchers/schema.rb
273
288
  - spec/support/matchers/table.rb
289
+ - spec/support/time_machine/helpers.rb
290
+ - spec/support/time_machine/structure.rb
274
291
  - sql/json_ops.sql
275
292
  - sql/uninstall-json_ops.sql
276
293
  homepage: https://github.com/ifad/chronomodel
@@ -308,22 +325,39 @@ test_files:
308
325
  - spec/aruba/fixtures/railsapp/config/environments/development.rb
309
326
  - spec/aruba/migrations_spec.rb
310
327
  - spec/aruba/rake_task_spec.rb
311
- - spec/chrono_model/adapter/counter_cache_race_spec.rb
312
- - spec/chrono_model/adapter/sti_bug_spec.rb
313
- - spec/chrono_model/adapter_spec.rb
328
+ - spec/chrono_model/adapter/base_spec.rb
329
+ - spec/chrono_model/adapter/ddl_spec.rb
330
+ - spec/chrono_model/adapter/indexes_spec.rb
331
+ - spec/chrono_model/adapter/migrations_spec.rb
314
332
  - spec/chrono_model/conversions_spec.rb
333
+ - spec/chrono_model/history_models_spec.rb
315
334
  - spec/chrono_model/json_ops_spec.rb
316
- - spec/chrono_model/time_machine_spec.rb
317
- - spec/chrono_model/time_query_spec.rb
335
+ - spec/chrono_model/time_machine/as_of_spec.rb
336
+ - spec/chrono_model/time_machine/changes_spec.rb
337
+ - spec/chrono_model/time_machine/counter_cache_race_spec.rb
338
+ - spec/chrono_model/time_machine/default_scope_spec.rb
339
+ - spec/chrono_model/time_machine/history_spec.rb
340
+ - spec/chrono_model/time_machine/keep_cool_spec.rb
341
+ - spec/chrono_model/time_machine/manipulations_spec.rb
342
+ - spec/chrono_model/time_machine/model_identification_spec.rb
343
+ - spec/chrono_model/time_machine/sequence_spec.rb
344
+ - spec/chrono_model/time_machine/sti_spec.rb
345
+ - spec/chrono_model/time_machine/time_query_spec.rb
346
+ - spec/chrono_model/time_machine/timeline_spec.rb
347
+ - spec/chrono_model/time_machine/timestamps_spec.rb
348
+ - spec/chrono_model/time_machine/transactions_spec.rb
318
349
  - spec/config.travis.yml
319
350
  - spec/config.yml.example
320
351
  - spec/spec_helper.rb
352
+ - spec/support/adapter/helpers.rb
353
+ - spec/support/adapter/structure.rb
321
354
  - spec/support/aruba.rb
322
355
  - spec/support/connection.rb
323
- - spec/support/helpers.rb
324
356
  - spec/support/matchers/base.rb
325
357
  - spec/support/matchers/column.rb
326
358
  - spec/support/matchers/function.rb
327
359
  - spec/support/matchers/index.rb
328
360
  - spec/support/matchers/schema.rb
329
361
  - spec/support/matchers/table.rb
362
+ - spec/support/time_machine/helpers.rb
363
+ - spec/support/time_machine/structure.rb
@@ -1,49 +0,0 @@
1
- require 'spec_helper'
2
- require 'support/helpers'
3
-
4
- describe 'models with STI' do
5
- include ChronoTest::Helpers::TimeMachine
6
-
7
- adapter.create_table 'animals', temporal: true do |t|
8
- t.string :type
9
- end
10
-
11
- class ::Animal < ActiveRecord::Base
12
- include ChronoModel::TimeMachine
13
- end
14
-
15
- class ::Dog < Animal
16
- end
17
-
18
- class ::Goat < Animal
19
- end
20
-
21
- describe 'it generates the right queries' do
22
- before do
23
- Dog.create!
24
- @later = Time.new
25
- Goat.create!
26
- end
27
-
28
- after do
29
- tables = ['temporal.animals', 'history.animals']
30
- ActiveRecord::Base.connection.execute "truncate #{tables.join(', ')} cascade"
31
- end
32
-
33
- specify "select" do
34
- expect(Animal.first).to_not be_nil
35
- expect(Animal.as_of(@later).first).to_not be_nil
36
- end
37
-
38
- specify "count" do
39
- expect(Animal.count).to eq(2)
40
- expect(Animal.as_of(@later).count).to eq(1)
41
-
42
- expect(Dog.count).to eq(1)
43
- expect(Dog.as_of(@later).count).to eq(1)
44
-
45
- expect(Goat.count).to eq(1)
46
- expect(Goat.as_of(@later).count).to eq(0)
47
- end
48
- end
49
- end
@@ -1,788 +0,0 @@
1
- require 'spec_helper'
2
- require 'support/helpers'
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::Helpers::Adapter
35
-
36
- context do
37
- subject { adapter }
38
- it { is_expected.to be_a_kind_of(ChronoModel::Adapter) }
39
-
40
- context do
41
- subject { adapter.adapter_name }
42
- it { is_expected.to eq 'PostgreSQL' }
43
- end
44
-
45
- context do
46
- before { expect(adapter).to receive(:postgresql_version).and_return(90300) }
47
- it { is_expected.to be_chrono_supported }
48
- end
49
-
50
- context do
51
- before { expect(adapter).to receive(:postgresql_version).and_return(90000) }
52
- it { is_expected.to_not be_chrono_supported }
53
- end
54
- end
55
-
56
- table 'test_table'
57
- subject { table }
58
-
59
- columns do
60
- native = [
61
- ['test', 'character varying'],
62
- ['foo', 'integer'],
63
- ['bar', 'double precision'],
64
- ['baz', 'text']
65
- ]
66
-
67
- def native.to_proc
68
- proc {|t|
69
- t.string :test, :null => false, :default => 'default-value'
70
- t.integer :foo
71
- t.float :bar
72
- t.text :baz
73
- t.integer :ary, :array => true, :null => false, :default => []
74
- t.boolean :bool, :null => false, :default => false
75
- }
76
- end
77
-
78
- native
79
- end
80
-
81
- describe '.is_chrono?' do
82
- with_temporal_table do
83
- it { expect(adapter.is_chrono?(table)).to be(true) }
84
- end
85
-
86
- with_plain_table do
87
- it { expect(adapter.is_chrono?(table)).to be(false) }
88
- end
89
-
90
- context 'when schemas are not there yet' do
91
- before(:all) do
92
- adapter.execute 'BEGIN'
93
- adapter.execute 'DROP SCHEMA temporal CASCADE'
94
- adapter.execute 'DROP SCHEMA history CASCADE'
95
- adapter.execute 'CREATE TABLE test_table (id integer)'
96
- end
97
-
98
- after(:all) do
99
- adapter.execute 'ROLLBACK'
100
- end
101
-
102
- it { expect { adapter.is_chrono?(table) }.to_not raise_error }
103
-
104
- it { expect(adapter.is_chrono?(table)).to be(false) }
105
- end
106
- end
107
-
108
- describe '.create_table' do
109
- with_temporal_table do
110
- it_should_behave_like 'temporal table'
111
- end
112
-
113
- with_plain_table do
114
- it_should_behave_like 'plain table'
115
- end
116
- end
117
-
118
- describe '.rename_table' do
119
- renamed = 'foo_table'
120
- subject { renamed }
121
-
122
- context ':temporal => true' do
123
- before :all do
124
- adapter.create_table table, :temporal => true, &columns
125
- adapter.add_index table, :test
126
- adapter.add_index table, [:foo, :bar]
127
-
128
- adapter.rename_table table, renamed
129
- end
130
- after(:all) { adapter.drop_table(renamed) }
131
-
132
- it_should_behave_like 'temporal table'
133
-
134
- it 'renames indexes' do
135
- new_index_names = adapter.indexes(renamed).map(&:name)
136
- expected_index_names = [[:test], [:foo, :bar]].map do |idx_cols|
137
- "index_#{renamed}_on_#{idx_cols.join('_and_')}"
138
- end
139
- expect(new_index_names.to_set).to eq expected_index_names.to_set
140
- end
141
- end
142
-
143
- context ':temporal => false' do
144
- before :all do
145
- adapter.create_table table, :temporal => false, &columns
146
-
147
- adapter.rename_table table, renamed
148
- end
149
- after(:all) { adapter.drop_table(renamed) }
150
-
151
- it_should_behave_like 'plain table'
152
- end
153
- end
154
-
155
- describe '.change_table' do
156
- with_temporal_table do
157
- before :all do
158
- adapter.change_table table, :temporal => false
159
- end
160
-
161
- it_should_behave_like 'plain table'
162
- end
163
-
164
- with_plain_table do
165
- before :all do
166
- adapter.add_index table, :foo
167
- adapter.add_index table, :bar, :unique => true
168
-
169
- adapter.change_table table, :temporal => true
170
- end
171
-
172
- it_should_behave_like 'temporal table'
173
-
174
- let(:history_indexes) do
175
- adapter.on_schema(ChronoModel::Adapter::HISTORY_SCHEMA) do
176
- adapter.indexes(table)
177
- end
178
- end
179
-
180
- it "copies plain index to history" do
181
- expect(history_indexes.find {|i| i.columns == ['foo']}).to be_present
182
- end
183
-
184
- it "copies unique index to history without uniqueness constraint" do
185
- expect(history_indexes.find {|i| i.columns == ['bar'] && i.unique == false}).to be_present
186
- end
187
- end
188
- end
189
-
190
- describe '.drop_table' do
191
- before :all do
192
- adapter.create_table table, :temporal => true, &columns
193
-
194
- adapter.drop_table table
195
- end
196
-
197
- it { is_expected.to_not have_public_backing }
198
- it { is_expected.to_not have_temporal_backing }
199
- it { is_expected.to_not have_history_backing }
200
- it { is_expected.to_not have_history_functions }
201
- it { is_expected.to_not have_public_interface }
202
- end
203
-
204
- describe '.add_index' do
205
- with_temporal_table do
206
- before :all do
207
- adapter.add_index table, [:foo, :bar], :name => 'foobar_index'
208
- adapter.add_index table, [:test], :name => 'test_index'
209
- end
210
-
211
- it { is_expected.to have_temporal_index 'foobar_index', %w( foo bar ) }
212
- it { is_expected.to have_history_index 'foobar_index', %w( foo bar ) }
213
- it { is_expected.to have_temporal_index 'test_index', %w( test ) }
214
- it { is_expected.to have_history_index 'test_index', %w( test ) }
215
-
216
- it { is_expected.to_not have_index 'foobar_index', %w( foo bar ) }
217
- it { is_expected.to_not have_index 'test_index', %w( test ) }
218
- end
219
-
220
- with_plain_table do
221
- before :all do
222
- adapter.add_index table, [:foo, :bar], :name => 'foobar_index'
223
- adapter.add_index table, [:test], :name => 'test_index'
224
- end
225
-
226
- it { is_expected.to_not have_temporal_index 'foobar_index', %w( foo bar ) }
227
- it { is_expected.to_not have_history_index 'foobar_index', %w( foo bar ) }
228
- it { is_expected.to_not have_temporal_index 'test_index', %w( test ) }
229
- it { is_expected.to_not have_history_index 'test_index', %w( test ) }
230
-
231
- it { is_expected.to have_index 'foobar_index', %w( foo bar ) }
232
- it { is_expected.to have_index 'test_index', %w( test ) }
233
- end
234
- end
235
-
236
- describe '.remove_index' do
237
- with_temporal_table do
238
- before :all do
239
- adapter.add_index table, [:foo, :bar], :name => 'foobar_index'
240
- adapter.add_index table, [:test], :name => 'test_index'
241
-
242
- adapter.remove_index table, :name => 'test_index'
243
- end
244
-
245
- it { is_expected.to_not have_temporal_index 'test_index', %w( test ) }
246
- it { is_expected.to_not have_history_index 'test_index', %w( test ) }
247
- it { is_expected.to_not have_index 'test_index', %w( test ) }
248
- end
249
-
250
- with_plain_table do
251
- before :all do
252
- adapter.add_index table, [:foo, :bar], :name => 'foobar_index'
253
- adapter.add_index table, [:test], :name => 'test_index'
254
-
255
- adapter.remove_index table, :name => 'test_index'
256
- end
257
-
258
- it { is_expected.to_not have_temporal_index 'test_index', %w( test ) }
259
- it { is_expected.to_not have_history_index 'test_index', %w( test ) }
260
- it { is_expected.to_not have_index 'test_index', %w( test ) }
261
- end
262
- end
263
-
264
- describe '.add_column' do
265
- let(:extra_columns) { [['foobarbaz', 'integer']] }
266
-
267
- with_temporal_table do
268
- before :all do
269
- adapter.add_column table, :foobarbaz, :integer
270
- end
271
-
272
- it { is_expected.to have_columns(extra_columns) }
273
- it { is_expected.to have_temporal_columns(extra_columns) }
274
- it { is_expected.to have_history_columns(extra_columns) }
275
- end
276
-
277
- with_plain_table do
278
- before :all do
279
- adapter.add_column table, :foobarbaz, :integer
280
- end
281
-
282
- it { is_expected.to have_columns(extra_columns) }
283
- end
284
- end
285
-
286
- describe '.remove_column' do
287
- let(:resulting_columns) { columns.reject {|c,_| c == 'foo'} }
288
-
289
- with_temporal_table do
290
- before :all do
291
- adapter.remove_column table, :foo
292
- end
293
-
294
- it { is_expected.to have_columns(resulting_columns) }
295
- it { is_expected.to have_temporal_columns(resulting_columns) }
296
- it { is_expected.to have_history_columns(resulting_columns) }
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 have_columns(resulting_columns) }
309
- it { is_expected.to_not have_columns([['foo', 'integer']]) }
310
- end
311
- end
312
-
313
- describe '.rename_column' do
314
- with_temporal_table do
315
- before :all do
316
- adapter.rename_column table, :foo, :taratapiatapioca
317
- end
318
-
319
- it { is_expected.to_not have_columns([['foo', 'integer']]) }
320
- it { is_expected.to_not have_temporal_columns([['foo', 'integer']]) }
321
- it { is_expected.to_not have_history_columns([['foo', 'integer']]) }
322
-
323
- it { is_expected.to have_columns([['taratapiatapioca', 'integer']]) }
324
- it { is_expected.to have_temporal_columns([['taratapiatapioca', 'integer']]) }
325
- it { is_expected.to have_history_columns([['taratapiatapioca', 'integer']]) }
326
- end
327
-
328
- with_plain_table do
329
- before :all do
330
- adapter.rename_column table, :foo, :taratapiatapioca
331
- end
332
-
333
- it { is_expected.to_not have_columns([['foo', 'integer']]) }
334
- it { is_expected.to have_columns([['taratapiatapioca', 'integer']]) }
335
- end
336
- end
337
-
338
- describe '.change_column' do
339
- with_temporal_table do
340
- before :all do
341
- adapter.change_column table, :foo, :float
342
- end
343
-
344
- it { is_expected.to_not have_columns([['foo', 'integer']]) }
345
- it { is_expected.to_not have_temporal_columns([['foo', 'integer']]) }
346
- it { is_expected.to_not have_history_columns([['foo', 'integer']]) }
347
-
348
- it { is_expected.to have_columns([['foo', 'double precision']]) }
349
- it { is_expected.to have_temporal_columns([['foo', 'double precision']]) }
350
- it { is_expected.to have_history_columns([['foo', 'double precision']]) }
351
- end
352
-
353
- with_plain_table do
354
- before(:all) do
355
- adapter.change_column table, :foo, :float
356
- end
357
-
358
- it { is_expected.to_not have_columns([['foo', 'integer']]) }
359
- it { is_expected.to have_columns([['foo', 'double precision']]) }
360
- end
361
- end
362
-
363
- describe '.remove_column' do
364
- with_temporal_table do
365
- before :all do
366
- adapter.remove_column table, :foo
367
- end
368
-
369
- it { is_expected.to_not have_columns([['foo', 'integer']]) }
370
- it { is_expected.to_not have_temporal_columns([['foo', 'integer']]) }
371
- it { is_expected.to_not have_history_columns([['foo', 'integer']]) }
372
- end
373
-
374
- with_plain_table do
375
- before :all do
376
- adapter.remove_column table, :foo
377
- end
378
-
379
- it { is_expected.to_not have_columns([['foo', 'integer']]) }
380
- end
381
- end
382
-
383
- describe '.column_definitions' do
384
- subject { adapter.column_definitions(table).map {|d| d.take(2)} }
385
-
386
- assert = proc do
387
- it { expect(subject & columns).to eq columns }
388
- it { is_expected.to include(['id', pk_type]) }
389
- end
390
-
391
- with_temporal_table(&assert)
392
- with_plain_table( &assert)
393
- end
394
-
395
- describe '.primary_key' do
396
- subject { adapter.primary_key(table) }
397
-
398
- assert = proc do
399
- it { is_expected.to eq 'id' }
400
- end
401
-
402
- with_temporal_table(&assert)
403
- with_plain_table( &assert)
404
- end
405
-
406
- describe '.indexes' do
407
- subject { adapter.indexes(table) }
408
-
409
- assert = proc do
410
- before(:all) do
411
- adapter.add_index table, :foo, :name => 'foo_index'
412
- adapter.add_index table, [:bar, :baz], :name => 'bar_index'
413
- end
414
-
415
- it { expect(subject.map(&:name)).to match_array %w( foo_index bar_index ) }
416
- it { expect(subject.map(&:columns)).to match_array [['foo'], ['bar', 'baz']] }
417
- end
418
-
419
- with_temporal_table(&assert)
420
- with_plain_table( &assert)
421
- end
422
-
423
- describe '.on_schema' do
424
- before(:all) do
425
- adapter.execute 'BEGIN'
426
- 5.times {|i| adapter.execute "CREATE SCHEMA test_#{i}"}
427
- end
428
-
429
- after(:all) do
430
- adapter.execute 'ROLLBACK'
431
- end
432
-
433
- context 'by default' do
434
- it 'saves the schema at each recursion' do
435
- is_expected.to be_in_schema(:default)
436
-
437
- adapter.on_schema('test_1') { is_expected.to be_in_schema('test_1')
438
- adapter.on_schema('test_2') { is_expected.to be_in_schema('test_2')
439
- adapter.on_schema('test_3') { is_expected.to be_in_schema('test_3')
440
- }
441
- is_expected.to be_in_schema('test_2')
442
- }
443
- is_expected.to be_in_schema('test_1')
444
- }
445
-
446
- is_expected.to be_in_schema(:default)
447
- end
448
-
449
- context 'when errors occur' do
450
- subject do
451
- adapter.on_schema('test_1') do
452
-
453
- adapter.on_schema('test_2') do
454
- adapter.execute 'BEGIN'
455
- adapter.execute 'ERRORING ON PURPOSE'
456
- end
457
-
458
- end
459
- end
460
-
461
- it {
462
- expect { subject }.
463
- to raise_error(/current transaction is aborted/).
464
- and change { adapter.instance_variable_get(:@schema_search_path) }
465
- }
466
-
467
- after do
468
- adapter.execute 'ROLLBACK'
469
- end
470
- end
471
- end
472
-
473
- context 'with recurse: :ignore' do
474
- it 'ignores recursive calls' do
475
- is_expected.to be_in_schema(:default)
476
-
477
- adapter.on_schema('test_1', recurse: :ignore) { is_expected.to be_in_schema('test_1')
478
- adapter.on_schema('test_2', recurse: :ignore) { is_expected.to be_in_schema('test_1')
479
- adapter.on_schema('test_3', recurse: :ignore) { is_expected.to be_in_schema('test_1')
480
- } } }
481
-
482
- is_expected.to be_in_schema(:default)
483
- end
484
- end
485
- end
486
-
487
- context 'migration extensions' do
488
- before :all do
489
- adapter.create_table :meetings do |t|
490
- t.string :name
491
- t.tsrange :interval
492
- end
493
- end
494
-
495
- after :all do
496
- adapter.drop_table :meetings
497
- end
498
-
499
- describe '.add_temporal_indexes' do
500
- before do
501
- adapter.add_temporal_indexes :meetings, :interval
502
- end
503
-
504
- it { expect(adapter.indexes(:meetings).map(&:name)).to eq [
505
- 'index_meetings_temporal_on_interval',
506
- 'index_meetings_temporal_on_lower_interval',
507
- 'index_meetings_temporal_on_upper_interval'
508
- ] }
509
-
510
- after do
511
- adapter.remove_temporal_indexes :meetings, :interval
512
- end
513
- end
514
-
515
- describe '.remove_temporal_indexes' do
516
- before :all do
517
- adapter.add_temporal_indexes :meetings, :interval
518
- end
519
-
520
- before do
521
- adapter.remove_temporal_indexes :meetings, :interval
522
- end
523
-
524
- it { expect(adapter.indexes(:meetings)).to be_empty }
525
- end
526
-
527
- describe '.add_timeline_consistency_constraint' do
528
- before do
529
- adapter.add_timeline_consistency_constraint(:meetings, :interval)
530
- end
531
-
532
- it { expect(adapter.indexes(:meetings).map(&:name)).to eq [
533
- 'meetings_timeline_consistency'
534
- ] }
535
-
536
- after do
537
- adapter.remove_timeline_consistency_constraint(:meetings)
538
- end
539
- end
540
-
541
- describe '.remove_timeline_consistency_constraint' do
542
- before :all do
543
- adapter.add_timeline_consistency_constraint :meetings, :interval
544
- end
545
-
546
- before do
547
- adapter.remove_timeline_consistency_constraint(:meetings)
548
- end
549
-
550
- it { expect(adapter.indexes(:meetings)).to be_empty }
551
- end
552
- end
553
-
554
- let(:current) { [ChronoModel::Adapter::TEMPORAL_SCHEMA, table].join('.') }
555
- let(:history) { [ChronoModel::Adapter::HISTORY_SCHEMA, table].join('.') }
556
-
557
- def count(table)
558
- adapter.select_value("SELECT COUNT(*) FROM ONLY #{table}").to_i
559
- end
560
-
561
- def ids(table)
562
- adapter.select_values("SELECT id FROM ONLY #{table} ORDER BY id")
563
- end
564
-
565
- context 'INSERT multiple values' do
566
- before :all do
567
- adapter.create_table table, :temporal => true, &columns
568
- end
569
-
570
- after :all do
571
- adapter.drop_table table
572
- end
573
-
574
- context 'when succeeding' do
575
- def insert
576
- adapter.execute <<-SQL
577
- INSERT INTO #{table} (test, foo) VALUES
578
- ('test1', 1),
579
- ('test2', 2);
580
- SQL
581
- end
582
-
583
- it { expect { insert }.to_not raise_error }
584
- it { expect(count(current)).to eq 2 }
585
- it { expect(count(history)).to eq 2 }
586
- end
587
-
588
- context 'when failing' do
589
- def insert
590
- adapter.execute <<-SQL
591
- INSERT INTO #{table} (test, foo) VALUES
592
- ('test3', 3),
593
- (NULL, 0);
594
- SQL
595
- end
596
-
597
- it { expect { insert }.to raise_error(ActiveRecord::StatementInvalid) }
598
- it { expect(count(current)).to eq 2 } # Because the previous
599
- it { expect(count(history)).to eq 2 } # records are preserved
600
- end
601
-
602
- context 'after a failure' do
603
- def insert
604
- adapter.execute <<-SQL
605
- INSERT INTO #{table} (test, foo) VALUES
606
- ('test4', 3),
607
- ('test5', 4);
608
- SQL
609
- end
610
-
611
- it { expect { insert }.to_not raise_error }
612
-
613
- it { expect(count(current)).to eq 4 }
614
- it { expect(count(history)).to eq 4 }
615
-
616
- it { expect(ids(current)).to eq ids(history) }
617
- end
618
- end
619
-
620
- context 'INSERT on NOT NULL columns but with a DEFAULT value' do
621
- before :all do
622
- adapter.create_table table, :temporal => true, &columns
623
- end
624
-
625
- after :all do
626
- adapter.drop_table table
627
- end
628
-
629
- def insert
630
- adapter.execute <<-SQL
631
- INSERT INTO #{table} DEFAULT VALUES
632
- SQL
633
- end
634
-
635
- def select
636
- adapter.select_values <<-SQL
637
- SELECT test FROM #{table}
638
- SQL
639
- end
640
-
641
- it { expect { insert }.to_not raise_error }
642
- it { insert; expect(select.uniq).to eq ['default-value'] }
643
- end
644
-
645
- context 'redundant UPDATEs' do
646
-
647
- before :all do
648
- adapter.create_table table, :temporal => true, &columns
649
-
650
- adapter.execute <<-SQL
651
- INSERT INTO #{table} (test, foo) VALUES ('test1', 1);
652
- SQL
653
-
654
- adapter.execute <<-SQL
655
- UPDATE #{table} SET test = 'test2';
656
- SQL
657
-
658
- adapter.execute <<-SQL
659
- UPDATE #{table} SET test = 'test2';
660
- SQL
661
- end
662
-
663
- after :all do
664
- adapter.drop_table table
665
- end
666
-
667
- it { expect(count(current)).to eq 1 }
668
- it { expect(count(history)).to eq 2 }
669
-
670
- end
671
-
672
- context 'updates on non-journaled fields' do
673
- before :all do
674
- adapter.create_table table, :temporal => true do |t|
675
- t.string 'test'
676
- t.timestamps null: false
677
- end
678
-
679
- adapter.execute <<-SQL
680
- INSERT INTO #{table} (test, created_at, updated_at) VALUES ('test', now(), now());
681
- SQL
682
-
683
- adapter.execute <<-SQL
684
- UPDATE #{table} SET test = 'test2', updated_at = now();
685
- SQL
686
-
687
- 2.times do
688
- adapter.execute <<-SQL # Redundant update with only updated_at change
689
- UPDATE #{table} SET test = 'test2', updated_at = now();
690
- SQL
691
-
692
- adapter.execute <<-SQL
693
- UPDATE #{table} SET updated_at = now();
694
- SQL
695
- end
696
- end
697
-
698
- after :all do
699
- adapter.drop_table table
700
- end
701
-
702
- it { expect(count(current)).to eq 1 }
703
- it { expect(count(history)).to eq 2 }
704
- end
705
-
706
- context 'selective journaled fields' do
707
- describe 'basic behaviour' do
708
- specify do
709
- adapter.create_table table, :temporal => true, :journal => %w( foo ) do |t|
710
- t.string 'foo'
711
- t.string 'bar'
712
- end
713
-
714
- adapter.execute <<-SQL
715
- INSERT INTO #{table} (foo, bar) VALUES ('test foo', 'test bar');
716
- SQL
717
-
718
- adapter.execute <<-SQL
719
- UPDATE #{table} SET foo = 'test foo', bar = 'no history';
720
- SQL
721
-
722
- 2.times do
723
- adapter.execute <<-SQL
724
- UPDATE #{table} SET bar = 'really no history';
725
- SQL
726
- end
727
-
728
- expect(count(current)).to eq 1
729
- expect(count(history)).to eq 1
730
-
731
- adapter.drop_table table
732
- end
733
- end
734
-
735
- describe 'schema changes' do
736
- table 'journaled_things'
737
-
738
- before do
739
- adapter.create_table table, :temporal => true, :journal => %w( foo ) do |t|
740
- t.string 'foo'
741
- t.string 'bar'
742
- t.string 'baz'
743
- end
744
- end
745
-
746
- after do
747
- adapter.drop_table table
748
- end
749
-
750
- it 'preserves options upon column change' do
751
- adapter.change_table table, temporal: true, journal: %w(foo bar)
752
-
753
- adapter.execute <<-SQL
754
- INSERT INTO #{table} (foo, bar) VALUES ('test foo', 'test bar');
755
- SQL
756
-
757
- expect(count(current)).to eq 1
758
- expect(count(history)).to eq 1
759
-
760
- adapter.execute <<-SQL
761
- UPDATE #{table} SET foo = 'test foo', bar = 'chronomodel';
762
- SQL
763
-
764
- expect(count(current)).to eq 1
765
- expect(count(history)).to eq 2
766
- end
767
-
768
- it 'changes option upon table change' do
769
- adapter.change_table table, temporal: true, journal: %w(bar)
770
-
771
- adapter.execute <<-SQL
772
- INSERT INTO #{table} (foo, bar) VALUES ('test foo', 'test bar');
773
- UPDATE #{table} SET foo = 'test foo', bar = 'no history';
774
- SQL
775
-
776
- expect(count(current)).to eq 1
777
- expect(count(history)).to eq 1
778
-
779
- adapter.execute <<-SQL
780
- UPDATE #{table} SET foo = 'test foo again', bar = 'no history';
781
- SQL
782
-
783
- expect(count(current)).to eq 1
784
- expect(count(history)).to eq 1
785
- end
786
- end
787
- end
788
- end