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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: efddd21db7e9259c1a6314f94384cb7d4da7e06e0553df5817fafea120bf23d9
4
- data.tar.gz: ac3c593aaedb772f5837c117a13d8eb391d3aa414eb68eaef16b863da1cffd11
3
+ metadata.gz: 87a493283c2b5a1746266fdedb4978b7ad768be307b73b40b2aa404b460a198c
4
+ data.tar.gz: ea10fd65c8c61878d1a92ae098a558094e7090e4ae09692ac15c1bd61b94a406
5
5
  SHA512:
6
- metadata.gz: 3decd8140332021022bcd6a265d2ef12ea16407665ceec52946ed79d14c9fc72b5848ff998ada1f297b17f1e76d563d8a8fd0e3c8c05dba0fcd87e4ea2cf8b30
7
- data.tar.gz: 9cfde96a0d807112c0486d755616d81cfa3d05d00571e40853c6bcb32e7a31e935b48bf1cc17263d958908375ec0545b58109925ef802d9a46e540ec44920438
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
- - Update subscription plans, but retain existing subscribers' original payment schedules
12
- - Allow users to see information as it was before their view permission was revoked
13
- - Understand why generated financial reports have changed recently
14
- - Restore erroneously updated data
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 columns** that serialize changes. Their data doesn't evolve with schema changes and cannot be easily integrated into Active Record queries, scopes, and associations.
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::SystemVersioningNamespace
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::SystemVersioningNamespace
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::SystemVersioningNamespace
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 "not head revision" unless head_revision?
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 "not head revision" unless head_revision?
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 "not head revision" unless head_revision?
115
+ raise ClosedRevisionError, "Cannot inactivate closed version" unless head_revision?
116
116
 
117
117
  set_time_dimension_end(time)
118
118
  save
@@ -2,6 +2,8 @@ module ActiveRecord::Temporal
2
2
  module ApplicationVersioning
3
3
  extend ActiveSupport::Concern
4
4
 
5
+ class ClosedRevisionError < StandardError; end
6
+
5
7
  class_methods do
6
8
  def application_versioned
7
9
  include ApplicationVersioned
@@ -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 versions of
5
- # Active Record, but until those patches are released it's included here.
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
- column_names = if (columns = options.fetch(:columns)) == :all
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
- ensure_table_exists!(source_table)
67
- ensure_table_exists!(history_table)
68
- ensure_columns_match!(source_table, history_table, column_names)
69
- ensure_columns_exists!(source_table, Array(primary_key))
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 = execute(<<~SQL.squish).first
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 = '#{update_function_name}'
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
- ensure_table_exists!(source_table)
129
- ensure_table_exists!(history_table)
130
- ensure_columns_match!(source_table, history_table, add_columns)
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
- ensure_hook_has_columns!(hook_definition, remove_columns)
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 ensure_table_exists!(table_name)
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 ensure_columns_match!(source_table, history_table, column_names)
171
- ensure_columns_exists!(source_table, column_names)
172
- ensure_columns_exists!(history_table, column_names)
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 ensure_columns_exists!(table_name, column_names)
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 ensure_hook_has_columns!(hook, column_names)
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,3 +1,3 @@
1
1
  module ActiveRecord::Temporal
2
- VERSION = "0.2.0"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -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" # TODO: add test
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 versions of
105
- # Active Record, but until those patches are released it's included here.
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.2.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