sequel_bitemporal 0.9.1 → 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +64 -0
- data/README.md +33 -15
- data/lib/sequel/plugins/bitemporal.rb +26 -25
- data/sequel_bitemporal.gemspec +1 -1
- data/spec/bitemporal_serialization_spec.rb +38 -0
- data/spec/spec_helper.rb +1 -5
- data/spec/support/db.rb +14 -1
- metadata +7 -8
- data/.travis.yml +0 -24
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 33df8e3ea5762139cd17bc15469b6f4c0535e698191eff1db9feb1387be4eb9b
|
4
|
+
data.tar.gz: da3482ad1fa5d018fc50e95c45940beba6013205574f65257be300abbfc119ee
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0b6f966365eaf5c42143b5e050c467aaef0b1e479c68fd792cbedc1cf4d3c420962651254ca9dd1344c95e4401f3ee2ba56f6acb54702248a8d4d5684d351265
|
7
|
+
data.tar.gz: 176b15f045e9b91b29857f0b5e1ffcbb313d44cd623bd10718cbdabd359ba929b55c231882d639e47499c496307c8b5bbf9c160cacc11569d46c624bca9439d3
|
@@ -0,0 +1,64 @@
|
|
1
|
+
name: CI
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
branches: [ master ]
|
6
|
+
pull_request:
|
7
|
+
branches: ['**']
|
8
|
+
|
9
|
+
jobs:
|
10
|
+
tests:
|
11
|
+
services:
|
12
|
+
postgres:
|
13
|
+
image: postgres:13
|
14
|
+
env:
|
15
|
+
POSTGRES_USER: postgres
|
16
|
+
POSTGRES_PASSWORD: postgres
|
17
|
+
ports:
|
18
|
+
- 5432
|
19
|
+
options: >-
|
20
|
+
--health-cmd pg_isready
|
21
|
+
--health-interval 10s
|
22
|
+
--health-timeout 5s
|
23
|
+
--health-retries 5
|
24
|
+
|
25
|
+
runs-on: ubuntu-latest
|
26
|
+
strategy:
|
27
|
+
fail-fast: false
|
28
|
+
matrix:
|
29
|
+
ruby:
|
30
|
+
- "2.7"
|
31
|
+
- "3.0"
|
32
|
+
- "3.1"
|
33
|
+
- "3.2"
|
34
|
+
- "3.3"
|
35
|
+
name: Ruby ${{ matrix.ruby }}
|
36
|
+
|
37
|
+
env:
|
38
|
+
SEQUEL: "~> 5.0"
|
39
|
+
BUNDLE_GEMFILE: "ci/sequel.gemfile"
|
40
|
+
steps:
|
41
|
+
- uses: actions/checkout@v3
|
42
|
+
- name: Install db dependencies and check connections
|
43
|
+
run: |
|
44
|
+
env PGPASSWORD=postgres psql -h localhost -p ${{ job.services.postgres.ports[5432] }} -U postgres -l
|
45
|
+
- uses: ruby/setup-ruby@v1
|
46
|
+
with:
|
47
|
+
ruby-version: ${{ matrix.ruby }}
|
48
|
+
bundler-cache: true
|
49
|
+
- name: Create databases
|
50
|
+
run: |
|
51
|
+
env PGPASSWORD=postgres psql -c 'create database sequel_bitemporal_test;' -U postgres -h localhost -p ${{ job.services.postgres.ports[5432] }}
|
52
|
+
- name: Run PostgreSQL tests
|
53
|
+
run: bundle exec rake spec
|
54
|
+
env:
|
55
|
+
TEST_ADAPTER: postgresql
|
56
|
+
TEST_DATABASE: sequel_bitemporal_test
|
57
|
+
TEST_DATABASE_HOST: localhost
|
58
|
+
TEST_DATABASE_PORT: ${{ job.services.postgres.ports[5432] }}
|
59
|
+
TEST_USERNAME: postgres
|
60
|
+
TEST_PASSWORD: postgres
|
61
|
+
- name: Run SQLite tests
|
62
|
+
run: bundle exec rake spec
|
63
|
+
env:
|
64
|
+
TEST_ADAPTER: sqlite
|
data/README.md
CHANGED
@@ -14,29 +14,37 @@ Dependencies
|
|
14
14
|
Usage
|
15
15
|
-----
|
16
16
|
|
17
|
-
|
17
|
+
Declare bitemporality inside your model:
|
18
18
|
|
19
|
-
|
20
|
-
|
19
|
+
```ruby
|
20
|
+
class HotelPriceVersion < Sequel::Model
|
21
|
+
end
|
21
22
|
|
22
|
-
|
23
|
-
|
24
|
-
|
23
|
+
class HotelPrice < Sequel::Model
|
24
|
+
plugin :bitemporal, version_class: HotelPriceVersion
|
25
|
+
end
|
26
|
+
```
|
25
27
|
|
26
|
-
|
28
|
+
You can now create a hotel price with bitemporal versions:
|
27
29
|
|
28
|
-
|
29
|
-
|
30
|
+
```ruby
|
31
|
+
price = HotelPrice.new
|
32
|
+
price.update_attributes price: 18
|
33
|
+
```
|
30
34
|
|
31
|
-
|
35
|
+
To show all versions:
|
32
36
|
|
33
|
-
|
37
|
+
```ruby
|
38
|
+
price.versions
|
39
|
+
```
|
34
40
|
|
35
|
-
|
41
|
+
To show current version:
|
36
42
|
|
37
|
-
|
43
|
+
```ruby
|
44
|
+
price.current_version
|
45
|
+
```
|
38
46
|
|
39
|
-
|
47
|
+
Look at the specs for more usage patterns.
|
40
48
|
|
41
49
|
Thanks
|
42
50
|
------
|
@@ -48,7 +56,17 @@ Thanks to Ksenia Zalesnaya (@ksenia-zalesnaya) for her contributions:
|
|
48
56
|
- define setter methods for versioned columns
|
49
57
|
|
50
58
|
Thanks to Denis Kalesnikov (@DenisKem) for his contributions:
|
51
|
-
-
|
59
|
+
- add support for composite primary key
|
60
|
+
[#8](https://github.com/TalentBox/sequel_bitemporal/pull/8)
|
61
|
+
|
62
|
+
Thanks to Olle Jonsson (@olleolleolle) for his contributions:
|
63
|
+
- update specs to work with RSpec: `config.disable_monkey_patching!`
|
64
|
+
[#10](https://github.com/TalentBox/sequel_bitemporal/pull/10)
|
65
|
+
- update TravisCI matrix to include more Ruby versions
|
66
|
+
[#11](https://github.com/TalentBox/sequel_bitemporal/pull/10)
|
67
|
+
- README improvements
|
68
|
+
[#9](https://github.com/TalentBox/sequel_bitemporal/pull/9)
|
69
|
+
[#12](https://github.com/TalentBox/sequel_bitemporal/pull/12)
|
52
70
|
|
53
71
|
License
|
54
72
|
-------
|
@@ -332,9 +332,9 @@ module Sequel
|
|
332
332
|
{ master_id: id }
|
333
333
|
end
|
334
334
|
|
335
|
-
current_version.
|
335
|
+
current_version.columns.each do |key|
|
336
336
|
next if excluded_columns.include? key
|
337
|
-
current_attributes[key] = current_version.
|
337
|
+
current_attributes[key] = current_version.public_send key
|
338
338
|
end if current_version?
|
339
339
|
model.version_class.new current_attributes
|
340
340
|
end
|
@@ -469,7 +469,7 @@ module Sequel
|
|
469
469
|
last_version_attributes = if last_version
|
470
470
|
last_version.columns.each_with_object({}) do |column, hash|
|
471
471
|
unless excluded_columns.include? column
|
472
|
-
hash[column] = last_version.
|
472
|
+
hash[column] = last_version.public_send column
|
473
473
|
end
|
474
474
|
end
|
475
475
|
else
|
@@ -539,33 +539,28 @@ module Sequel
|
|
539
539
|
to_check_columns = self.class.version_class.columns - excluded_columns
|
540
540
|
updated_by = (send(self.class.audit_updated_by_method) if audited?)
|
541
541
|
previous_values = @current_version_values || {}
|
542
|
-
|
543
|
-
columns = pending_version.columns - excluded_columns_for_changes
|
544
|
-
columns.each do |column|
|
545
|
-
current_version_values[column] = pending_version.public_send(column)
|
546
|
-
end
|
547
|
-
|
542
|
+
current_values = values_for_changes pending_version
|
548
543
|
futures.each do |future_version|
|
549
544
|
attrs = {}
|
550
545
|
to_check_columns.each do |col|
|
551
546
|
if previous_values[col]==future_version[col] &&
|
552
|
-
previous_values[col]!=
|
553
|
-
attrs[col] =
|
547
|
+
previous_values[col]!=current_values[col]
|
548
|
+
attrs[col] = current_values[col]
|
554
549
|
end
|
555
550
|
end
|
556
551
|
if attrs.any?
|
557
552
|
propagated = save_propagated future_version, attrs
|
553
|
+
previous_values = values_for_changes future_version
|
554
|
+
current_values = values_for_changes propagated
|
558
555
|
if !propagated.new? && audited? && updated_by
|
559
556
|
self.class.audit_class.audit(
|
560
557
|
self,
|
561
|
-
|
562
|
-
|
558
|
+
previous_values,
|
559
|
+
current_values,
|
563
560
|
propagated.valid_from,
|
564
561
|
updated_by
|
565
562
|
)
|
566
563
|
end
|
567
|
-
previous_values = future_version.values.dup
|
568
|
-
current_version_values = propagated.values
|
569
564
|
future_version.this.update :expired_at => Sequel::Plugins::Bitemporal.point_in_time
|
570
565
|
else
|
571
566
|
break
|
@@ -583,7 +578,7 @@ module Sequel
|
|
583
578
|
self.class.audit_class.audit(
|
584
579
|
self,
|
585
580
|
current_values_for_audit,
|
586
|
-
pending_version
|
581
|
+
values_for_changes(pending_version),
|
587
582
|
pending_version.valid_from,
|
588
583
|
updated_by
|
589
584
|
) if updated_by
|
@@ -597,8 +592,8 @@ module Sequel
|
|
597
592
|
|
598
593
|
def save_fossil(expired, attributes={})
|
599
594
|
fossil = model.version_class.new
|
600
|
-
expired_attributes = expired.
|
601
|
-
hash[key] = expired.
|
595
|
+
expired_attributes = expired.columns.each_with_object({}) do |key, hash|
|
596
|
+
hash[key] = expired.public_send key
|
602
597
|
end
|
603
598
|
expired_attributes.delete :id
|
604
599
|
fossil.set_all expired_attributes.merge(attributes)
|
@@ -608,8 +603,8 @@ module Sequel
|
|
608
603
|
|
609
604
|
def save_propagated(version, attributes={})
|
610
605
|
propagated = model.version_class.new
|
611
|
-
version_attributes = version.
|
612
|
-
hash[key] = version.
|
606
|
+
version_attributes = version.columns.each_with_object({}) do |key, hash|
|
607
|
+
hash[key] = version.public_send key
|
613
608
|
end
|
614
609
|
version_attributes.delete :id
|
615
610
|
version_attributes[:created_at] = Sequel::Plugins::Bitemporal.point_in_time
|
@@ -627,10 +622,8 @@ module Sequel
|
|
627
622
|
def pending_version_holds_changes?
|
628
623
|
return false unless @pending_version
|
629
624
|
return true unless current_version?
|
630
|
-
@current_version_values = current_version
|
631
|
-
|
632
|
-
columns.detect do |column|
|
633
|
-
new_value = pending_version.send column
|
625
|
+
@current_version_values = values_for_changes current_version
|
626
|
+
values_for_changes(pending_version).any? do |column, new_value|
|
634
627
|
case column
|
635
628
|
when :id, :created_at, :expired_at, *version_foreign_keys
|
636
629
|
false
|
@@ -653,7 +646,7 @@ module Sequel
|
|
653
646
|
new_value = model.version_class.input_transformers[:string_nilifier].call(new_value)
|
654
647
|
end
|
655
648
|
end
|
656
|
-
current_version.
|
649
|
+
current_version.public_send(column)!=new_value
|
657
650
|
end
|
658
651
|
end
|
659
652
|
end
|
@@ -670,6 +663,14 @@ module Sequel
|
|
670
663
|
@initial_version ||= model.version_class.new
|
671
664
|
end
|
672
665
|
|
666
|
+
def values_for_changes(version)
|
667
|
+
values = {}
|
668
|
+
(version.columns - excluded_columns_for_changes).each do |column|
|
669
|
+
values[column] = version.public_send column
|
670
|
+
end
|
671
|
+
values
|
672
|
+
end
|
673
|
+
|
673
674
|
end
|
674
675
|
end
|
675
676
|
end
|
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.
|
6
|
+
s.version = "0.11.0"
|
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"
|
@@ -72,4 +72,42 @@ RSpec.describe "Sequel::Plugins::Bitemporal", :skip_jdbc_sqlite do
|
|
72
72
|
end
|
73
73
|
end
|
74
74
|
|
75
|
+
it "can propage changes to future version per column with defaults" do
|
76
|
+
propagate_per_column = @master_class.propagate_per_column
|
77
|
+
begin
|
78
|
+
@master_class.instance_variable_set :@propagate_per_column, true
|
79
|
+
@version_class.class_eval do
|
80
|
+
define_method :name do
|
81
|
+
super() || {}
|
82
|
+
end
|
83
|
+
end
|
84
|
+
master = @master_class.new
|
85
|
+
expect( master.name ).to eql({})
|
86
|
+
expect( master.save ).to be_truthy
|
87
|
+
@version_class.create master_id: master.id, valid_from: Date.parse("2016-01-01"), valid_to: Date.parse("2017-01-01"), price: 0
|
88
|
+
@version_class.create master_id: master.id, valid_from: Date.parse("2017-01-01"), valid_to: Date.parse("2018-01-01"), price: 0, length: 10
|
89
|
+
@version_class.create master_id: master.id, valid_from: Date.parse("2018-01-01"), valid_to: Date.parse("2019-01-01"), price: 0, length: 20
|
90
|
+
@version_class.create master_id: master.id, valid_from: Date.parse("2019-01-01"), valid_to: Date.parse("2020-01-01"), price: 10, length: 20
|
91
|
+
expect(
|
92
|
+
master.update_attributes valid_from: Date.parse("2016-01-01"), price: 10
|
93
|
+
).to be_truthy
|
94
|
+
result = master.versions_dataset.all.map do |version|
|
95
|
+
[!!version.expired_at, version.valid_from.year, version.valid_to.year, version.price, version.length, version.width, version[:name]]
|
96
|
+
end
|
97
|
+
expect(result).to match_array [
|
98
|
+
[false, 2016, 2017, 10, nil, nil, nil],
|
99
|
+
[false, 2017, 2018, 0, 10, nil, "{}"],
|
100
|
+
[false, 2018, 2019, 0, 20, nil, nil],
|
101
|
+
[false, 2019, 2020, 10, 20, nil, nil],
|
102
|
+
[true, 2016, 2017, 0, nil, nil, nil],
|
103
|
+
[true, 2017, 2018, 0, 10, nil, nil],
|
104
|
+
]
|
105
|
+
ensure
|
106
|
+
@version_class.class_eval do
|
107
|
+
undef_method :name
|
108
|
+
end
|
109
|
+
@master_class.instance_variable_set :@propagate_per_column, propagate_per_column
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
75
113
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -12,11 +12,7 @@ rspec_exclusions = {}
|
|
12
12
|
DB = if DbHelpers.pg?
|
13
13
|
`createdb sequel_bitemporal_test`
|
14
14
|
Sequel.extension :pg_range, :pg_range_ops
|
15
|
-
|
16
|
-
Sequel.connect "jdbc:postgresql://localhost/sequel_bitemporal_test"
|
17
|
-
else
|
18
|
-
Sequel.postgres "sequel_bitemporal_test"
|
19
|
-
end
|
15
|
+
Sequel.connect DbHelpers.pg_ruby_connect_uri
|
20
16
|
else
|
21
17
|
if Sequel::Plugins::Bitemporal.jruby?
|
22
18
|
rspec_exclusions[:skip_jdbc_sqlite] = true
|
data/spec/support/db.rb
CHANGED
@@ -1,13 +1,26 @@
|
|
1
1
|
module DbHelpers
|
2
2
|
|
3
3
|
def self.pg?
|
4
|
-
ENV.has_key? "
|
4
|
+
!ENV.has_key?("TEST_ADAPTER") || ENV["TEST_ADAPTER"]=="postgresql"
|
5
5
|
end
|
6
6
|
|
7
7
|
def self.jruby?
|
8
8
|
(defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby') || defined?(JRUBY_VERSION)
|
9
9
|
end
|
10
10
|
|
11
|
+
def self.pg_ruby_connect_uri
|
12
|
+
username = ENV.fetch("TEST_USERNAME", nil)
|
13
|
+
password = ENV.fetch("TEST_PASSWORD", nil)
|
14
|
+
auth = [username, password].compact.join(":")
|
15
|
+
|
16
|
+
uri = "postgresql://#{"#{auth}@" unless auth.empty?}#{ENV.fetch("TEST_DATABASE_HOST", "127.0.0.1")}:#{ENV.fetch("TEST_DATABASE_PORT", "5432")}/#{ENV.fetch("TEST_DATABASE", "sequel_bitemporal_test")}"
|
17
|
+
if jruby?
|
18
|
+
"jdbc:#{uri}"
|
19
|
+
else
|
20
|
+
uri
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
11
24
|
def db_setup(opts={})
|
12
25
|
use_time = opts[:use_time]
|
13
26
|
DB.drop_table(:room_versions) if DB.table_exists?(:room_versions)
|
metadata
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sequel_bitemporal
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.11.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joseph HALTER
|
8
8
|
- Jonathan TRON
|
9
|
-
autorequire:
|
9
|
+
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2024-02-12 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: sequel
|
@@ -81,10 +81,10 @@ executables: []
|
|
81
81
|
extensions: []
|
82
82
|
extra_rdoc_files: []
|
83
83
|
files:
|
84
|
+
- ".github/workflows/ci.yml"
|
84
85
|
- ".gitignore"
|
85
86
|
- ".rspec"
|
86
87
|
- ".ruby-version"
|
87
|
-
- ".travis.yml"
|
88
88
|
- Gemfile
|
89
89
|
- LICENSE
|
90
90
|
- README.md
|
@@ -106,7 +106,7 @@ homepage: https://github.com/TalentBox/sequel_bitemporal
|
|
106
106
|
licenses:
|
107
107
|
- MIT
|
108
108
|
metadata: {}
|
109
|
-
post_install_message:
|
109
|
+
post_install_message:
|
110
110
|
rdoc_options: []
|
111
111
|
require_paths:
|
112
112
|
- lib
|
@@ -121,9 +121,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
121
121
|
- !ruby/object:Gem::Version
|
122
122
|
version: '0'
|
123
123
|
requirements: []
|
124
|
-
|
125
|
-
|
126
|
-
signing_key:
|
124
|
+
rubygems_version: 3.5.3
|
125
|
+
signing_key:
|
127
126
|
specification_version: 4
|
128
127
|
summary: Bitemporal versioning for sequel.
|
129
128
|
test_files:
|
data/.travis.yml
DELETED
@@ -1,24 +0,0 @@
|
|
1
|
-
language: ruby
|
2
|
-
cache:
|
3
|
-
bundler: true
|
4
|
-
sudo: false
|
5
|
-
rvm:
|
6
|
-
- 2.2.7
|
7
|
-
- 2.3.4
|
8
|
-
- 2.4.2
|
9
|
-
- jruby-9.1.13.0
|
10
|
-
before_script:
|
11
|
-
- psql -c 'create database sequel_bitemporal_test;' -U postgres
|
12
|
-
env:
|
13
|
-
- SQLITE=1 SEQUEL='~> 4.0'
|
14
|
-
- SQLITE=1 SEQUEL='~> 5.0'
|
15
|
-
- PG=1 SEQUEL='~> 4.0'
|
16
|
-
- PG=1 SEQUEL='~> 5.0'
|
17
|
-
gemfile:
|
18
|
-
- ci/sequel.gemfile
|
19
|
-
matrix:
|
20
|
-
allow_failures:
|
21
|
-
- rvm: jruby-9.1.13.0
|
22
|
-
env: PG=1 SEQUEL='~> 4.0'
|
23
|
-
- rvm: jruby-9.1.13.0
|
24
|
-
env: PG=1 SEQUEL='~> 5.0'
|