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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 97a21996b6ff931bf324653432cd481eac86731ee3de16445c98627987683639
4
- data.tar.gz: 34178dcfc6e5fe4cbd1f01554499f075d1be90208f05cc2d39a0039941d03607
3
+ metadata.gz: 25ef613d84eda786e3d04e90aeb9cc95b68887a84863200aa0622be9e89c73ea
4
+ data.tar.gz: 63920865d4f8f27703b6ffeaf4e0c38d297ea4bff22bf01a3884136875b2ccde
5
5
  SHA512:
6
- metadata.gz: da5f747f97ec3c16f14b65d592482b23cc8a0d8ba2d1c691dc98df8b5f84d5dfd8c92e8be67362f8b6be38b0bd584c2bfdd97f5169f9e4d91347a7286ad87770
7
- data.tar.gz: 2a1b5d95ac36a751943a87adcb9231b81633f61139a6ca38ca3d34859a7e9a2f647ce204b549eda50efca9cc48245d7aff7c21499397d4ef673d1f4c02de84a4
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 appraisal << parameters.gemfile >> bundle install
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
- - rails-6.0
32
- - rails-6.1
33
- - rails-7.0
34
- - rails-main
33
+ - rails_6.1
34
+ - rails_7.0
35
+ - rails_7.1
36
+ - rails_main
35
37
  exclude:
36
- - ruby-version: '3.0'
37
- gemfile: rails-6.0
38
- - ruby-version: '3.1'
39
- gemfile: rails-6.0
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 = ">= 2.7.0"
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.0"
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"
@@ -3,6 +3,6 @@
3
3
  source "https://rubygems.org"
4
4
 
5
5
  gem "appraisal"
6
- gem "rails", "~> 6.0.2"
6
+ gem "rails", "~> 7.1.0"
7
7
 
8
8
  gemspec path: "../"
@@ -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 `swapped_id`
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: Time.current)
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
- elsif Gem::Version.new("6.1.0") <= ActiveRecord.version
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
- self
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 (record.valid_datetime && (record.valid_from..record.valid_to).cover?(record.valid_datetime)) == false && (record.persisted?)
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 each_operatable_node_6_0(nodes = predicates, &block)
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
- each_operatable_node_6_1(node.children) { |node| y << node }
60
+ each_operatable_node(node.children) { |node| y << node }
78
61
  when Arel::Nodes::Binary
79
- each_operatable_node_6_1(node.left) { |node| y << node }
80
- each_operatable_node_6_1(node.right) { |node| y << node }
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
- each_operatable_node_6_1(node.expr) { |node| y << node }
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
- if ActiveRecord.version < Gem::Version.new("6.1.0")
243
- module ActiveRecordRelationScope
244
- refine ::ActiveRecord::Relation do
245
- %i(valid_from valid_to transaction_from transaction_to).each { |column|
246
- module_eval <<-STR, __FILE__, __LINE__ + 1
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
- refine ::Hash do
287
- def equal_attribute_name(other)
288
- self[:where].equal_attribute_name(other)
289
- end
202
+ end
203
+ refine ::Hash do
204
+ def equal_attribute_name(other)
205
+ self[:where].equal_attribute_name(other)
290
206
  end
291
- refine ::Array do
292
- def equal_attribute_name(other)
293
- first.equal_attribute_name(other)
294
- end
207
+ end
208
+ refine ::Array do
209
+ def equal_attribute_name(other)
210
+ first.equal_attribute_name(other)
295
211
  end
296
- refine ::String do
297
- def equal_attribute_name(other)
298
- self == other.to_s
299
- end
212
+ end
213
+ refine ::String do
214
+ def equal_attribute_name(other)
215
+ self == other.to_s
300
216
  end
301
- refine ::Symbol do
302
- def equal_attribute_name(other)
303
- self.to_s == other.to_s
304
- end
217
+ end
218
+ refine ::Symbol do
219
+ def equal_attribute_name(other)
220
+ self.to_s == other.to_s
305
221
  end
306
- refine ::Arel::Attributes::Attribute do
307
- def equal_attribute_name(other)
308
- "#{relation.name}.#{name}" == other.to_s
309
- end
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
- refine ::ActiveRecord::Relation do
314
- using EqualAttributeName
315
- using NodeBitemporalInclude
230
+ refine ::ActiveRecord::Relation do
231
+ using EqualAttributeName
232
+ using NodeBitemporalInclude
316
233
 
317
- def bitemporal_rewhere_bind(attr_name, operator, value, table = self.table)
318
- rewhere(table[attr_name].public_send(operator, predicate_builder.build_bind_attribute(attr_name, value)))
319
- end
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
- %i(valid_from valid_to transaction_from transaction_to).each { |column|
322
- module_eval <<-STR, __FILE__, __LINE__ + 1
323
- def _ignore_#{column}
324
- unscope(where: :"\#{table.name}.#{column}")
325
- .tap { |relation| relation.unscope!(where: bitemporal_value[:through].arel_table["#{column}"]) if bitemporal_value[:through] }
326
- end
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
- def _except_#{column}
329
- return self unless where_clause.send(:predicates).find { |node|
330
- node.bitemporal_include?("\#{table.name}.#{column}")
331
- }
332
- _ignore_#{column}.tap { |relation|
333
- relation.unscope_values.reject! { |query| query.equal_attribute_name("\#{table.name}.#{column}") }
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
- end
269
+ }
354
270
  end
355
271
  end
356
272
  using ActiveRecordRelationScope
@@ -2,6 +2,6 @@
2
2
 
3
3
  module ActiveRecord
4
4
  module Bitemporal
5
- VERSION = "4.2.0"
5
+ VERSION = "5.0.0"
6
6
  end
7
7
  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
- headers.print("| #{valid_time.strftime('%F %T.%3N')}", line: line, column: columns[valid_time])
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}| valid_datetime"
101
+ "#{transaction_label + ' ' * right_margin}| #{valid_label}"
100
102
  else
101
- "#{transaction_label[0...right_margin]}| valid_datetime"
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.2.0
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: 2023-07-24 00:00:00.000000000 Z
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.0'
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.0'
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: 2.7.0
186
+ version: '3.0'
187
187
  required_rubygems_version: !ruby/object:Gem::Requirement
188
188
  requirements:
189
189
  - - ">="