sequel_bitemporal 0.6.11 → 0.6.12

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/.travis.yml CHANGED
@@ -1,5 +1,8 @@
1
1
  rvm:
2
2
  - 1.9.3
3
3
  - 2.0.0
4
+ env:
5
+ - SQLITE=1
6
+ - PG=1
4
7
  notifications:
5
8
  disabled: true
@@ -53,33 +53,80 @@ module Sequel
53
53
  @audit_updated_by_method = opts.fetch(:audit_updated_by_method){ :updated_by }
54
54
  @propagate_per_column = opts.fetch(:propagate_per_column, false)
55
55
  @version_uses_string_nilifier = version.plugins.map(&:to_s).include? "Sequel::Plugins::StringNilifier"
56
+ @use_ranges = if opts[:ranges]
57
+ db = self.db
58
+ unless db.database_type==:postgres && db.server_version >= 90200
59
+ raise "Ranges require PostgreSQL 9.2"
60
+ end
61
+ true
62
+ else
63
+ false
64
+ end
56
65
  end
57
66
  master.one_to_many :versions, class: version, key: :master_id, graph_alias_base: master.versions_alias
58
67
  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|
59
68
  t = ::Sequel::Plugins::Bitemporal.point_in_time
60
69
  n = ::Sequel::Plugins::Bitemporal.now
61
- e = :expired_at.qualify(j)
62
- (:created_at.qualify(j) <= t) & ({e=>nil} | (e > t)) & (:valid_from.qualify(j) <= n) & (:valid_to.qualify(j) > n)
70
+ if master.use_ranges
71
+ master.existence_range_contains(t, j) & master.validity_range_contains(n, j)
72
+ else
73
+ e = Sequel.qualify j, :expired_at
74
+ (Sequel.qualify(j, :created_at) <= t) &
75
+ (Sequel.|({e=>nil}, e > t)) &
76
+ (Sequel.qualify(j, :valid_from) <= n) &
77
+ (Sequel.qualify(j, :valid_to) > n)
78
+ end
63
79
  end) do |ds|
64
80
  t = ::Sequel::Plugins::Bitemporal.point_in_time
65
81
  n = ::Sequel::Plugins::Bitemporal.now
66
- ds.where{(created_at <= t) & ({expired_at=>nil} | (expired_at > t)) & (valid_from <= n) & (valid_to > n)}
82
+ if model.use_ranges
83
+ ds.where(model.existence_range_contains(t) & model.validity_range_contains(n))
84
+ else
85
+ ds.where do
86
+ (created_at <= t) &
87
+ (Sequel.|({expired_at=>nil}, expired_at > t)) &
88
+ (valid_from <= n) &
89
+ (valid_to > n)
90
+ end
91
+ end
67
92
  end
68
93
  master.def_dataset_method :with_current_version do
69
- eager_graph(:current_version).where({:id.qualify(model.current_version_alias) => nil}.sql_negate)
94
+ eager_graph(:current_version).where(
95
+ Sequel.negate(
96
+ Sequel.qualify(model.current_version_alias, :id) => nil
97
+ )
98
+ )
70
99
  end
71
100
  master.one_to_many :current_or_future_versions, class: version, key: :master_id, :graph_block=>(proc do |j, lj, js|
72
101
  t = ::Sequel::Plugins::Bitemporal.point_in_time
73
102
  n = ::Sequel::Plugins::Bitemporal.now
74
- e = :expired_at.qualify(j)
75
- (:created_at.qualify(j) <= t) & ({e=>nil} | (e > t)) & (:valid_to.qualify(j) > n)
103
+ if master.use_ranges
104
+ master.existence_range_contains(t, j) &
105
+ (Sequel.qualify(j, :valid_to) > n)
106
+ else
107
+ e = Sequel.qualify j, :expired_at
108
+ (Sequel.qualify(j, :created_at) <= t) &
109
+ Sequel.|({e=>nil}, e > t) &
110
+ (Sequel.qualify(j, :valid_to) > n)
111
+ end
76
112
  end) do |ds|
77
113
  t = ::Sequel::Plugins::Bitemporal.point_in_time
