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.
- checksums.yaml +4 -4
- data/Rakefile +2 -1
- data/lib/chrono_model/time_machine.rb +1 -1
- data/lib/chrono_model/time_machine/history_model.rb +4 -0
- data/lib/chrono_model/version.rb +1 -1
- data/spec/chrono_model/adapter/base_spec.rb +157 -0
- data/spec/chrono_model/adapter/ddl_spec.rb +243 -0
- data/spec/chrono_model/adapter/indexes_spec.rb +72 -0
- data/spec/chrono_model/adapter/migrations_spec.rb +312 -0
- data/spec/chrono_model/history_models_spec.rb +32 -0
- data/spec/chrono_model/time_machine/as_of_spec.rb +188 -0
- data/spec/chrono_model/time_machine/changes_spec.rb +50 -0
- data/spec/chrono_model/{adapter → time_machine}/counter_cache_race_spec.rb +2 -2
- data/spec/chrono_model/time_machine/default_scope_spec.rb +37 -0
- data/spec/chrono_model/time_machine/history_spec.rb +104 -0
- data/spec/chrono_model/time_machine/keep_cool_spec.rb +27 -0
- data/spec/chrono_model/time_machine/manipulations_spec.rb +84 -0
- data/spec/chrono_model/time_machine/model_identification_spec.rb +46 -0
- data/spec/chrono_model/time_machine/sequence_spec.rb +74 -0
- data/spec/chrono_model/time_machine/sti_spec.rb +100 -0
- data/spec/chrono_model/{time_query_spec.rb → time_machine/time_query_spec.rb} +22 -5
- data/spec/chrono_model/time_machine/timeline_spec.rb +63 -0
- data/spec/chrono_model/time_machine/timestamps_spec.rb +43 -0
- data/spec/chrono_model/time_machine/transactions_spec.rb +69 -0
- data/spec/support/adapter/helpers.rb +53 -0
- data/spec/support/adapter/structure.rb +44 -0
- data/spec/support/time_machine/helpers.rb +47 -0
- data/spec/support/time_machine/structure.rb +111 -0
- metadata +48 -14
- data/spec/chrono_model/adapter/sti_bug_spec.rb +0 -49
- data/spec/chrono_model/adapter_spec.rb +0 -788
- data/spec/chrono_model/time_machine_spec.rb +0 -749
- 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.
|
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-
|
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/
|
256
|
-
- spec/chrono_model/adapter/
|
257
|
-
- spec/chrono_model/
|
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/
|
261
|
-
- spec/chrono_model/
|
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/
|
312
|
-
- spec/chrono_model/adapter/
|
313
|
-
- spec/chrono_model/
|
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/
|
317
|
-
- spec/chrono_model/
|
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
|