activerecord-bitemporal 6.0.0 → 7.0.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b36848b2facc997f9179abd32f96d7fde53cd9464f9009fa25a1ca5f13f57890
4
- data.tar.gz: 8498da159a6e051841ce253b7051f0722b0cb33d2358fc13a1b3f5c0664cf1f7
3
+ metadata.gz: 9644dc51d17a2d86e13daa5982fab733cdab79fd1fda78fbe408412a90ccd337
4
+ data.tar.gz: f535c500ce4f4f738e6c09102d89a449476407956460c1e5b80d211adb4b66c9
5
5
  SHA512:
6
- metadata.gz: bd8c531ad27a3ba0693d3923a00f62abb4b730251e235d0f3a78b449f2fdc7336052a4e5c0115748330e95a6f75eff2f20dcfc71d3fa1360d3d7e4c98c2b38af
7
- data.tar.gz: fe72790fbcb1372f8127afe702cf2696f930c901aaa2b6243c913243b299a74917bca867d068e09039a6df001c942f7db81b0a3c64daa54a919bbdb4714de4ac
6
+ metadata.gz: d0f7ac0703dd57cd8a837f24adc5c0598cbb5b083a20266c20bd35ae4014a28ee6488ed48e3b30119f01b78c40c063deb7bcbe0f8c61280ef6b7a6bc60d5b3eb
7
+ data.tar.gz: ad4faf9f2c337bcdcefd8f18a1dadaa191c327a82a5e7391d7b15f06b15bfeff631115466da7fb690062b35cf5174bf8ad991dd3041800abf6063c840bd241ac
data/CHANGELOG.md CHANGED
@@ -1,8 +1,100 @@
1
1
  # Changelog
2
2
 
