sequel_bitemporal 0.1.10 → 0.1.11
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/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
|