activerecord-bitemporal 4.2.0 → 5.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/.circleci/config.yml +13 -11
- data/Appraisals +4 -4
- data/CHANGELOG.md +45 -0
- data/activerecord-bitemporal.gemspec +2 -2
- data/gemfiles/{rails_6.0.gemfile → rails_7.1.gemfile} +1 -1
- data/lib/activerecord-bitemporal/bitemporal.rb +49 -30
- data/lib/activerecord-bitemporal/scope.rb +69 -153
- data/lib/activerecord-bitemporal/version.rb +1 -1
- data/lib/activerecord-bitemporal/visualizer.rb +5 -3
- data/lib/activerecord-bitemporal.rb +6 -0
- metadata +6 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 25ef613d84eda786e3d04e90aeb9cc95b68887a84863200aa0622be9e89c73ea
|
|
4
|
+
data.tar.gz: 63920865d4f8f27703b6ffeaf4e0c38d297ea4bff22bf01a3884136875b2ccde
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6fbe2830897e6c3f70f29431f591e8fa9cd332ae260e23ef3c23b7afff7259819bc54b8bb9de38d26c07a96c1cd3e93af5f5b7b559d2814b61b3cabfb3dcb2a6
|
|
7
|
+
data.tar.gz: 3e45d6a45de435475388a77a534521049b603bddc5910046fc5b8f4c2577ad25e75387f397014374d94b4d3c515c4e4456ff76c4673952f06b497446166a7927
|
data/.circleci/config.yml
CHANGED
|
@@ -9,13 +9,14 @@ jobs:
|
|
|
9
9
|
type: string
|
|
10
10
|
docker:
|
|
11
11
|
- image: ruby:<< parameters.ruby-version >>
|
|
12
|
+
environment:
|
|
13
|
+
BUNDLE_GEMFILE: gemfiles/<< parameters.gemfile >>.gemfile
|
|
12
14
|
- image: cimg/postgres:11.16
|
|
13
15
|
steps:
|
|
14
16
|
- checkout
|
|
15
17
|
- run: gem install bundler
|
|
16
18
|
- run: bundle install
|
|
17
|
-
- run: bundle exec
|
|
18
|
-
- run: bundle exec appraisal << parameters.gemfile >> rspec
|
|
19
|
+
- run: bundle exec rspec
|
|
19
20
|
|
|
20
21
|
workflows:
|
|
21
22
|
test:
|
|
@@ -24,16 +25,17 @@ workflows:
|
|
|
24
25
|
matrix:
|
|
25
26
|
parameters:
|
|
26
27
|
ruby-version:
|
|
27
|
-
- '2.7'
|
|
28
28
|
- '3.0'
|
|
29
29
|
- '3.1'
|
|
30
|
+
- '3.2'
|
|
31
|
+
- '3.3'
|
|
30
32
|
gemfile:
|
|
31
|
-
-
|
|
32
|
-
-
|
|
33
|
-
-
|
|
34
|
-
-
|
|
33
|
+
- rails_6.1
|
|
34
|
+
- rails_7.0
|
|
35
|
+
- rails_7.1
|
|
36
|
+
- rails_main
|
|
35
37
|
exclude:
|
|
36
|
-
- ruby-version: '3.
|
|
37
|
-
gemfile:
|
|
38
|
-
- ruby-version: '3.
|
|
39
|
-
gemfile:
|
|
38
|
+
- ruby-version: '3.2'
|
|
39
|
+
gemfile: rails_6.1
|
|
40
|
+
- ruby-version: '3.3'
|
|
41
|
+
gemfile: rails_6.1
|
data/Appraisals
CHANGED
|
@@ -4,10 +4,6 @@ appraise "rails-main" do
|
|
|
4
4
|
gem "rails", git: 'https://github.com/rails/rails.git', branch: "main"
|
|
5
5
|
end
|
|
6
6
|
|
|
7
|
-
appraise "rails-6.0" do
|
|
8
|
-
gem "rails", "~> 6.0.2"
|
|
9
|
-
end
|
|
10
|
-
|
|
11
7
|
appraise "rails-6.1" do
|
|
12
8
|
gem "rails", "~> 6.1.0"
|
|
13
9
|
end
|
|
@@ -15,3 +11,7 @@ end
|
|
|
15
11
|
appraise "rails-7.0" do
|
|
16
12
|
gem "rails", "~> 7.0.1"
|
|
17
13
|
end
|
|
14
|
+
|
|
15
|
+
appraise "rails-7.1" do
|
|
16
|
+
gem "rails", "~> 7.1.0"
|
|
17
|
+
end
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,50 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 5.0.0
|
|
4
|
+
|
|
5
|
+
### Breaking Changed
|
|
6
|
+
|
|
7
|
+
- [CI against Ruby 3.2, 3.3, Drop Ruby 2.7 and Rails 6.0 #150](https://github.com/kufu/activerecord-bitemporal/pull/150)
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- [Support date type of valid time #149](https://github.com/kufu/activerecord-bitemporal/pull/149)
|
|
12
|
+
- [Add support Rails 7.1 #151](https://github.com/kufu/activerecord-bitemporal/pull/151)
|
|
13
|
+
- [Add Persistence#bitemporal_at and ActiveRecord::Bitemporal.bitemporal_at #152](https://github.com/kufu/activerecord-bitemporal/pull/152)
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
|
|
17
|
+
### Deprecated
|
|
18
|
+
|
|
19
|
+
### Removed
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
|
|
23
|
+
- [Fix deleted time order between associations #154](https://github.com/kufu/activerecord-bitemporal/pull/154)
|
|
24
|
+
|
|
25
|
+
### Chores
|
|
26
|
+
|
|
27
|
+
- [Don't use appraisal in CI #145](https://github.com/kufu/activerecord-bitemporal/pull/145)
|
|
28
|
+
- [Remove code for Active Record 6.0 #153](https://github.com/kufu/activerecord-bitemporal/pull/153)
|
|
29
|
+
|
|
30
|
+
## 4.3.0
|
|
31
|
+
|
|
32
|
+
### Added
|
|
33
|
+
|
|
34
|
+
- [Add `previously_force_updated?` #142](https://github.com/kufu/activerecord-bitemporal/pull/142)
|
|
35
|
+
|
|
36
|
+
### Changed
|
|
37
|
+
|
|
38
|
+
### Deprecated
|
|
39
|
+
|
|
40
|
+
### Removed
|
|
41
|
+
|
|
42
|
+
### Fixed
|
|
43
|
+
|
|
44
|
+
### Chores
|
|
45
|
+
|
|
46
|
+
- [Remove unneeded spec for Rails 5.x #143](https://github.com/kufu/activerecord-bitemporal/pull/143)
|
|
47
|
+
|
|
3
48
|
## 4.2.0
|
|
4
49
|
|
|
5
50
|
### Added
|
|
@@ -14,7 +14,7 @@ Gem::Specification.new do |spec|
|
|
|
14
14
|
spec.description = %q{Enable ActiveRecord models to be handled as BiTemporal Data Model.}
|
|
15
15
|
spec.homepage = "https://github.com/kufu/activerecord-bitemporal"
|
|
16
16
|
spec.license = "Apache 2.0"
|
|
17
|
-
spec.required_ruby_version = ">=
|
|
17
|
+
spec.required_ruby_version = ">= 3.0"
|
|
18
18
|
|
|
19
19
|
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
|
20
20
|
f.match(%r{^(test|spec|features)/})
|
|
@@ -23,7 +23,7 @@ Gem::Specification.new do |spec|
|
|
|
23
23
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
24
24
|
spec.require_paths = ["lib"]
|
|
25
25
|
|
|
26
|
-
spec.add_dependency "activerecord", ">= 6.
|
|
26
|
+
spec.add_dependency "activerecord", ">= 6.1"
|
|
27
27
|
|
|
28
28
|
spec.add_development_dependency "bundler"
|
|
29
29
|
spec.add_development_dependency "rake", "~> 13.0"
|
|
@@ -68,6 +68,10 @@ module ActiveRecord
|
|
|
68
68
|
bitemporal_option[:valid_datetime]&.in_time_zone
|
|
69
69
|
end
|
|
70
70
|
|
|
71
|
+
def valid_date
|
|
72
|
+
valid_datetime&.to_date
|
|
73
|
+
end
|
|
74
|
+
|
|
71
75
|
def ignore_valid_datetime(&block)
|
|
72
76
|
with_bitemporal_option(ignore_valid_datetime: true, valid_datetime: nil, &block)
|
|
73
77
|
end
|
|
@@ -88,6 +92,14 @@ module ActiveRecord
|
|
|
88
92
|
with_bitemporal_option(ignore_transaction_datetime: true, transaction_datetime: nil, &block)
|
|
89
93
|
end
|
|
90
94
|
|
|
95
|
+
def bitemporal_at(datetime, &block)
|
|
96
|
+
transaction_at(datetime) { valid_at(datetime, &block) }
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def bitemporal_at!(datetime, &block)
|
|
100
|
+
transaction_at!(datetime) { valid_at!(datetime, &block) }
|
|
101
|
+
end
|
|
102
|
+
|
|
91
103
|
def merge_by(option)
|
|
92
104
|
option_ = option.dup
|
|
93
105
|
if bitemporal_option_storage[:force_valid_datetime]
|
|
@@ -224,6 +236,10 @@ module ActiveRecord
|
|
|
224
236
|
with_bitemporal_option(transaction_datetime: datetime, &block)
|
|
225
237
|
end
|
|
226
238
|
|
|
239
|
+
def bitemporal_at(datetime, &block)
|
|
240
|
+
transaction_at(datetime) { valid_at(datetime, &block) }
|
|
241
|
+
end
|
|
242
|
+
|
|
227
243
|
def bitemporal_option_merge_with_association!(other)
|
|
228
244
|
bitemporal_option_merge!(other)
|
|
229
245
|
|
|
@@ -238,6 +254,10 @@ module ActiveRecord
|
|
|
238
254
|
bitemporal_option[:valid_datetime]&.in_time_zone
|
|
239
255
|
end
|
|
240
256
|
|
|
257
|
+
def valid_date
|
|
258
|
+
valid_datetime&.to_date
|
|
259
|
+
end
|
|
260
|
+
|
|
241
261
|
def transaction_datetime
|
|
242
262
|
bitemporal_option[:transaction_datetime]&.in_time_zone
|
|
243
263
|
end
|
|
@@ -277,12 +297,13 @@ module ActiveRecord
|
|
|
277
297
|
end
|
|
278
298
|
|
|
279
299
|
refine ActiveRecord::Base do
|
|
280
|
-
# MEMO: Do not copy
|
|
300
|
+
# MEMO: Do not copy bitemporal internal status
|
|
281
301
|
def dup(*)
|
|
282
302
|
super.tap { |itself|
|
|
283
303
|
itself.instance_exec do
|
|
284
304
|
@_swapped_id_previously_was = nil
|
|
285
305
|
@_swapped_id = nil
|
|
306
|
+
@previously_force_updated = false
|
|
286
307
|
end unless itself.frozen?
|
|
287
308
|
}
|
|
288
309
|
end
|
|
@@ -320,6 +341,7 @@ module ActiveRecord
|
|
|
320
341
|
before_instance&.save_without_bitemporal_callbacks!(validate: false)
|
|
321
342
|
# NOTE: after_instance always exists
|
|
322
343
|
after_instance.save_without_bitemporal_callbacks!(validate: false)
|
|
344
|
+
@previously_force_updated = self.force_update?
|
|
323
345
|
|
|
324
346
|
# update 後に新しく生成したインスタンスのデータを移行する
|
|
325
347
|
@_swapped_id_previously_was = swapped_id
|
|
@@ -334,17 +356,19 @@ module ActiveRecord
|
|
|
334
356
|
end || false
|
|
335
357
|
end
|
|
336
358
|
|
|
337
|
-
def destroy(force_delete: false, operated_at:
|
|
359
|
+
def destroy(force_delete: false, operated_at: nil)
|
|
338
360
|
return super() if force_delete
|
|
339
361
|
|
|
340
|
-
target_datetime = valid_datetime || operated_at
|
|
341
|
-
|
|
342
|
-
duplicated_instance = self.class.find_at_time(target_datetime, self.id).dup
|
|
343
|
-
|
|
344
362
|
ActiveRecord::Base.transaction(requires_new: true, joinable: false) do
|
|
345
363
|
@destroyed = false
|
|
346
364
|
_run_destroy_callbacks {
|
|
365
|
+
operated_at ||= Time.current
|
|
366
|
+
target_datetime = valid_datetime || operated_at
|
|
367
|
+
|
|
368
|
+
duplicated_instance = self.class.find_at_time(target_datetime, self.id).dup
|
|
369
|
+
|
|
347
370
|
@destroyed = update_transaction_to(operated_at)
|
|
371
|
+
@previously_force_updated = force_update?
|
|
348
372
|
|
|
349
373
|
# force_update の場合は削除時の状態の履歴を残さない
|
|
350
374
|
unless force_update?
|
|
@@ -372,6 +396,18 @@ module ActiveRecord
|
|
|
372
396
|
false
|
|
373
397
|
end
|
|
374
398
|
|
|
399
|
+
if Gem::Version.new("7.1.0") <= ActiveRecord.version
|
|
400
|
+
# MEMO: Since Rails 7.1 #_find_record refers to a record with find_by!(@primary_key => id)
|
|
401
|
+
# 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
|
|
402
|
+
# see: https://github.com/rails/rails/blob/v7.1.0/activerecord/lib/active_record/persistence.rb#L1152-#L1171
|
|
403
|
+
def _find_record(*)
|
|
404
|
+
tmp_primary_key, @primary_key = @primary_key, self.class.bitemporal_id_key
|
|
405
|
+
super
|
|
406
|
+
ensure
|
|
407
|
+
@primary_key = tmp_primary_key
|
|
408
|
+
end
|
|
409
|
+
end
|
|
410
|
+
|
|
375
411
|
module ::ActiveRecord::Persistence
|
|
376
412
|
# MEMO: Must be override ActiveRecord::Persistence#reload
|
|
377
413
|
alias_method :active_record_bitemporal_original_reload, :reload unless method_defined? :active_record_bitemporal_original_reload
|
|
@@ -397,9 +433,10 @@ module ActiveRecord
|
|
|
397
433
|
# NOTE: Hook to copying swapped_id
|
|
398
434
|
@_swapped_id_previously_was = nil
|
|
399
435
|
@_swapped_id = fresh_object.swapped_id
|
|
436
|
+
@previously_force_updated = false
|
|
400
437
|
self
|
|
401
438
|
end
|
|
402
|
-
|
|
439
|
+
else
|
|
403
440
|
def reload(options = nil)
|
|
404
441
|
return active_record_bitemporal_original_reload(options) unless self.class.bi_temporal_model?
|
|
405
442
|
|
|
@@ -420,28 +457,7 @@ module ActiveRecord
|
|
|
420
457
|
# NOTE: Hook to copying swapped_id
|
|
421
458
|
@_swapped_id_previously_was = nil
|
|
422
459
|
@_swapped_id = fresh_object.swapped_id
|
|
423
|
-
|
|
424
|
-
end
|
|
425
|
-
else
|
|
426
|
-
def reload(options = nil)
|
|
427
|
-
return active_record_bitemporal_original_reload(options) unless self.class.bi_temporal_model?
|
|
428
|
-
|
|
429
|
-
self.class.connection.clear_query_cache
|
|
430
|
-
|
|
431
|
-
fresh_object =
|
|
432
|
-
ActiveRecord::Bitemporal.with_bitemporal_option(**bitemporal_option) {
|
|
433
|
-
if options && options[:lock]
|
|
434
|
-
self.class.unscoped { self.class.lock(options[:lock]).bitemporal_default_scope.find(id) }
|
|
435
|
-
else
|
|
436
|
-
self.class.unscoped { self.class.bitemporal_default_scope.find(id) }
|
|
437
|
-
end
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
@attributes = fresh_object.instance_variable_get("@attributes")
|
|
441
|
-
@new_record = false
|
|
442
|
-
# NOTE: Hook to copying swapped_id
|
|
443
|
-
@_swapped_id_previously_was = nil
|
|
444
|
-
@_swapped_id = fresh_object.swapped_id
|
|
460
|
+
@previously_force_updated = false
|
|
445
461
|
self
|
|
446
462
|
end
|
|
447
463
|
end
|
|
@@ -573,10 +589,13 @@ module ActiveRecord
|
|
|
573
589
|
valid_from = record.valid_from if record.force_update?
|
|
574
590
|
|
|
575
591
|
valid_to = record.valid_to.yield_self { |valid_to|
|
|
592
|
+
# NOTE: `cover?` may give incorrect results, when the time zone is not UTC and `valid_from` is date type
|
|
593
|
+
# Therefore, cast to type of `valid_from`
|
|
594
|
+
record_valid_time = finder_class.type_for_attribute(:valid_from).cast(record.valid_datetime)
|
|
576
595
|
# レコードを更新する時に valid_datetime が valid_from ~ valid_to の範囲外だった場合、
|
|
577
596
|
# 一番近い未来の履歴レコードを参照して更新する
|
|
578
597
|
# という仕様があるため、それを考慮して valid_to を設定する
|
|
579
|
-
if (
|
|
598
|
+
if (record_valid_time && (record.valid_from..record.valid_to).cover?(record_valid_time)) == false && (record.persisted?)
|
|
580
599
|
finder_class.ignore_valid_datetime.where(bitemporal_id: record.bitemporal_id).valid_from_gt(target_datetime).order(valid_from: :asc).first.valid_from
|
|
581
600
|
else
|
|
582
601
|
valid_to
|
|
@@ -47,26 +47,9 @@ module ActiveRecord::Bitemporal
|
|
|
47
47
|
end
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
def
|
|
50
|
+
def each_operatable_node(nodes = predicates, &block)
|
|
51
51
|
if block
|
|
52
52
|
each_operatable_node(nodes).each(&block)
|
|
53
|
-
else
|
|
54
|
-
Enumerator.new { |y|
|
|
55
|
-
Array(nodes).each { |node|
|
|
56
|
-
case node
|
|
57
|
-
when Arel::Nodes::LessThan, Arel::Nodes::LessThanOrEqual, Arel::Nodes::GreaterThan, Arel::Nodes::GreaterThanOrEqual
|
|
58
|
-
y << node if node && node.left.respond_to?(:relation)
|
|
59
|
-
when Arel::Nodes::Grouping
|
|
60
|
-
each_operatable_node(node.expr) { |node| y << node }
|
|
61
|
-
end
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
end
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
def each_operatable_node_6_1(nodes = predicates, &block)
|
|
68
|
-
if block
|
|
69
|
-
each_operatable_node_6_1(nodes).each(&block)
|
|
70
53
|
else
|
|
71
54
|
Enumerator.new { |y|
|
|
72
55
|
Array(nodes).each { |node|
|
|
@@ -74,26 +57,18 @@ module ActiveRecord::Bitemporal
|
|
|
74
57
|
when Arel::Nodes::LessThan, Arel::Nodes::LessThanOrEqual, Arel::Nodes::GreaterThan, Arel::Nodes::GreaterThanOrEqual
|
|
75
58
|
y << node if node && node.left.respond_to?(:relation)
|
|
76
59
|
when Arel::Nodes::And
|
|
77
|
-
|
|
60
|
+
each_operatable_node(node.children) { |node| y << node }
|
|
78
61
|
when Arel::Nodes::Binary
|
|
79
|
-
|
|
80
|
-
|
|
62
|
+
each_operatable_node(node.left) { |node| y << node }
|
|
63
|
+
each_operatable_node(node.right) { |node| y << node }
|
|
81
64
|
when Arel::Nodes::Unary
|
|
82
|
-
|
|
65
|
+
each_operatable_node(node.expr) { |node| y << node }
|
|
83
66
|
end
|
|
84
67
|
}
|
|
85
68
|
}
|
|
86
69
|
end
|
|
87
70
|
end
|
|
88
71
|
|
|
89
|
-
def each_operatable_node(nodes = predicates, &block)
|
|
90
|
-
if Gem::Version.new("6.1.0") <= ActiveRecord.version
|
|
91
|
-
each_operatable_node_6_1(nodes, &block)
|
|
92
|
-
else
|
|
93
|
-
each_operatable_node_6_0(nodes, &block)
|
|
94
|
-
end
|
|
95
|
-
end
|
|
96
|
-
|
|
97
72
|
def bitemporal_query_hash(*names)
|
|
98
73
|
each_operatable_node
|
|
99
74
|
.select { |node| names.include? node.left.name.to_s }
|
|
@@ -126,31 +101,6 @@ module ActiveRecord::Bitemporal
|
|
|
126
101
|
end
|
|
127
102
|
}
|
|
128
103
|
|
|
129
|
-
if ActiveRecord.version < Gem::Version.new("6.1.0")
|
|
130
|
-
class WhereClauseWithCheckTable < ActiveRecord::Relation::WhereClause
|
|
131
|
-
using NodeBitemporalInclude
|
|
132
|
-
|
|
133
|
-
def bitemporal_include?(column)
|
|
134
|
-
!!predicates.grep(::Arel::Nodes::Node).find do |node|
|
|
135
|
-
node.bitemporal_include?(column)
|
|
136
|
-
end
|
|
137
|
-
end
|
|
138
|
-
|
|
139
|
-
private
|
|
140
|
-
|
|
141
|
-
def except_predicates(columns)
|
|
142
|
-
columns = Array(columns)
|
|
143
|
-
predicates.reject do |node|
|
|
144
|
-
::Arel::Nodes::Node === node && node.bitemporal_include?(*columns)
|
|
145
|
-
end
|
|
146
|
-
end
|
|
147
|
-
end
|
|
148
|
-
|
|
149
|
-
def where_clause
|
|
150
|
-
WhereClauseWithCheckTable.new(super.send(:predicates))
|
|
151
|
-
end
|
|
152
|
-
end
|
|
153
|
-
|
|
154
104
|
module MergeWithExceptBitemporalDefaultScope
|
|
155
105
|
using BitemporalChecker
|
|
156
106
|
def merge(other)
|
|
@@ -166,6 +116,10 @@ module ActiveRecord::Bitemporal
|
|
|
166
116
|
bitemporal_clause[:valid_datetime]&.in_time_zone
|
|
167
117
|
end
|
|
168
118
|
|
|
119
|
+
def valid_date
|
|
120
|
+
valid_datetime&.to_date
|
|
121
|
+
end
|
|
122
|
+
|
|
169
123
|
def transaction_datetime
|
|
170
124
|
bitemporal_clause[:transaction_datetime]&.in_time_zone
|
|
171
125
|
end
|
|
@@ -239,118 +193,80 @@ module ActiveRecord::Bitemporal
|
|
|
239
193
|
end
|
|
240
194
|
}
|
|
241
195
|
|
|
242
|
-
|
|
243
|
-
module
|
|
244
|
-
refine ::
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
def _ignore_#{column}
|
|
248
|
-
unscope(where: "\#{table.name}.#{column}")
|
|
249
|
-
.tap { |relation| relation.merge!(bitemporal_value[:through].unscoped._ignore_#{column}) if bitemporal_value[:through] }
|
|
250
|
-
end
|
|
251
|
-
|
|
252
|
-
def _except_#{column}
|
|
253
|
-
return self unless where_clause.bitemporal_include?("\#{table.name}.#{column}")
|
|
254
|
-
all._ignore_#{column}.tap { |relation|
|
|
255
|
-
relation.unscope_values.delete({ where: "\#{table.name}.#{column}" })
|
|
256
|
-
}
|
|
257
|
-
end
|
|
258
|
-
STR
|
|
259
|
-
|
|
260
|
-
[
|
|
261
|
-
:lt, # column < datetime
|
|
262
|
-
:lteq, # column <= datetime
|
|
263
|
-
:gt, # column > datetime
|
|
264
|
-
:gteq # column >= datetime
|
|
265
|
-
].each { |op|
|
|
266
|
-
module_eval <<-STR, __FILE__, __LINE__ + 1
|
|
267
|
-
def _#{column}_#{op}(datetime, without_ignore: false)
|
|
268
|
-
target_datetime = datetime&.in_time_zone || Time.current
|
|
269
|
-
relation = self.tap { |relation| break relation._ignore_#{column} unless without_ignore }
|
|
270
|
-
relation.bitemporal_where_bind!(:#{column}, :#{op}, target_datetime)
|
|
271
|
-
.tap { |relation| relation.merge!(bitemporal_value[:through].unscoped._#{column}_#{op}(target_datetime)) if bitemporal_value[:through] }
|
|
272
|
-
end
|
|
273
|
-
STR
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
end
|
|
277
|
-
end
|
|
278
|
-
else
|
|
279
|
-
module ActiveRecordRelationScope
|
|
280
|
-
module EqualAttributeName
|
|
281
|
-
refine ::Object do
|
|
282
|
-
def equal_attribute_name(*)
|
|
283
|
-
false
|
|
284
|
-
end
|
|
196
|
+
module ActiveRecordRelationScope
|
|
197
|
+
module EqualAttributeName
|
|
198
|
+
refine ::Object do
|
|
199
|
+
def equal_attribute_name(*)
|
|
200
|
+
false
|
|
285
201
|
end
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
202
|
+
end
|
|
203
|
+
refine ::Hash do
|
|
204
|
+
def equal_attribute_name(other)
|
|
205
|
+
self[:where].equal_attribute_name(other)
|
|
290
206
|
end
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
207
|
+
end
|
|
208
|
+
refine ::Array do
|
|
209
|
+
def equal_attribute_name(other)
|
|
210
|
+
first.equal_attribute_name(other)
|
|
295
211
|
end
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
212
|
+
end
|
|
213
|
+
refine ::String do
|
|
214
|
+
def equal_attribute_name(other)
|
|
215
|
+
self == other.to_s
|
|
300
216
|
end
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
217
|
+
end
|
|
218
|
+
refine ::Symbol do
|
|
219
|
+
def equal_attribute_name(other)
|
|
220
|
+
self.to_s == other.to_s
|
|
305
221
|
end
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
222
|
+
end
|
|
223
|
+
refine ::Arel::Attributes::Attribute do
|
|
224
|
+
def equal_attribute_name(other)
|
|
225
|
+
"#{relation.name}.#{name}" == other.to_s
|
|
310
226
|
end
|
|
311
227
|
end
|
|
228
|
+
end
|
|
312
229
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
230
|
+
refine ::ActiveRecord::Relation do
|
|
231
|
+
using EqualAttributeName
|
|
232
|
+
using NodeBitemporalInclude
|
|
316
233
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
234
|
+
def bitemporal_rewhere_bind(attr_name, operator, value, table = self.table)
|
|
235
|
+
rewhere(table[attr_name].public_send(operator, predicate_builder.build_bind_attribute(attr_name, value)))
|
|
236
|
+
end
|
|
320
237
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
238
|
+
%i(valid_from valid_to transaction_from transaction_to).each { |column|
|
|
239
|
+
module_eval <<-STR, __FILE__, __LINE__ + 1
|
|
240
|
+
def _ignore_#{column}
|
|
241
|
+
unscope(where: :"\#{table.name}.#{column}")
|
|
242
|
+
.tap { |relation| relation.unscope!(where: bitemporal_value[:through].arel_table["#{column}"]) if bitemporal_value[:through] }
|
|
243
|
+
end
|
|
327
244
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
245
|
+
def _except_#{column}
|
|
246
|
+
return self unless where_clause.send(:predicates).find { |node|
|
|
247
|
+
node.bitemporal_include?("\#{table.name}.#{column}")
|
|
248
|
+
}
|
|
249
|
+
_ignore_#{column}.tap { |relation|
|
|
250
|
+
relation.unscope_values.reject! { |query| query.equal_attribute_name("\#{table.name}.#{column}") }
|
|
251
|
+
}
|
|
252
|
+
end
|
|
253
|
+
STR
|
|
254
|
+
|
|
255
|
+
[
|
|
256
|
+
:lt, # column < datetime
|
|
257
|
+
:lteq, # column <= datetime
|
|
258
|
+
:gt, # column > datetime
|
|
259
|
+
:gteq # column >= datetime
|
|
260
|
+
].each { |op|
|
|
261
|
+
module_eval <<-STR, __FILE__, __LINE__ + 1
|
|
262
|
+
def _#{column}_#{op}(datetime,**)
|
|
263
|
+
target_datetime = datetime&.in_time_zone || Time.current
|
|
264
|
+
bitemporal_rewhere_bind("#{column}", :#{op}, target_datetime)
|
|
265
|
+
.tap { |relation| break relation.bitemporal_rewhere_bind("#{column}", :#{op}, target_datetime, bitemporal_value[:through].arel_table) if bitemporal_value[:through] }
|
|
335
266
|
end
|
|
336
267
|
STR
|
|
337
|
-
|
|
338
|
-
[
|
|
339
|
-
:lt, # column < datetime
|
|
340
|
-
:lteq, # column <= datetime
|
|
341
|
-
:gt, # column > datetime
|
|
342
|
-
:gteq # column >= datetime
|
|
343
|
-
].each { |op|
|
|
344
|
-
module_eval <<-STR, __FILE__, __LINE__ + 1
|
|
345
|
-
def _#{column}_#{op}(datetime,**)
|
|
346
|
-
target_datetime = datetime&.in_time_zone || Time.current
|
|
347
|
-
bitemporal_rewhere_bind("#{column}", :#{op}, target_datetime)
|
|
348
|
-
.tap { |relation| break relation.bitemporal_rewhere_bind("#{column}", :#{op}, target_datetime, bitemporal_value[:through].arel_table) if bitemporal_value[:through] }
|
|
349
|
-
end
|
|
350
|
-
STR
|
|
351
|
-
}
|
|
352
268
|
}
|
|
353
|
-
|
|
269
|
+
}
|
|
354
270
|
end
|
|
355
271
|
end
|
|
356
272
|
using ActiveRecordRelationScope
|
|
@@ -49,7 +49,8 @@ module ActiveRecord::Bitemporal
|
|
|
49
49
|
prev_valid_times.each do |valid_time|
|
|
50
50
|
headers.print('|', line: line, column: columns[valid_time])
|
|
51
51
|
end
|
|
52
|
-
|
|
52
|
+
valid_time_str = valid_time.kind_of?(Date) ? valid_time.strftime('%F') : valid_time.strftime('%F %T.%3N')
|
|
53
|
+
headers.print("| #{valid_time_str}", line: line, column: columns[valid_time])
|
|
53
54
|
prev_valid_times << valid_time
|
|
54
55
|
end
|
|
55
56
|
|
|
@@ -92,13 +93,14 @@ module ActiveRecord::Bitemporal
|
|
|
92
93
|
end
|
|
93
94
|
end
|
|
94
95
|
|
|
96
|
+
valid_label = valid_times[0].kind_of?(Date) ? 'valid_date' : 'valid_datetime'
|
|
95
97
|
transaction_label = 'transaction_datetime'
|
|
96
98
|
right_margin = time_length + 1 - transaction_label.size
|
|
97
99
|
|
|
98
100
|
label = if right_margin >= 0
|
|
99
|
-
"#{transaction_label + ' ' * right_margin}|
|
|
101
|
+
"#{transaction_label + ' ' * right_margin}| #{valid_label}"
|
|
100
102
|
else
|
|
101
|
-
"#{transaction_label[0...right_margin]}|
|
|
103
|
+
"#{transaction_label[0...right_margin]}| #{valid_label}"
|
|
102
104
|
end
|
|
103
105
|
|
|
104
106
|
"#{label}\n#{headers.to_s}\n#{body.to_s}"
|
|
@@ -104,6 +104,10 @@ module ActiveRecord::Bitemporal::Bitemporalize
|
|
|
104
104
|
swapped_id.presence || super
|
|
105
105
|
end
|
|
106
106
|
|
|
107
|
+
def previously_force_updated?
|
|
108
|
+
@previously_force_updated
|
|
109
|
+
end
|
|
110
|
+
|
|
107
111
|
def valid_from_cannot_be_greater_equal_than_valid_to
|
|
108
112
|
if valid_from && valid_to && valid_from >= valid_to
|
|
109
113
|
errors.add(:valid_from, "can't be greater equal than valid_to")
|
|
@@ -141,10 +145,12 @@ module ActiveRecord::Bitemporal::Bitemporalize
|
|
|
141
145
|
# MEMO: #update_columns is not call #_update_row (and validations, callbacks)
|
|
142
146
|
update_columns(bitemporal_id_key => swapped_id) unless send(bitemporal_id_key)
|
|
143
147
|
swap_id!(without_clear_changes_information: true)
|
|
148
|
+
@previously_force_updated = false
|
|
144
149
|
end
|
|
145
150
|
|
|
146
151
|
after_find do
|
|
147
152
|
self.swap_id! if self.send(bitemporal_id_key).present?
|
|
153
|
+
@previously_force_updated = false
|
|
148
154
|
end
|
|
149
155
|
|
|
150
156
|
# Callback hook to `validates :xxx, uniqueness: true`
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: activerecord-bitemporal
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 5.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- SmartHR
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2024-02-02 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activerecord
|
|
@@ -16,14 +16,14 @@ dependencies:
|
|
|
16
16
|
requirements:
|
|
17
17
|
- - ">="
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: '6.
|
|
19
|
+
version: '6.1'
|
|
20
20
|
type: :runtime
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
24
|
- - ">="
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: '6.
|
|
26
|
+
version: '6.1'
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
28
|
name: bundler
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -159,9 +159,9 @@ files:
|
|
|
159
159
|
- bin/console
|
|
160
160
|
- bin/setup
|
|
161
161
|
- docker-compose.yml
|
|
162
|
-
- gemfiles/rails_6.0.gemfile
|
|
163
162
|
- gemfiles/rails_6.1.gemfile
|
|
164
163
|
- gemfiles/rails_7.0.gemfile
|
|
164
|
+
- gemfiles/rails_7.1.gemfile
|
|
165
165
|
- gemfiles/rails_main.gemfile
|
|
166
166
|
- lib/activerecord-bitemporal.rb
|
|
167
167
|
- lib/activerecord-bitemporal/bitemporal.rb
|
|
@@ -183,7 +183,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
183
183
|
requirements:
|
|
184
184
|
- - ">="
|
|
185
185
|
- !ruby/object:Gem::Version
|
|
186
|
-
version:
|
|
186
|
+
version: '3.0'
|
|
187
187
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
188
188
|
requirements:
|
|
189
189
|
- - ">="
|