sequel_bitemporal 0.1.10 → 0.1.11
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/sequel/plugins/bitemporal.rb +32 -12
- data/sequel_bitemporal.gemspec +1 -1
- data/spec/bitemporal_date_spec.rb +37 -3
- data/spec/bitemporal_time_spec.rb +52 -2
- metadata +12 -18
@@ -3,13 +3,26 @@ module Sequel
|
|
3
3
|
module Bitemporal
|
4
4
|
def self.as_we_knew_it(time)
|
5
5
|
raise ArgumentError, "requires a block" unless block_given?
|
6
|
-
|
6
|
+
key = :sequel_plugins_bitemporal_point_in_time
|
7
|
+
previous, Thread.current[key] = Thread.current[key], time.to_time
|
7
8
|
yield
|
8
|
-
|
9
|
+
Thread.current[key] = previous
|
9
10
|
end
|
10
11
|
|
11
12
|
def self.point_in_time
|
12
|
-
|
13
|
+
Thread.current[:sequel_plugins_bitemporal_point_in_time] || Time.now
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.at(time)
|
17
|
+
raise ArgumentError, "requires a block" unless block_given?
|
18
|
+
key = :sequel_plugins_bitemporal_now
|
19
|
+
previous, Thread.current[key] = Thread.current[key], time.to_time
|
20
|
+
yield
|
21
|
+
Thread.current[key] = previous
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.now
|
25
|
+
Thread.current[:sequel_plugins_bitemporal_now] || Time.now
|
13
26
|
end
|
14
27
|
|
15
28
|
def self.configure(master, opts = {})
|
@@ -21,34 +34,40 @@ module Sequel
|
|
21
34
|
master.one_to_many :versions, class: version, key: :master_id
|
22
35
|
master.one_to_one :current_version, class: version, key: :master_id, :graph_block=>(proc do |j, lj, js|
|
23
36
|
t = ::Sequel::Plugins::Bitemporal.point_in_time
|
37
|
+
n = ::Sequel::Plugins::Bitemporal.now
|
24
38
|
e = :expired_at.qualify(j)
|
25
|
-
(:created_at.qualify(j) <= t) & ({e=>nil} | (e > t)) & (:valid_from.qualify(j) <=
|
39
|
+
(:created_at.qualify(j) <= t) & ({e=>nil} | (e > t)) & (:valid_from.qualify(j) <= n) & (:valid_to.qualify(j) > n)
|
26
40
|
end) do |ds|
|
27
41
|
t = ::Sequel::Plugins::Bitemporal.point_in_time
|
28
|
-
|
42
|
+
n = ::Sequel::Plugins::Bitemporal.now
|
43
|
+
ds.where{(created_at <= t) & ({expired_at=>nil} | (expired_at > t)) & (valid_from <= n) & (valid_to > n)}
|
29
44
|
end
|
30
45
|
master.def_dataset_method :with_current_version do
|
31
46
|
eager_graph(:current_version).where({current_version__id: nil}.sql_negate)
|
32
47
|
end
|
33
48
|
master.one_to_many :current_or_future_versions, class: version, key: :master_id, :graph_block=>(proc do |j, lj, js|
|
34
49
|
t = ::Sequel::Plugins::Bitemporal.point_in_time
|
50
|
+
n = ::Sequel::Plugins::Bitemporal.now
|
35
51
|
e = :expired_at.qualify(j)
|
36
|
-
(:created_at.qualify(j) <= t) & ({e=>nil} | (e > t)) & (:valid_to.qualify(j) >
|
52
|
+
(:created_at.qualify(j) <= t) & ({e=>nil} | (e > t)) & (:valid_to.qualify(j) > n)
|
37
53
|
end) do |ds|
|
38
54
|
t = ::Sequel::Plugins::Bitemporal.point_in_time
|
39
|
-
|
55
|
+
n = ::Sequel::Plugins::Bitemporal.now
|
56
|
+
ds.where{(created_at <= t) & ({expired_at=>nil} | (expired_at > t)) & (valid_to > n)}
|
40
57
|
end
|
41
58
|
master.def_dataset_method :with_current_or_future_versions do
|
42
59
|
eager_graph(:current_or_future_versions).where({current_or_future_versions__id: nil}.sql_negate)
|
43
60
|
end
|
44
61
|
version.many_to_one :master, class: master, key: :master_id
|
45
62
|
version.class_eval do
|
46
|
-
def current?
|
63
|
+
def current?
|
64
|
+
t = ::Sequel::Plugins::Bitemporal.point_in_time
|
65
|
+
n = ::Sequel::Plugins::Bitemporal.now
|
47
66
|
!new? &&
|
48
|
-
created_at.to_time<=
|
49
|
-
(expired_at.nil? || expired_at.to_time>
|
50
|
-
valid_from.to_time<=
|
51
|
-
valid_to.to_time>
|
67
|
+
created_at.to_time<=t &&
|
68
|
+
(expired_at.nil? || expired_at.to_time>t) &&
|
69
|
+
valid_from.to_time<=n &&
|
70
|
+
valid_to.to_time>n
|
52
71
|
end
|
53
72
|
def destroy
|
54
73
|
master.destroy_version self
|
@@ -96,6 +115,7 @@ module Sequel
|
|
96
115
|
if !new? && attributes.delete(:partial_update) && current_version
|
97
116
|
current_attributes = current_version.values.dup
|
98
117
|
current_attributes.delete :valid_from
|
118
|
+
current_attributes.delete :valid_to
|
99
119
|
attributes = current_attributes.merge attributes
|
100
120
|
end
|
101
121
|
attributes.delete :id
|
data/sequel_bitemporal.gemspec
CHANGED
@@ -3,7 +3,7 @@ $:.push File.expand_path("../lib", __FILE__)
|
|
3
3
|
|
4
4
|
Gem::Specification.new do |s|
|
5
5
|
s.name = "sequel_bitemporal"
|
6
|
-
s.version = "0.1.
|
6
|
+
s.version = "0.1.11"
|
7
7
|
s.authors = ["Joseph HALTER", "Jonathan TRON"]
|
8
8
|
s.email = ["joseph.halter@thetalentbox.com", "jonathan.tron@thetalentbox.com"]
|
9
9
|
s.homepage = "https://github.com/TalentBox/sequel_bitemporal"
|
@@ -2,6 +2,8 @@ require "spec_helper"
|
|
2
2
|
|
3
3
|
describe "Sequel::Plugins::Bitemporal" do
|
4
4
|
before :all do
|
5
|
+
DB.drop_table(:room_versions) if DB.table_exists?(:room_versions)
|
6
|
+
DB.drop_table(:rooms) if DB.table_exists?(:rooms)
|
5
7
|
DB.create_table! :rooms do
|
6
8
|
primary_key :id
|
7
9
|
end
|
@@ -31,8 +33,8 @@ describe "Sequel::Plugins::Bitemporal" do
|
|
31
33
|
end
|
32
34
|
before do
|
33
35
|
Timecop.freeze 2009, 11, 28
|
34
|
-
@master_class.truncate
|
35
36
|
@version_class.truncate
|
37
|
+
@master_class.truncate
|
36
38
|
end
|
37
39
|
after do
|
38
40
|
Timecop.return
|
@@ -325,13 +327,45 @@ describe "Sequel::Plugins::Bitemporal" do
|
|
325
327
|
end
|
326
328
|
it "allows to go back in time" do
|
327
329
|
master = @master_class.new
|
328
|
-
master.update_attributes name: "Single Standard", price: 98
|
330
|
+
master.update_attributes name: "Single Standard", price: 98, valid_to: Date.today+1
|
331
|
+
master.update_attributes name: "Single Standard", price: 95, valid_from: Date.today+1, valid_to: Date.today+2
|
332
|
+
master.update_attributes name: "Single Standard", price: 93, valid_from: Date.today+2, valid_to: Date.today+3
|
333
|
+
master.update_attributes name: "Single Standard", price: 91, valid_from: Date.today+3
|
329
334
|
Timecop.freeze Date.today+1
|
330
335
|
master.update_attributes price: 94, partial_update: true
|
336
|
+
master.update_attributes price: 96, partial_update: true, valid_from: Date.today+2
|
337
|
+
master.should have_versions %Q{
|
338
|
+
| name | price | created_at | expired_at | valid_from | valid_to | current |
|
339
|
+
| Single Standard | 98 | 2009-11-28 | | 2009-11-28 | 2009-11-29 | |
|
340
|
+
| Single Standard | 95 | 2009-11-28 | 2009-11-29 | 2009-11-29 | 2009-11-30 | |
|
341
|
+
| Single Standard | 93 | 2009-11-28 | | 2009-11-30 | 2009-12-01 | |
|
342
|
+
| Single Standard | 91 | 2009-11-28 | 2009-11-29 | 2009-12-01 | MAX DATE | |
|
343
|
+
| Single Standard | 94 | 2009-11-29 | | 2009-11-29 | 2009-11-30 | true |
|
344
|
+
| Single Standard | 96 | 2009-11-29 | | 2009-12-01 | MAX DATE | |
|
345
|
+
}
|
331
346
|
master.current_version.price.should == 94
|
332
|
-
Sequel::Plugins::Bitemporal.
|
347
|
+
Sequel::Plugins::Bitemporal.at(Date.today-1) do
|
333
348
|
master.current_version(true).price.should == 98
|
334
349
|
end
|
350
|
+
Sequel::Plugins::Bitemporal.at(Date.today+1) do
|
351
|
+
master.current_version(true).price.should == 93
|
352
|
+
end
|
353
|
+
Sequel::Plugins::Bitemporal.at(Date.today+2) do
|
354
|
+
master.current_version(true).price.should == 96
|
355
|
+
end
|
356
|
+
Sequel::Plugins::Bitemporal.as_we_knew_it(Date.today-1) do
|
357
|
+
master.current_version(true).price.should == 95
|
358
|
+
master.current_version.should be_current
|
359
|
+
Sequel::Plugins::Bitemporal.at(Date.today-1) do
|
360
|
+
master.current_version(true).price.should == 98
|
361
|
+
end
|
362
|
+
Sequel::Plugins::Bitemporal.at(Date.today+1) do
|
363
|
+
master.current_version(true).price.should == 93
|
364
|
+
end
|
365
|
+
Sequel::Plugins::Bitemporal.at(Date.today+2) do
|
366
|
+
master.current_version(true).price.should == 91
|
367
|
+
end
|
368
|
+
end
|
335
369
|
end
|
336
370
|
it "allows eager loading with conditions on current or future versions" do
|
337
371
|
master = @master_class.new
|
@@ -3,6 +3,8 @@ require "spec_helper"
|
|
3
3
|
describe "Sequel::Plugins::Bitemporal" do
|
4
4
|
let(:hour){ 3600 }
|
5
5
|
before :all do
|
6
|
+
DB.drop_table(:room_versions) if DB.table_exists?(:room_versions)
|
7
|
+
DB.drop_table(:rooms) if DB.table_exists?(:rooms)
|
6
8
|
DB.create_table! :rooms do
|
7
9
|
primary_key :id
|
8
10
|
end
|
@@ -32,11 +34,11 @@ describe "Sequel::Plugins::Bitemporal" do
|
|
32
34
|
end
|
33
35
|
before do
|
34
36
|
Timecop.freeze 2009, 11, 28, 10
|
37
|
+
@version_class.truncate
|
38
|
+
@master_class.truncate
|
35
39
|
end
|
36
40
|
after do
|
37
41
|
Timecop.return
|
38
|
-
@master_class.truncate
|
39
|
-
@version_class.truncate
|
40
42
|
end
|
41
43
|
it "checks version class is given" do
|
42
44
|
lambda{
|
@@ -300,6 +302,16 @@ describe "Sequel::Plugins::Bitemporal" do
|
|
300
302
|
master.destroy
|
301
303
|
@master_class.eager_graph(:current_version).where("current_version.id IS NOT NULL").first.should be_nil
|
302
304
|
end
|
305
|
+
it "allows loading masters with a current version" do
|
306
|
+
master_destroyed = @master_class.new
|
307
|
+
master_destroyed.update_attributes name: "Single Standard", price: 98
|
308
|
+
master_destroyed.destroy
|
309
|
+
master_with_current = @master_class.new
|
310
|
+
master_with_current.update_attributes name: "Single Standard", price: 94
|
311
|
+
master_with_future = @master_class.new
|
312
|
+
master_with_future.update_attributes name: "Single Standard", price: 94, valid_from: Time.now+2*hour
|
313
|
+
@master_class.with_current_version.all.should have(1).item
|
314
|
+
end
|
303
315
|
it "gets pending or current version attributes" do
|
304
316
|
master = @master_class.new
|
305
317
|
master.attributes.should == {}
|
@@ -314,4 +326,42 @@ describe "Sequel::Plugins::Bitemporal" do
|
|
314
326
|
master.pending_version.should be
|
315
327
|
master.pending_or_current_version.name.should == "King Size"
|
316
328
|
end
|
329
|
+
it "allows to go back in time" do
|
330
|
+
master = @master_class.new
|
331
|
+
master.update_attributes name: "Single Standard", price: 98
|
332
|
+
Timecop.freeze Time.now+1*hour
|
333
|
+
master.update_attributes price: 94, partial_update: true
|
334
|
+
master.current_version.price.should == 94
|
335
|
+
Sequel::Plugins::Bitemporal.as_we_knew_it(Time.now-1*hour) do
|
336
|
+
master.current_version(true).price.should == 98
|
337
|
+
end
|
338
|
+
end
|
339
|
+
it "allows eager loading with conditions on current or future versions" do
|
340
|
+
master = @master_class.new
|
341
|
+
master.update_attributes name: "Single Standard", price: 98
|
342
|
+
Timecop.freeze Time.now+1*hour
|
343
|
+
master.update_attributes name: "Single Standard", price: 99
|
344
|
+
master.update_attributes name: "Single Standard", price: 94, valid_from: Time.now+2*hour
|
345
|
+
res = @master_class.eager_graph(:current_or_future_versions).where({current_or_future_versions__id: nil}.sql_negate & {price: 99}).all.first
|
346
|
+
res.should be
|
347
|
+
res.current_or_future_versions.should have(1).item
|
348
|
+
res.current_or_future_versions.first.price.should == 99
|
349
|
+
res = @master_class.eager_graph(:current_or_future_versions).where({current_or_future_versions__id: nil}.sql_negate & {price: 94}).all.first
|
350
|
+
res.should be
|
351
|
+
res.current_or_future_versions.should have(1).item
|
352
|
+
res.current_or_future_versions.first.price.should == 94
|
353
|
+
Timecop.freeze Time.now+1*hour
|
354
|
+
master.destroy
|
355
|
+
@master_class.eager_graph(:current_or_future_versions).where({current_or_future_versions__id: nil}.sql_negate).all.should be_empty
|
356
|
+
end
|
357
|
+
it "allows loading masters with current or future versions" do
|
358
|
+
master_destroyed = @master_class.new
|
359
|
+
master_destroyed.update_attributes name: "Single Standard", price: 98
|
360
|
+
master_destroyed.destroy
|
361
|
+
master_with_current = @master_class.new
|
362
|
+
master_with_current.update_attributes name: "Single Standard", price: 94
|
363
|
+
master_with_future = @master_class.new
|
364
|
+
master_with_future.update_attributes name: "Single Standard", price: 94, valid_from: Time.now+2*hour
|
365
|
+
@master_class.with_current_or_future_versions.all.should have(2).item
|
366
|
+
end
|
317
367
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sequel_bitemporal
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.11
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,11 +10,11 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2011-11-
|
13
|
+
date: 2011-11-17 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: sequel
|
17
|
-
requirement: &
|
17
|
+
requirement: &2165638640 !ruby/object:Gem::Requirement
|
18
18
|
none: false
|
19
19
|
requirements:
|
20
20
|
- - ! '>='
|
@@ -22,10 +22,10 @@ dependencies:
|
|
22
22
|
version: '0'
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
|
-
version_requirements: *
|
25
|
+
version_requirements: *2165638640
|
26
26
|
- !ruby/object:Gem::Dependency
|
27
27
|
name: sqlite3
|
28
|
-
requirement: &
|
28
|
+
requirement: &2165638200 !ruby/object:Gem::Requirement
|
29
29
|
none: false
|
30
30
|
requirements:
|
31
31
|
- - ! '>='
|
@@ -33,10 +33,10 @@ dependencies:
|
|
33
33
|
version: '0'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
|
-
version_requirements: *
|
36
|
+
version_requirements: *2165638200
|
37
37
|
- !ruby/object:Gem::Dependency
|
38
38
|
name: rspec
|
39
|
-
requirement: &
|
39
|
+
requirement: &2165637780 !ruby/object:Gem::Requirement
|
40
40
|
none: false
|
41
41
|
requirements:
|
42
42
|
- - ! '>='
|
@@ -44,10 +44,10 @@ dependencies:
|
|
44
44
|
version: '0'
|
45
45
|
type: :development
|
46
46
|
prerelease: false
|
47
|
-
version_requirements: *
|
47
|
+
version_requirements: *2165637780
|
48
48
|
- !ruby/object:Gem::Dependency
|
49
49
|
name: timecop
|
50
|
-
requirement: &
|
50
|
+
requirement: &2165637360 !ruby/object:Gem::Requirement
|
51
51
|
none: false
|
52
52
|
requirements:
|
53
53
|
- - ! '>='
|
@@ -55,10 +55,10 @@ dependencies:
|
|
55
55
|
version: '0'
|
56
56
|
type: :development
|
57
57
|
prerelease: false
|
58
|
-
version_requirements: *
|
58
|
+
version_requirements: *2165637360
|
59
59
|
- !ruby/object:Gem::Dependency
|
60
60
|
name: rake
|
61
|
-
requirement: &
|
61
|
+
requirement: &2165636940 !ruby/object:Gem::Requirement
|
62
62
|
none: false
|
63
63
|
requirements:
|
64
64
|
- - ! '>='
|
@@ -66,7 +66,7 @@ dependencies:
|
|
66
66
|
version: '0'
|
67
67
|
type: :development
|
68
68
|
prerelease: false
|
69
|
-
version_requirements: *
|
69
|
+
version_requirements: *2165636940
|
70
70
|
description: Bitemporal versioning for sequel, fully tested.
|
71
71
|
email:
|
72
72
|
- joseph.halter@thetalentbox.com
|
@@ -99,18 +99,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
99
99
|
- - ! '>='
|
100
100
|
- !ruby/object:Gem::Version
|
101
101
|
version: '0'
|
102
|
-
segments:
|
103
|
-
- 0
|
104
|
-
hash: -2412870867383750431
|
105
102
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
106
103
|
none: false
|
107
104
|
requirements:
|
108
105
|
- - ! '>='
|
109
106
|
- !ruby/object:Gem::Version
|
110
107
|
version: '0'
|
111
|
-
segments:
|
112
|
-
- 0
|
113
|
-
hash: -2412870867383750431
|
114
108
|
requirements: []
|
115
109
|
rubyforge_project:
|
116
110
|
rubygems_version: 1.8.10
|