3
+ ## Unreleased
4
+
5
+ ### Breaking Changes
6
+
7
+ ### Added
8
+
9
+ ### Changed
10
+
11
+ ### Deprecated
12
+
13
+ ### Removed
14
+
15
+ ### Fixed
16
+
17
+ ### Chores
18
+
19
+ ## 7.0.0
20
+
21
+ ### Breaking Changes
22
+
23
+ - [Drop support Rails 7.0 #230](https://github.com/kufu/activerecord-bitemporal/pull/230)
24
+
25
+ ### Added
26
+
27
+ - [Add Ruby 4.0 in CI #236](https://github.com/kufu/activerecord-bitemporal/pull/236)
28
+
29
+ ### Changed
30
+
31
+ ### Deprecated
32
+
33
+ ### Removed
34
+
35
+ ### Fixed
36
+
37
+ - [Localize patch for AR::Relation#primary_key in Rails 8.0+ #243](https://github.com/kufu/activerecord-bitemporal/pull/243)
38
+ - In Rails 8.0+, the global override of `ActiveRecord::Relation#primary_key` with `bitemporal_id_key` has been removed. As a result, the following methods now use `primary_key` (i.e., `id`) instead of `bitemporal_id_key`:
39
+ - `find_each`, `find_in_batches`, and `in_batches`: the default cursor column for ordering and keyset pagination changes (records are not missed or duplicated, but iteration order differs)
40
+ - [Fix finder methods ordering on Rails 8.0+ #237](https://github.com/kufu/activerecord-bitemporal/pull/237)
41
+
42
+ ### Chores
43
+
44
+ - [Manage development dependencies in gemfiles #240](https://github.com/kufu/activerecord-bitemporal/pull/240)
45
+ - [Randomize test execution order #239](https://github.com/kufu/activerecord-bitemporal/pull/239)
46
+ - [Update release GitHub Action #232](https://github.com/kufu/activerecord-bitemporal/pull/232)
47
+ - [Bump ruby/setup-ruby from 1.286.0 to 1.288.0 #244](https://github.com/kufu/activerecord-bitemporal/pull/244)
48
+ - [Bump actions/checkout from 6.0.1 to 6.0.2 #242](https://github.com/kufu/activerecord-bitemporal/pull/242)
49
+ - [Bump ruby/setup-ruby from 1.278.0 to 1.286.0 #241](https://github.com/kufu/activerecord-bitemporal/pull/241)
50
+ - [Bump ruby/setup-ruby from 1.268.0 to 1.278.0 #234](https://github.com/kufu/activerecord-bitemporal/pull/234)
51
+ - [Update reviewers #231](https://github.com/kufu/activerecord-bitemporal/pull/231)
52
+ - [Bump actions/checkout from 6.0.0 to 6.0.1 #229](https://github.com/kufu/activerecord-bitemporal/pull/229)
53
+ - [Bump ruby/setup-ruby from 1.267.0 to 1.268.0 #228](https://github.com/kufu/activerecord-bitemporal/pull/228)
54
+ - [Bump actions/checkout from 5.0.0 to 6.0.0 #227](https://github.com/kufu/activerecord-bitemporal/pull/227)
55
+ - [Bump codespell-project/actions-codespell from 2.1 to 2.2 #226](https://github.com/kufu/activerecord-bitemporal/pull/226)
56
+ - [Bump ruby/setup-ruby from 1.265.0 to 1.267.0 #225](https://github.com/kufu/activerecord-bitemporal/pull/225)
57
+
58
+ ## 6.1.0
59
+
60
+ ### Breaking Changes
61
+
62
+ ### Added
63
+
64
+ - [Support GlobalID integration #176](https://github.com/kufu/activerecord-bitemporal/pull/176)
65
+
66
+ ### Changed
67
+
68
+ - [Add explicit activesupport dependency #208](https://github.com/kufu/activerecord-bitemporal/pull/208)
69
+ - [Improve ValidDatetimeRangeError message with better grammar and context #207](https://github.com/kufu/activerecord-bitemporal/pull/207)
70
+ - [Delay execution of ActiveRecord::Base-related processing #189](https://github.com/kufu/activerecord-bitemporal/pull/189)
71
+
72
+ ### Deprecated
73
+
74
+ ### Removed
75
+
76
+ ### Fixed
77
+
78
+ ### Chores
79
+
80
+ - [Bump ruby/setup-ruby from 1.263.0 to 1.265.0 #223](https://github.com/kufu/activerecord-bitemporal/pull/223)
81
+ - [Update gemspec files to avoid using git #222](https://github.com/kufu/activerecord-bitemporal/pull/222)
82
+ - [Bump ruby/setup-ruby from 1.257.0 to 1.263.0 #221](https://github.com/kufu/activerecord-bitemporal/pull/221)
83
+ - [Add CodeSpell workflow for spell checking in pull requests #220](https://github.com/kufu/activerecord-bitemporal/pull/220)
84
+ - [Configure dependabot cooldown period to 3 days #219](https://github.com/kufu/activerecord-bitemporal/pull/219)
85
+ - [Bump ruby/setup-ruby from 1.255.0 to 1.257.0 #218](https://github.com/kufu/activerecord-bitemporal/pull/218)
86
+ - [Bump actions/checkout from 4.2.2 to 5.0.0 #215](https://github.com/kufu/activerecord-bitemporal/pull/215)
87
+ - [Bump ruby/setup-ruby from 1.254.0 to 1.255.0 #214](https://github.com/kufu/activerecord-bitemporal/pull/214)
88
+ - [Bump ruby/setup-ruby from 1.247.0 to 1.254.0 #213](https://github.com/kufu/activerecord-bitemporal/pull/213)
89
+ - [Bump ruby/setup-ruby from 1.247.0 to 1.253.0 #212](https://github.com/kufu/activerecord-bitemporal/pull/212)
90
+ - [Setup RuboCop #211](https://github.com/kufu/activerecord-bitemporal/pull/211)
91
+ - [Bump ruby/setup-ruby from 1.245.0 to 1.247.0 #210](https://github.com/kufu/activerecord-bitemporal/pull/210)
92
+ - [Using Trusted Publishing for RubyGems.org. #209](https://github.com/kufu/activerecord-bitemporal/pull/209)
93
+ - [Bump ruby/setup-ruby from 1.244.0 to 1.245.0 #206](https://github.com/kufu/activerecord-bitemporal/pull/206)
94
+
3
95
  ## 6.0.0
4
96
 
5
- ### Breaking Changed
97
+ ### Breaking Changes
6
98
 
7
99
  - [Add Ruby 3.4 and remove Ruby 3.0 in CI #185](https://github.com/kufu/activerecord-bitemporal/pull/185)
8
100
  - [Drop support Rails 6.1 #192](https://github.com/kufu/activerecord-bitemporal/pull/192)
@@ -26,7 +118,7 @@
26
118
 
27
119
  - [Add a note to the README that PostgreSQL is required to run the tests. #188](https://github.com/kufu/activerecord-bitemporal/pull/188)
28
120
  - [Remove specs for Rails 5.x #191](https://github.com/kufu/activerecord-bitemporal/pull/191)
29
- - [Update auto assgin member #193](https://github.com/kufu/activerecord-bitemporal/pull/193)
121
+ - [Update auto assign member #193](https://github.com/kufu/activerecord-bitemporal/pull/193)
30
122
  - [Pin GitHub Actions dependencies to specific commit hashes #194](https://github.com/kufu/activerecord-bitemporal/pull/194)
31
123
  - [Bump ruby/setup-ruby from 1.227.0 to 1.229.0 #195](https://github.com/kufu/activerecord-bitemporal/pull/195)
32
124
  - [Bump ruby/setup-ruby from 1.229.0 to 1.233.0 #197](https://github.com/kufu/activerecord-bitemporal/pull/197)
@@ -35,7 +127,7 @@
35
127
  - [Bump ruby/setup-ruby from 1.237.0 to 1.238.0 #201](https://github.com/kufu/activerecord-bitemporal/pull/201)
36
128
  - [Bump ruby/setup-ruby from 1.238.0 to 1.242.0 #202](https://github.com/kufu/activerecord-bitemporal/pull/202)
37
129
  - [Bump ruby/setup-ruby from 1.242.0 to 1.244.0 #203](https://github.com/kufu/activerecord-bitemporal/pull/203)
38
- - [Update auto assgin member #204](https://github.com/kufu/activerecord-bitemporal/pull/204)
130
+ - [Update auto assign member #204](https://github.com/kufu/activerecord-bitemporal/pull/204)
39
131
 
40
132
  ## 5.3.0
41
133
 
@@ -110,7 +202,7 @@
110
202
 
111
203
  ## 5.0.0
112
204
 
113
- ### Breaking Changed
205
+ ### Breaking Changes
114
206
 
115
207
  - [CI against Ruby 3.2, 3.3, Drop Ruby 2.7 and Rails 6.0 #150](https://github.com/kufu/activerecord-bitemporal/pull/150)
116
208
 
@@ -191,7 +283,7 @@
191
283
 
192
284
  ## 4.0.0
193
285
 
194
- ### Breaking Changed
286
+ ### Breaking Changes
195
287
 
196
288
  - [[proposal]When bitemporal_at exists inside the nest, the specified date was not prioritized, so the date of the inner bitemporal_at is now prioritized. #121](https://github.com/kufu/activerecord-bitemporal/pull/121)
197
289
  - [Drop support Rails 5.2 #122](https://github.com/kufu/activerecord-bitemporal/pull/122)
@@ -246,7 +338,7 @@
246
338
 
247
339
  ## 3.0.0
248
340
 
249
- ### Breaking Changed
341
+ ### Breaking Changes
250
342
  - [Assign updated bitemporal times to the receiver after update/destroy](https://github.com/kufu/activerecord-bitemporal/pull/118)
251
343
 
252
344
  ### Added
@@ -261,7 +353,7 @@
261
353
 
262
354
  ## 2.3.0
263
355
 
264
- ### Breaking Changed
356
+ ### Breaking Changes
265
357
 
266
358
  ### Added
267
359
  - [Add `InstanceMethods#swapped_id_previously_was`](https://github.com/kufu/activerecord-bitemporal/pull/114)
@@ -276,7 +368,7 @@
276
368
 
277
369
  ## 2.2.0
278
370
 
279
- ### Breaking Changed
371
+ ### Breaking Changes
280
372
 
281
373
  ### Added
282
374
  - [replace postgres docker image](https://github.com/kufu/activerecord-bitemporal/pull/103)
@@ -293,7 +385,7 @@
293
385
 
294
386
  ## 2.1.0
295
387
 
296
- ### Breaking Changed
388
+ ### Breaking Changes
297
389
 
298
390
  ### Added
299
391
  - [Update valid_to after #update](https://github.com/kufu/activerecord-bitemporal/pull/105)
@@ -310,7 +402,7 @@
310
402
 
311
403
  ## 2.0.0
312
404
 
313
- ### Breaking Changed
405
+ ### Breaking Changes
314
406
  - [[Proposal] Changed valid_in to exclude valid_from = to and valid_to = from. by osyo-manga · Pull Request #95](https://github.com/kufu/activerecord-bitemporal/pull/95)
315
407
 
316
408
  ### Added
data/README.md CHANGED
@@ -6,6 +6,12 @@ ActiveRecord::Bitemporal
6
6
  [![gem-download](https://img.shields.io/gem/dt/activerecord-bitemporal.svg)](https://rubygems.org/gems/activerecord-bitemporal)
7
7
  [![CircleCI](https://circleci.com/gh/kufu/activerecord-bitemporal.svg?style=svg)](https://circleci.com/gh/kufu/activerecord-bitemporal)
8
8
 
9
+ ## Requirements
10
+
11
+ - Ruby 3.1+
12
+ - Rails 7.1, 7.2, 8.0, or 8.1
13
+ - PostgreSQL
14
+
9
15
  ## Installation
10
16
 
11
17
  Add this line to your application's Gemfile:
@@ -1,20 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "activerecord-bitemporal/bitemporal_checker"
3
4
  module ActiveRecord
4
5
  module Bitemporal
5
- module BitemporalChecker
6
- refine ::Class do
7
- def bi_temporal_model?
8
- include?(ActiveRecord::Bitemporal)
9
- end
10
- end
11
-
12
- refine ::ActiveRecord::Relation do
13
- def bi_temporal_model?
14
- klass.include?(ActiveRecord::Bitemporal)
15
- end
16
- end
17
- end
18
6
  using BitemporalChecker
19
7
 
20
8
  module Optionable
@@ -124,15 +112,36 @@ module ActiveRecord
124
112
  end
125
113
 
126
114
  module Relation
127
- module Finder
128
- def find(*ids)
129
- return super if block_given?
130
- all.spawn.yield_self { |obj|
131
- def obj.primary_key
132
- "bitemporal_id"
115
+ module BitemporalIdAsPrimaryKey # :nodoc:
116
+ private
117
+
118
+ # Generate a method that temporarily changes the primary key to
119
+ # bitemporal_id for localizing the effect of the change to only the
120
+ # method specified by `name`.
121
+ #
122
+ # DO NOT use this method outside of this module.
123
+ def use_bitemporal_id_as_primary_key(name) # :nodoc:
124
+ module_eval <<~RUBY, __FILE__, __LINE__ + 1
125
+ def #{name}(...)
126
+ all.spawn.yield_self { |relation|
127
+ def relation.primary_key
128
+ bitemporal_id_key
129
+ end
130
+ relation.method(:#{name}).super_method.call(...)
131
+ }
133
132
  end
134
- obj.method(:find).super_method.call(*ids)
135
- }
133
+ RUBY
134
+ end
135
+ end
136
+ extend BitemporalIdAsPrimaryKey
137
+
138
+ module Finder
139
+ extend BitemporalIdAsPrimaryKey
140
+
141
+ use_bitemporal_id_as_primary_key :find
142
+
143
+ if ActiveRecord.version >= Gem::Version.new("8.0.0")
144
+ use_bitemporal_id_as_primary_key :exists?
136
145
  end
137
146
 
138
147
  def find_at_time!(datetime, *ids)
@@ -148,6 +157,10 @@ module ActiveRecord
148
157
  end
149
158
  include Finder
150
159
 
160
+ if ActiveRecord.version >= Gem::Version.new("8.0.0")
161
+ use_bitemporal_id_as_primary_key :ids
162
+ end
163
+
151
164
  def build_arel(*)
152
165
  ActiveRecord::Bitemporal.with_bitemporal_option(**bitemporal_option) {
153
166
  super
@@ -180,8 +193,12 @@ module ActiveRecord
180
193
  end
181
194
  end
182
195
 
183
- def primary_key
184
- bitemporal_id_key
196
+ # Use original primary_key for Active Record 8.0+ as much as possible
197
+ # to avoid issues with patching primary_key of AR::Relation globally.
198
+ if ActiveRecord.version < Gem::Version.new("8.0.0")
199
+ def primary_key
200
+ bitemporal_id_key
201
+ end
185
202
  end
186
203
  end
187
204
 
@@ -397,16 +414,14 @@ module ActiveRecord
397
414
  false
398
415
  end
399
416
 
400
- if Gem::Version.new("7.1.0") <= ActiveRecord.version
401
- # MEMO: Since Rails 7.1 #_find_record refers to a record with find_by!(@primary_key => id)
402
- # But if @primary_key is "id", it can't refer to the intended record, so we hack it to refer to the record based on self.class.bitemporal_id_key
403
- # see: https://github.com/rails/rails/blob/v7.1.0/activerecord/lib/active_record/persistence.rb#L1152-#L1171
404
- def _find_record(*)
405
- tmp_primary_key, @primary_key = @primary_key, self.class.bitemporal_id_key
406
- super
407
- ensure
408
- @primary_key = tmp_primary_key
409
- end
417
+ # MEMO: Since Rails 7.1 #_find_record refers to a record with find_by!(@primary_key => id)
418
+ # But if @primary_key is "id", it can't refer to the intended record, so we hack it to refer to the record based on self.class.bitemporal_id_key
419
+ # see: https://github.com/rails/rails/blob/v7.1.0/activerecord/lib/active_record/persistence.rb#L1152-#L1171
420
+ def _find_record(*)
421
+ tmp_primary_key, @primary_key = @primary_key, self.class.bitemporal_id_key
422
+ super
423
+ ensure
424
+ @primary_key = tmp_primary_key
410
425
  end
411
426
 
412
427
  module ::ActiveRecord::Persistence
@@ -493,7 +508,10 @@ module ActiveRecord
493
508
  # 以前の履歴データは valid_to を詰めて保存
494
509
  before_instance[valid_to_key] = target_datetime
495
510
  if before_instance.valid_from_cannot_be_greater_equal_than_valid_to
496
- raise ValidDatetimeRangeError.new("#{valid_from_key} #{before_instance[valid_from_key]} can't be greater equal than #{valid_to_key} #{before_instance[valid_to_key]}")
511
+ message = "#{valid_from_key} #{before_instance[valid_from_key]} can't be " \
512
+ "greater than or equal to #{valid_to_key} #{before_instance[valid_to_key]} " \
513
+ "for #{self.class} with bitemporal_id=#{bitemporal_id}"
514
+ raise ValidDatetimeRangeError.new(message)
497
515
  end
498
516
  before_instance.transaction_from = current_time
499
517
 
@@ -501,7 +519,10 @@ module ActiveRecord
501
519
  after_instance[valid_from_key] = target_datetime
502
520
  after_instance[valid_to_key] = current_valid_record[valid_to_key]
503
521
  if after_instance.valid_from_cannot_be_greater_equal_than_valid_to
504
- raise ValidDatetimeRangeError.new("#{valid_from_key} #{after_instance[valid_from_key]} can't be greater equal than #{valid_to_key} #{after_instance[valid_to_key]}")
522
+ message = "#{valid_from_key} #{after_instance[valid_from_key]} can't be " \
523
+ "greater than or equal to #{valid_to_key} #{after_instance[valid_to_key]} " \
524
+ "for #{self.class} with bitemporal_id=#{bitemporal_id}"
525
+ raise ValidDatetimeRangeError.new(message)
505
526
  end
506
527
  after_instance.transaction_from = current_time
507
528
 
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module Bitemporal
5
+ module BitemporalChecker
6
+ refine ::Class do
7
+ def bi_temporal_model?
8
+ include?(ActiveRecord::Bitemporal)
9
+ end
10
+ end
11
+
12
+ refine ::ActiveRecord::Relation do
13
+ def bi_temporal_model?
14
+ klass.include?(ActiveRecord::Bitemporal)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,158 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord::Bitemporal::Bitemporalize
4
+ using Module.new {
5
+ refine ::ActiveRecord::Base do
6
+ class << ::ActiveRecord::Base
7
+ def prepend_relation_delegate_class(mod)
8
+ relation_delegate_class(ActiveRecord::Relation).prepend mod
9
+ relation_delegate_class(ActiveRecord::AssociationRelation).prepend mod
10
+ end
11
+ end
12
+ end
13
+ }
14
+
15
+ module ClassMethods
16
+ include ActiveRecord::Bitemporal::Relation::Finder
17
+
18
+ def bitemporal_id_key
19
+ 'bitemporal_id'
20
+ end
21
+
22
+ # Override ActiveRecord::Core::ClassMethods#cached_find_by_statement
23
+ # `.find_by` not use caching
24
+ def cached_find_by_statement(key, &block)
25
+ ActiveRecord::StatementCache.create(connection, &block)
26
+ end
27
+
28
+ def inherited(klass)
29
+ super
30
+ klass.prepend_relation_delegate_class ActiveRecord::Bitemporal::Relation
31
+ klass.relation_delegate_class(ActiveRecord::Associations::CollectionProxy).prepend ActiveRecord::Bitemporal::CollectionProxy
32
+ if relation_delegate_class(ActiveRecord::Relation).ancestors.include? ActiveRecord::Bitemporal::Relation::MergeWithExceptBitemporalDefaultScope
33
+ klass.relation_delegate_class(ActiveRecord::Relation).prepend ActiveRecord::Bitemporal::Relation::MergeWithExceptBitemporalDefaultScope
34
+ end
35
+ end
36
+ end
37
+
38
+ module InstanceMethods
39
+ include ActiveRecord::Bitemporal::Persistence
40
+
41
+ def swap_id!(without_clear_changes_information: false)
42
+ @_swapped_id_previously_was = nil
43
+ @_swapped_id = self.id
44
+ self.id = self.send(bitemporal_id_key)
45
+ clear_attribute_changes([:id]) unless without_clear_changes_information
46
+ end
47
+
48
+ def swapped_id
49
+ @_swapped_id || self.id
50
+ end
51
+
52
+ def swapped_id_previously_was
53
+ @_swapped_id_previously_was
54
+ end
55
+
56
+ def bitemporal_id_key
57
+ self.class.bitemporal_id_key
58
+ end
59
+
60
+ def bitemporal_ignore_update_columns
61
+ []
62
+ end
63
+
64
+ def id_in_database
65
+ swapped_id.presence || super
66
+ end
67
+
68
+ def previously_force_updated?
69
+ @previously_force_updated
70
+ end
71
+
72
+ def valid_from_cannot_be_greater_equal_than_valid_to
73
+ if self[valid_from_key] && self[valid_to_key] && self[valid_from_key] >= self[valid_to_key]
74
+ errors.add(valid_from_key, "can't be greater equal than #{valid_to_key}")
75
+ end
76
+ end
77
+
78
+ def transaction_from_cannot_be_greater_equal_than_transaction_to
79
+ if transaction_from && transaction_to && transaction_from >= transaction_to
80
+ errors.add(:transaction_from, "can't be greater equal than transaction_to")
81
+ end
82
+ end
83
+ end
84
+
85
+ def bitemporalize(
86
+ enable_strict_by_validates_bitemporal_id: false,
87
+ enable_default_scope: true,
88
+ enable_merge_with_except_bitemporal_default_scope: false,
89
+ valid_from_key: :valid_from,
90
+ valid_to_key: :valid_to
91
+ )
92
+ return if ancestors.include? InstanceMethods
93
+
94
+ extend ClassMethods
95
+ include InstanceMethods
96
+ include ActiveRecord::Bitemporal
97
+ include ActiveRecord::Bitemporal::Scope
98
+ include ActiveRecord::Bitemporal::Callbacks
99
+ prepend ActiveRecord::Bitemporal::GlobalID if defined?(::GlobalID)
100
+
101
+ if enable_merge_with_except_bitemporal_default_scope
102
+ relation_delegate_class(ActiveRecord::Relation).prepend ActiveRecord::Bitemporal::Relation::MergeWithExceptBitemporalDefaultScope
103
+ end
104
+
105
+ if enable_default_scope
106
+ default_scope {
107
+ bitemporal_default_scope
108
+ }
109
+ end
110
+
111
+ after_create do
112
+ # MEMO: #update_columns is not call #_update_row (and validations, callbacks)
113
+ update_columns(bitemporal_id_key => swapped_id) unless send(bitemporal_id_key)
114
+ swap_id!(without_clear_changes_information: true)
115
+ @previously_force_updated = false
116
+ end
117
+
118
+ after_find do
119
+ self.swap_id! if self.send(bitemporal_id_key).present?
120
+ @previously_force_updated = false
121
+ end
122
+
123
+ self.class_attribute :valid_from_key, :valid_to_key, instance_writer: false
124
+ self.valid_from_key = valid_from_key.to_s
125
+ self.valid_to_key = valid_to_key.to_s
126
+ attribute valid_from_key, default: ActiveRecord::Bitemporal::DEFAULT_VALID_FROM
127
+ attribute valid_to_key, default: ActiveRecord::Bitemporal::DEFAULT_VALID_TO
128
+ attribute :transaction_from, default: ActiveRecord::Bitemporal::DEFAULT_TRANSACTION_FROM
129
+ attribute :transaction_to, default: ActiveRecord::Bitemporal::DEFAULT_TRANSACTION_TO
130
+
131
+ # Rails 8 support: ensure finder methods (.first, .last, etc.) use bitemporal_id ordering.
132
+ # Rails 8.0 changed _order_columns to use model.primary_key instead of Relation's primary_key.
133
+ # The nil-terminated format (Rails 8.1 feature, PR #54679) prevents automatic primary_key
134
+ # appending to the ORDER BY clause.
135
+ # See: https://github.com/rails/rails/pull/54679
136
+ if ActiveRecord.gem_version >= Gem::Version.new("8.0.0")
137
+ self.implicit_order_column ||= [bitemporal_id_key, nil]
138
+ end
139
+
140
+ # Callback hook to `validates :xxx, uniqueness: true`
141
+ const_set(:UniquenessValidator, Class.new(ActiveRecord::Validations::UniquenessValidator) {
142
+ prepend ActiveRecord::Bitemporal::Uniqueness
143
+ })
144
+
145
+ # validations
146
+ validates valid_from_key, presence: true
147
+ validates valid_to_key, presence: true
148
+ validates :transaction_from, presence: true
149
+ validates :transaction_to, presence: true
150
+ validate :valid_from_cannot_be_greater_equal_than_valid_to
151
+ validate :transaction_from_cannot_be_greater_equal_than_transaction_to
152
+
153
+ validates bitemporal_id_key, uniqueness: true, allow_nil: true, strict: enable_strict_by_validates_bitemporal_id
154
+
155
+ prepend_relation_delegate_class ActiveRecord::Bitemporal::Relation
156
+ relation_delegate_class(ActiveRecord::Associations::CollectionProxy).prepend ActiveRecord::Bitemporal::CollectionProxy
157
+ end
158
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ begin
4
+ require "globalid"
5
+ rescue LoadError
6
+ # If GlobalID is not available, we skip the GlobalID integration.
7
+ return
8
+ end
9
+
10
+ module ActiveRecord
11
+ module Bitemporal
12
+ module GlobalID
13
+ include ::GlobalID::Identification
14
+
15
+ def to_global_id(options = {})
16
+ super(options.merge(app: "bitemporal"))
17
+ end
18
+ alias to_gid to_global_id
19
+
20
+ class BitemporalLocator < ::GlobalID::Locator::BaseLocator
21
+ private
22
+
23
+ # @override https://github.com/rails/globalid/blob/v1.2.1/lib/global_id/locator.rb#L203
24
+ def primary_key(model_class)
25
+ model_class.respond_to?(:bitemporal_id_key) ? model_class.bitemporal_id_key : :id
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
+ # BiTemporal Data Model requires default scope, so `UnscopedLocator` cannot be used.
33
+ GlobalID::Locator.use :bitemporal, ActiveRecord::Bitemporal::GlobalID::BitemporalLocator.new
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "activerecord-bitemporal/bitemporal_checker"
4
+
3
5
  module ActiveRecord::Bitemporal
4
6
  module NodeBitemporalInclude
5
7
  refine String do
@@ -158,7 +160,13 @@ module ActiveRecord::Bitemporal
158
160
  # @see https://github.com/rails/rails/blob/v7.1.3.4/activerecord/lib/active_record/relation/delegation.rb#L117
159
161
  delegate :bitemporal_value, :bitemporal_value=, :valid_datetime, :valid_date,
160
162
  :transaction_datetime, :bitemporal_option, :bitemporal_option_merge!,
161
- :build_arel, :primary_key, to: :scope
163
+ :build_arel, to: :scope
164
+
165
+ if ActiveRecord.version < Gem::Version.new("8.0.0")
166
+ delegate :primary_key, to: :scope
167
+ else
168
+ delegate :ids, :exists?, to: :scope
169
+ end
162
170
  end
163
171
 
164
172
  module Scope
@@ -2,6 +2,6 @@
2
2
 
3
3
  module ActiveRecord
4
4
  module Bitemporal
5
- VERSION = "6.0.0"
5
+ VERSION = "7.0.0"
6
6
  end
7
7
  end