activerecord-bitemporal 4.3.0 → 5.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.circleci/config.yml +13 -11
- data/.github/auto_assign.yml +0 -5
- data/Appraisals +4 -4
- data/CHANGELOG.md +34 -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 +44 -31
- data/lib/activerecord-bitemporal/scope.rb +73 -166
- data/lib/activerecord-bitemporal/version.rb +1 -1
- data/lib/activerecord-bitemporal/visualizer.rb +5 -3
- metadata +7 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f0e6cafe3d918cd4bdf47c2610eaf3760cfcedbec0365a7675ef99790a05baaf
|
4
|
+
data.tar.gz: 79b7823949b22cbcacdfb75b561c70808135ebd3dc1c6a5ff0ef8152c75b8d79
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4331c2b17a26dfea76b5c1c85a6a0feac925a1c0b7d634fa734e35e5f7732e0d49d04172ae4bf6af5ae62b213481bd09da7807312f241578e72f2a9ee952f640
|
7
|
+
data.tar.gz: 98ffa072e196a6af9b61ef01ee5e7d8e3ef83a94abd8f666738831783b35bb16551fd0bd1d561f76a6a55aa522d4609274ec3e6a6a78d311b7c76e53c860b1c4
|
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/.github/auto_assign.yml
CHANGED
@@ -6,15 +6,10 @@ addAssignees: false
|
|
6
6
|
|
7
7
|
# A list of reviewers to be added to pull requests (GitHub user name)
|
8
8
|
reviewers:
|
9
|
-
- Dooor
|
10
|
-
- QWYNG
|
11
|
-
- bonobono555
|
12
9
|
- krororo
|
13
10
|
- lighty
|
14
11
|
- mkmn
|
15
|
-
- motsat
|
16
12
|
- osyo-manga
|
17
|
-
- wakasa51
|
18
13
|
- wata727
|
19
14
|
- yono
|
20
15
|
|
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,39 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## 5.0.1
|
4
|
+
|
5
|
+
- [Fix a typo #157](https://github.com/kufu/activerecord-bitemporal/pull/157)
|
6
|
+
- [Remove unneeded unscope values #159](https://github.com/kufu/activerecord-bitemporal/pull/159)
|
7
|
+
- [Remove unused `without_ignore` option #160](https://github.com/kufu/activerecord-bitemporal/pull/160)
|
8
|
+
- [update auto assign #161](https://github.com/kufu/activerecord-bitemporal/pull/161)
|
9
|
+
|
10
|
+
## 5.0.0
|
11
|
+
|
12
|
+
### Breaking Changed
|
13
|
+
|
14
|
+
- [CI against Ruby 3.2, 3.3, Drop Ruby 2.7 and Rails 6.0 #150](https://github.com/kufu/activerecord-bitemporal/pull/150)
|
15
|
+
|
16
|
+
### Added
|
17
|
+
|
18
|
+
- [Support date type of valid time #149](https://github.com/kufu/activerecord-bitemporal/pull/149)
|
19
|
+
- [Add support Rails 7.1 #151](https://github.com/kufu/activerecord-bitemporal/pull/151)
|
20
|
+
- [Add Persistence#bitemporal_at and ActiveRecord::Bitemporal.bitemporal_at #152](https://github.com/kufu/activerecord-bitemporal/pull/152)
|
21
|
+
|
22
|
+
### Changed
|
23
|
+
|
24
|
+
### Deprecated
|
25
|
+
|
26
|
+
### Removed
|
27
|
+
|
28
|
+
### Fixed
|
29
|
+
|
30
|
+
- [Fix deleted time order between associations #154](https://github.com/kufu/activerecord-bitemporal/pull/154)
|
31
|
+
|
32
|
+
### Chores
|
33
|
+
|
34
|
+
- [Don't use appraisal in CI #145](https://github.com/kufu/activerecord-bitemporal/pull/145)
|
35
|
+
- [Remove code for Active Record 6.0 #153](https://github.com/kufu/activerecord-bitemporal/pull/153)
|
36
|
+
|
3
37
|
## 4.3.0
|
4
38
|
|
5
39
|
### 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
|
@@ -336,16 +356,17 @@ module ActiveRecord
|
|
336
356
|
end || false
|
337
357
|
end
|
338
358
|
|
339
|
-
def destroy(force_delete: false, operated_at:
|
359
|
+
def destroy(force_delete: false, operated_at: nil)
|
340
360
|
return super() if force_delete
|
341
361
|
|
342
|
-
target_datetime = valid_datetime || operated_at
|
343
|
-
|
344
|
-
duplicated_instance = self.class.find_at_time(target_datetime, self.id).dup
|
345
|
-
|
346
362
|
ActiveRecord::Base.transaction(requires_new: true, joinable: false) do
|
347
363
|
@destroyed = false
|
348
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
|
+
|
349
370
|
@destroyed = update_transaction_to(operated_at)
|
350
371
|
@previously_force_updated = force_update?
|
351
372
|
|
@@ -375,6 +396,18 @@ module ActiveRecord
|
|
375
396
|
false
|
376
397
|
end
|
377
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
|
+
|
378
411
|
module ::ActiveRecord::Persistence
|
379
412
|
# MEMO: Must be override ActiveRecord::Persistence#reload
|
380
413
|
alias_method :active_record_bitemporal_original_reload, :reload unless method_defined? :active_record_bitemporal_original_reload
|
@@ -403,7 +436,7 @@ module ActiveRecord
|
|
403
436
|
@previously_force_updated = false
|
404
437
|
self
|
405
438
|
end
|
406
|
-
|
439
|
+
else
|
407
440
|
def reload(options = nil)
|
408
441
|
return active_record_bitemporal_original_reload(options) unless self.class.bi_temporal_model?
|
409
442
|
|
@@ -427,29 +460,6 @@ module ActiveRecord
|
|
427
460
|
@previously_force_updated = false
|
428
461
|
self
|
429
462
|
end
|
430
|
-
else
|
431
|
-
def reload(options = nil)
|
432
|
-
return active_record_bitemporal_original_reload(options) unless self.class.bi_temporal_model?
|
433
|
-
|
434
|
-
self.class.connection.clear_query_cache
|
435
|
-
|
436
|
-
fresh_object =
|
437
|
-
ActiveRecord::Bitemporal.with_bitemporal_option(**bitemporal_option) {
|
438
|
-
if options && options[:lock]
|
439
|
-
self.class.unscoped { self.class.lock(options[:lock]).bitemporal_default_scope.find(id) }
|
440
|
-
else
|
441
|
-
self.class.unscoped { self.class.bitemporal_default_scope.find(id) }
|
442
|
-
end
|
443
|
-
}
|
444
|
-
|
445
|
-
@attributes = fresh_object.instance_variable_get("@attributes")
|
446
|
-
@new_record = false
|
447
|
-
# NOTE: Hook to copying swapped_id
|
448
|
-
@_swapped_id_previously_was = nil
|
449
|
-
@_swapped_id = fresh_object.swapped_id
|
450
|
-
@previously_force_updated = false
|
451
|
-
self
|
452
|
-
end
|
453
463
|
end
|
454
464
|
end
|
455
465
|
|
@@ -567,7 +577,7 @@ module ActiveRecord
|
|
567
577
|
target_datetime
|
568
578
|
# NOTE: 新規作成時以外では target_datetime の値を基準としてバリデーションする
|
569
579
|
# 更新時にバリデーションする場合、valid_from の時間ではなくて target_datetime の時間を基準としているため
|
570
|
-
#
|
580
|
+
# valid_from を基準としてしまうと整合性が取れなくなってしまう
|
571
581
|
elsif !record.new_record?
|
572
582
|
target_datetime
|
573
583
|
else
|
@@ -579,10 +589,13 @@ module ActiveRecord
|
|
579
589
|
valid_from = record.valid_from if record.force_update?
|
580
590
|
|
581
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)
|
582
595
|
# レコードを更新する時に valid_datetime が valid_from ~ valid_to の範囲外だった場合、
|
583
596
|
# 一番近い未来の履歴レコードを参照して更新する
|
584
597
|
# という仕様があるため、それを考慮して valid_to を設定する
|
585
|
-
if (
|
598
|
+
if (record_valid_time && (record.valid_from..record.valid_to).cover?(record_valid_time)) == false && (record.persisted?)
|
586
599
|
finder_class.ignore_valid_datetime.where(bitemporal_id: record.bitemporal_id).valid_from_gt(target_datetime).order(valid_from: :asc).first.valid_from
|
587
600
|
else
|
588
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
|
@@ -439,14 +355,9 @@ module ActiveRecord::Bitemporal
|
|
439
355
|
relation.bitemporal_value[:with_transaction_datetime] = :default_scope
|
440
356
|
end
|
441
357
|
|
442
|
-
# Calling scope was slow, so don't call scope
|
443
|
-
relation.unscope_values += [
|
444
|
-
{ where: "#{table.name}.transaction_from" },
|
445
|
-
{ where: "#{table.name}.transaction_to" }
|
446
|
-
]
|
447
358
|
relation = relation
|
448
|
-
._transaction_from_lteq(transaction_datetime || datetime
|
449
|
-
._transaction_to_gt(transaction_datetime || datetime
|
359
|
+
._transaction_from_lteq(transaction_datetime || datetime)
|
360
|
+
._transaction_to_gt(transaction_datetime || datetime)
|
450
361
|
else
|
451
362
|
relation.tap { |relation| relation.without_transaction_datetime unless ActiveRecord::Bitemporal.transaction_datetime }
|
452
363
|
end
|
@@ -459,13 +370,9 @@ module ActiveRecord::Bitemporal
|
|
459
370
|
relation.bitemporal_value[:with_valid_datetime] = :default_scope
|
460
371
|
end
|
461
372
|
|
462
|
-
relation.unscope_values += [
|
463
|
-
{ where: "#{table.name}.valid_from" },
|
464
|
-
{ where: "#{table.name}.valid_to" }
|
465
|
-
]
|
466
373
|
relation = relation
|
467
|
-
._valid_from_lteq(valid_datetime || datetime
|
468
|
-
._valid_to_gt(valid_datetime || datetime
|
374
|
+
._valid_from_lteq(valid_datetime || datetime)
|
375
|
+
._valid_to_gt(valid_datetime || datetime)
|
469
376
|
else
|
470
377
|
relation.tap { |relation| relation.without_valid_datetime unless ActiveRecord::Bitemporal.valid_datetime }
|
471
378
|
end
|
@@ -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}"
|
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.1
|
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-05-10 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,14 +183,14 @@ 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
|
- - ">="
|
190
190
|
- !ruby/object:Gem::Version
|
191
191
|
version: '0'
|
192
192
|
requirements: []
|
193
|
-
rubygems_version: 3.3.
|
193
|
+
rubygems_version: 3.3.27
|
194
194
|
signing_key:
|
195
195
|
specification_version: 4
|
196
196
|
summary: BiTemporal Data Model for ActiveRecord
|