activerecord-bitemporal 5.1.0 → 5.3.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: 682d7ade7951759a2237d6395aab4dccc06d5c5ea6d1820b20e1bd1301d2efc7
4
- data.tar.gz: a58bb4c8b01a5f7724ad367ab69e45a54f715b96a0d789e7690689798d0f0a88
3
+ metadata.gz: c660ad9554b7d5285a28f3efcd6a6c10e2b5e45b6922956e37f931ae807ba483
4
+ data.tar.gz: 24646d6dc75344e11f613f70c7847639f8c86524ce3d1b178f4abb69dc60694f
5
5
  SHA512:
6
- metadata.gz: a1761e5e97fc83beac29d7e23edec2ab5bb4d02958b5a7600073b099ca800a6ad3ffa3f8f9bc423aafb412c4c11a2bb1cb03d09d4fd36b6d77e7dd02560a9493
7
- data.tar.gz: efc543c3eb6b2ab89d3556eb25f342983d8e5854ebbec77ee6cf853c01dd0ccd64945264c53f30bacb9d370a6083ba65b3791e88b26c0c5c4ecea4481ad66e0c
6
+ metadata.gz: 39e0aea4e39482bcb929d0df586232413193f57a232ca7b2b928ca9aaef90d84fa88fc1edb925bcbff466864314beafa69e9caf36057ba7f4529019791d33698
7
+ data.tar.gz: 84bc4e20042ffb77c2f317fcf6ce85b1b469edb338ec0a1346a9dd0008d1b01e49037503124778144521704ed97af904632bf91f424acc41448968d4beba0a5b
@@ -0,0 +1,6 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: "github-actions"
4
+ directory: "/"
5
+ schedule:
6
+ interval: "weekly"
@@ -19,7 +19,7 @@ jobs:
19
19
  GEM_HOST_API_KEY: ${{ secrets.GEM_HOST_API_KEY }}
20
20
  GEM_HOST_OTP_CODE: ${{ github.event.inputs.rubygems-otp-code }}
21
21
  steps:
22
- - uses: actions/checkout@v3
22
+ - uses: actions/checkout@v4
23
23
  with:
24
24
  fetch-depth: 0
25
25
 
@@ -0,0 +1,54 @@
1
+ # yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json
2
+
3
+ name: Test
4
+
5
+ on:
6
+ push:
7
+ branches:
8
+ - master
9
+ pull_request:
10
+
11
+ jobs:
12
+ test:
13
+ runs-on: ubuntu-latest
14
+ strategy:
15
+ fail-fast: false
16
+ matrix:
17
+ ruby-version:
18
+ - '3.0'
19
+ - '3.1'
20
+ - '3.2'
21
+ - '3.3'
22
+ gemfile:
23
+ - rails_6.1
24
+ - rails_7.0
25
+ - rails_7.1
26
+ exclude:
27
+ - ruby-version: '3.2'
28
+ gemfile: rails_6.1
29
+ - ruby-version: '3.3'
30
+ gemfile: rails_6.1
31
+ services:
32
+ postgres:
33
+ image: postgres:14
34
+ env:
35
+ POSTGRES_PASSWORD: postgres
36
+ # Set health checks to wait until postgres has started
37
+ options: >-
38
+ --health-cmd pg_isready
39
+ --health-interval 10s
40
+ --health-timeout 5s
41
+ --health-retries 5
42
+ ports:
43
+ - 5432:5432
44
+ env:
45
+ BUNDLE_GEMFILE: gemfiles/${{ matrix.gemfile }}.gemfile
46
+ steps:
47
+ - name: Checkout
48
+ uses: actions/checkout@v4
49
+ - name: Setup Ruby
50
+ uses: ruby/setup-ruby@v1
51
+ with:
52
+ ruby-version: ${{ matrix.ruby-version }}
53
+ bundler-cache: true
54
+ - run: bundle exec rspec
data/Appraisals CHANGED
@@ -1,9 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- appraise "rails-main" do
4
- gem "rails", git: 'https://github.com/rails/rails.git', branch: "main"
5
- end
6
-
7
3
  appraise "rails-6.1" do
