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.
- checksums.yaml +4 -4
- data/LICENSE +19 -20
- data/README.md +62 -40
- data/lib/active_record/connection_adapters/chronomodel_adapter.rb +17 -11
- data/lib/active_record/tasks/chronomodel_database_tasks.rb +64 -23
- data/lib/chrono_model/adapter/ddl.rb +168 -153
- data/lib/chrono_model/adapter/indexes.rb +99 -94
- data/lib/chrono_model/adapter/migrations.rb +81 -104
- data/lib/chrono_model/adapter/migrations_modules/legacy.rb +41 -0
- data/lib/chrono_model/adapter/migrations_modules/stable.rb +41 -0
- data/lib/chrono_model/adapter/tsrange.rb +20 -5
- data/lib/chrono_model/adapter/upgrade.rb +89 -91
- data/lib/chrono_model/adapter.rb +64 -31
- data/lib/chrono_model/chrono.rb +17 -0
- data/lib/chrono_model/conversions.rb +15 -9
- data/lib/chrono_model/db_console.rb +9 -0
- data/lib/chrono_model/json.rb +9 -6
- data/lib/chrono_model/patches/as_of_time_holder.rb +2 -2
- data/lib/chrono_model/patches/as_of_time_relation.rb +2 -2
- data/lib/chrono_model/patches/association.rb +15 -12
- data/lib/chrono_model/patches/batches.rb +17 -0
- data/lib/chrono_model/patches/db_console.rb +20 -4
- data/lib/chrono_model/patches/join_node.rb +4 -4
- data/lib/chrono_model/patches/preloader.rb +41 -11
- data/lib/chrono_model/patches/relation.rb +53 -8
- data/lib/chrono_model/patches.rb +3 -1
- data/lib/chrono_model/railtie.rb +29 -24
- data/lib/chrono_model/time_gate.rb +3 -3
- data/lib/chrono_model/time_machine/history_model.rb +65 -31
- data/lib/chrono_model/time_machine/time_query.rb +65 -49
- data/lib/chrono_model/time_machine/timeline.rb +52 -28
- data/lib/chrono_model/time_machine.rb +66 -25
- data/lib/chrono_model/utilities.rb +3 -3
- data/lib/chrono_model/version.rb +3 -1
- data/lib/chrono_model.rb +31 -36
- metadata +39 -136
- data/.gitignore +0 -21
- data/.rspec +0 -2
- data/.travis.yml +0 -41
- data/Gemfile +0 -4
- data/README.sql +0 -161
- data/Rakefile +0 -25
- data/chrono_model.gemspec +0 -33
- data/gemfiles/rails_5.0.gemfile +0 -6
- data/gemfiles/rails_5.1.gemfile +0 -6
- data/gemfiles/rails_5.2.gemfile +0 -6
- data/spec/aruba/dbconsole_spec.rb +0 -25
- data/spec/aruba/fixtures/database_with_default_username_and_password.yml +0 -14
- data/spec/aruba/fixtures/database_without_username_and_password.yml +0 -11
- data/spec/aruba/fixtures/empty_structure.sql +0 -27
- data/spec/aruba/fixtures/migrations/56/20160812190335_create_impressions.rb +0 -10
- data/spec/aruba/fixtures/migrations/56/20171115195229_add_temporal_extension_to_impressions.rb +0 -10
- data/spec/aruba/fixtures/railsapp/config/application.rb +0 -17
- data/spec/aruba/fixtures/railsapp/config/boot.rb +0 -5
- data/spec/aruba/fixtures/railsapp/config/environments/development.rb +0 -38
- data/spec/aruba/migrations_spec.rb +0 -48
- data/spec/aruba/rake_task_spec.rb +0 -71
- data/spec/chrono_model/adapter/base_spec.rb +0 -157
- data/spec/chrono_model/adapter/ddl_spec.rb +0 -243
- data/spec/chrono_model/adapter/indexes_spec.rb +0 -72
- data/spec/chrono_model/adapter/migrations_spec.rb +0 -312
- data/spec/chrono_model/conversions_spec.rb +0 -43
- data/spec/chrono_model/history_models_spec.rb +0 -32
- data/spec/chrono_model/json_ops_spec.rb +0 -59
- data/spec/chrono_model/time_machine/as_of_spec.rb +0 -188
- data/spec/chrono_model/time_machine/changes_spec.rb +0 -50
- data/spec/chrono_model/time_machine/counter_cache_race_spec.rb +0 -46
- data/spec/chrono_model/time_machine/default_scope_spec.rb +0 -37
- data/spec/chrono_model/time_machine/history_spec.rb +0 -104
- data/spec/chrono_model/time_machine/keep_cool_spec.rb +0 -27
- data/spec/chrono_model/time_machine/manipulations_spec.rb +0 -84
- data/spec/chrono_model/time_machine/model_identification_spec.rb +0 -46
- data/spec/chrono_model/time_machine/sequence_spec.rb +0 -74
- data/spec/chrono_model/time_machine/sti_spec.rb +0 -100
- data/spec/chrono_model/time_machine/time_query_spec.rb +0 -261
- data/spec/chrono_model/time_machine/timeline_spec.rb +0 -63
- data/spec/chrono_model/time_machine/timestamps_spec.rb +0 -43
- data/spec/chrono_model/time_machine/transactions_spec.rb +0 -69
- data/spec/config.travis.yml +0 -5
- data/spec/config.yml.example +0 -9
- data/spec/spec_helper.rb +0 -33
- data/spec/support/adapter/helpers.rb +0 -53
- data/spec/support/adapter/structure.rb +0 -44
- data/spec/support/aruba.rb +0 -44
- data/spec/support/connection.rb +0 -70
- data/spec/support/matchers/base.rb +0 -56
- data/spec/support/matchers/column.rb +0 -99
- data/spec/support/matchers/function.rb +0 -79
- data/spec/support/matchers/index.rb +0 -69
- data/spec/support/matchers/schema.rb +0 -39
- data/spec/support/matchers/table.rb +0 -275
- data/spec/support/time_machine/helpers.rb +0 -47
- data/spec/support/time_machine/structure.rb +0 -111
- data/sql/json_ops.sql +0 -56
- data/sql/uninstall-json_ops.sql +0 -24
|
@@ -1,261 +0,0 @@
|
|
|
1
|
-
require 'spec_helper'
|
|
2
|
-
require 'support/time_machine/structure'
|
|
3
|
-
|
|
4
|
-
describe ChronoModel::TimeMachine::TimeQuery do
|
|
5
|
-
include ChronoTest::TimeMachine::Helpers
|
|
6
|
-
|
|
7
|
-
adapter.create_table 'events' do |t|
|
|
8
|
-
t.string :name
|
|
9
|
-
t.daterange :interval
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
class ::Event < ActiveRecord::Base
|
|
13
|
-
extend ChronoModel::TimeMachine::TimeQuery
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
# Main timeline quick test
|
|
17
|
-
#
|
|
18
|
-
it { expect(Foo.history.time_query(:after, :now, inclusive: true ).count).to eq 3 }
|
|
19
|
-
it { expect(Foo.history.time_query(:after, :now, inclusive: false).count).to eq 0 }
|
|
20
|
-
it { expect(Foo.history.time_query(:before, :now, inclusive: true ).count).to eq 5 }
|
|
21
|
-
it { expect(Foo.history.time_query(:before, :now, inclusive: false).count).to eq 2 }
|
|
22
|
-
|
|
23
|
-
it { expect(Foo.history.past.size).to eq 2 }
|
|
24
|
-
|
|
25
|
-
# Extended thorough test.
|
|
26
|
-
#
|
|
27
|
-
# Create a set of events and then run time queries on them.
|
|
28
|
-
#
|
|
29
|
-
think = Event.create! name: 'think', interval: (15.days.ago.to_date...13.days.ago.to_date)
|
|
30
|
-
plan = Event.create! name: 'plan', interval: (14.days.ago.to_date...12.days.ago.to_date)
|
|
31
|
-
collect = Event.create! name: 'collect', interval: (12.days.ago.to_date...10.days.ago.to_date)
|
|
32
|
-
start = Event.create! name: 'start', interval: (8.days.ago.to_date...7.days.ago.to_date)
|
|
33
|
-
build = Event.create! name: 'build', interval: (7.days.ago.to_date...Date.yesterday)
|
|
34
|
-
profit = Event.create! name: 'profit', interval: (Date.tomorrow...1.year.from_now.to_date)
|
|
35
|
-
|
|
36
|
-
describe :at do
|
|
37
|
-
describe 'with a single timestamp' do
|
|
38
|
-
subject { Event.time_query(:at, time.try(:to_date) || time, on: :interval).to_a }
|
|
39
|
-
|
|
40
|
-
context 'no records' do
|
|
41
|
-
let(:time) { 16.days.ago }
|
|
42
|
-
it { is_expected.to be_empty }
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
context 'single record' do
|
|
46
|
-
let(:time) { 15.days.ago }
|
|
47
|
-
it { is_expected.to eq [think] }
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
context 'multiple overlapping records' do
|
|
51
|
-
let(:time) { 14.days.ago }
|
|
52
|
-
it { is_expected.to match_array [think, plan] }
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
context 'on an edge of an open interval' do
|
|
56
|
-
let(:time) { 10.days.ago }
|
|
57
|
-
it { is_expected.to be_empty }
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
context 'in an hole' do
|
|
61
|
-
let(:time) { 9.days.ago }
|
|
62
|
-
it { is_expected.to be_empty }
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
context 'today' do
|
|
66
|
-
let(:time) { Date.today }
|
|
67
|
-
it { is_expected.to be_empty }
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
context 'server-side :today' do
|
|
71
|
-
let(:time) { :today }
|
|
72
|
-
it { is_expected.to be_empty }
|
|
73
|
-
end
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
describe 'with a range' do
|
|
77
|
-
subject { Event.time_query(:at, times.map!(&:to_date), on: :interval, type: :daterange).to_a }
|
|
78
|
-
|
|
79
|
-
context 'that is empty' do
|
|
80
|
-
let(:times) { [ 14.days.ago, 14.days.ago ] }
|
|
81
|
-
it { is_expected.to_not be_empty }
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
context 'overlapping no records' do
|
|
85
|
-
let(:times) { [ 20.days.ago, 16.days.ago ] }
|
|
86
|
-
it { is_expected.to be_empty }
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
context 'overlapping a single record' do
|
|
90
|
-
let(:times) { [ 16.days.ago, 14.days.ago ] }
|
|
91
|
-
it { is_expected.to eq [think] }
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
context 'overlapping more records' do
|
|
95
|
-
let(:times) { [ 16.days.ago, 11.days.ago ] }
|
|
96
|
-
it { is_expected.to match_array [think, plan, collect] }
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
context 'on the edge of an open interval and an hole' do
|
|
100
|
-
let(:times) { [ 10.days.ago, 9.days.ago ] }
|
|
101
|
-
it { is_expected.to be_empty }
|
|
102
|
-
end
|
|
103
|
-
end
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
describe :before do
|
|
107
|
-
let(:inclusive) { true }
|
|
108
|
-
subject { Event.time_query(:before, time.try(:to_date) || time, on: :interval, type: :daterange, inclusive: inclusive).to_a }
|
|
109
|
-
|
|
110
|
-
context '16 days ago' do
|
|
111
|
-
let(:time) { 16.days.ago }
|
|
112
|
-
it { is_expected.to be_empty }
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
context '14 days ago' do
|
|
116
|
-
let(:time) { 14.days.ago }
|
|
117
|
-
it { is_expected.to eq [think] }
|
|
118
|
-
|
|
119
|
-
context 'not inclusive' do
|
|
120
|
-
let(:inclusive) { false }
|
|
121
|
-
it { is_expected.to be_empty }
|
|
122
|
-
end
|
|
123
|
-
end
|
|
124
|
-
|
|
125
|
-
context '11 days ago' do
|
|
126
|
-
let(:time) { 11.days.ago }
|
|
127
|
-
it { is_expected.to match_array [think, plan, collect] }
|
|
128
|
-
|
|
129
|
-
context 'not inclusive' do
|
|
130
|
-
let(:inclusive) { false }
|
|
131
|
-
it { is_expected.to eq [think, plan] }
|
|
132
|
-
end
|
|
133
|
-
end
|
|
134
|
-
|
|
135
|
-
context '10 days ago' do
|
|
136
|
-
let(:time) { 10.days.ago }
|
|
137
|
-
it { is_expected.to match_array [think, plan, collect] }
|
|
138
|
-
end
|
|
139
|
-
|
|
140
|
-
context '8 days ago' do
|
|
141
|
-
let(:time) { 8.days.ago }
|
|
142
|
-
it { is_expected.to match_array [think, plan, collect] }
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
context 'today' do
|
|
146
|
-
let(:time) { Date.today }
|
|
147
|
-
it { is_expected.to match_array [think, plan, collect, start, build] }
|
|
148
|
-
end
|
|
149
|
-
|
|
150
|
-
context ':today' do
|
|
151
|
-
let(:time) { :today }
|
|
152
|
-
it { is_expected.to match_array [think, plan, collect, start, build] }
|
|
153
|
-
end
|
|
154
|
-
end
|
|
155
|
-
|
|
156
|
-
describe :after do
|
|
157
|
-
let(:inclusive) { true }
|
|
158
|
-
subject { Event.time_query(:after, time.try(:to_date) || time, on: :interval, type: :daterange, inclusive: inclusive).to_a }
|
|
159
|
-
|
|
160
|
-
context 'one month ago' do
|
|
161
|
-
let(:time) { 1.month.ago }
|
|
162
|
-
it { is_expected.to match_array [think, plan, collect, start, build, profit] }
|
|
163
|
-
end
|
|
164
|
-
|
|
165
|
-
context '10 days ago' do
|
|
166
|
-
let(:time) { 10.days.ago }
|
|
167
|
-
it { is_expected.to match_array [start, build, profit] }
|
|
168
|
-
end
|
|
169
|
-
|
|
170
|
-
context 'yesterday' do
|
|
171
|
-
let(:time) { Date.yesterday }
|
|
172
|
-
it { is_expected.to eq [profit] }
|
|
173
|
-
end
|
|
174
|
-
|
|
175
|
-
context 'today' do
|
|
176
|
-
let(:time) { Date.today }
|
|
177
|
-
it { is_expected.to eq [profit] }
|
|
178
|
-
end
|
|
179
|
-
|
|
180
|
-
context 'server-side :today' do
|
|
181
|
-
let(:time) { :today }
|
|
182
|
-
it { is_expected.to eq [profit] }
|
|
183
|
-
end
|
|
184
|
-
|
|
185
|
-
context 'tomorrow' do
|
|
186
|
-
let(:time) { Date.tomorrow }
|
|
187
|
-
it { is_expected.to eq [profit] }
|
|
188
|
-
end
|
|
189
|
-
|
|
190
|
-
context 'one month from now' do
|
|
191
|
-
let(:time) { 1.month.from_now }
|
|
192
|
-
it { is_expected.to eq [profit] }
|
|
193
|
-
|
|
194
|
-
context 'not inclusive' do
|
|
195
|
-
let(:inclusive) { false }
|
|
196
|
-
it { is_expected.to be_empty }
|
|
197
|
-
end
|
|
198
|
-
end
|
|
199
|
-
|
|
200
|
-
context 'far future' do
|
|
201
|
-
let(:time) { 1.year.from_now }
|
|
202
|
-
it { is_expected.to be_empty }
|
|
203
|
-
end
|
|
204
|
-
end
|
|
205
|
-
|
|
206
|
-
describe :not do
|
|
207
|
-
context 'with a single timestamp' do
|
|
208
|
-
subject { Event.time_query(:not, time.try(:to_date) || time, on: :interval, type: :daterange).to_a }
|
|
209
|
-
|
|
210
|
-
context '14 days ago' do
|
|
211
|
-
let(:time) { 14.days.ago }
|
|
212
|
-
it { is_expected.to match_array [collect, start, build, profit] }
|
|
213
|
-
end
|
|
214
|
-
|
|
215
|
-
context '9 days ago' do
|
|
216
|
-
let(:time) { 9.days.ago }
|
|
217
|
-
it { is_expected.to match_array [think, plan, collect, start, build, profit] }
|
|
218
|
-
end
|
|
219
|
-
|
|
220
|
-
context '8 days ago' do
|
|
221
|
-
let(:time) { 8.days.ago }
|
|
222
|
-
it { is_expected.to match_array [think, plan, collect, build, profit] }
|
|
223
|
-
end
|
|
224
|
-
|
|
225
|
-
context 'today' do
|
|
226
|
-
let(:time) { Date.today }
|
|
227
|
-
it { is_expected.to match_array [think, plan, collect, start, build, profit] }
|
|
228
|
-
end
|
|
229
|
-
|
|
230
|
-
context ':today' do
|
|
231
|
-
let(:time) { :today }
|
|
232
|
-
it { is_expected.to match_array [think, plan, collect, start, build, profit] }
|
|
233
|
-
end
|
|
234
|
-
|
|
235
|
-
context '1 month from now' do
|
|
236
|
-
let(:time) { 1.month.from_now }
|
|
237
|
-
it { is_expected.to match_array [think, plan, collect, start, build] }
|
|
238
|
-
end
|
|
239
|
-
end
|
|
240
|
-
|
|
241
|
-
context 'with a range' do
|
|
242
|
-
subject { Event.time_query(:not, time.map(&:to_date), on: :interval, type: :daterange).to_a }
|
|
243
|
-
|
|
244
|
-
context 'eliminating a single record' do
|
|
245
|
-
let(:time) { [1.month.ago, 14.days.ago] }
|
|
246
|
-
it { is_expected.to match_array [plan, collect, start, build, profit] }
|
|
247
|
-
end
|
|
248
|
-
|
|
249
|
-
context 'eliminating multiple records' do
|
|
250
|
-
let(:time) { [1.month.ago, Date.today] }
|
|
251
|
-
it { is_expected.to eq [profit] }
|
|
252
|
-
end
|
|
253
|
-
|
|
254
|
-
context 'from an edge' do
|
|
255
|
-
let(:time) { [14.days.ago, 10.days.ago] }
|
|
256
|
-
it { is_expected.to eq [start, build, profit] }
|
|
257
|
-
end
|
|
258
|
-
end
|
|
259
|
-
end
|
|
260
|
-
|
|
261
|
-
end
|
|
@@ -1,63 +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 '#timeline' do
|
|
8
|
-
split = lambda {|ts| ts.map!{|t| [t.to_i, t.usec]} }
|
|
9
|
-
|
|
10
|
-
timestamps_from = lambda {|*records|
|
|
11
|
-
records.map(&:history).flatten!.inject([]) {|ret, rec|
|
|
12
|
-
ret.push [rec.valid_from.to_i, rec.valid_from.usec] if rec.try(:valid_from)
|
|
13
|
-
ret.push [rec.valid_to .to_i, rec.valid_to .usec] if rec.try(:valid_to)
|
|
14
|
-
ret
|
|
15
|
-
}.sort.uniq
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
describe 'on records having an :has_many relationship' do
|
|
19
|
-
describe 'by default returns timestamps of the record only' do
|
|
20
|
-
subject { split.call($t.foo.timeline) }
|
|
21
|
-
|
|
22
|
-
it { expect(subject.size).to eq $t.foo.ts.size }
|
|
23
|
-
it { is_expected.to eq timestamps_from.call($t.foo) }
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
describe 'when asked, returns timestamps including the related objects' do
|
|
27
|
-
subject { split.call($t.foo.timeline(with: :bars)) }
|
|
28
|
-
|
|
29
|
-
it { expect(subject.size).to eq($t.foo.ts.size + $t.bar.ts.size) }
|
|
30
|
-
it { is_expected.to eq(timestamps_from.call($t.foo, *$t.foo.bars)) }
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
describe 'on records using has_timeline :with' do
|
|
35
|
-
subject { split.call($t.bar.timeline) }
|
|
36
|
-
|
|
37
|
-
describe 'returns timestamps of the record and its associations' do
|
|
38
|
-
|
|
39
|
-
let!(:expected) do
|
|
40
|
-
creat = $t.bar.history.first.valid_from
|
|
41
|
-
c_sec, c_usec = creat.to_i, creat.usec
|
|
42
|
-
|
|
43
|
-
timestamps_from.call($t.foo, $t.bar).reject {|sec, usec|
|
|
44
|
-
sec < c_sec || ( sec == c_sec && usec < c_usec )
|
|
45
|
-
}
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
it { expect(subject.size).to eq expected.size }
|
|
49
|
-
it { is_expected.to eq expected }
|
|
50
|
-
end
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
describe 'on non-temporal records using has_timeline :with' do
|
|
54
|
-
subject { split.call($t.baz.timeline) }
|
|
55
|
-
|
|
56
|
-
describe 'returns timestamps of its temporal associations' do
|
|
57
|
-
it { expect(subject.size).to eq $t.bar.ts.size }
|
|
58
|
-
it { is_expected.to eq timestamps_from.call($t.bar) }
|
|
59
|
-
end
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
end
|
|
@@ -1,43 +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
|
-
history_methods = %w( valid_from valid_to recorded_at )
|
|
8
|
-
current_methods = %w( as_of_time )
|
|
9
|
-
|
|
10
|
-
context 'on history records' do
|
|
11
|
-
let(:record) { $t.foo.history.first }
|
|
12
|
-
|
|
13
|
-
(history_methods + current_methods).each do |attr|
|
|
14
|
-
describe ['#', attr].join do
|
|
15
|
-
subject { record.public_send(attr) }
|
|
16
|
-
|
|
17
|
-
it { is_expected.to be_present }
|
|
18
|
-
it { is_expected.to be_a(Time) }
|
|
19
|
-
it { is_expected.to be_utc }
|
|
20
|
-
end
|
|
21
|
-
end
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
context 'on current records' do
|
|
25
|
-
let(:record) { $t.foo }
|
|
26
|
-
|
|
27
|
-
history_methods.each do |attr|
|
|
28
|
-
describe ['#', attr].join do
|
|
29
|
-
subject { record.public_send(attr) }
|
|
30
|
-
|
|
31
|
-
it { expect { subject }.to raise_error(NoMethodError) }
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
current_methods.each do |attr|
|
|
36
|
-
describe ['#', attr].join do
|
|
37
|
-
subject { record.public_send(attr) }
|
|
38
|
-
|
|
39
|
-
it { is_expected.to be(nil) }
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
end
|
|
@@ -1,69 +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
|
-
# Transactions
|
|
8
|
-
context 'multiple updates to an existing record' do
|
|
9
|
-
let!(:r1) do
|
|
10
|
-
Foo.create!(:name => 'xact test').tap do |record|
|
|
11
|
-
Foo.transaction do
|
|
12
|
-
record.update_attribute 'name', 'lost into oblivion'
|
|
13
|
-
record.update_attribute 'name', 'does work'
|
|
14
|
-
end
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
it "generate only a single history record" do
|
|
19
|
-
expect(r1.history.size).to eq(2)
|
|
20
|
-
|
|
21
|
-
expect(r1.history.first.name).to eq 'xact test'
|
|
22
|
-
expect(r1.history.last.name).to eq 'does work'
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
after do
|
|
26
|
-
r1.destroy
|
|
27
|
-
r1.history.delete_all
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
context 'insertion and subsequent update' do
|
|
32
|
-
let!(:r2) do
|
|
33
|
-
Foo.transaction do
|
|
34
|
-
Foo.create!(:name => 'lost into oblivion').tap do |record|
|
|
35
|
-
record.update_attribute 'name', 'I am Bar'
|
|
36
|
-
record.update_attribute 'name', 'I am Foo'
|
|
37
|
-
end
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
it 'generates a single history record' do
|
|
42
|
-
expect(r2.history.size).to eq(1)
|
|
43
|
-
expect(r2.history.first.name).to eq 'I am Foo'
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
after do
|
|
47
|
-
r2.destroy
|
|
48
|
-
r2.history.delete_all
|
|
49
|
-
end
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
context 'insertion and subsequent deletion' do
|
|
53
|
-
let!(:r3) do
|
|
54
|
-
Foo.transaction do
|
|
55
|
-
Foo.create!(:name => 'it never happened').destroy
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
it 'does not generate any history' do
|
|
60
|
-
expect(Foo.history.where(:id => r3.id)).to be_empty
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
after do
|
|
64
|
-
r3.destroy
|
|
65
|
-
r3.history.delete_all
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
end
|
data/spec/config.travis.yml
DELETED
data/spec/config.yml.example
DELETED
data/spec/spec_helper.rb
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
|
2
|
-
#
|
|
3
|
-
require 'simplecov'
|
|
4
|
-
SimpleCov.start
|
|
5
|
-
|
|
6
|
-
require 'byebug'
|
|
7
|
-
|
|
8
|
-
require 'chrono_model'
|
|
9
|
-
|
|
10
|
-
require 'support/connection'
|
|
11
|
-
require 'support/matchers/schema'
|
|
12
|
-
require 'support/matchers/table'
|
|
13
|
-
require 'support/matchers/column'
|
|
14
|
-
require 'support/matchers/index'
|
|
15
|
-
require 'support/matchers/function'
|
|
16
|
-
require 'support/aruba'
|
|
17
|
-
|
|
18
|
-
puts "Testing against Active Record #{ActiveRecord::VERSION::STRING} with Arel #{Arel::VERSION}"
|
|
19
|
-
|
|
20
|
-
RSpec.configure do |config|
|
|
21
|
-
config.include(ChronoTest::Matchers::Schema)
|
|
22
|
-
config.include(ChronoTest::Matchers::Table)
|
|
23
|
-
config.include(ChronoTest::Matchers::Column)
|
|
24
|
-
config.include(ChronoTest::Matchers::Index)
|
|
25
|
-
config.include(ChronoTest::Aruba, type: :aruba)
|
|
26
|
-
|
|
27
|
-
ChronoTest.recreate_database!
|
|
28
|
-
|
|
29
|
-
config.before(:example, type: :aruba) do
|
|
30
|
-
copy_dummy_app_into_aruba_working_directory
|
|
31
|
-
recreate_railsapp_database
|
|
32
|
-
end
|
|
33
|
-
end
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
module ChronoTest::Adapter
|
|
2
|
-
|
|
3
|
-
module Helpers
|
|
4
|
-
def self.included(base)
|
|
5
|
-
base.extend DSL
|
|
6
|
-
|
|
7
|
-
base.instance_eval do
|
|
8
|
-
delegate :adapter, :to => ChronoTest
|
|
9
|
-
delegate :columns, :table, :pk_type, :to => DSL
|
|
10
|
-
end
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
module DSL
|
|
14
|
-
def with_temporal_table(&block)
|
|
15
|
-
context ':temporal => true' do
|
|
16
|
-
before(:all) { adapter.create_table(table, :temporal => true, &DSL.columns) }
|
|
17
|
-
after(:all) { adapter.drop_table table }
|
|
18
|
-
|
|
19
|
-
instance_eval(&block)
|
|
20
|
-
end
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def with_plain_table(&block)
|
|
24
|
-
context ':temporal => false' do
|
|
25
|
-
before(:all) { adapter.create_table(table, :temporal => false, &DSL.columns) }
|
|
26
|
-
after(:all) { adapter.drop_table table }
|
|
27
|
-
|
|
28
|
-
instance_eval(&block)
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def self.table(table = nil)
|
|
33
|
-
@table = table if table
|
|
34
|
-
@table
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
def self.columns(&block)
|
|
38
|
-
@columns = block.call if block
|
|
39
|
-
@columns
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
def self.pk_type
|
|
43
|
-
@pk_type ||= if ActiveRecord::VERSION::MAJOR == 5 && ActiveRecord::VERSION::MINOR >= 1
|
|
44
|
-
'bigint'
|
|
45
|
-
else
|
|
46
|
-
'integer'
|
|
47
|
-
end
|
|
48
|
-
end
|
|
49
|
-
delegate :columns, :table, :pk_type, :to => self
|
|
50
|
-
end
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
end
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
require 'support/adapter/helpers'
|
|
2
|
-
|
|
3
|
-
# This module contains the definition of a test structure that is used by the
|
|
4
|
-
# adapter methods tests, that look up in the database directly whether the
|
|
5
|
-
# expected objects have been created.
|
|
6
|
-
#
|
|
7
|
-
# The structure defintiion below serves as a blueprint of what it can be
|
|
8
|
-
# defined, ans as a reference of what it is expected to have been created by
|
|
9
|
-
# the +ChronoModel::Adapter+ methods.
|
|
10
|
-
#
|
|
11
|
-
module ChronoTest::Adapter
|
|
12
|
-
|
|
13
|
-
module Structure
|
|
14
|
-
extend ActiveSupport::Concern
|
|
15
|
-
|
|
16
|
-
included do
|
|
17
|
-
table 'test_table'
|
|
18
|
-
subject { table }
|
|
19
|
-
|
|
20
|
-
columns do
|
|
21
|
-
native = [
|
|
22
|
-
['test', 'character varying'],
|
|
23
|
-
['foo', 'integer'],
|
|
24
|
-
['bar', 'double precision'],
|
|
25
|
-
['baz', 'text']
|
|
26
|
-
]
|
|
27
|
-
|
|
28
|
-
def native.to_proc
|
|
29
|
-
proc {|t|
|
|
30
|
-
t.string :test, null: false, default: 'default-value'
|
|
31
|
-
t.integer :foo
|
|
32
|
-
t.float :bar
|
|
33
|
-
t.text :baz
|
|
34
|
-
t.integer :ary, array: true, null: false, default: []
|
|
35
|
-
t.boolean :bool, null: false, default: false
|
|
36
|
-
}
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
native
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
end
|
data/spec/support/aruba.rb
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
require 'aruba/rspec'
|
|
2
|
-
require 'arel'
|
|
3
|
-
|
|
4
|
-
module ChronoTest
|
|
5
|
-
|
|
6
|
-
module Aruba
|
|
7
|
-
def aruba_working_directory
|
|
8
|
-
File.expand_path("../../#{::Aruba.config.working_directory}", __dir__)
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
def dummy_app_directory
|
|
12
|
-
File.expand_path("../../tmp/railsapp/", __dir__)
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def copy_dummy_app_into_aruba_working_directory
|
|
16
|
-
unless File.file?(Pathname.new(dummy_app_directory).join('Rakefile'))
|
|
17
|
-
raise %q(
|
|
18
|
-
The dummy application does not exist
|
|
19
|
-
Run `bundle exec rake testapp:create`
|
|
20
|
-
)
|
|
21
|
-
end
|
|
22
|
-
FileUtils.rm_rf(Dir.glob("#{aruba_working_directory}/*"))
|
|
23
|
-
FileUtils.cp_r(Dir.glob("#{dummy_app_directory}/*"), aruba_working_directory)
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
def recreate_railsapp_database
|
|
27
|
-
connection = AR.connection # we're still the 'chronomodel' user
|
|
28
|
-
database = 'chronomodel_railsapp'
|
|
29
|
-
connection.drop_database database
|
|
30
|
-
connection.create_database database
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
def file_mangle!(file, &block)
|
|
34
|
-
# Read
|
|
35
|
-
file_contents = read(file).join("\n")
|
|
36
|
-
|
|
37
|
-
# Mangle
|
|
38
|
-
file_contents = block.call(file_contents)
|
|
39
|
-
|
|
40
|
-
# Write
|
|
41
|
-
write_file file, file_contents
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
end
|
data/spec/support/connection.rb
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
require 'pathname'
|
|
2
|
-
require 'active_record'
|
|
3
|
-
|
|
4
|
-
module ChronoTest
|
|
5
|
-
extend self
|
|
6
|
-
|
|
7
|
-
AR = ActiveRecord::Base
|
|
8
|
-
log = ENV['VERBOSE'].present? ? $stderr : 'spec/debug.log'.tap{|f| File.open(f, "ab") { |ft| ft.truncate(0) }}
|
|
9
|
-
AR.logger = ::Logger.new(log).tap do |l|
|
|
10
|
-
l.level = 0
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
def connect!(spec = self.config)
|
|
14
|
-
unless ENV['VERBOSE'].present?
|
|
15
|
-
spec = spec.merge(:min_messages => 'WARNING')
|
|
16
|
-
end
|
|
17
|
-
AR.establish_connection spec
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
def logger
|
|
21
|
-
AR.logger
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def connection
|
|
25
|
-
AR.connection
|
|
26
|
-
end
|
|
27
|
-
alias :adapter :connection
|
|
28
|
-
|
|
29
|
-
def recreate_database!
|
|
30
|
-
database = config.fetch(:database)
|
|
31
|
-
connect! config.merge(:database => :postgres)
|
|
32
|
-
|
|
33
|
-
connection.drop_database database
|
|
34
|
-
connection.create_database database
|
|
35
|
-
connect! config
|
|
36
|
-
|
|
37
|
-
connection.execute 'CREATE EXTENSION btree_gist'
|
|
38
|
-
logger.info "Connected to #{config}"
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
def config
|
|
42
|
-
@config ||= YAML.load(config_file.read).tap do |conf|
|
|
43
|
-
conf.symbolize_keys!
|
|
44
|
-
conf.update(:adapter => 'chronomodel')
|
|
45
|
-
|
|
46
|
-
def conf.to_s
|
|
47
|
-
'pgsql://%s:%s@%s/%s' % [
|
|
48
|
-
self[:username], self[:password], self[:hostname], self[:database]
|
|
49
|
-
]
|
|
50
|
-
end
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
rescue Errno::ENOENT
|
|
54
|
-
$stderr.puts <<EOM
|
|
55
|
-
|
|
56
|
-
Please define your AR database configuration
|
|
57
|
-
in spec/config.yml or reference your own configuration
|
|
58
|
-
file using the TEST_CONFIG environment variable
|
|
59
|
-
EOM
|
|
60
|
-
|
|
61
|
-
abort
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
def config_file
|
|
65
|
-
Pathname(ENV['TEST_CONFIG'] ||
|
|
66
|
-
File.join(File.dirname(__FILE__), '..', 'config.yml')
|
|
67
|
-
)
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
end
|