activerecord-bitemporal 0.0.1 → 1.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 +123 -0
- data/.github/auto_assign.yml +27 -0
- data/.gitignore +2 -8
- data/Appraisals +21 -0
- data/CHANGELOG.md +39 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +5 -2
- data/Gemfile.lock +62 -2
- data/LICENSE +202 -0
- data/README.md +724 -0
- data/Rakefile +6 -0
- data/activerecord-bitemporal.gemspec +20 -18
- data/bin/console +1 -0
- data/docker-compose.yml +11 -0
- data/gemfiles/rails_5.2.gemfile +8 -0
- data/gemfiles/rails_6.0.gemfile +8 -0
- data/gemfiles/rails_6.1.gemfile +8 -0
- data/gemfiles/rails_7.0.gemfile +8 -0
- data/gemfiles/rails_main.gemfile +8 -0
- data/lib/activerecord-bitemporal/bitemporal.rb +588 -0
- data/lib/activerecord-bitemporal/patches.rb +130 -0
- data/lib/activerecord-bitemporal/scope.rb +501 -0
- data/lib/activerecord-bitemporal/version.rb +4 -2
- data/lib/activerecord-bitemporal.rb +177 -4
- metadata +156 -15
@@ -1,8 +1,181 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_record"
|
4
|
+
require "active_support/core_ext/time/calculations"
|
5
|
+
require "activerecord-bitemporal/bitemporal"
|
6
|
+
require "activerecord-bitemporal/scope"
|
7
|
+
require "activerecord-bitemporal/patches"
|
1
8
|
require "activerecord-bitemporal/version"
|
2
9
|
|
3
|
-
module
|
4
|
-
|
5
|
-
|
6
|
-
|
10
|
+
module ActiveRecord::Bitemporal
|
11
|
+
DEFAULT_VALID_FROM = Time.utc(1900, 12, 31).in_time_zone.freeze
|
12
|
+
DEFAULT_VALID_TO = Time.utc(9999, 12, 31).in_time_zone.freeze
|
13
|
+
DEFAULT_TRANSACTION_FROM = Time.utc(1900, 12, 31).in_time_zone.freeze
|
14
|
+
DEFAULT_TRANSACTION_TO = Time.utc(9999, 12, 31).in_time_zone.freeze
|
15
|
+
|
16
|
+
extend ActiveSupport::Concern
|
17
|
+
included do
|
18
|
+
bitemporalize
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
module ActiveRecord::Bitemporal::Bitemporalize
|
23
|
+
using Module.new {
|
24
|
+
refine ::ActiveRecord::Base do
|
25
|
+
class << ::ActiveRecord::Base
|
26
|
+
def prepend_relation_delegate_class(mod)
|
27
|
+
relation_delegate_class(ActiveRecord::Relation).prepend mod
|
28
|
+
relation_delegate_class(ActiveRecord::AssociationRelation).prepend mod
|
29
|
+
relation_delegate_class(ActiveRecord::Associations::CollectionProxy).prepend mod
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
}
|
34
|
+
|
35
|
+
module ClassMethods
|
36
|
+
include ActiveRecord::Bitemporal::Relation::Finder
|
37
|
+
|
38
|
+
DEFAULT_ATTRIBUTES = {
|
39
|
+
valid_from: ActiveRecord::Bitemporal::DEFAULT_VALID_FROM,
|
40
|
+
valid_to: ActiveRecord::Bitemporal::DEFAULT_VALID_TO,
|
41
|
+
transaction_from: ActiveRecord::Bitemporal::DEFAULT_TRANSACTION_FROM,
|
42
|
+
transaction_to: ActiveRecord::Bitemporal::DEFAULT_TRANSACTION_TO
|
43
|
+
}.freeze
|
44
|
+
|
45
|
+
def bitemporal_id_key
|
46
|
+
'bitemporal_id'
|
47
|
+
end
|
48
|
+
|
49
|
+
# Override ActiveRecord::Core::ClassMethods#cached_find_by_statement
|
50
|
+
# `.find_by` not use caching
|
51
|
+
def cached_find_by_statement(key, &block)
|
52
|
+
ActiveRecord::StatementCache.create(connection, &block)
|
53
|
+
end
|
54
|
+
|
55
|
+
def inherited(klass)
|
56
|
+
super
|
57
|
+
klass.prepend_relation_delegate_class ActiveRecord::Bitemporal::Relation
|
58
|
+
if relation_delegate_class(ActiveRecord::Relation).ancestors.include? ActiveRecord::Bitemporal::Relation::MergeWithExceptBitemporalDefaultScope
|
59
|
+
klass.relation_delegate_class(ActiveRecord::Relation).prepend ActiveRecord::Bitemporal::Relation::MergeWithExceptBitemporalDefaultScope
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
def load_schema!
|
65
|
+
super
|
66
|
+
|
67
|
+
DEFAULT_ATTRIBUTES.each do |name, default_value|
|
68
|
+
type = type_for_attribute(name)
|
69
|
+
define_attribute(name.to_s, type, default: default_value)
|
70
|
+
end
|
71
|
+
end
|
7
72
|
end
|
73
|
+
|
74
|
+
module InstanceMethods
|
75
|
+
include ActiveRecord::Bitemporal::Persistence
|
76
|
+
|
77
|
+
def swap_id!(without_clear_changes_information: false)
|
78
|
+
@_swapped_id = self.id
|
79
|
+
self.id = self.send(bitemporal_id_key)
|
80
|
+
clear_attribute_changes([:id]) unless without_clear_changes_information
|
81
|
+
end
|
82
|
+
|
83
|
+
def swapped_id
|
84
|
+
@_swapped_id || self.id
|
85
|
+
end
|
86
|
+
|
87
|
+
def bitemporal_id_key
|
88
|
+
self.class.bitemporal_id_key
|
89
|
+
end
|
90
|
+
|
91
|
+
def bitemporal_ignore_update_columns
|
92
|
+
[]
|
93
|
+
end
|
94
|
+
|
95
|
+
def id_in_database
|
96
|
+
swapped_id.presence || super
|
97
|
+
end
|
98
|
+
|
99
|
+
def valid_from_cannot_be_greater_equal_than_valid_to
|
100
|
+
if valid_from && valid_to && valid_from >= valid_to
|
101
|
+
errors.add(:valid_from, "can't be greater equal than valid_to")
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def transaction_from_cannot_be_greater_equal_than_transaction_to
|
106
|
+
if transaction_from && transaction_to && transaction_from >= transaction_to
|
107
|
+
errors.add(:transaction_from, "can't be greater equal than transaction_to")
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def bitemporalize(
|
113
|
+
enable_strict_by_validates_bitemporal_id: false,
|
114
|
+
enable_default_scope: true,
|
115
|
+
enable_merge_with_except_bitemporal_default_scope: false
|
116
|
+
)
|
117
|
+
extend ClassMethods
|
118
|
+
include InstanceMethods
|
119
|
+
include ActiveRecord::Bitemporal::Scope
|
120
|
+
|
121
|
+
if enable_merge_with_except_bitemporal_default_scope
|
122
|
+
relation_delegate_class(ActiveRecord::Relation).prepend ActiveRecord::Bitemporal::Relation::MergeWithExceptBitemporalDefaultScope
|
123
|
+
end
|
124
|
+
|
125
|
+
if enable_default_scope
|
126
|
+
default_scope {
|
127
|
+
bitemporal_default_scope
|
128
|
+
}
|
129
|
+
end
|
130
|
+
|
131
|
+
after_create do
|
132
|
+
# MEMO: #update_columns is not call #_update_row (and validations, callbacks)
|
133
|
+
update_columns(bitemporal_id_key => swapped_id) unless send(bitemporal_id_key)
|
134
|
+
swap_id!(without_clear_changes_information: true)
|
135
|
+
end
|
136
|
+
|
137
|
+
after_find do
|
138
|
+
self.swap_id! if self.send(bitemporal_id_key).present?
|
139
|
+
end
|
140
|
+
|
141
|
+
# Callback hook to `validates :xxx, uniqueness: true`
|
142
|
+
const_set(:UniquenessValidator, Class.new(ActiveRecord::Validations::UniquenessValidator) {
|
143
|
+
prepend ActiveRecord::Bitemporal::Uniqueness
|
144
|
+
})
|
145
|
+
|
146
|
+
# validations
|
147
|
+
validates :valid_from, presence: true
|
148
|
+
validates :valid_to, presence: true
|
149
|
+
validates :transaction_from, presence: true
|
150
|
+
validates :transaction_to, presence: true
|
151
|
+
validate :valid_from_cannot_be_greater_equal_than_valid_to
|
152
|
+
validate :transaction_from_cannot_be_greater_equal_than_transaction_to
|
153
|
+
|
154
|
+
validates bitemporal_id_key, uniqueness: true, allow_nil: true, strict: enable_strict_by_validates_bitemporal_id
|
155
|
+
|
156
|
+
prepend_relation_delegate_class ActiveRecord::Bitemporal::Relation
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
ActiveSupport.on_load(:active_record) do
|
161
|
+
ActiveRecord::Base
|
162
|
+
.extend ActiveRecord::Bitemporal::Bitemporalize
|
163
|
+
|
164
|
+
ActiveRecord::Base
|
165
|
+
.prepend ActiveRecord::Bitemporal::Patches::Persistence
|
166
|
+
|
167
|
+
ActiveRecord::Relation::Merger
|
168
|
+
.prepend ActiveRecord::Bitemporal::Patches::Merger
|
169
|
+
|
170
|
+
ActiveRecord::Associations::Association
|
171
|
+
.prepend ActiveRecord::Bitemporal::Patches::Association
|
172
|
+
|
173
|
+
ActiveRecord::Associations::ThroughAssociation
|
174
|
+
.prepend ActiveRecord::Bitemporal::Patches::ThroughAssociation
|
175
|
+
|
176
|
+
ActiveRecord::Associations::SingularAssociation
|
177
|
+
.prepend ActiveRecord::Bitemporal::Patches::SingularAssociation
|
178
|
+
|
179
|
+
ActiveRecord::Reflection::AssociationReflection
|
180
|
+
.prepend ActiveRecord::Bitemporal::Patches::AssociationReflection
|
8
181
|
end
|
metadata
CHANGED
@@ -1,37 +1,178 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activerecord-bitemporal
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
-
|
8
|
-
autorequire:
|
7
|
+
- mserizawa
|
8
|
+
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
12
|
-
dependencies:
|
13
|
-
|
11
|
+
date: 2022-05-12 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activerecord
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '5.2'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '5.2'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '13.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '13.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '3.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '3.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: pg
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: pry
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: pry-byebug
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: database_cleaner
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: timecop
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
description: Enable ActiveRecord models to be handled as BiTemporal Data Model.
|
14
140
|
email:
|
15
|
-
-
|
141
|
+
- serizawa@smarthr.co.jp
|
16
142
|
executables: []
|
17
143
|
extensions: []
|
18
144
|
extra_rdoc_files: []
|
19
145
|
files:
|
146
|
+
- ".circleci/config.yml"
|
147
|
+
- ".github/auto_assign.yml"
|
20
148
|
- ".gitignore"
|
149
|
+
- Appraisals
|
150
|
+
- CHANGELOG.md
|
151
|
+
- CODE_OF_CONDUCT.md
|
21
152
|
- Gemfile
|
22
153
|
- Gemfile.lock
|
154
|
+
- LICENSE
|
155
|
+
- README.md
|
23
156
|
- Rakefile
|
24
157
|
- activerecord-bitemporal.gemspec
|
25
158
|
- bin/console
|
26
159
|
- bin/setup
|
160
|
+
- docker-compose.yml
|
161
|
+
- gemfiles/rails_5.2.gemfile
|
162
|
+
- gemfiles/rails_6.0.gemfile
|
163
|
+
- gemfiles/rails_6.1.gemfile
|
164
|
+
- gemfiles/rails_7.0.gemfile
|
165
|
+
- gemfiles/rails_main.gemfile
|
27
166
|
- lib/activerecord-bitemporal.rb
|
167
|
+
- lib/activerecord-bitemporal/bitemporal.rb
|
168
|
+
- lib/activerecord-bitemporal/patches.rb
|
169
|
+
- lib/activerecord-bitemporal/scope.rb
|
28
170
|
- lib/activerecord-bitemporal/version.rb
|
29
171
|
homepage: https://github.com/kufu/activerecord-bitemporal
|
30
|
-
licenses:
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
post_install_message:
|
172
|
+
licenses:
|
173
|
+
- Apache 2.0
|
174
|
+
metadata: {}
|
175
|
+
post_install_message:
|
35
176
|
rdoc_options: []
|
36
177
|
require_paths:
|
37
178
|
- lib
|
@@ -46,8 +187,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
46
187
|
- !ruby/object:Gem::Version
|
47
188
|
version: '0'
|
48
189
|
requirements: []
|
49
|
-
rubygems_version: 3.
|
50
|
-
signing_key:
|
190
|
+
rubygems_version: 3.3.3
|
191
|
+
signing_key:
|
51
192
|
specification_version: 4
|
52
|
-
summary:
|
193
|
+
summary: BiTemporal Data Model for ActiveRecord
|
53
194
|
test_files: []
|