activerecord-temporal 0.2.0 → 0.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 +4 -4
- data/CHANGELOG.md +6 -0
- data/README.md +11 -8
- data/lib/activerecord/temporal/application_versioning/application_versioned.rb +3 -3
- data/lib/activerecord/temporal/application_versioning.rb +2 -0
- data/lib/activerecord/temporal/patches/join_dependency.rb +3 -2
- data/lib/activerecord/temporal/system_versioning/command_recorder.rb +1 -27
- data/lib/activerecord/temporal/system_versioning/schema_creation.rb +6 -3
- data/lib/activerecord/temporal/system_versioning/schema_definitions.rb +10 -8
- data/lib/activerecord/temporal/system_versioning/schema_statements.rb +32 -66
- data/lib/activerecord/temporal/version.rb +1 -1
- data/lib/activerecord/temporal.rb +5 -19
- metadata +2 -7
- data/lib/activerecord/temporal/application_versioning/command_recorder.rb +0 -14
- data/lib/activerecord/temporal/application_versioning/migration.rb +0 -25
- data/lib/activerecord/temporal/application_versioning/schema_statements.rb +0 -33
- data/lib/activerecord/temporal/patches/command_recorder.rb +0 -23
- data/lib/activerecord/temporal/system_versioning/migration.rb +0 -35
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 87a493283c2b5a1746266fdedb4978b7ad768be307b73b40b2aa404b460a198c
|
|
4
|
+
data.tar.gz: ea10fd65c8c61878d1a92ae098a558094e7090e4ae09692ac15c1bd61b94a406
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1ffaf0ec3fdc39fb9c4239dcde8f10f27921726be199a4969125c6873d75f29c30f62c60627d229edd45bd4db532eabbbca3e64c760878599f2bd7b4bec0f6e8
|
|
7
|
+
data.tar.gz: 4cecd795b83076371ad73ac26014a77a7791202269c6fb6903bfef358d9226e445d1e40a0dc8fe433a668562d82c57463809dedab4892dfa9303a12d9154bd44
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [0.3.0] - 2025-11-20
|
|
4
|
+
|
|
5
|
+
- Application versioning: Raise `ClosedRevisionError` when trying to revise or inactive closed versions.
|
|
6
|
+
- System versioning: Store gem version in versioning hooks
|
|
7
|
+
- System versioning: Add assertion for matching primary key on versioning hook creation
|
|
8
|
+
|
|
3
9
|
## [0.2.0] - 2025-11-19
|
|
4
10
|
|
|
5
11
|
- Added ambient/global time scopes
|
data/README.md
CHANGED
|
@@ -8,16 +8,17 @@ It provides both system versioning and application versioning. They can be used
|
|
|
8
8
|
|
|
9
9
|
As applications mature, changing business requirements become increasingly complicated by the need to handle historical data. You might need to:
|
|
10
10
|
|
|
11
|
-
-
|
|
12
|
-
- Allow users to see
|
|
13
|
-
-
|
|
14
|
-
-
|
|
11
|
+
- Know the price of a product at the time it was added to a cart
|
|
12
|
+
- Allow users to see content as it was before their subscription ended
|
|
13
|
+
- Track the lifetime of long-lived, slowly change entities like projects or customers
|
|
14
|
+
- Generate reports based on the data as it was known at some time in the past
|
|
15
15
|
|
|
16
16
|
Many Rails applications use a patchwork of approaches:
|
|
17
17
|
|
|
18
18
|
- **Soft deletes** with a `deleted_at` column, but updates that still permanently overwrite data.
|
|
19
|
-
- **Audit gems or JSON
|
|
19
|
+
- **Audit gems or JSON fields** that serialize rows. Their data doesn't evolve with schema changes and can't be easily integrated into Active Record queries, scopes, and associations.
|
|
20
20
|
- **Event systems** that are used to fill gaps in the data model and gradually take on responsibilities that are implementation details with no business relevance.
|
|
21
|
+
- **Ad-hoc snapshot columns** that result in important business entities having their historical data duplicated across many different and incohesive tables.
|
|
21
22
|
|
|
22
23
|
Temporal databases solve these problems by providing a simple and coherent data model to reach for whenever historical data is needed.
|
|
23
24
|
|
|
@@ -38,6 +39,8 @@ gem "activerecord-temporal"
|
|
|
38
39
|
|
|
39
40
|
### Create a System Versioned Table
|
|
40
41
|
|
|
42
|
+
Make sure you're using the `:sql` schema dumper.
|
|
43
|
+
|
|
41
44
|
Create your regular `employees` table. For the `employees_history` table, add the `system_period` column and include it in the table's primary key. `#create_versioning_hook` is what enables system versioning.
|
|
42
45
|
|
|
43
46
|
```ruby
|
|
@@ -412,7 +415,7 @@ class LineItem < ApplicationRecord
|
|
|
412
415
|
end
|
|
413
416
|
|
|
414
417
|
module History
|
|
415
|
-
include Temporal::
|
|
418
|
+
include ActiveRecord::Temporal::HistoryModelNamespace
|
|
416
419
|
end
|
|
417
420
|
|
|
418
421
|
History::Product # => History::Product(id: integer, system_period: tstzrange, name: string)
|
|
@@ -442,7 +445,7 @@ By default, calling `system_versioning` will look for a namespace called `Histor
|
|
|
442
445
|
|
|
443
446
|
```ruby
|
|
444
447
|
module Versions
|
|
445
|
-
include Temporal::
|
|
448
|
+
include ActiveRecord::Temporal::HistoryModelNamespace
|
|
446
449
|
end
|
|
447
450
|
|
|
448
451
|
class ApplicationRecord < ActiveRecord::Base
|
|
@@ -462,7 +465,7 @@ By default, the namespace will only provide history models for models in the roo
|
|
|
462
465
|
|
|
463
466
|
```ruby
|
|
464
467
|
module History
|
|
465
|
-
include Temporal::
|
|
468
|
+
include ActiveRecord::Temporal::HistoryModelNamespace
|
|
466
469
|
|
|
467
470
|
namespace "Tenant"
|
|
468
471
|
|
|
@@ -92,7 +92,7 @@ module ActiveRecord::Temporal
|
|
|
92
92
|
end
|
|
93
93
|
|
|
94
94
|
def revise_at(time)
|
|
95
|
-
raise "
|
|
95
|
+
raise ClosedRevisionError, "Cannot revise closed version" unless head_revision?
|
|
96
96
|
|
|
97
97
|
Revision.new(self, time, save: true)
|
|
98
98
|
end
|
|
@@ -102,7 +102,7 @@ module ActiveRecord::Temporal
|
|
|
102
102
|
end
|
|
103
103
|
|
|
104
104
|
def revision_at(time)
|
|
105
|
-
raise "
|
|
105
|
+
raise ClosedRevisionError, "Cannot revise closed version" unless head_revision?
|
|
106
106
|
|
|
107
107
|
Revision.new(self, time, save: false)
|
|
108
108
|
end
|
|
@@ -112,7 +112,7 @@ module ActiveRecord::Temporal
|
|
|
112
112
|
end
|
|
113
113
|
|
|
114
114
|
def inactivate_at(time)
|
|
115
|
-
raise "
|
|
115
|
+
raise ClosedRevisionError, "Cannot inactivate closed version" unless head_revision?
|
|
116
116
|
|
|
117
117
|
set_time_dimension_end(time)
|
|
118
118
|
save
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
module ActiveRecord::Temporal
|
|
2
2
|
module Patches
|
|
3
3
|
# This is a copy of a fix from https://github.com/rails/rails/pull/56088 that
|
|
4
|
-
# impacts this gem. I has been backported to supported stable
|
|
5
|
-
# Active Record, but until those patches are released it's included
|
|
4
|
+
# impacts this gem. I has been merged and backported to supported stable
|
|
5
|
+
# versions of Active Record, but until those patches are released it's included
|
|
6
|
+
# here.
|
|
6
7
|
module JoinDependency
|
|
7
8
|
def instantiate(result_set, strict_loading_value, &block)
|
|
8
9
|
primary_key = Array(join_root.primary_key).map { |column| aliases.column_alias(join_root, column) }
|
|
@@ -1,26 +1,10 @@
|
|
|
1
1
|
module ActiveRecord::Temporal
|
|
2
2
|
module SystemVersioning
|
|
3
3
|
module CommandRecorder
|
|
4
|
-
module ArrayExtractOptions
|
|
5
|
-
refine Array do
|
|
6
|
-
def extract_options
|
|
7
|
-
if last.is_a?(Hash) && last.extractable_options?
|
|
8
|
-
last
|
|
9
|
-
else
|
|
10
|
-
{}
|
|
11
|
-
end
|
|
12
|
-
end
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
using ArrayExtractOptions
|
|
17
|
-
|
|
18
4
|
[
|
|
19
5
|
:create_versioning_hook,
|
|
20
6
|
:drop_versioning_hook,
|
|
21
|
-
:change_versioning_hook
|
|
22
|
-
:create_table_with_system_versioning,
|
|
23
|
-
:drop_table_with_system_versioning
|
|
7
|
+
:change_versioning_hook
|
|
24
8
|
].each do |method|
|
|
25
9
|
class_eval <<-EOV, __FILE__, __LINE__ + 1
|
|
26
10
|
def #{method}(*args)
|
|
@@ -58,16 +42,6 @@ module ActiveRecord::Temporal
|
|
|
58
42
|
]
|
|
59
43
|
]
|
|
60
44
|
end
|
|
61
|
-
|
|
62
|
-
def invert_create_table_with_system_versioning(args)
|
|
63
|
-
[:drop_table_with_system_versioning, args]
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
def invert_drop_table_with_system_versioning(args)
|
|
67
|
-
# TODO make this reversible
|
|
68
|
-
|
|
69
|
-
raise ActiveRecord::IrreversibleMigration, "drop_table_with_system_versioning is not reversible"
|
|
70
|
-
end
|
|
71
45
|
end
|
|
72
46
|
end
|
|
73
47
|
end
|
|
@@ -30,7 +30,8 @@ module ActiveRecord::Temporal
|
|
|
30
30
|
verb: :insert,
|
|
31
31
|
source_table: o.source_table,
|
|
32
32
|
history_table: o.history_table,
|
|
33
|
-
columns: o.columns
|
|
33
|
+
columns: o.columns,
|
|
34
|
+
gem_version: o.gem_version
|
|
34
35
|
}
|
|
35
36
|
|
|
36
37
|
<<~SQL
|
|
@@ -64,7 +65,8 @@ module ActiveRecord::Temporal
|
|
|
64
65
|
source_table: o.source_table,
|
|
65
66
|
history_table: o.history_table,
|
|
66
67
|
columns: o.columns,
|
|
67
|
-
primary_key: o.primary_key
|
|
68
|
+
primary_key: o.primary_key,
|
|
69
|
+
gem_version: o.gem_version
|
|
68
70
|
}
|
|
69
71
|
|
|
70
72
|
<<~SQL
|
|
@@ -101,7 +103,8 @@ module ActiveRecord::Temporal
|
|
|
101
103
|
verb: :delete,
|
|
102
104
|
source_table: o.source_table,
|
|
103
105
|
history_table: o.history_table,
|
|
104
|
-
primary_key: o.primary_key
|
|
106
|
+
primary_key: o.primary_key,
|
|
107
|
+
gem_version: o.gem_version
|
|
105
108
|
}
|
|
106
109
|
|
|
107
110
|
<<~SQL
|
|
@@ -1,37 +1,39 @@
|
|
|
1
1
|
module ActiveRecord::Temporal
|
|
2
2
|
module SystemVersioning
|
|
3
3
|
class VersioningHookDefinition
|
|
4
|
-
attr_accessor :source_table, :history_table, :columns, :primary_key
|
|
4
|
+
attr_accessor :source_table, :history_table, :columns, :primary_key, :gem_version
|
|
5
5
|
|
|
6
6
|
def initialize(
|
|
7
7
|
source_table,
|
|
8
8
|
history_table,
|
|
9
9
|
columns:,
|
|
10
|
-
primary_key
|
|
10
|
+
primary_key:,
|
|
11
|
+
gem_version:
|
|
11
12
|
)
|
|
12
13
|
@source_table = source_table
|
|
13
14
|
@history_table = history_table
|
|
14
15
|
@columns = columns
|
|
15
16
|
@primary_key = primary_key
|
|
17
|
+
@gem_version = gem_version
|
|
16
18
|
end
|
|
17
19
|
|
|
18
20
|
def insert_hook
|
|
19
|
-
InsertHookDefinition.new(@source_table, @history_table, @columns)
|
|
21
|
+
InsertHookDefinition.new(@source_table, @history_table, @columns, @gem_version)
|
|
20
22
|
end
|
|
21
23
|
|
|
22
24
|
def update_hook
|
|
23
|
-
UpdateHookDefinition.new(@source_table, @history_table, @columns, @primary_key)
|
|
25
|
+
UpdateHookDefinition.new(@source_table, @history_table, @columns, @primary_key, @gem_version)
|
|
24
26
|
end
|
|
25
27
|
|
|
26
28
|
def delete_hook
|
|
27
|
-
DeleteHookDefinition.new(@source_table, @history_table, @primary_key)
|
|
29
|
+
DeleteHookDefinition.new(@source_table, @history_table, @primary_key, @gem_version)
|
|
28
30
|
end
|
|
29
31
|
end
|
|
30
32
|
|
|
31
|
-
InsertHookDefinition = Struct.new(:source_table, :history_table, :columns)
|
|
33
|
+
InsertHookDefinition = Struct.new(:source_table, :history_table, :columns, :gem_version)
|
|
32
34
|
|
|
33
|
-
UpdateHookDefinition = Struct.new(:source_table, :history_table, :columns, :primary_key)
|
|
35
|
+
UpdateHookDefinition = Struct.new(:source_table, :history_table, :columns, :primary_key, :gem_version)
|
|
34
36
|
|
|
35
|
-
DeleteHookDefinition = Struct.new(:source_table, :history_table, :primary_key)
|
|
37
|
+
DeleteHookDefinition = Struct.new(:source_table, :history_table, :primary_key, :gem_version)
|
|
36
38
|
end
|
|
37
39
|
end
|
|
@@ -1,72 +1,29 @@
|
|
|
1
1
|
module ActiveRecord::Temporal
|
|
2
2
|
module SystemVersioning
|
|
3
3
|
module SchemaStatements
|
|
4
|
-
def create_table_with_system_versioning(table_name, **options, &block)
|
|
5
|
-
create_table(table_name, **options, &block)
|
|
6
|
-
|
|
7
|
-
source_pk = Array(primary_key(table_name))
|
|
8
|
-
history_options = options.merge(primary_key: source_pk + ["system_period"])
|
|
9
|
-
|
|
10
|
-
exclusion_constraint_expression = source_pk.map do |col|
|
|
11
|
-
"#{col} WITH ="
|
|
12
|
-
end.join(", ") + ", system_period WITH &&"
|
|
13
|
-
|
|
14
|
-
create_table("#{table_name}_history", **history_options) do |t|
|
|
15
|
-
columns(table_name).each do |column|
|
|
16
|
-
t.send(
|
|
17
|
-
column.type,
|
|
18
|
-
column.name,
|
|
19
|
-
comment: column.comment,
|
|
20
|
-
collation: column.collation,
|
|
21
|
-
default: nil,
|
|
22
|
-
limit: column.limit,
|
|
23
|
-
null: column.null,
|
|
24
|
-
precision: column.precision,
|
|
25
|
-
scale: column.scale
|
|
26
|
-
)
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
t.tstzrange :system_period, null: false
|
|
30
|
-
t.exclusion_constraint exclusion_constraint_expression, using: :gist
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
create_versioning_hook table_name,
|
|
34
|
-
"#{table_name}_history",
|
|
35
|
-
columns: :all,
|
|
36
|
-
primary_key: source_pk
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
def drop_table_with_system_versioning(*table_names, **options)
|
|
40
|
-
table_names.each do |table_name|
|
|
41
|
-
history_table_name = "#{table_name}_history"
|
|
42
|
-
|
|
43
|
-
drop_table(table_name, **options)
|
|
44
|
-
drop_table(history_table_name, **options)
|
|
45
|
-
drop_versioning_hook(table_name, history_table_name, **options.slice(:columns, :primary_key, :if_exists))
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
|
|
49
4
|
def create_versioning_hook(source_table, history_table, **options)
|
|
50
5
|
options.assert_valid_keys(:columns, :primary_key)
|
|
51
6
|
|
|
52
|
-
|
|
7
|
+
columns = options.fetch(:columns, :all)
|
|
8
|
+
primary_key = options.fetch(:primary_key, :id)
|
|
9
|
+
|
|
10
|
+
column_names = if columns == :all
|
|
53
11
|
columns(source_table).map(&:name)
|
|
54
12
|
else
|
|
55
13
|
Array(columns).map(&:to_s)
|
|
56
14
|
end
|
|
57
15
|
|
|
58
|
-
primary_key = options.fetch(:primary_key, :id)
|
|
59
|
-
|
|
60
16
|
primary_key = if primary_key.is_a?(Array) && primary_key.length == 1
|
|
61
17
|
primary_key.first
|
|
62
18
|
else
|
|
63
19
|
primary_key
|
|
64
20
|
end
|
|
65
21
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
22
|
+
assert_table_exists!(source_table)
|
|
23
|
+
assert_table_exists!(history_table)
|
|
24
|
+
assert_columns_match!(source_table, history_table, column_names)
|
|
25
|
+
assert_columns_exists!(source_table, Array(primary_key))
|
|
26
|
+
assert_primary_key_matches!(source_table, Array(primary_key))
|
|
70
27
|
|
|
71
28
|
schema_creation = SchemaCreation.new(self)
|
|
72
29
|
|
|
@@ -74,7 +31,8 @@ module ActiveRecord::Temporal
|
|
|
74
31
|
source_table,
|
|
75
32
|
history_table,
|
|
76
33
|
columns: column_names,
|
|
77
|
-
primary_key: primary_key
|
|
34
|
+
primary_key: primary_key,
|
|
35
|
+
gem_version: VERSION
|
|
78
36
|
)
|
|
79
37
|
|
|
80
38
|
execute schema_creation.accept(hook_definition)
|
|
@@ -97,14 +55,14 @@ module ActiveRecord::Temporal
|
|
|
97
55
|
def versioning_hook(source_table)
|
|
98
56
|
update_function_name = versioning_function_name(source_table, :update)
|
|
99
57
|
|
|
100
|
-
row =
|
|
58
|
+
row = exec_query(<<~SQL.squish, "SQL", [update_function_name]).first
|
|
101
59
|
SELECT
|
|
102
60
|
pg_proc.proname as function_name,
|
|
103
61
|
obj_description(pg_proc.oid, 'pg_proc') as comment
|
|
104
62
|
FROM pg_proc
|
|
105
63
|
JOIN pg_namespace ON pg_proc.pronamespace = pg_namespace.oid
|
|
106
64
|
WHERE pg_namespace.nspname NOT IN ('pg_catalog', 'information_schema')
|
|
107
|
-
AND pg_proc.proname =
|
|
65
|
+
AND pg_proc.proname = $1
|
|
108
66
|
SQL
|
|
109
67
|
|
|
110
68
|
return unless row
|
|
@@ -115,7 +73,8 @@ module ActiveRecord::Temporal
|
|
|
115
73
|
metadata["source_table"],
|
|
116
74
|
metadata["history_table"],
|
|
117
75
|
columns: metadata["columns"],
|
|
118
|
-
primary_key: metadata["primary_key"]
|
|
76
|
+
primary_key: metadata["primary_key"],
|
|
77
|
+
gem_version: metadata["gem_version"]
|
|
119
78
|
)
|
|
120
79
|
end
|
|
121
80
|
|
|
@@ -125,13 +84,13 @@ module ActiveRecord::Temporal
|
|
|
125
84
|
add_columns = (options[:add_columns] || []).map(&:to_s)
|
|
126
85
|
remove_columns = (options[:remove_columns] || []).map(&:to_s)
|
|
127
86
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
87
|
+
assert_table_exists!(source_table)
|
|
88
|
+
assert_table_exists!(history_table)
|
|
89
|
+
assert_columns_match!(source_table, history_table, add_columns)
|
|
131
90
|
|
|
132
91
|
hook_definition = versioning_hook(source_table)
|
|
133
92
|
|
|
134
|
-
|
|
93
|
+
assert_hook_has_columns!(hook_definition, remove_columns)
|
|
135
94
|
|
|
136
95
|
drop_versioning_hook(source_table, history_table)
|
|
137
96
|
|
|
@@ -161,15 +120,15 @@ module ActiveRecord::Temporal
|
|
|
161
120
|
def validate_create_versioning_hook_options!(options)
|
|
162
121
|
end
|
|
163
122
|
|
|
164
|
-
def
|
|
123
|
+
def assert_table_exists!(table_name)
|
|
165
124
|
return if table_exists?(table_name)
|
|
166
125
|
|
|
167
126
|
raise ArgumentError, "table '#{table_name}' does not exist"
|
|
168
127
|
end
|
|
169
128
|
|
|
170
|
-
def
|
|
171
|
-
|
|
172
|
-
|
|
129
|
+
def assert_columns_match!(source_table, history_table, column_names)
|
|
130
|
+
assert_columns_exists!(source_table, column_names)
|
|
131
|
+
assert_columns_exists!(history_table, column_names)
|
|
173
132
|
|
|
174
133
|
column_names.each do |column|
|
|
175
134
|
source_column = columns(source_table).find { _1.name == column }
|
|
@@ -181,7 +140,7 @@ module ActiveRecord::Temporal
|
|
|
181
140
|
end
|
|
182
141
|
end
|
|
183
142
|
|
|
184
|
-
def
|
|
143
|
+
def assert_columns_exists!(table_name, column_names)
|
|
185
144
|
column_names.each do |column|
|
|
186
145
|
next if column_exists?(table_name, column)
|
|
187
146
|
|
|
@@ -189,7 +148,14 @@ module ActiveRecord::Temporal
|
|
|
189
148
|
end
|
|
190
149
|
end
|
|
191
150
|
|
|
192
|
-
def
|
|
151
|
+
def assert_primary_key_matches!(source_table, primary_key)
|
|
152
|
+
primary_key = primary_key&.map(&:to_s)
|
|
153
|
+
unless Array(primary_key(source_table)) == primary_key
|
|
154
|
+
raise ArgumentError, "table '#{source_table}' does not have primary key #{primary_key}"
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def assert_hook_has_columns!(hook, column_names)
|
|
193
159
|
column_names.each do |column_name|
|
|
194
160
|
next if hook.columns.include?(column_name)
|
|
195
161
|
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
require "active_support"
|
|
2
2
|
|
|
3
3
|
require_relative "temporal/application_versioning/application_versioned"
|
|
4
|
-
require_relative "temporal/application_versioning/command_recorder"
|
|
5
|
-
require_relative "temporal/application_versioning/migration"
|
|
6
|
-
require_relative "temporal/application_versioning/schema_statements"
|
|
7
4
|
require_relative "temporal/querying/association_macros"
|
|
8
5
|
require_relative "temporal/querying/association_scope"
|
|
9
6
|
require_relative "temporal/querying/association_walker"
|
|
@@ -13,7 +10,6 @@ require_relative "temporal/querying/scope_registry"
|
|
|
13
10
|
require_relative "temporal/querying/scoping"
|
|
14
11
|
require_relative "temporal/querying/time_dimensions"
|
|
15
12
|
require_relative "temporal/patches/association_reflection"
|
|
16
|
-
require_relative "temporal/patches/command_recorder"
|
|
17
13
|
require_relative "temporal/patches/join_dependency"
|
|
18
14
|
require_relative "temporal/patches/merger"
|
|
19
15
|
require_relative "temporal/patches/relation"
|
|
@@ -22,7 +18,6 @@ require_relative "temporal/system_versioning/command_recorder"
|
|
|
22
18
|
require_relative "temporal/system_versioning/history_model_namespace"
|
|
23
19
|
require_relative "temporal/system_versioning/history_model"
|
|
24
20
|
require_relative "temporal/system_versioning/history_models"
|
|
25
|
-
require_relative "temporal/system_versioning/migration"
|
|
26
21
|
require_relative "temporal/system_versioning/schema_creation"
|
|
27
22
|
require_relative "temporal/system_versioning/schema_definitions"
|
|
28
23
|
require_relative "temporal/system_versioning/schema_statements"
|
|
@@ -31,6 +26,7 @@ require_relative "temporal/application_versioning"
|
|
|
31
26
|
require_relative "temporal/querying"
|
|
32
27
|
require_relative "temporal/scoping"
|
|
33
28
|
require_relative "temporal/system_versioning"
|
|
29
|
+
require_relative "temporal/version"
|
|
34
30
|
|
|
35
31
|
module ActiveRecord::Temporal
|
|
36
32
|
def system_versioning
|
|
@@ -46,13 +42,7 @@ module ActiveRecord::Temporal
|
|
|
46
42
|
end
|
|
47
43
|
|
|
48
44
|
ActiveSupport.on_load(:active_record) do
|
|
49
|
-
require "active_record/connection_adapters/postgresql_adapter"
|
|
50
|
-
|
|
51
|
-
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter
|
|
52
|
-
.include ActiveRecord::Temporal::ApplicationVersioning::SchemaStatements
|
|
53
|
-
|
|
54
|
-
ActiveRecord::Migration::CommandRecorder
|
|
55
|
-
.include ActiveRecord::Temporal::ApplicationVersioning::CommandRecorder
|
|
45
|
+
require "active_record/connection_adapters/postgresql_adapter"
|
|
56
46
|
|
|
57
47
|
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter
|
|
58
48
|
.include ActiveRecord::Temporal::SystemVersioning::SchemaStatements
|
|
@@ -65,11 +55,6 @@ ActiveSupport.on_load(:active_record) do
|
|
|
65
55
|
|
|
66
56
|
# Patches
|
|
67
57
|
|
|
68
|
-
# Patches `#invert_drop` to remove the `system_versioning` option. The original
|
|
69
|
-
# method determines reversibility by looking for the presence of arguments
|
|
70
|
-
ActiveRecord::Migration::CommandRecorder
|
|
71
|
-
.prepend ActiveRecord::Temporal::Patches::CommandRecorder
|
|
72
|
-
|
|
73
58
|
# Patches `#build_arel` to wrap itself in the as-of query scope registry.
|
|
74
59
|
# This is what allows temporal association scopes to be aware of the time-scope
|
|
75
60
|
# value of the relation that included them.
|
|
@@ -101,8 +86,9 @@ ActiveSupport.on_load(:active_record) do
|
|
|
101
86
|
.prepend ActiveRecord::Temporal::Patches::AssociationReflection
|
|
102
87
|
|
|
103
88
|
# This is a copy of a fix from https://github.com/rails/rails/pull/56088 that
|
|
104
|
-
# impacts this gem. I has been backported to supported stable
|
|
105
|
-
# Active Record, but until those patches are released it's included
|
|
89
|
+
# impacts this gem. I has been merged and backported to supported stable
|
|
90
|
+
# versions of Active Record, but until those patches are released it's included
|
|
91
|
+
# here.
|
|
106
92
|
ActiveRecord::Associations::JoinDependency
|
|
107
93
|
.prepend ActiveRecord::Temporal::Patches::JoinDependency
|
|
108
94
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: activerecord-temporal
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Martin-Alexander
|
|
@@ -78,11 +78,7 @@ files:
|
|
|
78
78
|
- lib/activerecord/temporal.rb
|
|
79
79
|
- lib/activerecord/temporal/application_versioning.rb
|
|
80
80
|
- lib/activerecord/temporal/application_versioning/application_versioned.rb
|
|
81
|
-
- lib/activerecord/temporal/application_versioning/command_recorder.rb
|
|
82
|
-
- lib/activerecord/temporal/application_versioning/migration.rb
|
|
83
|
-
- lib/activerecord/temporal/application_versioning/schema_statements.rb
|
|
84
81
|
- lib/activerecord/temporal/patches/association_reflection.rb
|
|
85
|
-
- lib/activerecord/temporal/patches/command_recorder.rb
|
|
86
82
|
- lib/activerecord/temporal/patches/join_dependency.rb
|
|
87
83
|
- lib/activerecord/temporal/patches/merger.rb
|
|
88
84
|
- lib/activerecord/temporal/patches/relation.rb
|
|
@@ -104,7 +100,6 @@ files:
|
|
|
104
100
|
- lib/activerecord/temporal/system_versioning/history_model.rb
|
|
105
101
|
- lib/activerecord/temporal/system_versioning/history_model_namespace.rb
|
|
106
102
|
- lib/activerecord/temporal/system_versioning/history_models.rb
|
|
107
|
-
- lib/activerecord/temporal/system_versioning/migration.rb
|
|
108
103
|
- lib/activerecord/temporal/system_versioning/schema_creation.rb
|
|
109
104
|
- lib/activerecord/temporal/system_versioning/schema_definitions.rb
|
|
110
105
|
- lib/activerecord/temporal/system_versioning/schema_statements.rb
|
|
@@ -115,7 +110,7 @@ licenses:
|
|
|
115
110
|
- MIT
|
|
116
111
|
metadata:
|
|
117
112
|
bug_tracker_uri: https://github.com/Martin-Alexander/activerecord-temporal/issues
|
|
118
|
-
changelog_uri: https://github.com/Martin-Alexander/activerecord-temporal/CHANGELOG.md
|
|
113
|
+
changelog_uri: https://github.com/Martin-Alexander/activerecord-temporal/blob/master/CHANGELOG.md
|
|
119
114
|
homepage_uri: https://github.com/Martin-Alexander/activerecord-temporal
|
|
120
115
|
source_code_uri: https://github.com/Martin-Alexander/activerecord-temporal
|
|
121
116
|
rdoc_options: []
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
module ActiveRecord::Temporal
|
|
2
|
-
module ApplicationVersioning
|
|
3
|
-
module CommandRecorder
|
|
4
|
-
def create_application_versioned_table(*args)
|
|
5
|
-
record(:create_application_versioned_table, args)
|
|
6
|
-
end
|
|
7
|
-
ruby2_keywords(:create_application_versioned_table)
|
|
8
|
-
|
|
9
|
-
def invert_create_application_versioned_table(args)
|
|
10
|
-
[:drop_table, args]
|
|
11
|
-
end
|
|
12
|
-
end
|
|
13
|
-
end
|
|
14
|
-
end
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
module ActiveRecord::Temporal
|
|
2
|
-
module ApplicationVersioning
|
|
3
|
-
module Migration
|
|
4
|
-
extend ActiveSupport::Concern
|
|
5
|
-
|
|
6
|
-
included do
|
|
7
|
-
prepend Patches
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
module Patches
|
|
11
|
-
def create_table(table_name, id: :primary_key, primary_key: nil, force: nil, **options, &block)
|
|
12
|
-
application_versioning = options.delete(:application_versioning)
|
|
13
|
-
|
|
14
|
-
if application_versioning
|
|
15
|
-
create_application_versioned_table(
|
|
16
|
-
table_name, id:, primary_key:, force:, **options, &block
|
|
17
|
-
)
|
|
18
|
-
else
|
|
19
|
-
super
|
|
20
|
-
end
|
|
21
|
-
end
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
end
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
module ActiveRecord::Temporal
|
|
2
|
-
module ApplicationVersioning
|
|
3
|
-
module SchemaStatements
|
|
4
|
-
def create_application_versioned_table(table_name, **options, &block)
|
|
5
|
-
pk_option = options[:primary_key]
|
|
6
|
-
|
|
7
|
-
primary_key = if options[:primary_key]
|
|
8
|
-
Array(options[:primary_key]) | [:version]
|
|
9
|
-
else
|
|
10
|
-
[:id, :version]
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
exclusion_constraint_expression = (primary_key - [:version]).map do |col|
|
|
14
|
-
"#{col} WITH ="
|
|
15
|
-
end.join(", ") + ", validity WITH &&"
|
|
16
|
-
|
|
17
|
-
options = options.merge(primary_key: primary_key)
|
|
18
|
-
|
|
19
|
-
create_table(table_name, **options) do |t|
|
|
20
|
-
unless pk_option.is_a?(Array)
|
|
21
|
-
t.bigserial pk_option || :id, null: false
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
t.bigint :version, null: false, default: 1
|
|
25
|
-
t.tstzrange :validity, null: false
|
|
26
|
-
t.exclusion_constraint exclusion_constraint_expression, using: :gist
|
|
27
|
-
|
|
28
|
-
instance_exec(t, &block)
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
end
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
module ActiveRecord::Temporal
|
|
2
|
-
module Patches
|
|
3
|
-
module CommandRecorder
|
|
4
|
-
def invert_drop_table(args, &block)
|
|
5
|
-
if extract_options(args).delete(:system_versioning)
|
|
6
|
-
raise ActiveRecord::IrreversibleMigration, "drop_table with system versioning is not supported"
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
super
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
private
|
|
13
|
-
|
|
14
|
-
def extract_options(array)
|
|
15
|
-
if array.last.is_a?(Hash) && array.last.extractable_options?
|
|
16
|
-
array.last
|
|
17
|
-
else
|
|
18
|
-
{}
|
|
19
|
-
end
|
|
20
|
-
end
|
|
21
|
-
end
|
|
22
|
-
end
|
|
23
|
-
end
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
module ActiveRecord::Temporal
|
|
2
|
-
module SystemVersioning
|
|
3
|
-
module Migration
|
|
4
|
-
extend ActiveSupport::Concern
|
|
5
|
-
|
|
6
|
-
included do
|
|
7
|
-
prepend Patches
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
module Patches
|
|
11
|
-
def create_table(table_name, id: :primary_key, primary_key: nil, force: nil, **options, &block)
|
|
12
|
-
system_versioning = options.delete(:system_versioning)
|
|
13
|
-
|
|
14
|
-
if system_versioning
|
|
15
|
-
create_table_with_system_versioning(
|
|
16
|
-
table_name, id:, primary_key:, force:, **options, &block
|
|
17
|
-
)
|
|
18
|
-
else
|
|
19
|
-
super
|
|
20
|
-
end
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def drop_table(*table_names, **options)
|
|
24
|
-
system_versioning = options.delete(:system_versioning)
|
|
25
|
-
|
|
26
|
-
if system_versioning
|
|
27
|
-
drop_table_with_system_versioning(*table_names, **options)
|
|
28
|
-
else
|
|
29
|
-
super
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
|
-
end
|