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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4f44bf401ad71c80c9815ff7d2e3508edf8e5bdbef4112b36bf86d06a6d02523
4
- data.tar.gz: 19b320461ebbdac3b2e7ace0117ffa2921180dd602b59e5145d2c1771987d993
3
+ metadata.gz: 7f6daa2d7f025cd1281e8cbfc7235d0a6ddd85d2251f8d9195d85cc5340aafb3
4
+ data.tar.gz: 5d5de257ee011436a1c3959206c31c985d6b11597d63fcab468876865394f9ee
5
5
  SHA512:
6
- metadata.gz: 1e9928807145999a4ba2215b10e3169bb26e63bc42137c28b9fc05ad6ac9a2e66f10c69ccdda40ae1ae8d7bbfabad0f1f921122fc3e0bb155da559b335ffa97f
7
- data.tar.gz: a7fac94af384e4478fd9d6f98a6381bf03278e1310d8d97dff5bea449f2e3c5baeb962e3552774535a14b0a021a5b0ce22d76b659b08f7fcf9fe9a17a36d31dc
6
+ metadata.gz: '0309ac260a37ec550e417efb01eaa72120583a5c26c6306f52c15910d36c8dc24f9e74d39319f763e3a566192ddd5887b0b022ce747f820beee388a444726dc8'
7
+ data.tar.gz: f4f61ba42c6510bcc744f96a27ca9a91fe965762ce4ec6206720441346ec1e5402475e27e87a7b838d342898c47df99a1e8025abf5ebce59691250417bc0015f
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 2.3.1
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.png?branch=master)](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 sequel.
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
@@ -3,4 +3,4 @@ require "rspec/core/rake_task"
3
3
  RSpec::Core::RakeTask.new("spec").tap do |config|
4
4
  config.rspec_opts = "--color"
5
5
  end
6
- task :default => :spec
6
+ task :default => :spec
@@ -31,19 +31,25 @@ module Sequel
31
31
  Thread.current[THREAD_NOW_KEY] || DateTime.now
32
32
  end
33
33
 
34
- def self.bitemporal_version_columns
35
- @bitemporal_version_columns ||= [:master_id, :valid_from, :valid_to, :created_at, :expired_at]
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.bitemporal_excluded_columns
39
- @bitemporal_excluded_columns ||= [:id, *bitemporal_version_columns]
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: :master_id, graph_alias_base: master.versions_alias
104
- master.one_to_one :current_version, class: version, key: :master_id, graph_alias_base: master.current_version_alias, :graph_block=>(proc do |j, lj, js|
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: :master_id, :graph_block=>(proc do |j, lj, js|
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: :master_id
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 = {master_id: id}
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, :master_id, :created_at, :expired_at
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) && (
@@ -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.0"
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,7 +1,7 @@
1
1
  require "spec_helper"
2
2
  require "json"
3
3
 
4
- describe "Sequel::Plugins::Bitemporal", :skip_jdbc_sqlite do
4
+ RSpec.describe "Sequel::Plugins::Bitemporal", :skip_jdbc_sqlite do
5
5
  before :all do
6
6
  db_setup
7
7
  @version_class.instance_eval do
@@ -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
@@ -35,6 +35,7 @@ end
35
35
  RSpec.configure do |config|
36
36
  config.include DbHelpers
37
37
  config.filter_run_excluding rspec_exclusions
38
+ config.disable_monkey_patching!
38
39
  config.before :each do
39
40
  db_truncate
40
41
  end
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.0
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: 2018-11-29 00:00:00.000000000 Z
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