chrono_model 1.2.2 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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,157 +0,0 @@
1
- require 'spec_helper'
2
- require 'support/adapter/structure'
3
-
4
- describe ChronoModel::Adapter do
5
- include ChronoTest::Adapter::Helpers
6
- include ChronoTest::Adapter::Structure
7
-
8
- subject { adapter }
9
- it { is_expected.to be_a_kind_of(ChronoModel::Adapter) }
10
-
11
- context do
12
- subject { adapter.adapter_name }
13
- it { is_expected.to eq 'PostgreSQL' }
14
- end
15
-
16
- context do
17
- before { expect(adapter).to receive(:postgresql_version).and_return(90300) }
18
- it { is_expected.to be_chrono_supported }
19
- end
20
-
21
- context do
22
- before { expect(adapter).to receive(:postgresql_version).and_return(90000) }
23
- it { is_expected.to_not be_chrono_supported }
24
- end
25
-
26
- describe '.primary_key' do
27
- subject { adapter.primary_key(table) }
28
-
29
- assert = proc do
30
- it { is_expected.to eq 'id' }
31
- end
32
-
33
- with_temporal_table(&assert)
34
- with_plain_table( &assert)
35
- end
36
-
37
- describe '.indexes' do
38
- subject { adapter.indexes(table) }
39
-
40
- assert = proc do
41
- before(:all) do
42
- adapter.add_index table, :foo, :name => 'foo_index'
43
- adapter.add_index table, [:bar, :baz], :name => 'bar_index'
44
- end
45
-
46
- it { expect(subject.map(&:name)).to match_array %w( foo_index bar_index ) }
47
- it { expect(subject.map(&:columns)).to match_array [['foo'], ['bar', 'baz']] }
48
- end
49
-
50
- with_temporal_table(&assert)
51
- with_plain_table( &assert)
52
- end
53
-
54
- describe '.column_definitions' do
55
- subject { adapter.column_definitions(table).map {|d| d.take(2)} }
56
-
57
- assert = proc do
58
- it { expect(subject & columns).to eq columns }
59
- it { is_expected.to include(['id', pk_type]) }
60
- end
61
-
62
- with_temporal_table(&assert)
63
- with_plain_table( &assert)
64
- end
65
-
66
- describe '.on_schema' do
67
- before(:all) do
68
- adapter.execute 'BEGIN'
69
- 5.times {|i| adapter.execute "CREATE SCHEMA test_#{i}"}
70
- end
71
-
72
- after(:all) do
73
- adapter.execute 'ROLLBACK'
74
- end
75
-
76
- context 'by default' do
77
- it 'saves the schema at each recursion' do
78
- is_expected.to be_in_schema(:default)
79
-
80
- adapter.on_schema('test_1') { is_expected.to be_in_schema('test_1')
81
- adapter.on_schema('test_2') { is_expected.to be_in_schema('test_2')
82
- adapter.on_schema('test_3') { is_expected.to be_in_schema('test_3')
83
- }
84
- is_expected.to be_in_schema('test_2')
85
- }
86
- is_expected.to be_in_schema('test_1')
87
- }
88
-
89
- is_expected.to be_in_schema(:default)
90
- end
91
-
92
- context 'when errors occur' do
93
- subject do
94
- adapter.on_schema('test_1') do
95
-
96
- adapter.on_schema('test_2') do
97
- adapter.execute 'BEGIN'
98
- adapter.execute 'ERRORING ON PURPOSE'
99
- end
100
-
101
- end
102
- end
103
-
104
- it {
105
- expect { subject }.
106
- to raise_error(/current transaction is aborted/).
107
- and change { adapter.instance_variable_get(:@schema_search_path) }
108
- }
109
-
110
- after do
111
- adapter.execute 'ROLLBACK'
112
- end
113
- end
114
- end
115
-
116
- context 'with recurse: :ignore' do
117
- it 'ignores recursive calls' do
118
- is_expected.to be_in_schema(:default)
119
-
120
- adapter.on_schema('test_1', recurse: :ignore) { is_expected.to be_in_schema('test_1')
121
- adapter.on_schema('test_2', recurse: :ignore) { is_expected.to be_in_schema('test_1')
122
- adapter.on_schema('test_3', recurse: :ignore) { is_expected.to be_in_schema('test_1')
123
- } } }
124
-
125
- is_expected.to be_in_schema(:default)
126
- end
127
- end
128
- end
129
-
130
- describe '.is_chrono?' do
131
- with_temporal_table do
132
- it { expect(adapter.is_chrono?(table)).to be(true) }
133
- end
134
-
135
- with_plain_table do
136
- it { expect(adapter.is_chrono?(table)).to be(false) }
137
- end
138
-
139
- context 'when schemas are not there yet' do
140
- before(:all) do
141
- adapter.execute 'BEGIN'
142
- adapter.execute 'DROP SCHEMA temporal CASCADE'
143
- adapter.execute 'DROP SCHEMA history CASCADE'
144
- adapter.execute 'CREATE TABLE test_table (id integer)'
145
- end
146
-
147
- after(:all) do
148
- adapter.execute 'ROLLBACK'
149
- end
150
-
151
- it { expect { adapter.is_chrono?(table) }.to_not raise_error }
152
-
153
- it { expect(adapter.is_chrono?(table)).to be(false) }
154
- end
155
- end
156
-
157
- end
@@ -1,243 +0,0 @@
1
- require 'spec_helper'
2
- require 'support/adapter/structure'
3
-
4
- describe ChronoModel::Adapter do
5
- include ChronoTest::Adapter::Helpers
6
- include ChronoTest::Adapter::Structure
7
-
8
- let(:current) { [ChronoModel::Adapter::TEMPORAL_SCHEMA, table].join('.') }
9
- let(:history) { [ChronoModel::Adapter::HISTORY_SCHEMA, table].join('.') }
10
-
11
- def count(table)
12
- adapter.select_value("SELECT COUNT(*) FROM ONLY #{table}").to_i
13
- end
14
-
15
- def ids(table)
16
- adapter.select_values("SELECT id FROM ONLY #{table} ORDER BY id")
17
- end
18
-
19
- context 'INSERT multiple values' do
20
- before :all do
21
- adapter.create_table table, :temporal => true, &columns
22
- end
23
-
24
- after :all do
25
- adapter.drop_table table
26
- end
27
-
28
- context 'when succeeding' do
29
- def insert
30
- adapter.execute <<-SQL
31
- INSERT INTO #{table} (test, foo) VALUES
32
- ('test1', 1),
33
- ('test2', 2);
34
- SQL
35
- end
36
-
37
- it { expect { insert }.to_not raise_error }
38
- it { expect(count(current)).to eq 2 }
39
- it { expect(count(history)).to eq 2 }
40
- end
41
-
42
- context 'when failing' do
43
- def insert
44
- adapter.execute <<-SQL
45
- INSERT INTO #{table} (test, foo) VALUES
46
- ('test3', 3),
47
- (NULL, 0);
48
- SQL
49
- end
50
-
51
- it { expect { insert }.to raise_error(ActiveRecord::StatementInvalid) }
52
- it { expect(count(current)).to eq 2 } # Because the previous
53
- it { expect(count(history)).to eq 2 } # records are preserved
54
- end
55
-
56
- context 'after a failure' do
57
- def insert
58
- adapter.execute <<-SQL
59
- INSERT INTO #{table} (test, foo) VALUES
60
- ('test4', 3),
61
- ('test5', 4);
62
- SQL
63
- end
64
-
65
- it { expect { insert }.to_not raise_error }
66
-
67
- it { expect(count(current)).to eq 4 }
68
- it { expect(count(history)).to eq 4 }
69
-
70
- it { expect(ids(current)).to eq ids(history) }
71
- end
72
- end
73
-
74
- context 'INSERT on NOT NULL columns but with a DEFAULT value' do
75
- before :all do
76
- adapter.create_table table, :temporal => true, &columns
77
- end
78
-
79
- after :all do
80
- adapter.drop_table table
81
- end
82
-
83
- def insert
84
- adapter.execute <<-SQL
85
- INSERT INTO #{table} DEFAULT VALUES
86
- SQL
87
- end
88
-
89
- def select
90
- adapter.select_values <<-SQL
91
- SELECT test FROM #{table}
92
- SQL
93
- end
94
-
95
- it { expect { insert }.to_not raise_error }
96
- it { insert; expect(select.uniq).to eq ['default-value'] }
97
- end
98
-
99
- context 'redundant UPDATEs' do
100
-
101
- before :all do
102
- adapter.create_table table, :temporal => true, &columns
103
-
104
- adapter.execute <<-SQL
105
- INSERT INTO #{table} (test, foo) VALUES ('test1', 1);
106
- SQL
107
-
108
- adapter.execute <<-SQL
109
- UPDATE #{table} SET test = 'test2';
110
- SQL
111
-
112
- adapter.execute <<-SQL
113
- UPDATE #{table} SET test = 'test2';
114
- SQL
115
- end
116
-
117
- after :all do
118
- adapter.drop_table table
119
- end
120
-
121
- it { expect(count(current)).to eq 1 }
122
- it { expect(count(history)).to eq 2 }
123
-
124
- end
125
-
126
- context 'updates on non-journaled fields' do
127
- before :all do
128
- adapter.create_table table, :temporal => true do |t|
129
- t.string 'test'
130
- t.timestamps null: false
131
- end
132
-
133
- adapter.execute <<-SQL
134
- INSERT INTO #{table} (test, created_at, updated_at) VALUES ('test', now(), now());
135
- SQL
136
-
137
- adapter.execute <<-SQL
138
- UPDATE #{table} SET test = 'test2', updated_at = now();
139
- SQL
140
-
141
- 2.times do
142
- adapter.execute <<-SQL # Redundant update with only updated_at change
143
- UPDATE #{table} SET test = 'test2', updated_at = now();
144
- SQL
145
-
146
- adapter.execute <<-SQL
147
- UPDATE #{table} SET updated_at = now();
148
- SQL
149
- end
150
- end
151
-
152
- after :all do
153
- adapter.drop_table table
154
- end
155
-
156
- it { expect(count(current)).to eq 1 }
157
- it { expect(count(history)).to eq 2 }
158
- end
159
-
160
- context 'selective journaled fields' do
161
- describe 'basic behaviour' do
162
- specify do
163
- adapter.create_table table, :temporal => true, :journal => %w( foo ) do |t|
164
- t.string 'foo'
165
- t.string 'bar'
166
- end
167
-
168
- adapter.execute <<-SQL
169
- INSERT INTO #{table} (foo, bar) VALUES ('test foo', 'test bar');
170
- SQL
171
-
172
- adapter.execute <<-SQL
173
- UPDATE #{table} SET foo = 'test foo', bar = 'no history';
174
- SQL
175
-
176
- 2.times do
177
- adapter.execute <<-SQL
178
- UPDATE #{table} SET bar = 'really no history';
179
- SQL
180
- end
181
-
182
- expect(count(current)).to eq 1
183
- expect(count(history)).to eq 1
184
-
185
- adapter.drop_table table
186
- end
187
- end
188
-
189
- describe 'schema changes' do
190
- table 'journaled_things'
191
-
192
- before do
193
- adapter.create_table table, :temporal => true, :journal => %w( foo ) do |t|
194
- t.string 'foo'
195
- t.string 'bar'
196
- t.string 'baz'
197
- end
198
- end
199
-
200
- after do
201
- adapter.drop_table table
202
- end
203
-
204
- it 'preserves options upon column change' do
205
- adapter.change_table table, temporal: true, journal: %w(foo bar)
206
-
207
- adapter.execute <<-SQL
208
- INSERT INTO #{table} (foo, bar) VALUES ('test foo', 'test bar');
209
- SQL
210
-
211
- expect(count(current)).to eq 1
212
- expect(count(history)).to eq 1
213
-
214
- adapter.execute <<-SQL
215
- UPDATE #{table} SET foo = 'test foo', bar = 'chronomodel';
216
- SQL
217
-
218
- expect(count(current)).to eq 1
219
- expect(count(history)).to eq 2
220
- end
221
-
222
- it 'changes option upon table change' do
223
- adapter.change_table table, temporal: true, journal: %w(bar)
224
-
225
- adapter.execute <<-SQL
226
- INSERT INTO #{table} (foo, bar) VALUES ('test foo', 'test bar');
227
- UPDATE #{table} SET foo = 'test foo', bar = 'no history';
228
- SQL
229
-
230
- expect(count(current)).to eq 1
231
- expect(count(history)).to eq 1
232
-
233
- adapter.execute <<-SQL
234
- UPDATE #{table} SET foo = 'test foo again', bar = 'no history';
235
- SQL
236
-
237
- expect(count(current)).to eq 1
238
- expect(count(history)).to eq 1
239
- end
240
- end
241
- end
242
-
243
- end
@@ -1,72 +0,0 @@
1
- require 'spec_helper'
2
- require 'support/adapter/structure'
3
-
4
- describe ChronoModel::Adapter do
5
- include ChronoTest::Adapter::Helpers
6
- include ChronoTest::Adapter::Structure
7
-
8
- before :all do
9
- adapter.create_table :meetings do |t|
10
- t.string :name
11
- t.tsrange :interval
12
- end
13
- end
14
-
15
- after :all do
16
- adapter.drop_table :meetings
17
- end
18
-
19
- describe '.add_temporal_indexes' do
20
- before do
21
- adapter.add_temporal_indexes :meetings, :interval
22
- end
23
-
24
- it { expect(adapter.indexes(:meetings).map(&:name)).to eq [
25
- 'index_meetings_temporal_on_interval',
26
- 'index_meetings_temporal_on_lower_interval',
27
- 'index_meetings_temporal_on_upper_interval'
28
- ] }
29
-
30
- after do
31
- adapter.remove_temporal_indexes :meetings, :interval
32
- end
33
- end
34
-
35
- describe '.remove_temporal_indexes' do
36
- before :all do
37
- adapter.add_temporal_indexes :meetings, :interval
38
- end
39
-
40
- before do
41
- adapter.remove_temporal_indexes :meetings, :interval
42
- end
43
-
44
- it { expect(adapter.indexes(:meetings)).to be_empty }
45
- end
46
-
47
- describe '.add_timeline_consistency_constraint' do
48
- before do
49
- adapter.add_timeline_consistency_constraint(:meetings, :interval)
50
- end
51
-
52
- it { expect(adapter.indexes(:meetings).map(&:name)).to eq [
53
- 'meetings_timeline_consistency'
54
- ] }
55
-
56
- after do
57
- adapter.remove_timeline_consistency_constraint(:meetings)
58
- end
59
- end
60
-
61
- describe '.remove_timeline_consistency_constraint' do
62
- before :all do
63
- adapter.add_timeline_consistency_constraint :meetings, :interval
64
- end
65
-
66
- before do
67
- adapter.remove_timeline_consistency_constraint(:meetings)
68
- end
69
-
70
- it { expect(adapter.indexes(:meetings)).to be_empty }
71
- end
72
- end