hoardable 0.19.0 → 0.19.3

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: 12ee4c5fbbd0b35bda5c9b7427015a1d60ca9cc919ac992caca386d4c3664163
4
- data.tar.gz: 0020d7f28b85fb70efc3a1b80695f1e9c69f60c954b6aeccb71cedadccb97e0c
3
+ metadata.gz: 27747103c387757c5faf3cf3771651d221d67b8367d5b6f6623a34c43b3afe9d
4
+ data.tar.gz: 4abd9206358508019a69273c33fe9d772ad28b9ea4f76f85f499315b17bd99c2
5
5
  SHA512:
6
- metadata.gz: 2fe01feb0e5260d8072f4a45640517a5041a83c566db15b45cc32d141fa8883cf84a1bec16b8ec043a6b914b28e0e824d1e2820454756501941831613efe95a7
7
- data.tar.gz: ceef85ede42a50189fb41de31fe0d4ab6882c2139d825569cd674fdf9611f89fd96721156cad5dee347359f67ecba68d8e9a794c2c6513710a39c2e599975668
6
+ metadata.gz: a771a19b506f170a17fd672219a4899dcbfebc55e9ab2422a2d22198d0efd6c13667e5fbb2c69e1da724222bb9f9923fc4263f7b3ae14b3e260c62cc22eb42fd
7
+ data.tar.gz: c8eab3064758767b33f46943f0e09544e096545ad3f436c3b43e237d5be6e11589bc7840241561ddd04f860a1371656285badcd3a890cc003244829d6abf4010
data/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ ## 0.19.3
2
+
3
+ - Support providing `event_uuid` in `with`.
4
+
5
+ ## 0.19.2
6
+
7
+ - Consult `inheritance_column` when constructing default scope
8
+
9
+ ## 0.19.1
10
+
11
+ - Thread-safety support added for `with_hoardable_config`.
12
+
1
13
  ## 0.19.0
2
14
 
3
15
  - Ensure that stateful Hoardable class methods `with`, `travel_to` and `at` are thread-safe.
data/Gemfile CHANGED
@@ -2,7 +2,12 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
+ gem "bigdecimal"
5
6
  gem "debug"
7
+ gem "drb"
8
+ gem "logger"
9
+ gem "mutex_m"
10
+ gem "ostruct"
6
11
  if (rails_version = ENV["RAILS_VERSION"])
7
12
  gem "rails", "~> #{rails_version}"
8
13
  else
@@ -19,7 +19,7 @@ module Hoardable
19
19
  returning: source_primary_key.to_sym
20
20
  )
21
21
  version_id = version[0][source_primary_key]
22
- source_record.instance_variable_set("@hoardable_version", version_class.find(version_id))
22
+ source_record.instance_variable_set(:@hoardable_version, version_class.find(version_id))
23
23
  source_record.run_callbacks(:versioned, &block)
24
24
  end
25
25
 
@@ -28,9 +28,8 @@ module Hoardable
28
28
  end
29
29
 
30
30
  def find_or_initialize_hoardable_event_uuid
31
- Thread.current[:hoardable_event_uuid] ||= (
32
- ActiveRecord::Base.connection.query("SELECT gen_random_uuid();")[0][0]
33
- )
31
+ Thread.current[:hoardable_event_uuid] ||= Thread.current[:contextual_event_uuid] ||
32
+ SecureRandom.uuid
34
33
  end
35
34
 
36
35
  def initialize_version_attributes(operation)
@@ -94,7 +93,7 @@ module Hoardable
94
93
 
95
94
  def initialize_temporal_range
96
95
  upper_bound = Thread.current[:hoardable_travel_to] || Time.now.utc
97
- lower_bound = (previous_temporal_tsrange_end || hoardable_source_epoch)
96
+ lower_bound = previous_temporal_tsrange_end || hoardable_source_epoch
98
97
 
99
98
  if upper_bound < lower_bound
100
99
  raise InvalidTemporalUpperBoundError.new(upper_bound, lower_bound)
@@ -114,7 +113,7 @@ module Hoardable
114
113
  end
115
114
 
116
115
  def unset_hoardable_version_and_event_uuid
117
- source_record.instance_variable_set("@hoardable_version", nil)
116
+ source_record.instance_variable_set(:@hoardable_version, nil)
118
117
  return if source_record.class.connection.transaction_open?
119
118
 
120
119
  Thread.current[:hoardable_event_uuid] = nil
@@ -6,7 +6,7 @@ module Hoardable
6
6
 
7
7
  # Symbols for use with setting contextual data, when creating versions. See
8
8
  # {file:README.md#tracking-contextual-data README} for more.
9
- DATA_KEYS = %i[meta whodunit event_uuid].freeze
9
+ DATA_KEYS = %i[meta whodunit].freeze
10
10
 
11
11
  # Symbols for use with setting {Hoardable} configuration. See {file:README.md#configuration
12
12
  # README} for more.