78
114
  n = ::Sequel::Plugins::Bitemporal.now
79
- ds.where{(created_at <= t) & ({expired_at=>nil} | (expired_at > t)) & (valid_to > n)}
115
+ if model.use_ranges
116
+ existence_conditions = model.existence_range_contains t, j
117
+ ds.where{ existence_conditions & (:valid_to > n) }
118
+ else
119
+ ds.where do
120
+ (created_at <= t) &
121
+ Sequel.|({expired_at=>nil}, expired_at > t) &
122
+ (valid_to > n)
123
+ end
124
+ end
80
125
  end
81
126
  master.def_dataset_method :with_current_or_future_versions do
82
- eager_graph(:current_or_future_versions).where({current_or_future_versions__id: nil}.sql_negate)
127
+ eager_graph(:current_or_future_versions).where(
128
+ Sequel.negate(current_or_future_versions__id: nil)
129
+ )
83
130
  end
84
131
  version.many_to_one :master, class: master, key: :master_id
85
132
  version.class_eval do
@@ -112,7 +159,75 @@ module Sequel
112
159
  module ClassMethods
113
160
  attr_reader :version_class, :versions_alias, :current_version_alias,
114
161
  :propagate_per_column, :audit_class, :audit_updated_by_method,
115
- :version_uses_string_nilifier
162
+ :version_uses_string_nilifier, :use_ranges
163
+
164
+ def validity_range_type
165
+ @validity_range_type ||= begin
166
+ valid_from_infos = db.schema(
167
+ version_class.table_name
168
+ ).detect do |column_name, _|
169
+ column_name==:valid_from
170
+ end
171
+ unless valid_from_infos
172
+ raise "Could not find valid_from column in #{version_class.table_name}"
173
+ end
174
+ case valid_from_infos.last[:db_type]
175
+ when "date"
176
+ :daterange
177
+ when "timestamp without time zone"
178
+ :tsrange
179
+ when "timestamp with time zone"
180
+ :tstzrange
181
+ else
182
+ raise "Don't know how to handle ranges for type: #{valid_from_infos[:db_type]}"
183
+ end
184
+ end
185
+ end
186
+
187
+ def validity_cast_type
188
+ case validity_range_type
189
+ when :daterange
190
+ :date
191
+ when :tsrange, :tstzrange
192
+ :timestamp
193
+ else
194
+ raise "Don't know how to handle cast for range type: #{validity_range_type}"
195
+ end
196
+ end
197
+
198
+ def existence_range(qualifier=nil)
199
+ created_at_column = :created_at
200
+ created_at_column = Sequel.qualify qualifier, created_at_column if qualifier
201
+ expired_at_column = :expired_at
202
+ expired_at_column = Sequel.qualify qualifier, expired_at_column if qualifier
203
+ Sequel.function(
204
+ :tsrange, created_at_column, expired_at_column, "[)"
205
+ ).pg_range
206
+ end
207
+
208
+ def existence_range_contains(point_in_time, qualifier=nil)
209
+ existence_range(qualifier).contains(
210
+ Sequel.cast(point_in_time, :timestamp)
211
+ )
212
+ end
213
+
214
+ def validity_range(qualifier=nil)
215
+ valid_from_column = :valid_from
216
+ valid_from_column = Sequel.qualify qualifier, valid_from_column if qualifier
217
+ valid_to_column = :valid_to
218
+ valid_to_column = Sequel.qualify qualifier, valid_to_column if qualifier
219
+
220
+ Sequel.function(
221
+ validity_range_type, valid_from_column, valid_to_column, "[)"
222
+ ).pg_range
223
+ end
224
+
225
+ def validity_range_contains(now, qualifier=nil)
226
+ validity_range(qualifier).contains(
227
+ Sequel.cast(now, validity_cast_type)
228
+ )
229
+ end
230
+
116
231
  end
117
232
  module DatasetMethods
118
233
  end
@@ -229,10 +344,17 @@ module Sequel
229
344
  return if new?
230
345
  t = ::Sequel::Plugins::Bitemporal.point_in_time
231
346
  n = ::Sequel::Plugins::Bitemporal.now
