active_record_compose 1.3.0 → 1.4.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: 148413ab669022c937e7f4775cff13dc018c9ba190014b130c10b9ca7e767a3f
4
- data.tar.gz: 51ec332dcf2decf04dfb7f3bf8f0d38bf791d36d960d0d19c388608ef3f33a32
3
+ metadata.gz: 91c3305d64d7b4e8b0cf57e127b9b1e48c0a413a5749d79f1afef5d659814271
4
+ data.tar.gz: 321aa91ad6500b97a38f02ea70f5904c2852016eddf7fb4388186e25c83ff825
5
5
  SHA512:
6
- metadata.gz: 7db6b03f1a56d9d9d1ef0427362b65966bcfca997a465c4ce843d5dad9c4237e43b443ce0620629c665ab09163bf46fd362976009c2e426ae40849c1a41d18bd
7
- data.tar.gz: 43ecb29b976c4136650e2152f1533af7e06de40afeba68f7265e97ba6eff208165271c338b4348d44127d850ebe75de2908eea60a18e986ef47e181d95d6e784
6
+ metadata.gz: ad550be47844182ac031aaa4c9df4c344e0b14a3173a3402c2e7d44123bd953c1b2ef4f62e0ff5ca8d49c17f688cb9a6c5385d1aeb810bb34263d7f2203cd943
7
+ data.tar.gz: a97affe0e201a77cb207ba0d013194c9b1a30a79611a80d4d0e133904bfd8f016e2f330a126428acdec22f248bfa2ed999df62db46a5174fe8deb3ec2ebaee84
data/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [1.4.0] - 2026-10-14
4
+
5
+ * Use a dedicated deprecator for deprecated APIs.
6
+ (https://github.com/hamajyotan/active_record_compose/pull/75)
7
+ * Fix `before_commit` callback timing for transactions spanning multiple database connections.
8
+ (https://github.com/hamajyotan/active_record_compose/pull/76)
9
+ - Ensure that `before_commit` callbacks are invoked at the appropriate point before the outermost transaction commits.
10
+ - Leave behavior unchanged when all models use a single database connection.
11
+ * Perform validations outside a transaction.
12
+ (https://github.com/hamajyotan/active_record_compose/pull/78)
13
+ * Execute inner models' `after_commit` callbacks before outer models' callbacks.
14
+ (https://github.com/hamajyotan/active_record_compose/pull/79)
15
+ * Add `ActiveRecordCompose::UninitializedAttribute#record`.
16
+
3
17
  ## [1.3.0] - 2026-08-09
4
18
 
5
19
  * Support secondary and multiple database
@@ -167,14 +167,16 @@ module ActiveRecordCompose
167
167
 
168
168
  private
169
169
 
170
- def _write_attribute(...) = _require_attributes_initialized { super } # steep:ignore
170
+ # @private
171
+ def _write_attribute(...) = _require_attributes_initialized { super }
171
172
 
173
+ # @private
172
174
  def attribute(...) = _require_attributes_initialized { super }
173
175
 
176
+ # @private
174
177
  def _require_attributes_initialized
175
178
  unless @attributes
176
- raise ActiveRecordCompose::UninitializedAttribute,
177
- "No attributes have been set. Is proper initialization performed, such as calling `super` in `initialize`?"
179
+ raise ActiveRecordCompose::UninitializedAttribute.new(self)
178
180
  end
179
181
 
180
182
  yield
@@ -1,6 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveRecordCompose
4
+ # Generic Active Record Compose exception class.
5
+ #
6
+ class ActiveRecordComposeError < StandardError
7
+ end
8
+
4
9
  # Occurs when a circular reference is detected in the containing model.
5
10
  #
6
11
  # @example
@@ -25,7 +30,7 @@ module ActiveRecordCompose
25
30
  # inner.model = outer # There is a circular reference in the form outer > middle > inner > outer.
26
31
  # outer.save #=> raises ActiveRecordCompose::CircularReferenceDetected
27
32
  #
28
- class CircularReferenceDetected < StandardError; end
33
+ class CircularReferenceDetected < ActiveRecordComposeError; end
29
34
 
30
35
  # Occurs when accessing Attributes without initializing it.
31
36
  #
@@ -40,5 +45,13 @@ module ActiveRecordCompose
40
45
  # model = Model.new
41
46
  # model.foo = 1 #=> raises ActiveRecordCompose::UninitializedAttribute
42
47
  #
43
- class UninitializedAttribute < StandardError; end
48
+ class UninitializedAttribute < ActiveRecordComposeError
49
+ attr_reader :record
50
+
51
+ def initialize(record = nil)
52
+ @record = record
53
+ message = "No attributes have been set. Is proper initialization performed, such as calling `super` in `initialize`?"
54
+ super(message)
55
+ end
56
+ end
44
57
  end
@@ -85,8 +85,8 @@ module ActiveRecordCompose
85
85
 
86
86
  include ActiveRecordCompose::Attributes
87
87
  include ActiveRecordCompose::Persistence
88
- include ActiveRecordCompose::Validations
89
88
  include ActiveRecordCompose::TransactionSupport
89
+ include ActiveRecordCompose::Validations
90
90
  include ActiveRecordCompose::Inspectable
91
91
 
92
92
  def initialize(attributes = {})
@@ -15,10 +15,9 @@ module ActiveRecordCompose
15
15
  # but not just before any inner transactions commit.
16
16
  #
17
17
  def on_before_comitted
18
- return if @_before_committed_called
18
+ return unless final_transaction?
19
19
 
20
20
  yield
21
- @_before_committed_called = true
22
21
  end
23
22
 
24
23
  # It processes after_commit/after_rollback only immediately
@@ -35,10 +34,11 @@ module ActiveRecordCompose
35
34
  def with_transaction_cleanup
36
35
  yield
37
36
  ensure
38
- @_before_committed_called = false
39
37
  transactions.reject! { _1.closed? }
40
38
  end
41
39
 
40
+ def final_transaction? = transactions.one? { !_1.closed? }
41
+
42
42
  def all_finished? = transactions.all? { _1.closed? }
43
43
 
44
44
  def transactions = @transactions ||= Set.new
@@ -15,24 +15,26 @@ module ActiveRecordCompose
15
15
  # steep:ignore:start
16
16
 
17
17
  class_methods do
18
- # @private
19
- # @deprecated
20
- def with_connection(...)
21
- ActiveRecord.deprecator.warn("`with_connection` is deprecated. Use `ActiveRecord::Base.with_connection` instead.")
22
- ActiveRecord::Base.with_connection(...)
23
- end
18
+ if ActiveRecord.version >= Gem::Version.new("7.2")
19
+ # @private
20
+ # @deprecated
21
+ def with_connection(...)
22
+ ActiveRecordCompose.deprecator.deprecation_warning(:with_connection, "Use `ActiveRecord::Base.with_connection` instead.")
23
+ ActiveRecord::Base.with_connection(...)
24
+ end
24
25
 
25
- # @private
26
- # @deprecated
27
- def lease_connection(...)
28
- ActiveRecord.deprecator.warn("`lease_connection` is deprecated. Use `ActiveRecord::Base.lease_connection` instead.")
29
- ActiveRecord::Base.lease_connection(...)
26
+ # @private
27
+ # @deprecated
28
+ def lease_connection(...)
29
+ ActiveRecordCompose.deprecator.deprecation_warning(:lease_connection, "Use `ActiveRecord::Base.lease_connection` instead.")
30
+ ActiveRecord::Base.lease_connection(...)
31
+ end
30
32
  end
31
33
 
32
34
  # @private
33
35
  # @deprecated
34
36
  def connection(...)
35
- ActiveRecord.deprecator.warn("`connection` is deprecated. Use `ActiveRecord::Base.connection` instead.")
37
+ ActiveRecordCompose.deprecator.deprecation_warning(:connection, "Use `ActiveRecord::Base.connection` instead.")
36
38
  ActiveRecord::Base.connection(...)
37
39
  end
38
40
  end
@@ -135,9 +137,12 @@ module ActiveRecordCompose
135
137
  connection.transaction do |tran|
136
138
  tran ||= ActiveTransaction.new(connection.current_transaction) # steep:ignore
137
139
  transaction_coordinator.add_transaction(tran)
138
- connection.add_transaction_record(self, ensure_finalize || has_transactional_callbacks?)
139
140
 
140
- inner_proc.call.tap { raise ActiveRecord::Rollback unless _1 }
141
+ begin
142
+ inner_proc.call.tap { raise ActiveRecord::Rollback unless _1 }
143
+ ensure
144
+ connection.add_transaction_record(self, ensure_finalize || has_transactional_callbacks?)
145
+ end
141
146
  end
142
147
  end
143
148
  end
@@ -155,7 +160,7 @@ module ActiveRecordCompose
155
160
  # @private
156
161
  def connection_pool(ar_class: default_ar_class)
157
162
  connection_specification_name = ar_class.connection_specification_name
158
- role = ar_class.current_role
163
+ role = ar_class.current_role # steep:ignore
159
164
  shard = ar_class.current_shard # steep:ignore
160
165
  connection_handler = ar_class.connection_handler # steep:ignore
161
166
  retrieve_options = { role:, shard: }
@@ -167,7 +172,7 @@ module ActiveRecordCompose
167
172
  # @private
168
173
  def with_pool_transaction_isolation_level(connection, &block)
169
174
  if ActiveRecord.gem_version.release >= Gem::Version.new("8.1.0")
170
- isolation_level = ActiveRecord.default_transaction_isolation_level # steep:ignore
175
+ isolation_level = ActiveRecord.default_transaction_isolation_level
171
176
  connection.pool.with_pool_transaction_isolation_level(isolation_level, connection.transaction_open?, &block)
172
177
  else
173
178
  block.call
@@ -1,5 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveRecordCompose
4
- VERSION = "1.3.0"
4
+ VERSION = "1.4.0"
5
+
6
+ class << self
7
+ def version = Gem::Version.new(VERSION)
8
+
9
+ private
10
+
11
+ # @private
12
+ def next_major_version
13
+ version.segments.first + 1 # steep:ignore
14
+ end
15
+ end
5
16
  end
@@ -11,6 +11,10 @@ require_relative "active_record_compose/model"
11
11
  # Most of the functionality resides in {ActiveRecordCompose::Model}.
12
12
  #
13
13
  module ActiveRecordCompose
14
+ # @private
15
+ def self.deprecator
16
+ @deprecator ||= ActiveSupport::Deprecation.new(next_major_version, "ActiveRecordCompose") # steep:ignore
17
+ end
14
18
  end
15
19
 
16
20
  require "active_record_compose/railtie" if defined?(::Rails::Railtie)
@@ -1,4 +1,7 @@
1
1
  module ActiveRecordCompose
2
+ private
3
+ def self.next_major_version: () -> Integer
4
+
2
5
  module Attributes
3
6
  extend ActiveSupport::Concern
4
7
  include ActiveModel::Attributes
@@ -139,7 +142,6 @@ module ActiveRecordCompose
139
142
  def transaction_coordinator: () -> ActiveRecordCompose::TransactionSupport::TransactionCoordinator
140
143
 
141
144
  class TransactionCoordinator
142
- @_before_committed_called: bool
143
145
  @transactions: Set[ActiveRecord::ConnectionAdapters::Transaction]
144
146
 
145
147
  def add_transaction: (ActiveRecord::ConnectionAdapters::Transaction) -> void
@@ -147,6 +149,7 @@ module ActiveRecordCompose
147
149
  def on_before_comitted: () { () -> void } -> void
148
150
 
149
151
  private
152
+ def final_transaction?: -> bool
150
153
  def all_finished?: -> bool
151
154
  def transactions: () -> Set[ActiveRecord::ConnectionAdapters::Transaction]
152
155
  def with_transaction_cleanup: () { () -> void } -> void
@@ -4,6 +4,9 @@
4
4
  module ActiveRecordCompose
5
5
  VERSION: String
6
6
 
7
+ def self.version: () -> Gem::Version
8
+ def self.deprecator: () -> ActiveSupport::Deprecation
9
+
7
10
  interface _ARLike
8
11
  def save: (**untyped options) -> bool
9
12
  def save!: (**untyped options) -> untyped
@@ -36,6 +39,9 @@ module ActiveRecordCompose
36
39
  type destroy_context_type = ((^() -> boolish) | (^(ar_like) -> boolish))
37
40
  type condition_type = ((^() -> boolish) | (^(ar_like) -> boolish))
38
41
 
42
+ class ActiveRecordComposeError < StandardError
43
+ end
44
+
39
45
  class ComposedCollection
40
46
  include ::Enumerable[ar_like]
41
47
 
@@ -47,7 +53,7 @@ module ActiveRecordCompose
47
53
  def delete: (ar_like) -> ComposedCollection?
48
54
  end
49
55
 
50
- class CircularReferenceDetected < StandardError
56
+ class CircularReferenceDetected < ActiveRecordComposeError
51
57
  end
52
58
 
53
59
  class Model
@@ -99,6 +105,9 @@ module ActiveRecordCompose
99
105
  extend Rails::Initializable::ClassMethods
100
106
  end
101
107
 
102
- class UninitializedAttribute < StandardError
108
+ class UninitializedAttribute < ActiveRecordComposeError
109
+ def initialize: (?untyped) -> void
110
+
111
+ attr_reader record: untyped
103
112
  end
104
113
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_record_compose
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.0
4
+ version: 1.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - hamajyotan