@@ -44,21 +44,15 @@ module Hoardable
44
44
 
45
45
  class << self
46
46
  CONFIG_KEYS.each do |key|
47
- define_method(key) do
48
- local_config = Thread.current[:hoardable_config] || @config
49
- local_config[key]
50
- end
47
+ define_method(key) { hoardable_config[key] }
51
48
 
52
- define_method("#{key}=") { |value| @config[key] = value }
49
+ define_method(:"#{key}=") { |value| @config[key] = value }
53
50
  end
54
51
 
55
52
  DATA_KEYS.each do |key|
56
- define_method(key) do
57
- local_context = Thread.current[:hoardable_context] || @context
58
- local_context[key]
59
- end
53
+ define_method(key) { hoardable_context[key] }
60
54
 
61
- define_method("#{key}=") { |value| @context[key] = value }
55
+ define_method(:"#{key}=") { |value| @context[key] = value }
62
56
  end
63
57
 
64
58
  # This is a general use method for setting {file:README.md#tracking-contextual-data Contextual
@@ -67,12 +61,36 @@ module Hoardable
67
61
  # @param hash [Hash] config and contextual data to set within a block
68
62
  def with(hash)
69
63
  thread = Thread.current
70
- thread[:hoardable_config] = @config.merge(hash.slice(*CONFIG_KEYS))
71
- thread[:hoardable_context] = @context.merge(hash.slice(*DATA_KEYS))
64
+ current_thread_config = thread[:hoardable_config]
65
+ current_thread_context = thread[:hoardable_context]
66
+
67
+ if hash.include?(:event_uuid)
68
+ contextual_event_uuid = hash[:event_uuid]
69
+ unless valid_event_uuid?(contextual_event_uuid)
70
+ raise InvalidEventUUID, contextual_event_uuid
71
+ end
72
+ thread[:contextual_event_uuid] = contextual_event_uuid
73
+ end
74
+
75
+ thread[:hoardable_config] = hoardable_config.merge(hash.slice(*CONFIG_KEYS))
76
+ thread[:hoardable_context] = hoardable_context.merge(hash.slice(*DATA_KEYS))
72
77
  yield
73
78
  ensure
74
- thread[:hoardable_config] = nil
75
- thread[:hoardable_context] = nil
79
+ thread[:hoardable_config] = current_thread_config
80
+ thread[:hoardable_context] = current_thread_context
81
+ thread[:contextual_event_uuid] = nil
82
+ end
83
+
84
+ private def hoardable_config
85
+ @config.merge(Thread.current[:hoardable_config] ||= {})
86
+ end
87
+
88
+ private def valid_event_uuid?(value)
89
+ /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.match?(value.to_s.downcase)
90
+ end
91
+
92
+ private def hoardable_context
93
+ @context.merge(Thread.current[:hoardable_context] ||= {})
76
94
  end
77
95
 
78
96
  # Allows performing a query for record states at a certain time. Returned {SourceModel}
@@ -118,7 +136,7 @@ module Hoardable
118
136
  initializer "hoardable.schema_statements" do
119
137
  ActiveSupport.on_load(:active_record_postgresqladapter) do
120
138
  # We need to control the table dumping order of tables, so revert these to just +super+
121
- Fx::SchemaDumper.module_eval("def tables(streams); super; end")
139
+ Fx::SchemaDumper.module_eval("def tables(streams); super; end", __FILE__, __LINE__)
122
140
 
123
141
  ActiveRecord::ConnectionAdapters::PostgreSQL::SchemaDumper.prepend(SchemaDumper)
124
142
  ActiveRecord::ConnectionAdapters::PostgreSQL::SchemaStatements.prepend(SchemaStatements)
@@ -9,9 +9,9 @@ module Hoardable
9
9
  class CreatedAtColumnMissingError < Error
10
10
  def initialize(source_table_name)
11
11
  super(<<~LOG)
12
- '#{source_table_name}' does not have a 'created_at' column, so the start of the first
13
- version’s temporal period cannot be known. Add a 'created_at' column to '#{source_table_name}'.
14
- LOG
12
+ '#{source_table_name}' does not have a 'created_at' column, so the start of the first
13
+ version’s temporal period cannot be known. Add a 'created_at' column to '#{source_table_name}'.
14
+ LOG
15
15
  end
16
16
  end
17
17
 
@@ -19,9 +19,9 @@ module Hoardable
19
19
  class UpdatedAtColumnMissingError < Error
20
20
  def initialize(source_table_name)
21
21
  super(<<~LOG)
22
- '#{source_table_name}' does not have an 'updated_at' column, so Hoardable cannot look up
23
- associated record versions with it. Add an 'updated_at' column to '#{source_table_name}'.
24
- LOG
22
+ '#{source_table_name}' does not have an 'updated_at' column, so Hoardable cannot look up
23
+ associated record versions with it. Add an 'updated_at' column to '#{source_table_name}'.
24
+ LOG
25
25
  end