347
+ if use_ranges = self.class.use_ranges
348
+ range_conditions = self.class.existence_range_contains t
349
+ end
232
350
  versions_dataset.where do
233
- (created_at <= t) & ({expired_at=>nil} | (expired_at > t)) &
234
- (valid_from <= n)
235
- end.order(:valid_to.desc, :created_at.desc).first
351
+ if use_ranges
352
+ range_conditions
353
+ else
354
+ (created_at <= t) &
355
+ Sequel.|({expired_at=>nil}, expired_at > t)
356
+ end & (valid_from <= n)
357
+ end.order(Sequel.desc(:valid_to), Sequel.desc(:created_at)).first
236
358
  end
237
359
  end
238
360
 
@@ -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.11"
6
+ s.version = "0.6.12"
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"
@@ -18,6 +18,7 @@ Gem::Specification.new do |s|
18
18
  s.add_runtime_dependency "sequel", "~> 3.30"
19
19
 
20
20
  s.add_development_dependency "sqlite3"
21
+ s.add_development_dependency "pg"
21
22
  s.add_development_dependency "rspec", "~> 2.10.0"
22
23
  s.add_development_dependency "timecop"
23
24
  s.add_development_dependency "rake"
@@ -1,7 +1,6 @@
1
1
  require "spec_helper"
2
2
 
3
3
  describe "Sequel::Plugins::Bitemporal" do
4
- include DbHelpers
5
4
  before :all do
6
5
  db_setup
7
6
  end
@@ -469,17 +468,17 @@ describe "Sequel::Plugins::Bitemporal" do
469
468
  Timecop.freeze Date.today+1
470
469
  master.update_attributes name: "Single Standard", price: 99
471
470
  master.update_attributes name: "Single Standard", price: 94, valid_from: Date.today+2
472
- res = @master_class.eager_graph(:current_or_future_versions).where({current_or_future_versions__id: nil}.sql_negate & {price: 99}).all.first
471
+ res = @master_class.eager_graph(:current_or_future_versions).where(Sequel.negate(current_or_future_versions__id: nil) & {price: 99}).all.first
473
472
  res.should be
474
473
  res.current_or_future_versions.should have(1).item
475
474
  res.current_or_future_versions.first.price.should == 99
476
- res = @master_class.eager_graph(:current_or_future_versions).where({current_or_future_versions__id: nil}.sql_negate & {price: 94}).all.first
475
+ res = @master_class.eager_graph(:current_or_future_versions).where(Sequel.negate(current_or_future_versions__id: nil) & {price: 94}).all.first
477
476
  res.should be
478
477
  res.current_or_future_versions.should have(1).item
479
478
  res.current_or_future_versions.first.price.should == 94
480
479
  Timecop.freeze Date.today+1
481
480
  master.destroy
482
- @master_class.eager_graph(:current_or_future_versions).where({current_or_future_versions__id: nil}.sql_negate).all.should be_empty
481
+ @master_class.eager_graph(:current_or_future_versions).where(Sequel.negate(current_or_future_versions__id: nil)).all.should be_empty
483
482
  end
484
483
  it "allows loading masters with current or future versions" do
485
484
  master_destroyed = @master_class.new
@@ -636,7 +635,6 @@ describe "Sequel::Plugins::Bitemporal" do
636
635
  end
637
636
 
638
637
  describe "Sequel::Plugins::Bitemporal", "with audit" do
639
- include DbHelpers
640
638
  before :all do
641
639
  @audit_class = Class.new do
642
640
  def self.audit(*args); end
@@ -732,7 +730,6 @@ describe "Sequel::Plugins::Bitemporal", "with audit" do
732
730
  end
733
731
  end
734
732
  describe "Sequel::Plugins::Bitemporal", "with audit, specifying how to get the author" do
735
- include DbHelpers
736
733
  before :all do
737
734
  @audit_class = Class.new do
738
735
  def self.audit(*args); end
@@ -785,4 +782,3 @@ describe "Sequel::Plugins::Bitemporal", "with audit, specifying how to get the a
785
782
  master.update_attributes name: "King size", price: 98
786
783
  end
787
784
  end
788
-