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 +4 -4
- data/CHANGELOG.md +102 -10
- data/README.md +6 -0
- data/lib/activerecord-bitemporal/bitemporal.rb +56 -35
- data/lib/activerecord-bitemporal/bitemporal_checker.rb +19 -0
- data/lib/activerecord-bitemporal/bitemporalize.rb +158 -0
- data/lib/activerecord-bitemporal/global_id.rb +33 -0
- data/lib/activerecord-bitemporal/scope.rb +9 -1
- data/lib/activerecord-bitemporal/version.rb +1 -1
- data/lib/activerecord-bitemporal.rb +5 -149
- metadata +12 -113
- data/.github/auto_assign.yml +0 -21
- data/.github/dependabot.yml +0 -6
- data/.github/workflows/release.yml +0 -37
- data/.github/workflows/test.yml +0 -49
- data/.gitignore +0 -4
- data/Appraisals +0 -23
- data/CODE_OF_CONDUCT.jp.md +0 -86
- data/CODE_OF_CONDUCT.md +0 -133
- data/Gemfile +0 -9
- data/Rakefile +0 -8
- data/activerecord-bitemporal.gemspec +0 -35
- data/bin/console +0 -15
- data/bin/setup +0 -8
- data/compose.yml +0 -10
- data/gemfiles/rails_7.0.gemfile +0 -12
- data/gemfiles/rails_7.1.gemfile +0 -8
- data/gemfiles/rails_7.2.gemfile +0 -8
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9644dc51d17a2d86e13daa5982fab733cdab79fd1fda78fbe408412a90ccd337
|
|
4
|
+
data.tar.gz: f535c500ce4f4f738e6c09102d89a449476407956460c1e5b80d211adb4b66c9
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
[](https://rubygems.org/gems/activerecord-bitemporal)
|
|
7
7
|
[](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
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
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
|
-
|
|
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
|
-
|
|
184
|
-
|
|
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
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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
|