26
26
  end
27
27
 
@@ -29,9 +29,18 @@ module Hoardable
29
29
  class InvalidTemporalUpperBoundError < Error
30
30
  def initialize(upper, lower)
31
31
  super(<<~LOG)
32
- 'The supplied value to `Hoardable.travel_to` (#{upper}) is before the calculated lower bound (#{lower}).
33
- You must provide a datetime > the lower bound.
34
- LOG
32
+ 'The supplied value to `Hoardable.travel_to` (#{upper}) is before the calculated lower bound (#{lower}).
33
+ You must provide a datetime > the lower bound.
34
+ LOG
35
+ end
36
+ end
37
+
38
+ # An error to be raised when an invalid (non-UUID) value is provided as an event_uuid
39
+ class InvalidEventUUID < Error
40
+ def initialize(value)
41
+ super(<<~LOG)
42
+ 'The supplied 'event_uuid' value must be a valid UUID'
43
+ LOG
35
44
  end
36
45
  end
37
46
  end
@@ -9,9 +9,6 @@ module Hoardable
9
9
  extend ActiveSupport::Concern
10
10
 
11
11
  class_methods do
12
- # @!visibility private
13
- attr_reader :_hoardable_config
14
-
15
12
  # If called with a hash, this will set the model-level +Hoardable+ configuration variables. If
16
13
  # called without an argument it will return the computed +Hoardable+ configuration considering
17
14
  # both model-level and global values.
@@ -23,10 +20,7 @@ module Hoardable
23
20
  if hash
24
21
  @_hoardable_config = hash.slice(*CONFIG_KEYS)
25
22
  else
26
- @_hoardable_config ||= {}
27
- CONFIG_KEYS.to_h do |key|
28
- [key, @_hoardable_config.key?(key) ? @_hoardable_config[key] : Hoardable.send(key)]
29
- end
23
+ CONFIG_KEYS.to_h { |key| [key, _hoardable_config.fetch(key) { Hoardable.send(key) }] }
30
24
  end
31
25
  end
32
26
 
@@ -36,11 +30,20 @@ module Hoardable
36
30
  # @param hash [Hash] The +Hoardable+ configuration for the model. Keys must be present in
37
31
  # {CONFIG_KEYS}
38
32
  def with_hoardable_config(hash)
39
- current_config = @_hoardable_config
40
- @_hoardable_config = hash.slice(*CONFIG_KEYS)
33
+ thread = Thread.current
34
+ current_thread_config = thread[hoardable_current_config_key]
35
+ thread[hoardable_current_config_key] = _hoardable_config.merge(hash.slice(*CONFIG_KEYS))
41
36
  yield
42
37
  ensure
43
- @_hoardable_config = current_config
38
+ thread[hoardable_current_config_key] = current_thread_config
39
+ end
40
+
41
+ private def _hoardable_config
42
+ (@_hoardable_config ||= {}).merge(Thread.current[hoardable_current_config_key] ||= {})
43
+ end
44
+
45
+ private def hoardable_current_config_key
46
+ "hoardable_#{name}_config".to_sym
44
47
  end
45
48
  end
46
49
 
@@ -10,6 +10,7 @@ module Hoardable
10
10
  super
11
11
  dump_inherited_tables(stream)
12
12
  empty_line(stream)
13
+ functions(stream)
13
14
  triggers(stream)
14
15
  end
15
16
 
@@ -20,9 +20,9 @@ module Hoardable
20
20
  exclude_versions
21
21
  end
22
22
  )
23
- next scope unless klass == version_class && "type".in?(column_names)
23
+ next scope unless klass == version_class && superclass.inheritance_column.in?(column_names)
24
24
 
25
- scope.where(type: superclass.sti_name)
25
+ scope.where(superclass.inheritance_column => superclass.sti_name)
26
26
  end
27
27
 
28
28
  # @!scope class
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Hoardable
4
- VERSION = "0.19.0"
4
+ VERSION = "0.19.3"
5
5
  end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hoardable
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.19.0
4
+ version: 0.19.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - justin talbott
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2025-01-30 00:00:00.000000000 Z
10
+ date: 2025-11-10 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: activerecord
@@ -162,7 +161,6 @@ metadata:
162
161
  homepage_uri: https://github.com/waymondo/hoardable
163
162
  source_code_uri: https://github.com/waymondo/hoardable
164
163
  rubygems_mfa_required: 'true'
165
- post_install_message:
166
164
  rdoc_options: []
167
165
  require_paths:
168
166
  - lib
@@ -177,8 +175,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
177
175
  - !ruby/object:Gem::Version
178
176
  version: '0'
179
177
  requirements: []
180
- rubygems_version: 3.5.16
181
- signing_key:
178
+ rubygems_version: 3.6.2
182
179
  specification_version: 4
183
180
  summary: An ActiveRecord extension for versioning and soft-deletion of records in
184
181
  Postgres