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.
@@ -2,7 +2,6 @@ require "spec_helper"
2
2
  require "json"
3
3
 
4
4
  describe "Sequel::Plugins::Bitemporal" do
5
- include DbHelpers
6
5
  before :all do
7
6
  db_setup
8
7
  @version_class.instance_eval do
@@ -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({current_or_future_versions__id: nil}.sql_negate & {price: 99}).all.first
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({current_or_future_versions__id: nil}.sql_negate & {price: 94}).all.first
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({current_or_future_versions__id: nil}.sql_negate).all.should be_empty
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
- DB = Sequel.sqlite
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
- def self.included(klass)
3
- klass.before do
4
- db_truncate
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
- @version_class.truncate
50
- @master_class.truncate
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