sequel_bitemporal 0.9.0 → 0.9.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.ruby-version +1 -1
- data/README.md +7 -2
- data/Rakefile +1 -1
- data/lib/sequel/plugins/bitemporal.rb +36 -12
- data/sequel_bitemporal.gemspec +1 -1
- data/spec/bitemporal_date_spec.rb +3 -3
- data/spec/bitemporal_date_with_range_spec.rb +3 -3
- data/spec/bitemporal_serialization_spec.rb +1 -1
- data/spec/bitemporal_time_spec.rb +3 -3
- data/spec/bitemporal_time_with_range_spec.rb +3 -3
- data/spec/composite_primary_key_spec.rb +59 -0
- data/spec/spec_helper.rb +1 -0
- data/spec/support/db.rb +42 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7f6daa2d7f025cd1281e8cbfc7235d0a6ddd85d2251f8d9195d85cc5340aafb3
|
4
|
+
data.tar.gz: 5d5de257ee011436a1c3959206c31c985d6b11597d63fcab468876865394f9ee
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '0309ac260a37ec550e417efb01eaa72120583a5c26c6306f52c15910d36c8dc24f9e74d39319f763e3a566192ddd5887b0b022ce747f820beee388a444726dc8'
|
7
|
+
data.tar.gz: f4f61ba42c6510bcc744f96a27ca9a91fe965762ce4ec6206720441346ec1e5402475e27e87a7b838d342898c47df99a1e8025abf5ebce59691250417bc0015f
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.3
|
1
|
+
2.5.3
|
data/README.md
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
sequel_bitemporal
|
2
2
|
=================
|
3
3
|
|
4
|
-
[![Build Status](https://travis-ci.org/TalentBox/sequel_bitemporal.
|
4
|
+
[![Build Status](https://travis-ci.org/TalentBox/sequel_bitemporal.svg?branch=master)](https://travis-ci.org/TalentBox/sequel_bitemporal)
|
5
5
|
|
6
|
-
Bitemporal versioning for
|
6
|
+
Bitemporal versioning for [Sequel].
|
7
7
|
|
8
8
|
Dependencies
|
9
9
|
------------
|
@@ -47,7 +47,12 @@ Thanks to Evgeniy L (@fiscal-cliff) for his contributions:
|
|
47
47
|
Thanks to Ksenia Zalesnaya (@ksenia-zalesnaya) for her contributions:
|
48
48
|
- define setter methods for versioned columns
|
49
49
|
|
50
|
+
Thanks to Denis Kalesnikov (@DenisKem) for his contributions:
|
51
|
+
- allow composite primary key
|
52
|
+
|
50
53
|
License
|
51
54
|
-------
|
52
55
|
|
53
56
|
sequel_bitemporal is Copyright © 2011 TalentBox SA. It is free software, and may be redistributed under the terms specified in the LICENSE file.
|
57
|
+
|
58
|
+
[Sequel]: http://sequel.jeremyevans.net/
|
data/Rakefile
CHANGED
@@ -31,19 +31,25 @@ module Sequel
|
|
31
31
|
Thread.current[THREAD_NOW_KEY] || DateTime.now
|
32
32
|
end
|
33
33
|
|
34
|
-
def self.
|
35
|
-
|
34
|
+
def self.version_foreign_keys(master = nil)
|
35
|
+
return :master_id unless master
|
36
|
+
primary_key = [*master.primary_key]
|
37
|
+
primary_key.size > 1 ? primary_key : :master_id
|
36
38
|
end
|
37
39
|
|
38
|
-
def self.
|
39
|
-
|
40
|
+
def self.bitemporal_version_columns(master = nil)
|
41
|
+
[*version_foreign_keys(master), :valid_from, :valid_to, :created_at, :expired_at]
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.bitemporal_excluded_columns(master = nil)
|
45
|
+
[:id, *bitemporal_version_columns(master)]
|
40
46
|
end
|
41
47
|
|
42
48
|
def self.configure(master, opts = {})
|
43
49
|
version = opts[:version_class]
|
44
50
|
raise Error, "please specify version class to use for bitemporal plugin" unless version
|
45
51
|
return version.db.log_info("Version table does not exist for #{version.name}") unless version.db.table_exists?(version.table_name)
|
46
|
-
missing = bitemporal_version_columns - version.columns
|
52
|
+
missing = bitemporal_version_columns(master) - version.columns
|
47
53
|
raise Error, "bitemporal plugin requires the following missing column#{"s" if missing.size>1} on version class: #{missing.join(", ")}" unless missing.empty?
|
48
54
|
|
49
55
|
if Sequel::Plugins::Bitemporal.jdbc?(master.db)
|
@@ -76,7 +82,7 @@ module Sequel
|
|
76
82
|
@audit_updated_by_method = opts.fetch(:audit_updated_by_method){ :updated_by }
|
77
83
|
@propagate_per_column = opts.fetch(:propagate_per_column, false)
|
78
84
|
@version_uses_string_nilifier = version.plugins.map(&:to_s).include? "Sequel::Plugins::StringNilifier"
|
79
|
-
@excluded_columns = Sequel::Plugins::Bitemporal.bitemporal_excluded_columns
|
85
|
+
@excluded_columns = Sequel::Plugins::Bitemporal.bitemporal_excluded_columns(master)
|
80
86
|
@excluded_columns += Array opts[:excluded_columns] if opts[:excluded_columns]
|
81
87
|
@use_ranges = if opts[:ranges]
|
82
88
|
db = self.db
|
@@ -100,8 +106,8 @@ module Sequel
|
|
100
106
|
end
|
101
107
|
end
|
102
108
|
end
|
103
|
-
master.one_to_many :versions, class: version, key:
|
104
|
-
master.one_to_one :current_version, class: version, key:
|
109
|
+
master.one_to_many :versions, class: version, key: version_foreign_keys(master), graph_alias_base: master.versions_alias
|
110
|
+
master.one_to_one :current_version, class: version, key: version_foreign_keys(master), graph_alias_base: master.current_version_alias, :graph_block=>(proc do |j, lj, js|
|
105
111
|
t = Sequel.delay{ ::Sequel::Plugins::Bitemporal.point_in_time }
|
106
112
|
n = Sequel.delay{ ::Sequel::Plugins::Bitemporal.now }
|
107
113
|
if master.use_ranges
|
@@ -135,7 +141,7 @@ module Sequel
|
|
135
141
|
)
|
136
142
|
)
|
137
143
|
end
|
138
|
-
master.one_to_many :current_or_future_versions, class: version, key:
|
144
|
+
master.one_to_many :current_or_future_versions, class: version, key: version_foreign_keys(master), :graph_block=>(proc do |j, lj, js|
|
139
145
|
t = Sequel.delay{ ::Sequel::Plugins::Bitemporal.point_in_time }
|
140
146
|
n = Sequel.delay{ ::Sequel::Plugins::Bitemporal.now }
|
141
147
|
if master.use_ranges
|
@@ -169,7 +175,7 @@ module Sequel
|
|
169
175
|
Sequel.negate(Sequel.qualify(:current_or_future_versions, :id) => nil)
|
170
176
|
)
|
171
177
|
end
|
172
|
-
version.many_to_one :master, class: master, key:
|
178
|
+
version.many_to_one :master, class: master, key: version_foreign_keys(master)
|
173
179
|
version.class_eval do
|
174
180
|
if Sequel::Plugins::Bitemporal.jdbc?(master.db)
|
175
181
|
plugin :typecast_on_load, *columns
|
@@ -319,7 +325,13 @@ module Sequel
|
|
319
325
|
|
320
326
|
def attributes=(attributes)
|
321
327
|
@pending_version ||= begin
|
322
|
-
current_attributes =
|
328
|
+
current_attributes =
|
329
|
+
if composite_primary_key?
|
330
|
+
version_values
|
331
|
+
else
|
332
|
+
{ master_id: id }
|
333
|
+
end
|
334
|
+
|
323
335
|
current_version.keys.each do |key|
|
324
336
|
next if excluded_columns.include? key
|
325
337
|
current_attributes[key] = current_version.send key
|
@@ -329,6 +341,10 @@ module Sequel
|
|
329
341
|
pending_version.set_all attributes
|
330
342
|
end
|
331
343
|
|
344
|
+
def version_values
|
345
|
+
version_foreign_keys.map { |k| [k, public_send(k)] }.to_h
|
346
|
+
end
|
347
|
+
|
332
348
|
def update_attributes(attributes={})
|
333
349
|
self.attributes = attributes
|
334
350
|
if save raise_on_failure: false
|
@@ -364,6 +380,10 @@ module Sequel
|
|
364
380
|
_refresh_set_values @values
|
365
381
|
end
|
366
382
|
|
383
|
+
def composite_primary_key?
|
384
|
+
[*primary_key].size > 1
|
385
|
+
end
|
386
|
+
|
367
387
|
def destroy
|
368
388
|
point_in_time = ::Sequel::Plugins::Bitemporal.point_in_time
|
369
389
|
versions_dataset.where(
|
@@ -471,6 +491,10 @@ module Sequel
|
|
471
491
|
@propagated_during_last_save ||= []
|
472
492
|
end
|
473
493
|
|
494
|
+
def version_foreign_keys
|
495
|
+
composite_primary_key? ? primary_key : :master_id
|
496
|
+
end
|
497
|
+
|
474
498
|
private
|
475
499
|
|
476
500
|
def prepare_pending_version
|
@@ -608,7 +632,7 @@ module Sequel
|
|
608
632
|
columns.detect do |column|
|
609
633
|
new_value = pending_version.send column
|
610
634
|
case column
|
611
|
-
when :id, :
|
635
|
+
when :id, :created_at, :expired_at, *version_foreign_keys
|
612
636
|
false
|
613
637
|
when :valid_from
|
614
638
|
pending_version.values.has_key?(:valid_from) && (
|
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.9.
|
6
|
+
s.version = "0.9.1"
|
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"
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
|
3
|
-
describe "Sequel::Plugins::Bitemporal" do
|
3
|
+
RSpec.describe "Sequel::Plugins::Bitemporal" do
|
4
4
|
before :all do
|
5
5
|
db_setup
|
6
6
|
end
|
@@ -791,7 +791,7 @@ describe "Sequel::Plugins::Bitemporal" do
|
|
791
791
|
end
|
792
792
|
end
|
793
793
|
|
794
|
-
describe "Sequel::Plugins::Bitemporal", "with audit" do
|
794
|
+
RSpec.describe "Sequel::Plugins::Bitemporal", "with audit" do
|
795
795
|
before :all do
|
796
796
|
@audit_class = Class.new do
|
797
797
|
def self.audit(*args); end
|
@@ -889,7 +889,7 @@ describe "Sequel::Plugins::Bitemporal", "with audit" do
|
|
889
889
|
end
|
890
890
|
end
|
891
891
|
end
|
892
|
-
describe "Sequel::Plugins::Bitemporal", "with audit, specifying how to get the author" do
|
892
|
+
RSpec.describe "Sequel::Plugins::Bitemporal", "with audit, specifying how to get the author" do
|
893
893
|
before :all do
|
894
894
|
@audit_class = Class.new do
|
895
895
|
def self.audit(*args); end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
if DbHelpers.pg?
|
3
|
-
describe "Sequel::Plugins::Bitemporal", "with ranges" do
|
3
|
+
RSpec.describe "Sequel::Plugins::Bitemporal", "with ranges" do
|
4
4
|
before :all do
|
5
5
|
db_setup ranges: true
|
6
6
|
end
|
@@ -637,7 +637,7 @@ if DbHelpers.pg?
|
|
637
637
|
end
|
638
638
|
end
|
639
639
|
|
640
|
-
describe "Sequel::Plugins::Bitemporal", "with audit and ranges" do
|
640
|
+
RSpec.describe "Sequel::Plugins::Bitemporal", "with audit and ranges" do
|
641
641
|
before :all do
|
642
642
|
@audit_class = Class.new do
|
643
643
|
def self.audit(*args); end
|
@@ -735,7 +735,7 @@ if DbHelpers.pg?
|
|
735
735
|
end
|
736
736
|
end
|
737
737
|
end
|
738
|
-
describe "Sequel::Plugins::Bitemporal", "with audit, ranges, specifying how to get the author" do
|
738
|
+
RSpec.describe "Sequel::Plugins::Bitemporal", "with audit, ranges, specifying how to get the author" do
|
739
739
|
before :all do
|
740
740
|
@audit_class = Class.new do
|
741
741
|
def self.audit(*args); end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
|
3
|
-
describe "Sequel::Plugins::Bitemporal" do
|
3
|
+
RSpec.describe "Sequel::Plugins::Bitemporal" do
|
4
4
|
let(:hour){ 3600 }
|
5
5
|
before :all do
|
6
6
|
db_setup use_time: true
|
@@ -381,7 +381,7 @@ describe "Sequel::Plugins::Bitemporal" do
|
|
381
381
|
expect(@master_class.with_current_or_future_versions.all.size).to eq(2)
|
382
382
|
end
|
383
383
|
end
|
384
|
-
describe "Sequel::Plugins::Bitemporal", "with audit" do
|
384
|
+
RSpec.describe "Sequel::Plugins::Bitemporal", "with audit" do
|
385
385
|
before :all do
|
386
386
|
@audit_class = Class.new
|
387
387
|
db_setup use_time: true, audit_class: @audit_class
|
@@ -435,7 +435,7 @@ describe "Sequel::Plugins::Bitemporal", "with audit" do
|
|
435
435
|
end
|
436
436
|
end
|
437
437
|
|
438
|
-
describe "Sequel::Plugins::Bitemporal", "with audit, specifying how to get the author" do
|
438
|
+
RSpec.describe "Sequel::Plugins::Bitemporal", "with audit, specifying how to get the author" do
|
439
439
|
before :all do
|
440
440
|
@audit_class = Class.new
|
441
441
|
db_setup use_time: true, audit_class: @audit_class, audit_updated_by_method: :author
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
if DbHelpers.pg?
|
3
|
-
describe "Sequel::Plugins::Bitemporal" do
|
3
|
+
RSpec.describe "Sequel::Plugins::Bitemporal" do
|
4
4
|
let(:hour){ 3600 }
|
5
5
|
before :all do
|
6
6
|
db_setup use_time: true, ranges: true
|
@@ -381,7 +381,7 @@ if DbHelpers.pg?
|
|
381
381
|
expect(@master_class.with_current_or_future_versions.all.size).to eq(2)
|
382
382
|
end
|
383
383
|
end
|
384
|
-
describe "Sequel::Plugins::Bitemporal", "with audit" do
|
384
|
+
RSpec.describe "Sequel::Plugins::Bitemporal", "with audit" do
|
385
385
|
before :all do
|
386
386
|
@audit_class = Class.new
|
387
387
|
db_setup use_time: true, audit_class: @audit_class, ranges: true
|
@@ -435,7 +435,7 @@ if DbHelpers.pg?
|
|
435
435
|
end
|
436
436
|
end
|
437
437
|
|
438
|
-
describe "Sequel::Plugins::Bitemporal", "with audit, specifying how to get the author" do
|
438
|
+
RSpec.describe "Sequel::Plugins::Bitemporal", "with audit, specifying how to get the author" do
|
439
439
|
before :all do
|
440
440
|
@audit_class = Class.new
|
441
441
|
db_setup(
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
RSpec.describe "Sequel::Plugins::Bitemporal", "composite_primary_key" do
|
4
|
+
before :all do
|
5
|
+
setup_composite_primary_key
|
6
|
+
end
|
7
|
+
|
8
|
+
describe "::version_foreign_keys" do
|
9
|
+
context "when is invoked without args" do
|
10
|
+
it "returns master_id" do
|
11
|
+
expect(Sequel::Plugins::Bitemporal.bitemporal_excluded_columns).to(
|
12
|
+
eq([:id, :master_id, :valid_from, :valid_to, :created_at, :expired_at])
|
13
|
+
)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "missing required foreign keys in version table" do
|
19
|
+
ERROR_TEXT = "bitemporal plugin requires the following missing columns on version class: department_id, team_id"
|
20
|
+
|
21
|
+
it "raises error" do
|
22
|
+
expect do
|
23
|
+
setup_composite_primary_key(with_foreign_key: false)
|
24
|
+
end.to raise_error Sequel::Error, ERROR_TEXT
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe "versions work correctly" do
|
29
|
+
let(:master) { @master_class.new(name: "john Smith") }
|
30
|
+
let(:junior) { "Junior Ruby-developer" }
|
31
|
+
let(:senior) { "Senior Ruby-developer" }
|
32
|
+
let(:two_years_in_seconds) { 2 * 365 * 24 * 60 * 60 }
|
33
|
+
|
34
|
+
before do
|
35
|
+
setup_composite_primary_key
|
36
|
+
master.department_id = 1
|
37
|
+
master.team_id = 1
|
38
|
+
master.save
|
39
|
+
|
40
|
+
Sequel::Plugins::Bitemporal.at(Time.now) do
|
41
|
+
master.update_attributes(position: junior)
|
42
|
+
end
|
43
|
+
|
44
|
+
Sequel::Plugins::Bitemporal.at(Time.now + two_years_in_seconds) do
|
45
|
+
master.update_attributes(position: senior)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
specify do
|
50
|
+
Sequel::Plugins::Bitemporal.at(Time.now) do
|
51
|
+
expect(master.reload.position).to eq(junior)
|
52
|
+
end
|
53
|
+
|
54
|
+
Sequel::Plugins::Bitemporal.at(Time.now + two_years_in_seconds) do
|
55
|
+
expect(master.reload.position).to eq(senior)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/spec/spec_helper.rb
CHANGED
data/spec/support/db.rb
CHANGED
@@ -63,6 +63,48 @@ module DbHelpers
|
|
63
63
|
end
|
64
64
|
end
|
65
65
|
|
66
|
+
def setup_composite_primary_key(with_foreign_key: true)
|
67
|
+
DB.drop_table(:employee_versions) if DB.table_exists?(:employee_versions)
|
68
|
+
DB.drop_table(:employees) if DB.table_exists?(:employees)
|
69
|
+
|
70
|
+
DB.create_table! :employees do
|
71
|
+
Integer :department_id
|
72
|
+
Integer :team_id
|
73
|
+
primary_key [:department_id, :team_id]
|
74
|
+
String :name, null: false
|
75
|
+
end
|
76
|
+
|
77
|
+
DB.create_table! :employee_versions do
|
78
|
+
if with_foreign_key
|
79
|
+
Integer :department_id
|
80
|
+
Integer :team_id
|
81
|
+
|
82
|
+
foreign_key [:department_id, :team_id], :employees
|
83
|
+
end
|
84
|
+
|
85
|
+
primary_key :id
|
86
|
+
String :position
|
87
|
+
Date :created_at
|
88
|
+
Date :expired_at
|
89
|
+
Date :valid_from
|
90
|
+
Date :valid_to
|
91
|
+
end
|
92
|
+
|
93
|
+
@version_class = Class.new Sequel::Model do
|
94
|
+
set_dataset :employee_versions
|
95
|
+
end
|
96
|
+
|
97
|
+
bitemporal_options = {
|
98
|
+
version_class: @version_class,
|
99
|
+
key: [:department_id, :team_id]
|
100
|
+
}
|
101
|
+
|
102
|
+
@master_class = Class.new Sequel::Model do
|
103
|
+
set_dataset :employees
|
104
|
+
plugin :bitemporal, bitemporal_options
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
66
108
|
def db_truncate
|
67
109
|
if DbHelpers.pg?
|
68
110
|
@version_class.truncate cascade: true
|
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.9.
|
4
|
+
version: 0.9.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joseph HALTER
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2019-01-29 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: sequel
|
@@ -98,6 +98,7 @@ files:
|
|
98
98
|
- spec/bitemporal_serialization_spec.rb
|
99
99
|
- spec/bitemporal_time_spec.rb
|
100
100
|
- spec/bitemporal_time_with_range_spec.rb
|
101
|
+
- spec/composite_primary_key_spec.rb
|
101
102
|
- spec/spec_helper.rb
|
102
103
|
- spec/support/bitemporal_matchers.rb
|
103
104
|
- spec/support/db.rb
|
@@ -131,6 +132,7 @@ test_files:
|
|
131
132
|
- spec/bitemporal_serialization_spec.rb
|
132
133
|
- spec/bitemporal_time_spec.rb
|
133
134
|
- spec/bitemporal_time_with_range_spec.rb
|
135
|
+
- spec/composite_primary_key_spec.rb
|
134
136
|
- spec/spec_helper.rb
|
135
137
|
- spec/support/bitemporal_matchers.rb
|
136
138
|
- spec/support/db.rb
|