sequel_bitemporal 0.6.11 → 0.6.12
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.
- data/.travis.yml +3 -0
- data/lib/sequel/plugins/bitemporal.rb +134 -12
- data/sequel_bitemporal.gemspec +2 -1
- data/spec/bitemporal_date_spec.rb +3 -7
- data/spec/bitemporal_date_with_range_spec.rb +785 -0
- data/spec/bitemporal_serialization_spec.rb +0 -1
- data/spec/bitemporal_time_spec.rb +3 -6
- data/spec/bitemporal_time_with_range_spec.rb +496 -0
- data/spec/spec_helper.rb +22 -1
- data/spec/support/db.rb +11 -7
- metadata +52 -14
- checksums.yaml +0 -15
@@ -2,7 +2,6 @@ require "spec_helper"
|
|
2
2
|
|
3
3
|
describe "Sequel::Plugins::Bitemporal" do
|
4
4
|
let(:hour){ 3600 }
|
5
|
-
include DbHelpers
|
6
5
|
before :all do
|
7
6
|
db_setup use_time: true
|
8
7
|
end
|
@@ -359,17 +358,17 @@ describe "Sequel::Plugins::Bitemporal" do
|
|
359
358
|
Timecop.freeze Time.now+1*hour
|
360
359
|
master.update_attributes name: "Single Standard", price: 99
|
361
360
|
master.update_attributes name: "Single Standard", price: 94, valid_from: Time.now+2*hour
|
362
|
-
res = @master_class.eager_graph(:current_or_future_versions).where(
|
361
|
+
res = @master_class.eager_graph(:current_or_future_versions).where(Sequel.negate(current_or_future_versions__id: nil) & {price: 99}).all.first
|
363
362
|
res.should be
|
364
363
|
res.current_or_future_versions.should have(1).item
|
365
364
|
res.current_or_future_versions.first.price.should == 99
|
366
|
-
res = @master_class.eager_graph(:current_or_future_versions).where(
|
365
|
+
res = @master_class.eager_graph(:current_or_future_versions).where(Sequel.negate(current_or_future_versions__id: nil) & {price: 94}).all.first
|
367
366
|
res.should be
|
368
367
|
res.current_or_future_versions.should have(1).item
|
369
368
|
res.current_or_future_versions.first.price.should == 94
|
370
369
|
Timecop.freeze Time.now+1*hour
|
371
370
|
master.destroy
|
372
|
-
@master_class.eager_graph(:current_or_future_versions).where(
|
371
|
+
@master_class.eager_graph(:current_or_future_versions).where(Sequel.negate(current_or_future_versions__id: nil)).all.should be_empty
|
373
372
|
end
|
374
373
|
it "allows loading masters with current or future versions" do
|
375
374
|
master_destroyed = @master_class.new
|
@@ -383,7 +382,6 @@ describe "Sequel::Plugins::Bitemporal" do
|
|
383
382
|
end
|
384
383
|
end
|
385
384
|
describe "Sequel::Plugins::Bitemporal", "with audit" do
|
386
|
-
include DbHelpers
|
387
385
|
before :all do
|
388
386
|
@audit_class = Class.new
|
389
387
|
db_setup use_time: true, audit_class: @audit_class
|
@@ -438,7 +436,6 @@ describe "Sequel::Plugins::Bitemporal", "with audit" do
|
|
438
436
|
end
|
439
437
|
|
440
438
|
describe "Sequel::Plugins::Bitemporal", "with audit, specifying how to get the author" do
|
441
|
-
include DbHelpers
|
442
439
|
before :all do
|
443
440
|
@audit_class = Class.new
|
444
441
|
db_setup use_time: true, audit_class: @audit_class, audit_updated_by_method: :author
|
@@ -0,0 +1,496 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
if DbHelpers.pg?
|
3
|
+
describe "Sequel::Plugins::Bitemporal" do
|
4
|
+
let(:hour){ 3600 }
|
5
|
+
before :all do
|
6
|
+
db_setup use_time: true, ranges: true
|
7
|
+
end
|
8
|
+
before do
|
9
|
+
Timecop.freeze 2009, 11, 28, 10
|
10
|
+
end
|
11
|
+
after do
|
12
|
+
Timecop.return
|
13
|
+
end
|
14
|
+
it "checks version class is given" do
|
15
|
+
lambda{
|
16
|
+
@version_class.plugin :bitemporal
|
17
|
+
}.should raise_error Sequel::Error, "please specify version class to use for bitemporal plugin"
|
18
|
+
end
|
19
|
+
it "checks required columns are present" do
|
20
|
+
lambda{
|
21
|
+
@version_class.plugin :bitemporal, :version_class => @master_class
|
22
|
+
}.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"
|
23
|
+
end
|
24
|
+
it "propagates errors from version to master" do
|
25
|
+
master = @master_class.new
|
26
|
+
master.should be_valid
|
27
|
+
master.attributes = {name: "Single Standard"}
|
28
|
+
master.should_not be_valid
|
29
|
+
master.errors.should == {price: ["is required"]}
|
30
|
+
end
|
31
|
+
it "#update_attributes returns false instead of raising errors" do
|
32
|
+
master = @master_class.new
|
33
|
+
master.update_attributes(name: "Single Standard").should be_false
|
34
|
+
master.should be_new
|
35
|
+
master.errors.should == {price: ["is required"]}
|
36
|
+
master.update_attributes(price: 98).should be_true
|
37
|
+
end
|
38
|
+
it "allows creating a master and its first version in one step" do
|
39
|
+
master = @master_class.new
|
40
|
+
master.update_attributes(name: "Single Standard", price: 98).should be_true
|
41
|
+
master.should_not be_new
|
42
|
+
master.should have_versions %Q{
|
43
|
+
| name | price | created_at | expired_at | valid_from | valid_to | current |
|
44
|
+
| Single Standard | 98 | 2009-11-28 10:00:00 +0000 | | 2009-11-28 10:00:00 +0000 | MAX TIME | true |
|
45
|
+
}
|
46
|
+
end
|
47
|
+
it "allows creating a new version in the past" do
|
48
|
+
master = @master_class.new
|
49
|
+
master.update_attributes name: "Single Standard", price: 98, valid_from: Time.now-hour
|
50
|
+
master.should have_versions %Q{
|
51
|
+
| name | price | created_at | expired_at | valid_from | valid_to | current |
|
52
|
+
| Single Standard | 98 | 2009-11-28 10:00:00 +0000 | | 2009-11-28 09:00:00 +0000 | MAX TIME | true |
|
53
|
+
}
|
54
|
+
end
|
55
|
+
it "allows creating a new version in the future" do
|
56
|
+
master = @master_class.new
|
57
|
+
master.update_attributes name: "Single Standard", price: 98, valid_from: Time.now+hour
|
58
|
+
master.should have_versions %Q{
|
59
|
+
| name | price | created_at | expired_at | valid_from | valid_to | current |
|
60
|
+
| Single Standard | 98 | 2009-11-28 10:00:00 +0000 | | 2009-11-28 11:00:00 +0000 | MAX TIME | |
|
61
|
+
}
|
62
|
+
end
|
63
|
+
it "doesn't loose previous version in same-day update" do
|
64
|
+
master = @master_class.new
|
65
|
+
master.update_attributes name: "Single Standard", price: 98
|
66
|
+
master.update_attributes name: "Single Standard", price: 94
|
67
|
+
master.should have_versions %Q{
|
68
|
+
| name | price | created_at | expired_at | valid_from | valid_to | current |
|
69
|
+
| Single Standard | 98 | 2009-11-28 10:00:00 +0000 | 2009-11-28 10:00:00 +0000 | 2009-11-28 10:00:00 +0000 | MAX TIME | |
|
70
|
+
| Single Standard | 94 | 2009-11-28 10:00:00 +0000 | | 2009-11-28 10:00:00 +0000 | MAX TIME | true |
|
71
|
+
}
|
72
|
+
end
|
73
|
+
it "allows partial updating based on current version" do
|
74
|
+
master = @master_class.new
|
75
|
+
master.update_attributes name: "Single Standard", price: 98
|
76
|
+
master.update_attributes price: 94
|
77
|
+
master.update_attributes name: "King Size"
|
78
|
+
master.should have_versions %Q{
|
79
|
+
| name | price | created_at | expired_at | valid_from | valid_to | current |
|
80
|
+
| Single Standard | 98 | 2009-11-28 10:00:00 +0000 | 2009-11-28 10:00:00 +0000 | 2009-11-28 10:00:00 +0000 | MAX TIME | |
|
81
|
+
| Single Standard | 94 | 2009-11-28 10:00:00 +0000 | 2009-11-28 10:00:00 +0000 | 2009-11-28 10:00:00 +0000 | MAX TIME | |
|
82
|
+
| King Size | 94 | 2009-11-28 10:00:00 +0000 | | 2009-11-28 10:00:00 +0000 | MAX TIME | true |
|
83
|
+
}
|
84
|
+
end
|
85
|
+
it "expires previous version but keep it in history" do
|
86
|
+
master = @master_class.new
|
87
|
+
master.update_attributes name: "Single Standard", price: 98
|
88
|
+
Timecop.freeze Time.now+hour
|
89
|
+
master.update_attributes price: 94
|
90
|
+
master.should have_versions %Q{
|
91
|
+
| name | price | created_at | expired_at | valid_from | valid_to | current |
|
92
|
+
| Single Standard | 98 | 2009-11-28 10:00:00 +0000 | 2009-11-28 11:00:00 +0000 | 2009-11-28 10:00:00 +0000 | MAX TIME | |
|
93
|
+
| Single Standard | 98 | 2009-11-28 11:00:00 +0000 | | 2009-11-28 10:00:00 +0000 | 2009-11-28 11:00:00 +0000 | |
|
94
|
+
| Single Standard | 94 | 2009-11-28 11:00:00 +0000 | | 2009-11-28 11:00:00 +0000 | MAX TIME | true |
|
95
|
+
}
|
96
|
+
end
|
97
|
+
it "doesn't expire no longer valid versions" do
|
98
|
+
master = @master_class.new
|
99
|
+
master.update_attributes name: "Single Standard", price: 98, valid_to: Time.now+hour
|
100
|
+
Timecop.freeze Time.now+hour
|
101
|
+
master.update_attributes(price: 94).should be_false
|
102
|
+
master.update_attributes name: "Single Standard", price: 94
|
103
|
+
master.should have_versions %Q{
|
104
|
+
| name | price | created_at | expired_at | valid_from | valid_to | current |
|
105
|
+
| Single Standard | 98 | 2009-11-28 10:00:00 +0000 | | 2009-11-28 10:00:00 +0000 | 2009-11-28 11:00:00 +0000 | |
|
106
|
+
| Single Standard | 94 | 2009-11-28 11:00:00 +0000 | | 2009-11-28 11:00:00 +0000 | MAX TIME | true |
|
107
|
+
}
|
108
|
+
end
|
109
|
+
it "allows shortening validity (SEE COMMENTS FOR IMPROVEMENTS)" do
|
110
|
+
master = @master_class.new
|
111
|
+
master.update_attributes name: "Single Standard", price: 98
|
112
|
+
Timecop.freeze Time.now+hour
|
113
|
+
master.update_attributes valid_to: Time.now+10*hour
|
114
|
+
master.should have_versions %Q{
|
115
|
+
| name | price | created_at | expired_at | valid_from | valid_to | current |
|
116
|
+
| Single Standard | 98 | 2009-11-28 10:00:00 +0000 | 2009-11-28 11:00:00 +0000 | 2009-11-28 10:00:00 +0000 | MAX TIME | |
|
117
|
+
| Single Standard | 98 | 2009-11-28 11:00:00 +0000 | | 2009-11-28 10:00:00 +0000 | 2009-11-28 11:00:00 +0000 | |
|
118
|
+
| Single Standard | 98 | 2009-11-28 11:00:00 +0000 | | 2009-11-28 11:00:00 +0000 | 2009-11-28 21:00:00 +0000 | true |
|
119
|
+
}
|
120
|
+
# would be even better if it could be:
|
121
|
+
# | name | price | created_at | expired_at | valid_from | valid_to | current |
|
122
|
+
# | Single Standard | 98 | 2009-11-28 10:00:00 +0000 | 2009-11-28 11:00:00 +0000 | 2009-11-28 10:00:00 +0000 | MAX TIME | |
|
123
|
+
# | Single Standard | 98 | 2009-11-28 11:00:00 +0000 | | 2009-11-28 10:00:00 +0000 | 2009-11-28 21:00:00 +0000 | true |
|
124
|
+
end
|
125
|
+
it "allows extending validity (SEE COMMENTS FOR IMPROVEMENTS)" do
|
126
|
+
master = @master_class.new
|
127
|
+
master.update_attributes name: "Single Standard", price: 98, valid_to: Time.now+2*hour
|
128
|
+
Timecop.freeze Time.now+hour
|
129
|
+
master.should have_versions %Q{
|
130
|
+
| name | price | created_at | expired_at | valid_from | valid_to | current |
|
131
|
+
| Single Standard | 98 | 2009-11-28 10:00:00 +0000 | | 2009-11-28 10:00:00 +0000 | 2009-11-28 12:00:00 +0000 | true |
|
132
|
+
}
|
133
|
+
master.update_attributes valid_to: nil
|
134
|
+
master.should have_versions %Q{
|
135
|
+
| name | price | created_at | expired_at | valid_from | valid_to | current |
|
136
|
+
| Single Standard | 98 | 2009-11-28 10:00:00 +0000 | 2009-11-28 11:00:00 +0000 | 2009-11-28 10:00:00 +0000 | 2009-11-28 12:00:00 +0000 | |
|
137
|
+
| Single Standard | 98 | 2009-11-28 11:00:00 +0000 | | 2009-11-28 10:00:00 +0000 | 2009-11-28 11:00:00 +0000 | |
|
138
|
+
| Single Standard | 98 | 2009-11-28 11:00:00 +0000 | | 2009-11-28 11:00:00 +0000 | MAX TIME | true |
|
139
|
+
}
|
140
|
+
# would be even better if it could be:
|
141
|
+
# | name | price | created_at | expired_at | valid_from | valid_to | current |
|
142
|
+
# | Single Standard | 98 | 2009-11-28 10:00:00 +0000 | 2009-11-28 11:00:00 +0000 | 2009-11-28 10:00:00 +0000 | 2009-11-28 12:00:00 +0000 | |
|
143
|
+
# | Single Standard | 98 | 2009-11-28 11:00:00 +0000 | | 2009-11-28 10:00:00 +0000 | MAX TIME | true |
|
144
|
+
end
|
145
|
+
it "don't create any new version without change " do
|
146
|
+
master = @master_class.new
|
147
|
+
master.update_attributes name: "Single Standard", price: 98
|
148
|
+
master.update_attributes price: 98
|
149
|
+
master.update_attributes name: "Single Standard", price: 98
|
150
|
+
master.should have_versions %Q{
|
151
|
+
| name | price | created_at | expired_at | valid_from | valid_to | current |
|
152
|
+
| Single Standard | 98 | 2009-11-28 10:00:00 +0000 | | 2009-11-28 10:00:00 +0000| MAX TIME | true |
|
153
|
+
}
|
154
|
+
end
|
155
|
+
it "change in validity still creates a new version (SEE COMMENTS FOR IMPROVEMENTS)" do
|
156
|
+
master = @master_class.new
|
157
|
+
master.update_attributes name: "Single Standard", price: 98
|
158
|
+
Timecop.freeze Time.now+hour
|
159
|
+
master.update_attributes price: 98, valid_from: Time.now-2*hour
|
160
|
+
master.update_attributes price: 98, valid_from: Time.now+1*hour
|
161
|
+
master.should have_versions %Q{
|
162
|
+
| name | price | created_at | expired_at | valid_from | valid_to | current |
|
163
|
+
| Single Standard | 98 | 2009-11-28 10:00:00 +0000 | | 2009-11-28 10:00:00 +0000 | MAX TIME | true |
|
164
|
+
| Single Standard | 98 | 2009-11-28 11:00:00 +0000 | | 2009-11-28 09:00:00 +0000 | 2009-11-28 10:00:00 +0000 | |
|
165
|
+
}
|
166
|
+
# would be even better if it could be:
|
167
|
+
# | name | price | created_at | expired_at | valid_from | valid_to | current |
|
168
|
+
# | Single Standard | 98 | 2009-11-28 10:00:00 +0000 | 2009-11-28 11:00:00 +0000 | 2009-11-28 10:00:00 +0000 | MAX TIME | |
|
169
|
+
# | Single Standard | 98 | 2009-11-28 11:00:00 +0000 | | 2009-11-28 09:00:00 +0000 | MAX TIME | true |
|
170
|
+
end
|
171
|
+
it "overrides no future versions" do
|
172
|
+
master = @master_class.new
|
173
|
+
master.update_attributes name: "Single Standard", price: 98, valid_to: Time.now+2*hour
|
174
|
+
master.update_attributes name: "Single Standard", price: 94, valid_from: Time.now+2*hour, valid_to: Time.now+4*hour
|
175
|
+
master.update_attributes name: "Single Standard", price: 95, valid_from: Time.now+4*hour, valid_to: Time.now+6*hour
|
176
|
+
Timecop.freeze Time.now+hour
|
177
|
+
master.update_attributes name: "King Size", valid_to: nil
|
178
|
+
master.should have_versions %Q{
|
179
|
+
| name | price | created_at | expired_at | valid_from | valid_to | current |
|
180
|
+
| Single Standard | 98 | 2009-11-28 10:00:00 +0000 | 2009-11-28 11:00:00 +0000 | 2009-11-28 10:00:00 +0000 | 2009-11-28 12:00:00 +0000 | |
|
181
|
+
| Single Standard | 94 | 2009-11-28 10:00:00 +0000 | | 2009-11-28 12:00:00 +0000 | 2009-11-28 14:00:00 +0000 | |
|
182
|
+
| Single Standard | 95 | 2009-11-28 10:00:00 +0000 | | 2009-11-28 14:00:00 +0000 | 2009-11-28 16:00:00 +0000 | |
|
183
|
+
| Single Standard | 98 | 2009-11-28 11:00:00 +0000 | | 2009-11-28 10:00:00 +0000 | 2009-11-28 11:00:00 +0000 | |
|
184
|
+
| King Size | 98 | 2009-11-28 11:00:00 +0000 | | 2009-11-28 11:00:00 +0000 | 2009-11-28 12:00:00 +0000 | true |
|
185
|
+
}
|
186
|
+
end
|
187
|
+
it "overrides multiple future versions" do
|
188
|
+
master = @master_class.new
|
189
|
+
master.update_attributes name: "Single Standard", price: 98, valid_to: Time.now+2*hour
|
190
|
+
master.update_attributes name: "Single Standard", price: 94, valid_from: Time.now+2*hour, valid_to: Time.now+4*hour
|
191
|
+
master.update_attributes name: "Single Standard", price: 95, valid_from: Time.now+4*hour, valid_to: Time.now+6*hour
|
192
|
+
Timecop.freeze Time.now+hour
|
193
|
+
master.update_attributes name: "King Size", valid_to: Time.now+4*hour
|
194
|
+
master.should have_versions %Q{
|
195
|
+
| name | price | created_at | expired_at | valid_from | valid_to | current |
|
196
|
+
| Single Standard | 98 | 2009-11-28 10:00:00 +0000 | 2009-11-28 11:00:00 +0000 | 2009-11-28 10:00:00 +0000 | 2009-11-28 12:00:00 +0000 | |
|
197
|
+
| Single Standard | 94 | 2009-11-28 10:00:00 +0000 | 2009-11-28 11:00:00 +0000 | 2009-11-28 12:00:00 +0000 | 2009-11-28 14:00:00 +0000 | |
|
198
|
+
| Single Standard | 95 | 2009-11-28 10:00:00 +0000 | 2009-11-28 11:00:00 +0000 | 2009-11-28 14:00:00 +0000 | 2009-11-28 16:00:00 +0000 | |
|
199
|
+
| Single Standard | 98 | 2009-11-28 11:00:00 +0000 | | 2009-11-28 10:00:00 +0000 | 2009-11-28 11:00:00 +0000 | |
|
200
|
+
| Single Standard | 95 | 2009-11-28 11:00:00 +0000 | | 2009-11-28 15:00:00 +0000 | 2009-11-28 16:00:00 +0000 | |
|
201
|
+
| King Size | 98 | 2009-11-28 11:00:00 +0000 | | 2009-11-28 11:00:00 +0000 | 2009-11-28 15:00:00 +0000 | true |
|
202
|
+
}
|
203
|
+
end
|
204
|
+
it "overrides all future versions" do
|
205
|
+
master = @master_class.new
|
206
|
+
master.update_attributes name: "Single Standard", price: 98, valid_to: Time.now+2*hour
|
207
|
+
master.update_attributes name: "Single Standard", price: 94, valid_from: Time.now+2*hour, valid_to: Time.now+4*hour
|
208
|
+
master.update_attributes name: "Single Standard", price: 95, valid_from: Time.now+4*hour, valid_to: Time.now+6*hour
|
209
|
+
Timecop.freeze Time.now+hour
|
210
|
+
master.update_attributes name: "King Size", valid_to: Time.utc(9999)
|
211
|
+
master.should have_versions %Q{
|
212
|
+
| name | price | created_at | expired_at | valid_from | valid_to | current |
|
213
|
+
| Single Standard | 98 | 2009-11-28 10:00:00 +0000 | 2009-11-28 11:00:00 +0000 | 2009-11-28 10:00:00 +0000 | 2009-11-28 12:00:00 +0000 | |
|
214
|
+
| Single Standard | 94 | 2009-11-28 10:00:00 +0000 | 2009-11-28 11:00:00 +0000 | 2009-11-28 12:00:00 +0000 | 2009-11-28 14:00:00 +0000 | |
|
215
|
+
| Single Standard | 95 | 2009-11-28 10:00:00 +0000 | 2009-11-28 11:00:00 +0000 | 2009-11-28 14:00:00 +0000 | 2009-11-28 16:00:00 +0000 | |
|
216
|
+
| Single Standard | 98 | 2009-11-28 11:00:00 +0000 | | 2009-11-28 10:00:00 +0000 | 2009-11-28 11:00:00 +0000 | |
|
217
|
+
| King Size | 98 | 2009-11-28 11:00:00 +0000 | | 2009-11-28 11:00:00 +0000 | MAX TIME | true |
|
218
|
+
}
|
219
|
+
end
|
220
|
+
it "allows deleting current version" do
|
221
|
+
master = @master_class.new
|
222
|
+
master.update_attributes name: "Single Standard", price: 98
|
223
|
+
master.update_attributes name: "Single Standard", price: 94, valid_from: Time.now+2*hour
|
224
|
+
Timecop.freeze Time.now+hour
|
225
|
+
master.current_version.destroy.should be_true
|
226
|
+
master.should have_versions %Q{
|
227
|
+
| name | price | created_at | expired_at | valid_from | valid_to | current |
|
228
|
+
| Single Standard | 98 | 2009-11-28 10:00:00 +0000 | 2009-11-28 10:00:00 +0000 | 2009-11-28 10:00:00 +0000 | MAX TIME | |
|
229
|
+
| Single Standard | 98 | 2009-11-28 10:00:00 +0000 | 2009-11-28 11:00:00 +0000 | 2009-11-28 10:00:00 +0000 | 2009-11-28 12:00:00 +0000 | |
|
230
|
+
| Single Standard | 94 | 2009-11-28 10:00:00 +0000 | | 2009-11-28 12:00:00 +0000 | MAX TIME | |
|
231
|
+
| Single Standard | 98 | 2009-11-28 11:00:00 +0000 | | 2009-11-28 10:00:00 +0000 | 2009-11-28 11:00:00 +0000 | |
|
232
|
+
}
|
233
|
+
end
|
234
|
+
it "allows deleting a future version" do
|
235
|
+
master = @master_class.new
|
236
|
+
master.update_attributes name: "Single Standard", price: 98
|
237
|
+
master.update_attributes name: "Single Standard", price: 94, valid_from: Time.now+2*hour
|
238
|
+
Timecop.freeze Time.now+hour
|
239
|
+
master.versions.last.destroy.should be_true
|
240
|
+
master.should have_versions %Q{
|
241
|
+
| name | price | created_at | expired_at | valid_from | valid_to | current |
|
242
|
+
| Single Standard | 98 | 2009-11-28 10:00:00 +0000 | 2009-11-28 10:00:00 +0000 | 2009-11-28 10:00:00 +0000 | MAX TIME | |
|
243
|
+
| Single Standard | 98 | 2009-11-28 10:00:00 +0000 | 2009-11-28 11:00:00 +0000 | 2009-11-28 10:00:00 +0000 | 2009-11-28 12:00:00 +0000 | |
|
244
|
+
| Single Standard | 94 | 2009-11-28 10:00:00 +0000 | 2009-11-28 11:00:00 +0000 | 2009-11-28 12:00:00 +0000 | MAX TIME | |
|
245
|
+
| Single Standard | 98 | 2009-11-28 11:00:00 +0000 | | 2009-11-28 10:00:00 +0000 | MAX TIME | true |
|
246
|
+
}
|
247
|
+
end
|
248
|
+
it "allows deleting all versions" do
|
249
|
+
master = @master_class.new
|
250
|
+
master.update_attributes name: "Single Standard", price: 98
|
251
|
+
master.update_attributes name: "Single Standard", price: 94, valid_from: Time.now+2*hour
|
252
|
+
Timecop.freeze Time.now+hour
|
253
|
+
master.destroy.should be_true
|
254
|
+
master.should have_versions %Q{
|
255
|
+
| name | price | created_at | expired_at | valid_from | valid_to | current |
|
256
|
+
| Single Standard | 98 | 2009-11-28 10:00:00 +0000 | 2009-11-28 10:00:00 +0000 | 2009-11-28 10:00:00 +0000 | MAX TIME | |
|
257
|
+
| Single Standard | 98 | 2009-11-28 10:00:00 +0000 | 2009-11-28 11:00:00 +0000 | 2009-11-28 10:00:00 +0000 | 2009-11-28 12:00:00 +0000 | |
|
258
|
+
| Single Standard | 94 | 2009-11-28 10:00:00 +0000 | 2009-11-28 11:00:00 +0000 | 2009-11-28 12:00:00 +0000 | MAX TIME | |
|
259
|
+
}
|
260
|
+
end
|
261
|
+
it "allows simultaneous updates without information loss" do
|
262
|
+
master = @master_class.new
|
263
|
+
master.update_attributes name: "Single Standard", price: 98
|
264
|
+
Timecop.freeze Time.now+hour
|
265
|
+
master2 = @master_class.find id: master.id
|
266
|
+
master.update_attributes name: "Single Standard", price: 94
|
267
|
+
master2.update_attributes name: "Single Standard", price: 95
|
268
|
+
master.should have_versions %Q{
|
269
|
+
| name | price | created_at | expired_at | valid_from | valid_to | current |
|
270
|
+
| Single Standard | 98 | 2009-11-28 10:00:00 +0000 | 2009-11-28 11:00:00 +0000 | 2009-11-28 10:00:00 +0000 | MAX TIME | |
|
271
|
+
| Single Standard | 98 | 2009-11-28 11:00:00 +0000 | | 2009-11-28 10:00:00 +0000 | 2009-11-28 11:00:00 +0000 | |
|
272
|
+
| Single Standard | 94 | 2009-11-28 11:00:00 +0000 | 2009-11-28 11:00:00 +0000 | 2009-11-28 11:00:00 +0000 | MAX TIME | |
|
273
|
+
| Single Standard | 95 | 2009-11-28 11:00:00 +0000 | | 2009-11-28 11:00:00 +0000 | MAX TIME | true |
|
274
|
+
}
|
275
|
+
end
|
276
|
+
it "allows simultaneous cumulative updates" do
|
277
|
+
master = @master_class.new
|
278
|
+
master.update_attributes name: "Single Standard", price: 98
|
279
|
+
Timecop.freeze Time.now+hour
|
280
|
+
master2 = @master_class.find id: master.id
|
281
|
+
master.update_attributes price: 94
|
282
|
+
master2.update_attributes name: "King Size"
|
283
|
+
master.should have_versions %Q{
|
284
|
+
| name | price | created_at | expired_at | valid_from | valid_to | current |
|
285
|
+
| Single Standard | 98 | 2009-11-28 10:00:00 +0000 | 2009-11-28 11:00:00 +0000 | 2009-11-28 10:00:00 +0000 | MAX TIME | |
|
286
|
+
| Single Standard | 98 | 2009-11-28 11:00:00 +0000 | | 2009-11-28 10:00:00 +0000 | 2009-11-28 11:00:00 +0000 | |
|
287
|
+
| Single Standard | 94 | 2009-11-28 11:00:00 +0000 | 2009-11-28 11:00:00 +0000 | 2009-11-28 11:00:00 +0000 | MAX TIME | |
|
288
|
+
| King Size | 94 | 2009-11-28 11:00:00 +0000 | | 2009-11-28 11:00:00 +0000 | MAX TIME | true |
|
289
|
+
}
|
290
|
+
end
|
291
|
+
it "allows eager loading with conditions on current version" do
|
292
|
+
master = @master_class.new
|
293
|
+
master.update_attributes name: "Single Standard", price: 98
|
294
|
+
master.update_attributes name: "Single Standard", price: 94, valid_from: Time.now+2*hour
|
295
|
+
@master_class.eager_graph(:current_version).where("rooms_current_version.id IS NOT NULL").first.should be
|
296
|
+
Timecop.freeze Time.now+hour
|
297
|
+
master.destroy
|
298
|
+
@master_class.eager_graph(:current_version).where("rooms_current_version.id IS NOT NULL").first.should be_nil
|
299
|
+
end
|
300
|
+
it "allows loading masters with a current version" do
|
301
|
+
master_destroyed = @master_class.new
|
302
|
+
master_destroyed.update_attributes name: "Single Standard", price: 98
|
303
|
+
master_destroyed.destroy
|
304
|
+
master_with_current = @master_class.new
|
305
|
+
master_with_current.update_attributes name: "Single Standard", price: 94
|
306
|
+
master_with_future = @master_class.new
|
307
|
+
master_with_future.update_attributes name: "Single Standard", price: 94, valid_from: Time.now+2*hour
|
308
|
+
@master_class.with_current_version.all.should have(1).item
|
309
|
+
end
|
310
|
+
it "gets pending or current version attributes" do
|
311
|
+
master = @master_class.new
|
312
|
+
master.attributes.should == {}
|
313
|
+
master.pending_version.should be_nil
|
314
|
+
master.current_version.should be_nil
|
315
|
+
master.pending_or_current_version.name.should be_nil
|
316
|
+
master.name.should be_nil
|
317
|
+
|
318
|
+
master.update_attributes name: "Single Standard", price: 98
|
319
|
+
master.attributes[:name].should == "Single Standard"
|
320
|
+
master.pending_version.should be_nil
|
321
|
+
master.pending_or_current_version.name.should == "Single Standard"
|
322
|
+
master.name.should == "Single Standard"
|
323
|
+
|
324
|
+
master.attributes = {name: "King Size"}
|
325
|
+
master.attributes[:name].should == "King Size"
|
326
|
+
master.pending_version.should be
|
327
|
+
master.pending_or_current_version.name.should == "King Size"
|
328
|
+
master.name.should == "King Size"
|
329
|
+
end
|
330
|
+
it "allows to go back in time" do
|
331
|
+
master = @master_class.new
|
332
|
+
master.update_attributes name: "Single Standard", price: 98
|
333
|
+
Timecop.freeze Time.now+1*hour
|
334
|
+
master.update_attributes price: 94
|
335
|
+
master.current_version.price.should == 94
|
336
|
+
Sequel::Plugins::Bitemporal.as_we_knew_it(Time.now-1*hour) do
|
337
|
+
master.current_version(true).price.should == 98
|
338
|
+
end
|
339
|
+
end
|
340
|
+
it "correctly reset time if failure when going back in time" do
|
341
|
+
before = Sequel::Plugins::Bitemporal.now
|
342
|
+
lambda do
|
343
|
+
Sequel::Plugins::Bitemporal.at(Time.now+1*hour) do
|
344
|
+
raise StandardError, "error during back in time"
|
345
|
+
end
|
346
|
+
end.should raise_error StandardError
|
347
|
+
Sequel::Plugins::Bitemporal.now.should == before
|
348
|
+
lambda do
|
349
|
+
Sequel::Plugins::Bitemporal.as_we_knew_it(Time.now-1*hour) do
|
350
|
+
raise StandardError, "error during back in time"
|
351
|
+
end
|
352
|
+
end.should raise_error StandardError
|
353
|
+
Sequel::Plugins::Bitemporal.now.should == before
|
354
|
+
end
|
355
|
+
it "allows eager loading with conditions on current or future versions" do
|
356
|
+
master = @master_class.new
|
357
|
+
master.update_attributes name: "Single Standard", price: 98
|
358
|
+
Timecop.freeze Time.now+1*hour
|
359
|
+
master.update_attributes name: "Single Standard", price: 99
|
360
|
+
master.update_attributes name: "Single Standard", price: 94, valid_from: Time.now+2*hour
|
361
|
+
res = @master_class.eager_graph(:current_or_future_versions).where(Sequel.negate(current_or_future_versions__id: nil) & {price: 99}).all.first
|
362
|
+
res.should be
|
363
|
+
res.current_or_future_versions.should have(1).item
|
364
|
+
res.current_or_future_versions.first.price.should == 99
|
365
|
+
res = @master_class.eager_graph(:current_or_future_versions).where(Sequel.negate(current_or_future_versions__id: nil) & {price: 94}).all.first
|
366
|
+
res.should be
|
367
|
+
res.current_or_future_versions.should have(1).item
|
368
|
+
res.current_or_future_versions.first.price.should == 94
|
369
|
+
Timecop.freeze Time.now+1*hour
|
370
|
+
master.destroy
|
371
|
+
@master_class.eager_graph(:current_or_future_versions).where(Sequel.negate(current_or_future_versions__id: nil)).all.should be_empty
|
372
|
+
end
|
373
|
+
it "allows loading masters with current or future versions" do
|
374
|
+
master_destroyed = @master_class.new
|
375
|
+
master_destroyed.update_attributes name: "Single Standard", price: 98
|
376
|
+
master_destroyed.destroy
|
377
|
+
master_with_current = @master_class.new
|
378
|
+
master_with_current.update_attributes name: "Single Standard", price: 94
|
379
|
+
master_with_future = @master_class.new
|
380
|
+
master_with_future.update_attributes name: "Single Standard", price: 94, valid_from: Time.now+2*hour
|
381
|
+
@master_class.with_current_or_future_versions.all.should have(2).item
|
382
|
+
end
|
383
|
+
end
|
384
|
+
describe "Sequel::Plugins::Bitemporal", "with audit" do
|
385
|
+
before :all do
|
386
|
+
@audit_class = Class.new
|
387
|
+
db_setup use_time: true, audit_class: @audit_class, ranges: true
|
388
|
+
end
|
389
|
+
before do
|
390
|
+
Timecop.freeze 2009, 11, 28, 10
|
391
|
+
end
|
392
|
+
after do
|
393
|
+
Timecop.return
|
394
|
+
end
|
395
|
+
let(:author){ mock :author, audit_kind: "user" }
|
396
|
+
it "generates a new audit on creation" do
|
397
|
+
master = @master_class.new
|
398
|
+
master.should_receive(:updated_by).and_return author
|
399
|
+
@audit_class.should_receive(:audit).with(
|
400
|
+
master,
|
401
|
+
{},
|
402
|
+
hash_including({name: "Single Standard", price: 98}),
|
403
|
+
Time.now,
|
404
|
+
author
|
405
|
+
)
|
406
|
+
master.update_attributes name: "Single Standard", price: 98
|
407
|
+
end
|
408
|
+
it "generates a new audit on full update" do
|
409
|
+
master = @master_class.new
|
410
|
+
master.should_receive(:updated_by).twice.and_return author
|
411
|
+
@audit_class.stub(:audit)
|
412
|
+
master.update_attributes name: "Single Standard", price: 98
|
413
|
+
@audit_class.should_receive(:audit).with(
|
414
|
+
master,
|
415
|
+
hash_including({name: "Single Standard", price: 98}),
|
416
|
+
hash_including({name: "King size", price: 98}),
|
417
|
+
Time.now,
|
418
|
+
author
|
419
|
+
)
|
420
|
+
master.update_attributes name: "King size", price: 98
|
421
|
+
end
|
422
|
+
it "generates a new audit on partial update" do
|
423
|
+
master = @master_class.new
|
424
|
+
master.should_receive(:updated_by).twice.and_return author
|
425
|
+
@audit_class.stub(:audit)
|
426
|
+
master.update_attributes name: "Single Standard", price: 98
|
427
|
+
@audit_class.should_receive(:audit).with(
|
428
|
+
master,
|
429
|
+
hash_including({name: "Single Standard", price: 98}),
|
430
|
+
hash_including({name: "King size", price: 98}),
|
431
|
+
Time.now,
|
432
|
+
author
|
433
|
+
)
|
434
|
+
master.update_attributes name: "King size", price: 98
|
435
|
+
end
|
436
|
+
end
|
437
|
+
|
438
|
+
describe "Sequel::Plugins::Bitemporal", "with audit, specifying how to get the author" do
|
439
|
+
before :all do
|
440
|
+
@audit_class = Class.new
|
441
|
+
db_setup(
|
442
|
+
use_time: true,
|
443
|
+
audit_class: @audit_class,
|
444
|
+
audit_updated_by_method: :author,
|
445
|
+
ranges: true
|
446
|
+
)
|
447
|
+
end
|
448
|
+
before do
|
449
|
+
Timecop.freeze 2009, 11, 28, 10
|
450
|
+
end
|
451
|
+
after do
|
452
|
+
Timecop.return
|
453
|
+
end
|
454
|
+
let(:author){ mock :author, audit_kind: "user" }
|
455
|
+
it "generates a new audit on creation" do
|
456
|
+
master = @master_class.new
|
457
|
+
master.should_receive(:author).and_return author
|
458
|
+
@audit_class.should_receive(:audit).with(
|
459
|
+
master,
|
460
|
+
{},
|
461
|
+
hash_including({name: "Single Standard", price: 98}),
|
462
|
+
Time.now,
|
463
|
+
author
|
464
|
+
)
|
465
|
+
master.update_attributes name: "Single Standard", price: 98
|
466
|
+
end
|
467
|
+
it "generates a new audit on full update" do
|
468
|
+
master = @master_class.new
|
469
|
+
master.should_receive(:author).twice.and_return author
|
470
|
+
@audit_class.stub(:audit)
|
471
|
+
master.update_attributes name: "Single Standard", price: 98
|
472
|
+
@audit_class.should_receive(:audit).with(
|
473
|
+
master,
|
474
|
+
hash_including({name: "Single Standard", price: 98}),
|
475
|
+
hash_including({name: "King size", price: 98}),
|
476
|
+
Time.now,
|
477
|
+
author
|
478
|
+
)
|
479
|
+
master.update_attributes name: "King size", price: 98
|
480
|
+
end
|
481
|
+
it "generates a new audit on partial update" do
|
482
|
+
master = @master_class.new
|
483
|
+
master.should_receive(:author).twice.and_return author
|
484
|
+
@audit_class.stub(:audit)
|
485
|
+
master.update_attributes name: "Single Standard", price: 98
|
486
|
+
@audit_class.should_receive(:audit).with(
|
487
|
+
master,
|
488
|
+
hash_including({name: "Single Standard", price: 98}),
|
489
|
+
hash_including({name: "King size", price: 98}),
|
490
|
+
Time.now,
|
491
|
+
author
|
492
|
+
)
|
493
|
+
master.update_attributes name: "King size", price: 98
|
494
|
+
end
|
495
|
+
end
|
496
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,6 +1,27 @@
|
|
1
1
|
require "sequel"
|
2
2
|
require "timecop"
|
3
3
|
require "pry"
|
4
|
-
|
4
|
+
|
5
5
|
Dir[File.expand_path("../support/*.rb", __FILE__)].each{|f| require f}
|
6
6
|
ENV["TZ"]="UTC"
|
7
|
+
|
8
|
+
DB = if DbHelpers.pg?
|
9
|
+
`createdb sequel_bitemporal_test`
|
10
|
+
Sequel.extension :pg_range, :pg_range_ops
|
11
|
+
Sequel.postgres "sequel_bitemporal_test"
|
12
|
+
else
|
13
|
+
Sequel.sqlite
|
14
|
+
end
|
15
|
+
|
16
|
+
if ENV["DEBUG"]
|
17
|
+
require "logger"
|
18
|
+
DB.loggers << Logger.new($stdout)
|
19
|
+
end
|
20
|
+
|
21
|
+
RSpec.configure do |config|
|
22
|
+
config.include DbHelpers
|
23
|
+
|
24
|
+
config.before :each do
|
25
|
+
db_truncate
|
26
|
+
end
|
27
|
+
end
|
data/spec/support/db.rb
CHANGED
@@ -1,13 +1,11 @@
|
|
1
1
|
module DbHelpers
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
end
|
2
|
+
|
3
|
+
def self.pg?
|
4
|
+
ENV.has_key? "PG"
|
6
5
|
end
|
7
6
|
|
8
7
|
def db_setup(opts={})
|
9
8
|
use_time = opts[:use_time]
|
10
|
-
|
11
9
|
DB.drop_table(:room_versions) if DB.table_exists?(:room_versions)
|
12
10
|
DB.drop_table(:rooms) if DB.table_exists?(:rooms)
|
13
11
|
DB.create_table! :rooms do
|
@@ -38,6 +36,7 @@ module DbHelpers
|
|
38
36
|
bitemporal_options = {version_class: @version_class}
|
39
37
|
bitemporal_options[:audit_class] = opts[:audit_class] if opts[:audit_class]
|
40
38
|
bitemporal_options[:audit_updated_by_method] = opts[:audit_updated_by_method] if opts[:audit_updated_by_method]
|
39
|
+
bitemporal_options[:ranges] = opts[:ranges] if opts[:ranges]
|
41
40
|
|
42
41
|
@master_class = Class.new Sequel::Model do
|
43
42
|
set_dataset :rooms
|
@@ -46,7 +45,12 @@ module DbHelpers
|
|
46
45
|
end
|
47
46
|
|
48
47
|
def db_truncate
|
49
|
-
|
50
|
-
|
48
|
+
if DbHelpers.pg?
|
49
|
+
@version_class.truncate cascade: true
|
50
|
+
@master_class.truncate cascade: true
|
51
|
+
else
|
52
|
+
@version_class.truncate
|
53
|
+
@master_class.truncate
|
54
|
+
end
|
51
55
|
end
|
52
56
|
end
|