sequel_bitemporal 0.6.11 → 0.6.12

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,785 @@
1
+ require "spec_helper"
2
+ if DbHelpers.pg?
3
+ describe "Sequel::Plugins::Bitemporal", "with ranges" do
4
+ before :all do
5
+ db_setup ranges: true
6
+ end
7
+ before do
8
+ Timecop.freeze 2009, 11, 28
9
+ end
10
+ after do
11
+ Timecop.return
12
+ end
13
+ it "checks version class is given" do
14
+ lambda{
15
+ @version_class.plugin :bitemporal
16
+ }.should raise_error Sequel::Error, "please specify version class to use for bitemporal plugin"
17
+ end
18
+ it "checks required columns are present" do
19
+ lambda{
20
+ @version_class.plugin :bitemporal, version_class: @master_class
21
+ }.should raise_error Sequel::Error, "bitemporal plugin requires the following missing columns on version class: master_id, valid_from, valid_to, created_at, expired_at"
22
+ end
23
+ it "propagates errors from version to master" do
24
+ master = @master_class.new
25
+ master.should be_valid
26
+ master.attributes = {name: "Single Standard"}
27
+ master.should_not be_valid
28
+ master.errors.should == {price: ["is required"]}
29
+ end
30
+ it "#update_attributes returns false instead of raising errors" do
31
+ master = @master_class.new
32
+ master.update_attributes(name: "Single Standard").should be_false
33
+ master.should be_new
34
+ master.errors.should == {price: ["is required"]}
35
+ master.update_attributes(price: 98).should be_true
36
+ end
37
+ it "allows creating a master and its first version in one step" do
38
+ master = @master_class.new
39
+ result = master.update_attributes name: "Single Standard", price: 98
40
+ result.should be_true
41
+ result.should == master
42
+ master.should_not be_new
43
+ master.should have_versions %Q{
44
+ | name | price | created_at | expired_at | valid_from | valid_to | current |
45
+ | Single Standard | 98 | 2009-11-28 | | 2009-11-28 | MAX DATE | true |
46
+ }
47
+ end
48
+ it "allows creating a new version in the past" do
49
+ master = @master_class.new
50
+ master.update_attributes name: "Single Standard", price: 98, valid_from: Date.today-1
51
+ master.should have_versions %Q{
52
+ | name | price | created_at | expired_at | valid_from | valid_to | current |
53
+ | Single Standard | 98 | 2009-11-28 | | 2009-11-27 | MAX DATE | true |
54
+ }
55
+ end
56
+ it "allows creating a new version in the future" do
57
+ master = @master_class.new
58
+ master.update_attributes name: "Single Standard", price: 98, valid_from: Date.today+1
59
+ master.should have_versions %Q{
60
+ | name | price | created_at | expired_at | valid_from | valid_to | current |
61
+ | Single Standard | 98 | 2009-11-28 | | 2009-11-29 | MAX DATE | |
62
+ }
63
+ end
64
+ it "doesn't loose previous version in same-day update" do
65
+ master = @master_class.new
66
+ master.update_attributes name: "Single Standard", price: 98
67
+ master.update_attributes name: "Single Standard", price: 94
68
+ master.should have_versions %Q{
69
+ | name | price | created_at | expired_at | valid_from | valid_to | current |
70
+ | Single Standard | 98 | 2009-11-28 | 2009-11-28 | 2009-11-28 | MAX DATE | |
71
+ | Single Standard | 94 | 2009-11-28 | | 2009-11-28 | MAX DATE | true |
72
+ }
73
+ end
74
+ it "allows partial updating based on current version" do
75
+ master = @master_class.new
76
+ master.update_attributes name: "Single Standard", price: 98
77
+ master.update_attributes price: 94
78
+ master.update_attributes name: "King Size"
79
+ master.should have_versions %Q{
80
+ | name | price | created_at | expired_at | valid_from | valid_to | current |
81
+ | Single Standard | 98 | 2009-11-28 | 2009-11-28 | 2009-11-28 | MAX DATE | |
82
+ | Single Standard | 94 | 2009-11-28 | 2009-11-28 | 2009-11-28 | MAX DATE | |
83
+ | King Size | 94 | 2009-11-28 | | 2009-11-28 | MAX DATE | true |
84
+ }
85
+ end
86
+ it "expires previous version but keep it in history" do
87
+ master = @master_class.new
88
+ master.update_attributes name: "Single Standard", price: 98
89
+ Timecop.freeze Date.today+1
90
+ master.update_attributes price: 94
91
+ master.should have_versions %Q{
92
+ | name | price | created_at | expired_at | valid_from | valid_to | current |
93
+ | Single Standard | 98 | 2009-11-28 | 2009-11-29 | 2009-11-28 | MAX DATE | |
94
+ | Single Standard | 98 | 2009-11-29 | | 2009-11-28 | 2009-11-29 | |
95
+ | Single Standard | 94 | 2009-11-29 | | 2009-11-29 | MAX DATE | true |
96
+ }
97
+ end
98
+ it "doesn't expire no longer valid versions" do
99
+ master = @master_class.new
100
+ master.update_attributes name: "Single Standard", price: 98, valid_to: Date.today+1
101
+ Timecop.freeze Date.today+1
102
+ master.update_attributes(price: 94).should be_false
103
+ master.update_attributes name: "Single Standard", price: 94
104
+ master.should have_versions %Q{
105
+ | name | price | created_at | expired_at | valid_from | valid_to | current |
106
+ | Single Standard | 98 | 2009-11-28 | | 2009-11-28 | 2009-11-29 | |
107
+ | Single Standard | 94 | 2009-11-29 | | 2009-11-29 | MAX DATE | true |
108
+ }
109
+ end
110
+ it "allows shortening validity (SEE COMMENTS FOR IMPROVEMENTS)" do
111
+ master = @master_class.new
112
+ master.update_attributes name: "Single Standard", price: 98
113
+ Timecop.freeze Date.today+1
114
+ master.update_attributes valid_to: Date.today+10
115
+ master.should have_versions %Q{
116
+ | name | price | created_at | expired_at | valid_from | valid_to | current |
117
+ | Single Standard | 98 | 2009-11-28 | 2009-11-29 | 2009-11-28 | MAX DATE | |
118
+ | Single Standard | 98 | 2009-11-29 | | 2009-11-28 | 2009-11-29 | |
119
+ | Single Standard | 98 | 2009-11-29 | | 2009-11-29 | 2009-12-09 | true |
120
+ }
121
+ # would be even better if it could be:
122
+ # | name | price | created_at | expired_at | valid_from | valid_to | current |
123
+ # | Single Standard | 98 | 2009-11-28 | 2009-11-29 | 2009-11-28 | 2009-11-30 | |
124
+ # | Single Standard | 98 | 2009-11-29 | | 2009-11-28 | 2009-12-09 | true |
125
+ end
126
+ it "allows extending validity (SEE COMMENTS FOR IMPROVEMENTS)" do
127
+ master = @master_class.new
128
+ master.update_attributes name: "Single Standard", price: 98, valid_to: Date.today+2
129
+ Timecop.freeze Date.today+1
130
+ master.should have_versions %Q{
131
+ | name | price | created_at | expired_at | valid_from | valid_to | current |
132
+ | Single Standard | 98 | 2009-11-28 | | 2009-11-28 | 2009-11-30 | true |
133
+ }
134
+ master.update_attributes valid_to: nil
135
+ master.should have_versions %Q{
136
+ | name | price | created_at | expired_at | valid_from | valid_to | current |
137
+ | Single Standard | 98 | 2009-11-28 | 2009-11-29 | 2009-11-28 | 2009-11-30 | |
138
+ | Single Standard | 98 | 2009-11-29 | | 2009-11-28 | 2009-11-29 | |
139
+ | Single Standard | 98 | 2009-11-29 | | 2009-11-29 | MAX DATE | true |
140
+ }
141
+ # would be even better if it could be:
142
+ # | name | price | created_at | expired_at | valid_from | valid_to | current |
143
+ # | Single Standard | 98 | 2009-11-28 | 2009-11-29 | 2009-11-28 | 2009-11-30 | |
144
+ # | Single Standard | 98 | 2009-11-29 | | 2009-11-28 | MAX DATE | true |
145
+ end
146
+ it "don't create any new version without change" do
147
+ master = @master_class.new
148
+ master.update_attributes name: "Single Standard", price: 98
149
+ master.update_attributes price: 98
150
+ master.update_attributes name: "Single Standard", price: 98
151
+ master.should have_versions %Q{
152
+ | name | price | created_at | expired_at | valid_from | valid_to | current |
153
+ | Single Standard | 98 | 2009-11-28 | | 2009-11-28 | MAX DATE | true |
154
+ }
155
+ end
156
+ it "change in validity still creates a new version (SEE COMMENTS FOR IMPROVEMENTS)" do
157
+ master = @master_class.new
158
+ master.update_attributes name: "Single Standard", price: 98
159
+ Timecop.freeze Date.today+1
160
+ master.update_attributes price: 98, valid_from: Date.today-2
161
+ master.update_attributes price: 98, valid_from: Date.today+1
162
+ master.should have_versions %Q{
163
+ | name | price | created_at | expired_at | valid_from | valid_to | current |
164
+ | Single Standard | 98 | 2009-11-28 | | 2009-11-28 | MAX DATE | true |
165
+ | Single Standard | 98 | 2009-11-29 | | 2009-11-27 | 2009-11-28 | |
166
+ }
167
+ # would be even better if it could be:
168
+ # | name | price | created_at | expired_at | valid_from | valid_to | current |
169
+ # | Single Standard | 98 | 2009-11-28 | 2009-11-29 | 2009-11-28 | MAX DATE | |
170
+ # | Single Standard | 98 | 2009-11-29 | | 2009-11-27 | MAX DATE | true |
171
+ end
172
+ it "overrides no future versions" do
173
+ master = @master_class.new
174
+ master.update_attributes name: "Single Standard", price: 98, valid_to: Date.today+2
175
+ master.update_attributes name: "Single Standard", price: 94, valid_from: Date.today+2, valid_to: Date.today+4
176
+ master.update_attributes name: "Single Standard", price: 95, valid_from: Date.today+4, valid_to: Date.today+6
177
+ Timecop.freeze Date.today+1
178
+ master.update_attributes name: "King Size", valid_to: nil
179
+ master.should have_versions %Q{
180
+ | name | price | created_at | expired_at | valid_from | valid_to | current |
181
+ | Single Standard | 98 | 2009-11-28 | 2009-11-29 | 2009-11-28 | 2009-11-30 | |
182
+ | Single Standard | 94 | 2009-11-28 | | 2009-11-30 | 2009-12-02 | |
183
+ | Single Standard | 95 | 2009-11-28 | | 2009-12-02 | 2009-12-04 | |
184
+ | Single Standard | 98 | 2009-11-29 | | 2009-11-28 | 2009-11-29 | |
185
+ | King Size | 98 | 2009-11-29 | | 2009-11-29 | 2009-11-30 | true |
186
+ }
187
+ end
188
+ it "overrides multiple future versions" do
189
+ master = @master_class.new
190
+ master.update_attributes name: "Single Standard", price: 98, valid_to: Date.today+2
191
+ master.update_attributes name: "Single Standard", price: 94, valid_from: Date.today+2, valid_to: Date.today+4
192
+ master.update_attributes name: "Single Standard", price: 95, valid_from: Date.today+4, valid_to: Date.today+6
193
+ Timecop.freeze Date.today+1
194
+ master.update_attributes name: "King Size", valid_to: Date.today+4
195
+ master.should have_versions %Q{
196
+ | name | price | created_at | expired_at | valid_from | valid_to | current |
197
+ | Single Standard | 98 | 2009-11-28 | 2009-11-29 | 2009-11-28 | 2009-11-30 | |
198
+ | Single Standard | 94 | 2009-11-28 | 2009-11-29 | 2009-11-30 | 2009-12-02 | |
199
+ | Single Standard | 95 | 2009-11-28 | 2009-11-29 | 2009-12-02 | 2009-12-04 | |
200
+ | Single Standard | 98 | 2009-11-29 | | 2009-11-28 | 2009-11-29 | |
201
+ | Single Standard | 95 | 2009-11-29 | | 2009-12-03 | 2009-12-04 | |
202
+ | King Size | 98 | 2009-11-29 | | 2009-11-29 | 2009-12-03 | true |
203
+ }
204
+ end
205
+ it "overrides all future versions" do
206
+ master = @master_class.new
207
+ master.update_attributes name: "Single Standard", price: 98, valid_to: Date.today+2
208
+ master.update_attributes name: "Single Standard", price: 94, valid_from: Date.today+2, valid_to: Date.today+4
209
+ master.update_attributes name: "Single Standard", price: 95, valid_from: Date.today+4, valid_to: Date.today+6
210
+ Timecop.freeze Date.today+1
211
+ master.update_attributes name: "King Size", valid_to: Time.utc(9999)
212
+ master.should have_versions %Q{
213
+ | name | price | created_at | expired_at | valid_from | valid_to | current |
214
+ | Single Standard | 98 | 2009-11-28 | 2009-11-29 | 2009-11-28 | 2009-11-30 | |
215
+ | Single Standard | 94 | 2009-11-28 | 2009-11-29 | 2009-11-30 | 2009-12-02 | |
216
+ | Single Standard | 95 | 2009-11-28 | 2009-11-29 | 2009-12-02 | 2009-12-04 | |
217
+ | Single Standard | 98 | 2009-11-29 | | 2009-11-28 | 2009-11-29 | |
218
+ | King Size | 98 | 2009-11-29 | | 2009-11-29 | MAX DATE | true |
219
+ }
220
+ end
221
+ it "allows deleting current version" do
222
+ master = @master_class.new
223
+ master.update_attributes name: "Single Standard", price: 92, valid_from: Date.today-2, valid_to: Date.today
224
+ master.update_attributes name: "Single Standard", price: 98
225
+ master.update_attributes name: "Single Standard", price: 94, valid_from: Date.today+2
226
+ Timecop.freeze Date.today+1
227
+ master.current_version.destroy.should be_true
228
+ master.should have_versions %Q{
229
+ | name | price | created_at | expired_at | valid_from | valid_to | current |
230
+ | Single Standard | 92 | 2009-11-28 | | 2009-11-26 | 2009-11-28 | |
231
+ | Single Standard | 98 | 2009-11-28 | 2009-11-28 | 2009-11-28 | MAX DATE | |
232
+ | Single Standard | 98 | 2009-11-28 | 2009-11-29 | 2009-11-28 | 2009-11-30 | |
233
+ | Single Standard | 94 | 2009-11-28 | | 2009-11-30 | MAX DATE | |
234
+ | Single Standard | 98 | 2009-11-29 | | 2009-11-28 | 2009-11-29 | |
235
+ }
236
+ end
237
+ it "allows deleting current version to restore the previous one" do
238
+ master = @master_class.new
239
+ master.update_attributes name: "Single Standard", price: 92, valid_from: Date.today-2, valid_to: Date.today
240
+ master.update_attributes name: "Single Standard", price: 98
241
+ master.update_attributes name: "Single Standard", price: 94, valid_from: Date.today+2
242
+ Timecop.freeze Date.today+1
243
+ master.current_version.destroy(expand_previous_version: true).should be_true
244
+ master.should have_versions %Q{
245
+ | name | price | created_at | expired_at | valid_from | valid_to | current |
246
+ | Single Standard | 92 | 2009-11-28 | | 2009-11-26 | 2009-11-28 | |
247
+ | Single Standard | 98 | 2009-11-28 | 2009-11-28 | 2009-11-28 | MAX DATE | |
248
+ | Single Standard | 98 | 2009-11-28 | 2009-11-29 | 2009-11-28 | 2009-11-30 | |
249
+ | Single Standard | 94 | 2009-11-28 | | 2009-11-30 | MAX DATE | |
250
+ | Single Standard | 92 | 2009-11-29 | | 2009-11-29 | 2009-11-30 | true |
251
+ | Single Standard | 98 | 2009-11-29 | | 2009-11-28 | 2009-11-29 | |
252
+ }
253
+ end
254
+ it "allows deleting a future version" do
255
+ master = @master_class.new
256
+ master.update_attributes name: "Single Standard", price: 98
257
+ master.update_attributes name: "Single Standard", price: 94, valid_from: Date.today+2
258
+ Timecop.freeze Date.today+1
259
+ master.versions.last.destroy.should be_true
260
+ master.should have_versions %Q{
261
+ | name | price | created_at | expired_at | valid_from | valid_to | current |
262
+ | Single Standard | 98 | 2009-11-28 | 2009-11-28 | 2009-11-28 | MAX DATE | |
263
+ | Single Standard | 98 | 2009-11-28 | 2009-11-29 | 2009-11-28 | 2009-11-30 | |
264
+ | Single Standard | 94 | 2009-11-28 | 2009-11-29 | 2009-11-30 | MAX DATE | |
265
+ | Single Standard | 98 | 2009-11-29 | | 2009-11-28 | MAX DATE | true |
266
+ }
267
+ end
268
+ it "allows deleting a future version without expanding the current one" do
269
+ master = @master_class.new
270
+ master.update_attributes name: "Single Standard", price: 98
271
+ master.update_attributes name: "Single Standard", price: 94, valid_from: Date.today+2
272
+ Timecop.freeze Date.today+1
273
+ master.versions.last.destroy(expand_previous_version: false).should be_true
274
+ master.should have_versions %Q{
275
+ | name | price | created_at | expired_at | valid_from | valid_to | current |
276
+ | Single Standard | 98 | 2009-11-28 | 2009-11-28 | 2009-11-28 | MAX DATE | |
277
+ | Single Standard | 98 | 2009-11-28 | | 2009-11-28 | 2009-11-30 | true |
278
+ | Single Standard | 94 | 2009-11-28 | 2009-11-29 | 2009-11-30 | MAX DATE | |
279
+ }
280
+ end
281
+ it "allows deleting all versions" do
282
+ master = @master_class.new
283
+ master.update_attributes name: "Single Standard", price: 98
284
+ master.update_attributes name: "Single Standard", price: 94, valid_from: Date.today+2
285
+ Timecop.freeze Date.today+1
286
+ master.destroy.should be_true
287
+ master.should have_versions %Q{
288
+ | name | price | created_at | expired_at | valid_from | valid_to | current |
289
+ | Single Standard | 98 | 2009-11-28 | 2009-11-28 | 2009-11-28 | MAX DATE | |
290
+ | Single Standard | 98 | 2009-11-28 | 2009-11-29 | 2009-11-28 | 2009-11-30 | |
291
+ | Single Standard | 94 | 2009-11-28 | 2009-11-29 | 2009-11-30 | MAX DATE | |
292
+ }
293
+ end
294
+ it "allows simultaneous updates without information loss" do
295
+ master = @master_class.new
296
+ master.update_attributes name: "Single Standard", price: 98
297
+ Timecop.freeze Date.today+1
298
+ master2 = @master_class.find id: master.id
299
+ master.update_attributes name: "Single Standard", price: 94
300
+ master2.update_attributes name: "Single Standard", price: 95
301
+ master.should have_versions %Q{
302
+ | name | price | created_at | expired_at | valid_from | valid_to | current |
303
+ | Single Standard | 98 | 2009-11-28 | 2009-11-29 | 2009-11-28 | MAX DATE | |
304
+ | Single Standard | 98 | 2009-11-29 | | 2009-11-28 | 2009-11-29 | |
305
+ | Single Standard | 94 | 2009-11-29 | 2009-11-29 | 2009-11-29 | MAX DATE | |
306
+ | Single Standard | 95 | 2009-11-29 | | 2009-11-29 | MAX DATE | true |
307
+ }
308
+ end
309
+ it "allows simultaneous cumulative updates" do
310
+ master = @master_class.new
311
+ master.update_attributes name: "Single Standard", price: 98
312
+ Timecop.freeze Date.today+1
313
+ master2 = @master_class.find id: master.id
314
+ master.update_attributes price: 94
315
+ master2.update_attributes name: "King Size"
316
+ master.should have_versions %Q{
317
+ | name | price | created_at | expired_at | valid_from | valid_to | current |
318
+ | Single Standard | 98 | 2009-11-28 | 2009-11-29 | 2009-11-28 | MAX DATE | |
319
+ | Single Standard | 98 | 2009-11-29 | | 2009-11-28 | 2009-11-29 | |
320
+ | Single Standard | 94 | 2009-11-29 | 2009-11-29 | 2009-11-29 | MAX DATE | |
321
+ | King Size | 94 | 2009-11-29 | | 2009-11-29 | MAX DATE | true |
322
+ }
323
+ end
324
+ it "can expire invalid versions" do
325
+ master = @master_class.new.update_attributes name: "Single Standard", price: 98
326
+ master.current_version.price = nil
327
+ master.current_version.should_not be_valid
328
+ master.current_version.save validate: false
329
+ Timecop.freeze Date.today+1
330
+ master.update_attributes price: 94
331
+ master.should have_versions %Q{
332
+ | name | price | created_at | expired_at | valid_from | valid_to | current |
333
+ | Single Standard | | 2009-11-28 | 2009-11-29 | 2009-11-28 | MAX DATE | |
334
+ | Single Standard | | 2009-11-29 | | 2009-11-28 | 2009-11-29 | |
335
+ | Single Standard | 94 | 2009-11-29 | | 2009-11-29 | MAX DATE | true |
336
+ }
337
+ end
338
+ it "can propagate changes to future versions per column" do
339
+ propagate_per_column = @master_class.propagate_per_column
340
+ begin
341
+ @master_class.instance_variable_set :@propagate_per_column, true
342
+ master = @master_class.new
343
+ master.update_attributes name: "Single Standard", price: 12, length: nil, width: 1
344
+ initial_today = Date.today
345
+ Timecop.freeze initial_today+1 do
346
+ master.update_attributes valid_from: initial_today+4, name: "King Size", price: 15, length: 2, width: 2
347
+ end
348
+ Timecop.freeze initial_today+2 do
349
+ master.update_attributes valid_from: initial_today+3, length: 1, width: 1
350
+ end
351
+ Timecop.freeze initial_today+3 do
352
+ master.update_attributes valid_from: initial_today+2, length: 3, width: 4
353
+ end
354
+ master.should have_versions %Q{
355
+ | name | price | length | width | created_at | expired_at | valid_from | valid_to | current |
356
+ | Single Standard | 12 | | 1 | 2009-11-28 | 2009-11-29 | 2009-11-28 | MAX DATE | true |
357
+ | Single Standard | 12 | | 1 | 2009-11-29 | 2009-11-30 | 2009-11-28 | 2009-12-02 | |
358
+ | King Size | 15 | 2 | 2 | 2009-11-29 | | 2009-12-02 | MAX DATE | |
359
+ | Single Standard | 12 | | 1 | 2009-11-30 | 2009-12-01 | 2009-11-28 | 2009-12-01 | |
360
+ | Single Standard | 12 | 1 | 1 | 2009-11-30 | 2009-12-01 | 2009-12-01 | 2009-12-02 | |
361
+ | Single Standard | 12 | | 1 | 2009-12-01 | | 2009-11-28 | 2009-11-30 | |
362
+ | Single Standard | 12 | 3 | 4 | 2009-12-01 | | 2009-11-30 | 2009-12-01 | |
363
+ | Single Standard | 12 | 1 | 4 | 2009-12-01 | | 2009-12-01 | 2009-12-02 | |
364
+ }
365
+ ensure
366
+ @master_class.instance_variable_set :@propagate_per_column, propagate_per_column
367
+ end
368
+ end
369
+ it "allows eager loading with conditions on current version" do
370
+ master = @master_class.new
371
+ master.update_attributes name: "Single Standard", price: 98
372
+ master.update_attributes name: "Single Standard", price: 94, valid_from: Date.today+2
373
+ @master_class.eager_graph(:current_version).where("rooms_current_version.id IS NOT NULL").first.should be
374
+ Timecop.freeze Date.today+1
375
+ master.destroy
376
+ @master_class.eager_graph(:current_version).where("rooms_current_version.id IS NOT NULL").first.should be_nil
377
+ end
378
+ it "allows loading masters with a current version" do
379
+ master_destroyed = @master_class.new
380
+ master_destroyed.update_attributes name: "Single Standard", price: 98
381
+ master_destroyed.destroy
382
+ master_with_current = @master_class.new
383
+ master_with_current.update_attributes name: "Single Standard", price: 94
384
+ master_with_future = @master_class.new
385
+ master_with_future.update_attributes name: "Single Standard", price: 94, valid_from: Date.today+2
386
+ @master_class.with_current_version.all.should have(1).item
387
+ end
388
+ it "gets pending or current version attributes" do
389
+ master = @master_class.new
390
+ master.attributes.should == {}
391
+ master.pending_version.should be_nil
392
+ master.current_version.should be_nil
393
+ master.name.should be_nil
394
+
395
+ master.pending_or_current_version.name.should be_nil
396
+ master.update_attributes name: "Single Standard", price: 98
397
+ master.attributes[:name].should == "Single Standard"
398
+ master.pending_version.should be_nil
399
+ master.pending_or_current_version.name.should == "Single Standard"
400
+ master.name.should == "Single Standard"
401
+
402
+ master.attributes = {name: "King Size"}
403
+ master.attributes[:name].should == "King Size"
404
+ master.pending_version.should be
405
+ master.pending_or_current_version.name.should == "King Size"
406
+ master.name.should == "King Size"
407
+ end
408
+ it "allows to go back in time" do
409
+ master = @master_class.new
410
+ master.update_attributes name: "Single Standard", price: 98, valid_to: Date.today+1
411
+ master.update_attributes name: "Single Standard", price: 95, valid_from: Date.today+1, valid_to: Date.today+2
412
+ master.update_attributes name: "Single Standard", price: 93, valid_from: Date.today+2, valid_to: Date.today+3
413
+ master.update_attributes name: "Single Standard", price: 91, valid_from: Date.today+3
414
+ Timecop.freeze Date.today+1
415
+ master.update_attributes price: 94
416
+ master.update_attributes price: 96, valid_from: Date.today+2
417
+ master.should have_versions %Q{
418
+ | name | price | created_at | expired_at | valid_from | valid_to | current |
419
+ | Single Standard | 98 | 2009-11-28 | | 2009-11-28 | 2009-11-29 | |
420
+ | Single Standard | 95 | 2009-11-28 | 2009-11-29 | 2009-11-29 | 2009-11-30 | |
421
+ | Single Standard | 93 | 2009-11-28 | | 2009-11-30 | 2009-12-01 | |
422
+ | Single Standard | 91 | 2009-11-28 | 2009-11-29 | 2009-12-01 | MAX DATE | |
423
+ | Single Standard | 94 | 2009-11-29 | | 2009-11-29 | 2009-11-30 | true |
424
+ | Single Standard | 96 | 2009-11-29 | | 2009-12-01 | MAX DATE | |
425
+ }
426
+ master.current_version.price.should == 94
427
+ Sequel::Plugins::Bitemporal.at(Date.today-1) do
428
+ master.current_version(true).price.should == 98
429
+ end
430
+ Sequel::Plugins::Bitemporal.at(Date.today+1) do
431
+ master.current_version(true).price.should == 93
432
+ end
433
+ Sequel::Plugins::Bitemporal.at(Date.today+2) do
434
+ master.current_version(true).price.should == 96
435
+ end
436
+ Sequel::Plugins::Bitemporal.as_we_knew_it(Date.today-1) do
437
+ master.current_version(true).price.should == 95
438
+ master.current_version.should be_current
439
+ Sequel::Plugins::Bitemporal.at(Date.today-1) do
440
+ master.current_version(true).price.should == 98
441
+ end
442
+ Sequel::Plugins::Bitemporal.at(Date.today+1) do
443
+ master.current_version(true).price.should == 93
444
+ end
445
+ Sequel::Plugins::Bitemporal.at(Date.today+2) do
446
+ master.current_version(true).price.should == 91
447
+ end
448
+ end
449
+ end
450
+ it "correctly reset time if failure when going back in time" do
451
+ before = Sequel::Plugins::Bitemporal.now
452
+ lambda do
453
+ Sequel::Plugins::Bitemporal.at(Date.today+2) do
454
+ raise StandardError, "error during back in time"
455
+ end
456
+ end.should raise_error StandardError
457
+ Sequel::Plugins::Bitemporal.now.should == before
458
+ lambda do
459
+ Sequel::Plugins::Bitemporal.as_we_knew_it(Date.today+2) do
460
+ raise StandardError, "error during back in time"
461
+ end
462
+ end.should raise_error StandardError
463
+ Sequel::Plugins::Bitemporal.now.should == before
464
+ end
465
+ it "allows eager loading with conditions on current or future versions" do
466
+ master = @master_class.new
467
+ master.update_attributes name: "Single Standard", price: 98
468
+ Timecop.freeze Date.today+1
469
+ master.update_attributes name: "Single Standard", price: 99
470
+ master.update_attributes name: "Single Standard", price: 94, valid_from: Date.today+2
471
+ res = @master_class.eager_graph(:current_or_future_versions).where(Sequel.negate(current_or_future_versions__id: nil) & {price: 99}).all.first
472
+ res.should be
473
+ res.current_or_future_versions.should have(1).item
474
+ res.current_or_future_versions.first.price.should == 99
475
+ res = @master_class.eager_graph(:current_or_future_versions).where(Sequel.negate(current_or_future_versions__id: nil) & {price: 94}).all.first
476
+ res.should be
477
+ res.current_or_future_versions.should have(1).item
478
+ res.current_or_future_versions.first.price.should == 94
479
+ Timecop.freeze Date.today+1
480
+ master.destroy
481
+ @master_class.eager_graph(:current_or_future_versions).where(Sequel.negate(current_or_future_versions__id: nil)).all.should be_empty
482
+ end
483
+ it "allows loading masters with current or future versions" do
484
+ master_destroyed = @master_class.new
485
+ master_destroyed.update_attributes name: "Single Standard", price: 98
486
+ master_destroyed.destroy
487
+ master_with_current = @master_class.new
488
+ master_with_current.update_attributes name: "Single Standard", price: 94
489
+ master_with_future = @master_class.new
490
+ master_with_future.update_attributes name: "Single Standard", price: 94, valid_from: Date.today+2
491
+ @master_class.with_current_or_future_versions.all.should have(2).item
492
+ end
493
+ it "delegates attributes from master to pending_or_current_version" do
494
+ master = @master_class.new
495
+ master.name.should be_nil
496
+ master.update_attributes name: "Single Standard", price: 98
497
+ master.name.should == "Single Standard"
498
+ master.attributes = {name: "King Size"}
499
+ master.name.should == "King Size"
500
+ end
501
+ it "avoids delegation with option delegate: false" do
502
+ closure = @version_class
503
+ without_delegation_class = Class.new Sequel::Model do
504
+ set_dataset :rooms
505
+ plugin :bitemporal, version_class: closure, delegate: false
506
+ end
507
+ master = without_delegation_class.new
508
+ expect{ master.name }.to raise_error NoMethodError
509
+ end
510
+ it "get current_version association name from class name" do
511
+ class MyNameVersion < Sequel::Model
512
+ set_dataset :room_versions
513
+ end
514
+ class MyName < Sequel::Model
515
+ set_dataset :rooms
516
+ plugin :bitemporal, version_class: MyNameVersion
517
+ end
518
+ expect do
519
+ MyName.eager_graph(:current_version).where("my_name_current_version.id IS NOT NULL").first
520
+ end.not_to raise_error
521
+ Object.send :remove_const, :MyName
522
+ Object.send :remove_const, :MyNameVersion
523
+ end
524
+ it "can update master and current version at the same time" do
525
+ master = @master_class.new.update_attributes name: "Single Standard", price: 98
526
+ master.disabled = true
527
+ master.update_attributes price: 94
528
+ master.reload.disabled.should be_true
529
+ end
530
+ it "uses current version for partial_update even if valid_from is specified" do
531
+ master = @master_class.new
532
+ master.update_attributes name: "Single Standard", price: 98, valid_from: Date.today-2, valid_to: Date.today
533
+ master.update_attributes name: "Single Standard", price: 94
534
+ master.should have_versions %Q{
535
+ | name | price | created_at | expired_at | valid_from | valid_to | current |
536
+ | Single Standard | 98 | 2009-11-28 | | 2009-11-26 | 2009-11-28 | |
537
+ | Single Standard | 94 | 2009-11-28 | | 2009-11-28 | MAX DATE | true |
538
+ }
539
+ master.update_attributes name: "King Size", valid_from: Date.today-2
540
+ master.should have_versions %Q{
541
+ | name | price | created_at | expired_at | valid_from | valid_to | current |
542
+ | Single Standard | 98 | 2009-11-28 | 2009-11-28 | 2009-11-26 | 2009-11-28 | |
543
+ | Single Standard | 94 | 2009-11-28 | | 2009-11-28 | MAX DATE | true |
544
+ | King Size | 94 | 2009-11-28 | | 2009-11-26 | 2009-11-28 | |
545
+ }
546
+ end
547
+ it "as_we_knew_it also allows creating and deleting at that time" do
548
+ master = @master_class.new
549
+ master.update_attributes name: "Single Standard", price: 98
550
+ Sequel::Plugins::Bitemporal.as_we_knew_it(Date.today+1) do
551
+ master.update_attributes name: "King Size"
552
+ end
553
+ master.should have_versions %Q{
554
+ | name | price | created_at | expired_at | valid_from | valid_to | current |
555
+ | Single Standard | 98 | 2009-11-28 | 2009-11-29 | 2009-11-28 | MAX DATE | true |
556
+ | King Size | 98 | 2009-11-29 | | 2009-11-28 | MAX DATE | |
557
+ }
558
+ Sequel::Plugins::Bitemporal.as_we_knew_it(Date.today+2) do
559
+ master.current_version(true).destroy
560
+ end
561
+ master.should have_versions %Q{
562
+ | name | price | created_at | expired_at | valid_from | valid_to | current |
563
+ | Single Standard | 98 | 2009-11-28 | 2009-11-29 | 2009-11-28 | MAX DATE | true |
564
+ | King Size | 98 | 2009-11-29 | 2009-11-30 | 2009-11-28 | MAX DATE | |
565
+ | King Size | 98 | 2009-11-30 | | 2009-11-28 | 2009-11-28 | |
566
+ }
567
+ end
568
+ it "combines as_we_knew_it and at to set valid_from" do
569
+ master = @master_class.new
570
+ master.update_attributes name: "Single Standard", price: 98, valid_from: Date.today-2, valid_to: Date.today
571
+ master.update_attributes name: "Single Standard", price: 94
572
+ master.should have_versions %Q{
573
+ | name | price | created_at | expired_at | valid_from | valid_to | current |
574
+ | Single Standard | 98 | 2009-11-28 | | 2009-11-26 | 2009-11-28 | |
575
+ | Single Standard | 94 | 2009-11-28 | | 2009-11-28 | MAX DATE | true |
576
+ }
577
+ Sequel::Plugins::Bitemporal.as_we_knew_it(Date.today+1) do
578
+ Sequel::Plugins::Bitemporal.at(Date.today-1) do
579
+ master.update_attributes name: "King Size"
580
+ end
581
+ end
582
+ master.should have_versions %Q{
583
+ | name | price | created_at | expired_at | valid_from | valid_to | current |
584
+ | Single Standard | 98 | 2009-11-28 | 2009-11-29 | 2009-11-26 | 2009-11-28 | |
585
+ | Single Standard | 94 | 2009-11-28 | | 2009-11-28 | MAX DATE | true |
586
+ | Single Standard | 98 | 2009-11-29 | | 2009-11-26 | 2009-11-27 | |
587
+ | King Size | 98 | 2009-11-29 | | 2009-11-27 | 2009-11-28 | |
588
+ }
589
+ end
590
+ context "#deleted?" do
591
+ subject{ @master_class.new }
592
+ it "is false unless persisted" do
593
+ subject.should_not be_deleted
594
+ end
595
+ it "is false when persisted with a current version" do
596
+ subject.update_attributes(name: "Single Standard", price: 94).should_not be_deleted
597
+ end
598
+ it "is true when persisted without a current version" do
599
+ subject.update_attributes(name: "Single Standard", price: 94, valid_from: Date.today+1).should be_deleted
600
+ end
601
+ end
602
+ context "#last_version" do
603
+ subject{ @master_class.new }
604
+ it "is nil unless persisted" do
605
+ subject.last_version.should be_nil
606
+ end
607
+ it "is current version when persisted with a current version" do
608
+ subject.update_attributes name: "Single Standard", price: 94
609
+ subject.last_version.should == subject.current_version
610
+ end
611
+ it "is nil with future version but no current version" do
612
+ subject.update_attributes name: "Single Standard", price: 94, valid_from: Date.today+1
613
+ subject.last_version.should be_nil
614
+ end
615
+ it "is last version with previous version but no current version" do
616
+ subject.update_attributes name: "Single Standard", price: 94, valid_from: Date.today-2, valid_to: Date.today-1
617
+ subject.current_version.should be_nil
618
+ subject.last_version.should == subject.versions.last
619
+ end
620
+ end
621
+ context "#restore" do
622
+ subject{ @master_class.new }
623
+ it "make last version current" do
624
+ subject.update_attributes name: "Single Standard", price: 94, valid_from: Date.today-2, valid_to: Date.today-1
625
+ subject.restore
626
+ subject.current_version.should == subject.last_version
627
+ end
628
+ it "can add additional attributes to apply" do
629
+ subject.update_attributes name: "Single Standard", price: 94, valid_from: Date.today-2, valid_to: Date.today-1
630
+ subject.restore name: "New Standard"
631
+ subject.current_version.name.should == "New Standard"
632
+ subject.current_version.price.should == 94
633
+ end
634
+ end
635
+ end
636
+
637
+ describe "Sequel::Plugins::Bitemporal", "with audit and ranges" do
638
+ before :all do
639
+ @audit_class = Class.new do
640
+ def self.audit(*args); end
641
+ end
642
+ db_setup audit_class: @audit_class, ranges: true
643
+ end
644
+ before do
645
+ Timecop.freeze 2009, 11, 28
646
+ end
647
+ after do
648
+ Timecop.return
649
+ end
650
+ let(:author){ mock :author, audit_kind: "user" }
651
+ it "generates a new audit on creation" do
652
+ master = @master_class.new
653
+ master.should_receive(:updated_by).and_return author
654
+ @audit_class.should_receive(:audit).with(
655
+ master,
656
+ {},
657
+ hash_including({name: "Single Standard", price: 98}),
658
+ Date.today,
659
+ author
660
+ )
661
+ master.update_attributes name: "Single Standard", price: 98
662
+ end
663
+ it "generates a new audit on full update" do
664
+ master = @master_class.new
665
+ master.should_receive(:updated_by).twice.and_return author
666
+ master.update_attributes name: "Single Standard", price: 98
667
+ @audit_class.should_receive(:audit).with(
668
+ master,
669
+ hash_including({name: "Single Standard", price: 98}),
670
+ hash_including({name: "King size", price: 98}),
671
+ Date.today,
672
+ author
673
+ )
674
+ master.update_attributes name: "King size", price: 98
675
+ end
676
+ it "generates a new audit on partial update" do
677
+ master = @master_class.new
678
+ master.should_receive(:updated_by).twice.and_return author
679
+ master.update_attributes name: "Single Standard", price: 98
680
+ @audit_class.should_receive(:audit).with(
681
+ master,
682
+ hash_including({name: "Single Standard", price: 98}),
683
+ hash_including({name: "King size", price: 98}),
684
+ Date.today,
685
+ author
686
+ )
687
+ master.update_attributes name: "King size", price: 98
688
+ end
689
+ it "generate a new audit for each future version update when propagating changes" do
690
+ propagate_per_column = @master_class.propagate_per_column
691
+ begin
692
+ @master_class.instance_variable_set :@propagate_per_column, true
693
+ master = @master_class.new
694
+ master.should_receive(:updated_by).exactly(8).times.and_return author
695
+
696
+ master.update_attributes name: "Single Standard", price: 12, length: nil, width: 1
697
+ initial_today = Date.today
698
+ Timecop.freeze initial_today+1 do
699
+ Sequel::Plugins::Bitemporal.at initial_today+4 do
700
+ master.update_attributes valid_from: initial_today+4, name: "King Size", price: 15, length: 2, width: 2
701
+ end
702
+ end
703
+ Timecop.freeze initial_today+2 do
704
+ Sequel::Plugins::Bitemporal.at initial_today+3 do
705
+ master.update_attributes valid_from: initial_today+3, length: 1, width: 1
706
+ end
707
+ end
708
+ @audit_class.should_receive(:audit).with(
709
+ master,
710
+ hash_including({name: "Single Standard", price: 12, length: nil, width: 1}),
711
+ hash_including({name: "Single Standard", price: 12, length: 3, width: 4}),
712
+ initial_today+2,
713
+ author
714
+ )
715
+ @audit_class.should_receive(:audit).with(
716
+ master,
717
+ hash_including({name: "Single Standard", price: 12, length: 1, width: 1}),
718
+ hash_including({name: "Single Standard", price: 12, length: 1, width: 4}),
719
+ initial_today+3,
720
+ author
721
+ )
722
+ Timecop.freeze initial_today+3 do
723
+ Sequel::Plugins::Bitemporal.at initial_today+2 do
724
+ master.update_attributes valid_from: initial_today+2, length: 3, width: 4
725
+ end
726
+ end
727
+ ensure
728
+ @master_class.instance_variable_set :@propagate_per_column, propagate_per_column
729
+ end
730
+ end
731
+ end
732
+ describe "Sequel::Plugins::Bitemporal", "with audit, ranges, specifying how to get the author" do
733
+ before :all do
734
+ @audit_class = Class.new do
735
+ def self.audit(*args); end
736
+ end
737
+ db_setup audit_class: @audit_class, audit_updated_by_method: :author, ranges: true
738
+ end
739
+ let(:author){ mock :author, audit_kind: "user" }
740
+ before do
741
+ Timecop.freeze 2009, 11, 28
742
+ end
743
+ after do
744
+ Timecop.return
745
+ end
746
+ it "generates a new audit on creation" do
747
+ master = @master_class.new
748
+ master.should_receive(:author).and_return author
749
+ @audit_class.should_receive(:audit).with(
750
+ master,
751
+ {},
752
+ hash_including({name: "Single Standard", price: 98}),
753
+ Date.today,
754
+ author
755
+ )
756
+ master.update_attributes name: "Single Standard", price: 98
757
+ end
758
+ it "generates a new audit on full update" do
759
+ master = @master_class.new
760
+ master.should_receive(:author).twice.and_return author
761
+ master.update_attributes name: "Single Standard", price: 98
762
+ @audit_class.should_receive(:audit).with(
763
+ master,
764
+ hash_including({name: "Single Standard", price: 98}),
765
+ hash_including({name: "King size", price: 98}),
766
+ Date.today,
767
+ author
768
+ )
769
+ master.update_attributes name: "King size", price: 98
770
+ end
771
+ it "generates a new audit on partial update" do
772
+ master = @master_class.new
773
+ master.should_receive(:author).twice.and_return author
774
+ master.update_attributes name: "Single Standard", price: 98
775
+ @audit_class.should_receive(:audit).with(
776
+ master,
777
+ hash_including({name: "Single Standard", price: 98}),
778
+ hash_including({name: "King size", price: 98}),
779
+ Date.today,
780
+ author
781
+ )
782
+ master.update_attributes name: "King size", price: 98
783
+ end
784
+ end
785
+ end