8
4
  gem "rails", "~> 6.1.0"
9
5
  end
data/CHANGELOG.md CHANGED
@@ -1,5 +1,51 @@
1
1
  # Changelog
2
2
 
3
+ ## 5.3.0
4
+
5
+ ### Added
6
+
7
+ ### Changed
8
+
9
+ - [Replace `default` overrides from `load_schema!` to `attribute` #172](https://github.com/kufu/activerecord-bitemporal/pull/172)
10
+ - [`Or` node changed to `Nary` in Rails 7.2 #173](https://github.com/kufu/activerecord-bitemporal/pull/173)
11
+ - [Exclude record valid range end in uniqueness validation #184](https://github.com/kufu/activerecord-bitemporal/pull/184)
12
+
13
+ ### Deprecated
14
+
15
+ ### Removed
16
+
17
+ ### Fixed
18
+
19
+ ### Chores
20
+
21
+ - [Add specs under specific conditions #171](https://github.com/kufu/activerecord-bitemporal/pull/171)
22
+ - [Add Dependabot for GitHub Actions #174](https://github.com/kufu/activerecord-bitemporal/pull/174)
23
+ - [Bump actions/checkout from 3 to 4 #175](https://github.com/kufu/activerecord-bitemporal/pull/175)
24
+ - [Replace CircleCI with GitHub Actions #177](https://github.com/kufu/activerecord-bitemporal/pull/177)
25
+ - [Update README as `within_deleted` and `without_deleted` are deprecated #178](https://github.com/kufu/activerecord-bitemporal/pull/178)
26
+ - [Fix typo #181](https://github.com/kufu/activerecord-bitemporal/pull/181)
27
+ - [Fix #force_update block comment #183](https://github.com/kufu/activerecord-bitemporal/pull/183)
28
+ - [Support Docker Compose V2 #186](https://github.com/kufu/activerecord-bitemporal/pull/186)
29
+
30
+ ## 5.2.0
31
+
32
+ ### Added
33
+
34
+ ### Changed
35
+
36
+ ### Deprecated
37
+
38
+ ### Removed
39
+
40
+ ### Fixed
41
+
42
+ - [Delegate CollectionProxy#bitemporal_value to Relation #168](https://github.com/kufu/activerecord-bitemporal/pull/168)
43
+ - [Fix unintended valid_datetime set when `CollectionProxy#load` #169](https://github.com/kufu/activerecord-bitemporal/pull/169)
44
+
45
+ ### Chores
46
+
47
+ - [Do not run CI against rails_main #166](https://github.com/kufu/activerecord-bitemporal/pull/166)
48
+
3
49
  ## 5.1.0
4
50
 
5
51
  ### Added
data/README.md CHANGED
@@ -317,7 +317,7 @@ Timecop.freeze("2019/1/10") {
317
317
  }
318
318
 
319
319
  Timecop.freeze("2019/1/20") {
320
- # #force_update のでは自身を受け取る
320
+ # #force_update のブロック内で自身を受け取る
321
321
  # このブロック内であれば履歴を生成せずにレコードの変更が行われる
322
322
  employee.force_update { |employee|
323
323
  employee.update(name: "Tom")
@@ -415,7 +415,7 @@ BTDM では DB からレコードを参照する場合、暗黙的に
415
415
  Timecop.freeze("2019/1/20") {
416
416
  # 現在の時間の履歴を返すために暗黙的に時間指定や論理削除されたレコードが除かれる
417
417
  puts Employee.all.to_sql
418
- # => SELECT "employees".* FROM "employees" WHERE "employees"."valid_from" <= '2019-01-20 00:00:00' AND "employees"."valid_to" > '2019-01-20 00:00:00' AND "employees"."transaction_to" = '9999-12-31 00:00:00'
418
+ # => SELECT "employees".* FROM "employees" WHERE "employees"."valid_from" <= '2019-01-20 00:00:00' AND "employees"."valid_to" > '2019-01-20 00:00:00' AND "employees"."transaction_from" <= '2019-01-20 00:00:00' AND "employees"."transaction_to" > '2019-01-20 00:00:00'
419
419
  }
420
420
  ```
421
421
 
@@ -454,7 +454,7 @@ Timecop.freeze("2019/1/20") {
454
454
 
455
455
  # なぜなら暗黙的に時間指定のクエリが追加されている為
456
456
  puts Employee.where(name: "Jane").to_sql
457
- # => SELECT "employees".* FROM "employees" WHERE "employees"."valid_from" <= '2019-01-20 00:00:00' AND "employees"."valid_to" > '2019-01-20 00:00:00' AND "employees"."transaction_to" = '9999-12-31 00:00:00' AND "employees"."name" = 'Jane'
457
+ # => SELECT "employees".* FROM "employees" WHERE "employees"."valid_from" <= '2019-01-20 00:00:00' AND "employees"."valid_to" > '2019-01-20 00:00:00' AND "employees"."transaction_from" <= '2019-01-20 00:00:00' AND "employees"."transaction_to" > '2019-01-20 00:00:00' AND "employees"."name" = 'Jane'
458
458
  }
459
459
  ```
460
460
 
@@ -464,7 +464,7 @@ Timecop.freeze("2019/1/20") {
464
464
  ```ruby
465
465
  # default_scope であれば unscoped で無効化することが出来るが、BTDM のデフォルトクエリはそのまま
466
466
  puts Employee.unscoped { Employee.all.to_sql }
467
- # => SELECT "employees".* FROM "employees" WHERE "employees"."valid_from" <= '2019-10-25 07:56:06.731259' AND "employees"."valid_to" > '2019-10-25 07:56:06.731259' AND "employees"."transaction_to" = '9999-12-31 00:00:00'
467
+ # => SELECT "employees".* FROM "employees" WHERE "employees"."valid_from" <= '2019-10-25 07:56:06.731259' AND "employees"."valid_to" > '2019-10-25 07:56:06.731259' AND "employees"."transaction_from" <= '2019-10-25 07:56:06.731259' AND "employees"."transaction_to" > '2019-10-25 07:56:06.731259'
468
468
  ```
469
469
 
470
470
 
@@ -475,21 +475,21 @@ puts Employee.unscoped { Employee.all.to_sql }
475
475
  | スコープ | 動作 |
476
476
  | --- | --- |
477
477
  | `.ignore_valid_datetime` | 時間指定を無視する |
478
- | `.within_deleted` | 論理削除されているレコードを含める |
479
- | `.without_deleted` | 論理削除されているレコードを含めない |
478
+ | `.ignore_transaction_datetime` | 論理削除されているレコードを含める |
479
+ | `.ignore_bitemporal_datetime` | 全てのレコードを対象とする |
480
480
 
481
481
  ```ruby
482
482
  Timecop.freeze("2019/1/20") {
483
483
  # 時間指定をしているクエリを取り除く
484
484
  puts Employee.ignore_valid_datetime.to_sql
485
- # => SELECT "employees".* FROM "employees" WHERE "employees"."transaction_to" = '9999-12-31 00:00:00'
485
+ # => SELECT "employees".* FROM "employees" WHERE "employees"."transaction_from" <= '2019-01-20 00:00:00' AND "employees"."transaction_to" > '2019-01-20 00:00:00'
486
486
 
487
487
  # 論理削除しているレコードも含める
488
- puts Employee.within_deleted.to_sql
488
+ puts Employee.ignore_transaction_datetime.to_sql
489
489
  # => SELECT "employees".* FROM "employees" WHERE "employees"."valid_from" <= '2019-01-20 00:00:00' AND "employees"."valid_to" > '2019-01-20 00:00:00'
490
490
 
491
491
  # 全てのレコードを対象とする
492
- puts Employee.ignore_valid_datetime.within_deleted.to_sql
492
+ puts Employee.ignore_bitemporal_datetime.to_sql
493
493
  # => SELECT "employees".* FROM "employees"
494
494
  }
495
495
  ```
@@ -534,7 +534,7 @@ Timecop.freeze("2019/1/15") {
534
534
  Timecop.freeze("2019/1/20") {
535
535
  # valid_at で任意の時間を参照して検索する事が出来る
536
536
  puts Employee.valid_at("2019/1/10").to_sql
537
- # => SELECT "employees".* FROM "employees" WHERE "employees"."valid_from" <= '2019-01-10 00:00:00' AND "employees"."valid_to" > '2019-01-10 00:00:00' AND "employees"."transaction_to" = '9999-12-31 00:00:00'
537
+ # => SELECT "employees".* FROM "employees" WHERE "employees"."valid_from" <= '2019-01-10 00:00:00' AND "employees"."valid_to" > '2019-01-10 00:00:00' AND "employees"."transaction_from" <= '2019-01-20 00:00:00' AND "employees"."transaction_to" > '2019-01-20 00:00:00'
538
538
 
539
539
  pp Employee.valid_at("2019/1/10").map(&:name)
540
540
  # => ["Jane"]
@@ -696,7 +696,7 @@ BTDM では `find_by(id: xxx)` や `where(id: xxx)` を行う場合 `id` では
696
696
  Employee.find_by(id: employee.id)
697
697
 
698
698
  # OK : bitemporal_id で検索を行う
699
- # MEMO: id = bitemporal_id なの
699
+ # MEMO: id = bitemporal_id なので
700
700
  # find_by(bitemporal_id: employee.id)
701
701
  # でも動作するが employee.bitemporal_id と書いたほうが意図が伝わりやすい
702
702
  Employee.find_by(bitemporal_id: employee.bitemporal_id)
@@ -1,4 +1,3 @@
1
- version: '2.1'
2
1
  services:
3
2
  db:
4
3
  image: postgres:latest
@@ -595,8 +595,8 @@ module ActiveRecord
595
595
  # レコードを更新する時に valid_datetime が valid_from ~ valid_to の範囲外だった場合、
596
596
  # 一番近い未来の履歴レコードを参照して更新する
597
597
  # という仕様があるため、それを考慮して valid_to を設定する
598
- if (record_valid_time && (record.valid_from..record.valid_to).cover?(record_valid_time)) == false && (record.persisted?)
599
- finder_class.ignore_valid_datetime.where(bitemporal_id: record.bitemporal_id).valid_from_gt(target_datetime).order(valid_from: :asc).first.valid_from
598
+ if (record_valid_time && (record.valid_from...record.valid_to).cover?(record_valid_time)) == false && (record.persisted?)
599
+ finder_class.ignore_valid_datetime.where(bitemporal_id: record.bitemporal_id).valid_from_gteq(target_datetime).order(valid_from: :asc).first.valid_from
600
600
  else
601
601
  valid_to
602
602
  end
@@ -27,6 +27,10 @@ module ActiveRecord::Bitemporal
27
27
  end
28
28
 
29
29
  module Relation
30
+ # see: https://github.com/rails/rails/pull/51492
31
+ NARY_NODE = ActiveRecord.gem_version >= Gem::Version.new("7.2.0") ? Arel::Nodes::Nary : Arel::Nodes::And
32
+ private_constant :NARY_NODE
33
+
30
34
  using Module.new {
31
35
  refine ActiveRecord::Relation::WhereClause do
32
36
  using Module.new {
@@ -56,7 +60,7 @@ module ActiveRecord::Bitemporal
56
60
  case node
57
61
  when Arel::Nodes::LessThan, Arel::Nodes::LessThanOrEqual, Arel::Nodes::GreaterThan, Arel::Nodes::GreaterThanOrEqual
58
62
  y << node if node && node.left.respond_to?(:relation)
59
- when Arel::Nodes::And
63
+ when NARY_NODE
60
64
  each_operatable_node(node.children) { |node| y << node }
61
65
  when Arel::Nodes::Binary
62
66
  each_operatable_node(node.left) { |node| y << node }
@@ -141,6 +145,22 @@ module ActiveRecord::Bitemporal
141
145
  end
142
146
  end
143
147
 
148
+ module CollectionProxy
149
+ # Delegate to ActiveRecord::Bitemporal::Relation
150
+ # @see https://github.com/rails/rails/blob/v7.1.3.4/activerecord/lib/active_record/associations/collection_proxy.rb#L1115-L1124
151
+ #
152
+ # In order to update the CollectionProxy state, `load` needs to be excluded.
153
+ # The reason for using `@association` instead of delegating this method is to preserve state such as `loaded`.
154
+ # @see https://github.com/rails/rails/blob/v7.1.3.4/activerecord/lib/active_record/associations/collection_proxy.rb#L44
155
+ #
156
+ # There is no need to delegate to `scope` as `ActiveRecord::Bitemporal::Relation::Finder`'s methods are delegated
157
+ # by `ActiveRecord::Delegation`. This is not a problem because `scoping` used in this is delegated to `scope`.
158
+ # @see https://github.com/rails/rails/blob/v7.1.3.4/activerecord/lib/active_record/relation/delegation.rb#L117
159
+ delegate :bitemporal_value, :bitemporal_value=, :valid_datetime, :valid_date,
160
+ :transaction_datetime, :bitemporal_option, :bitemporal_option_merge!,
161
+ :build_arel, :primary_key, to: :scope
162
+ end
163
+
144
164
  module Scope
145
165
  extend ActiveSupport::Concern
146
166
 
@@ -2,6 +2,6 @@
2
2
 
3
3
  module ActiveRecord
4
4
  module Bitemporal
5
- VERSION = "5.1.0"
5
+ VERSION = "5.3.0"
6
6
  end
7
7
  end
@@ -29,7 +29,6 @@ module ActiveRecord::Bitemporal::Bitemporalize
29
29
  def prepend_relation_delegate_class(mod)
30
30
  relation_delegate_class(ActiveRecord::Relation).prepend mod
31
31
  relation_delegate_class(ActiveRecord::AssociationRelation).prepend mod
32
- relation_delegate_class(ActiveRecord::Associations::CollectionProxy).prepend mod
33
32
  end
34
33
  end
35
34
  end
@@ -38,13 +37,6 @@ module ActiveRecord::Bitemporal::Bitemporalize
38
37
  module ClassMethods
39
38
  include ActiveRecord::Bitemporal::Relation::Finder
40
39
 
41
- DEFAULT_ATTRIBUTES = {
42
- valid_from: ActiveRecord::Bitemporal::DEFAULT_VALID_FROM,
43
- valid_to: ActiveRecord::Bitemporal::DEFAULT_VALID_TO,
44
- transaction_from: ActiveRecord::Bitemporal::DEFAULT_TRANSACTION_FROM,
45
- transaction_to: ActiveRecord::Bitemporal::DEFAULT_TRANSACTION_TO
46
- }.freeze
47
-
48
40
  def bitemporal_id_key
49
41
  'bitemporal_id'
50
42
  end
@@ -58,20 +50,11 @@ module ActiveRecord::Bitemporal::Bitemporalize
58
50
  def inherited(klass)
59
51
  super
60
52
  klass.prepend_relation_delegate_class ActiveRecord::Bitemporal::Relation
53
+ klass.relation_delegate_class(ActiveRecord::Associations::CollectionProxy).prepend ActiveRecord::Bitemporal::CollectionProxy
61
54
  if relation_delegate_class(ActiveRecord::Relation).ancestors.include? ActiveRecord::Bitemporal::Relation::MergeWithExceptBitemporalDefaultScope
62
55
  klass.relation_delegate_class(ActiveRecord::Relation).prepend ActiveRecord::Bitemporal::Relation::MergeWithExceptBitemporalDefaultScope
63
56
  end
64
57
  end
65
-
66
- private
67
- def load_schema!
68
- super
69
-
70
- DEFAULT_ATTRIBUTES.each do |name, default_value|
71
- type = type_for_attribute(name)
72
- define_attribute(name.to_s, type, default: default_value)
73
- end
74
- end
75
58
  end
76
59
 
77
60
  module InstanceMethods
@@ -153,6 +136,11 @@ module ActiveRecord::Bitemporal::Bitemporalize
153
136
  @previously_force_updated = false
154
137
  end
155
138
 
139
+ attribute :valid_from, default: ActiveRecord::Bitemporal::DEFAULT_VALID_FROM
140
+ attribute :valid_to, default: ActiveRecord::Bitemporal::DEFAULT_VALID_TO
141
+ attribute :transaction_from, default: ActiveRecord::Bitemporal::DEFAULT_TRANSACTION_FROM
142
+ attribute :transaction_to, default: ActiveRecord::Bitemporal::DEFAULT_TRANSACTION_TO
143
+
156
144
  # Callback hook to `validates :xxx, uniqueness: true`
157
145
  const_set(:UniquenessValidator, Class.new(ActiveRecord::Validations::UniquenessValidator) {
158
146
  prepend ActiveRecord::Bitemporal::Uniqueness
@@ -169,6 +157,7 @@ module ActiveRecord::Bitemporal::Bitemporalize
169
157
  validates bitemporal_id_key, uniqueness: true, allow_nil: true, strict: enable_strict_by_validates_bitemporal_id
170
158
 
171
159
  prepend_relation_delegate_class ActiveRecord::Bitemporal::Relation
160
+ relation_delegate_class(ActiveRecord::Associations::CollectionProxy).prepend ActiveRecord::Bitemporal::CollectionProxy
172
161
  end
173
162
  end
174
163
 
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: 5.1.0
4
+ version: 5.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - SmartHR
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-06-05 00:00:00.000000000 Z
11
+ date: 2025-01-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -129,9 +129,10 @@ executables: []
129
129
  extensions: []
130
130
  extra_rdoc_files: []
131
131
  files:
132
- - ".circleci/config.yml"
133
132
  - ".github/auto_assign.yml"
133
+ - ".github/dependabot.yml"
134
134
  - ".github/workflows/release.yml"
135
+ - ".github/workflows/test.yml"
135
136
  - ".gitignore"
136
137
  - Appraisals
137
138
  - CHANGELOG.md
@@ -144,11 +145,10 @@ files:
144
145
  - activerecord-bitemporal.gemspec
145
146
  - bin/console
146
147
  - bin/setup
147
- - docker-compose.yml
148
+ - compose.yml
148
149
  - gemfiles/rails_6.1.gemfile
149
150
  - gemfiles/rails_7.0.gemfile
150
151
  - gemfiles/rails_7.1.gemfile
151
- - gemfiles/rails_main.gemfile
152
152
  - lib/activerecord-bitemporal.rb
153
153
  - lib/activerecord-bitemporal/bitemporal.rb
154
154
  - lib/activerecord-bitemporal/callbacks.rb
data/.circleci/config.yml DELETED
@@ -1,41 +0,0 @@
1
- version: 2.1
2
-
3
- jobs:
4
- rspec:
5
- parameters:
6
- ruby-version:
7
- type: string
8
- gemfile:
9
- type: string
10
- docker:
11
- - image: ruby:<< parameters.ruby-version >>
12
- environment:
13
- BUNDLE_GEMFILE: gemfiles/<< parameters.gemfile >>.gemfile
14
- - image: cimg/postgres:11.16
15
- steps:
16
- - checkout
17
- - run: gem install bundler
18
- - run: bundle install
19
- - run: bundle exec rspec
20
-
21
- workflows:
22
- test:
23
- jobs:
24
- - rspec:
25
- matrix:
26
- parameters:
27
- ruby-version:
28
- - '3.0'
29
- - '3.1'
30
- - '3.2'
31
- - '3.3'
32
- gemfile:
33
- - rails_6.1
34
- - rails_7.0
35
- - rails_7.1
36
- - rails_main
37
- exclude:
38
- - ruby-version: '3.2'
39
- gemfile: rails_6.1
40
- - ruby-version: '3.3'
41
- gemfile: rails_6.1
@@ -1,8 +0,0 @@
1
- # This file was generated by Appraisal
2
-
3
- source "https://rubygems.org"
4
-
5
- gem "appraisal"
6
- gem "rails", git: "https://github.com/rails/rails.git", branch: "main"
7
-
8
- gemspec path: "../"