active_record_compose 1.1.0 → 1.2.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/.yardopts +2 -0
- data/CHANGELOG.md +16 -0
- data/lib/active_record_compose/attributes.rb +63 -55
- data/lib/active_record_compose/callbacks.rb +30 -2
- data/lib/active_record_compose/composed_collection.rb +35 -8
- data/lib/active_record_compose/exceptions.rb +29 -0
- data/lib/active_record_compose/inspectable.rb +58 -16
- data/lib/active_record_compose/model.rb +0 -313
- data/lib/active_record_compose/persistence.rb +61 -13
- data/lib/active_record_compose/transaction_support.rb +36 -12
- data/lib/active_record_compose/validations.rb +73 -1
- data/lib/active_record_compose/version.rb +1 -1
- data/lib/active_record_compose/wrapped_model.rb +13 -4
- data/lib/active_record_compose.rb +1 -0
- data/sig/_internal/package_private.rbs +20 -43
- data/sig/active_record_compose.rbs +8 -0
- metadata +3 -2
|
@@ -89,315 +89,6 @@ module ActiveRecordCompose
|
|
|
89
89
|
include ActiveRecordCompose::TransactionSupport
|
|
90
90
|
include ActiveRecordCompose::Inspectable
|
|
91
91
|
|
|
92
|
-
begin
|
|
93
|
-
# @group Model Core
|
|
94
|
-
|
|
95
|
-
# @!method self.delegate_attribute(*attributes, to:, allow_nil: false)
|
|
96
|
-
# Provides a method of attribute access to the encapsulated model.
|
|
97
|
-
#
|
|
98
|
-
# It provides a way to access the attributes of the model it encompasses,
|
|
99
|
-
# allowing transparent access as if it had those attributes itself.
|
|
100
|
-
#
|
|
101
|
-
# @param [Array<Symbol, String>] attributes
|
|
102
|
-
# attributes A variable-length list of attribute names to delegate.
|
|
103
|
-
# @param [Symbol, String] to
|
|
104
|
-
# The target object to which attributes are delegated (keyword argument).
|
|
105
|
-
# @param [Boolean] allow_nil
|
|
106
|
-
# allow_nil Whether to allow nil values. Defaults to false.
|
|
107
|
-
# @example Basic usage
|
|
108
|
-
# delegate_attribute :name, :email, to: :profile
|
|
109
|
-
# @example Allowing nil
|
|
110
|
-
# delegate_attribute :bio, to: :profile, allow_nil: true
|
|
111
|
-
# @see Module#delegate for similar behavior in ActiveSupport
|
|
112
|
-
|
|
113
|
-
# @!method self.attribute_names
|
|
114
|
-
# Returns a array of attribute name.
|
|
115
|
-
# Attributes declared with {.delegate_attribute} are also merged.
|
|
116
|
-
#
|
|
117
|
-
# @see #attribute_names
|
|
118
|
-
# @return [Array<String>] array of attribute name.
|
|
119
|
-
|
|
120
|
-
# @!method attribute_names
|
|
121
|
-
# Returns a array of attribute name.
|
|
122
|
-
# Attributes declared with {.delegate_attribute} are also merged.
|
|
123
|
-
#
|
|
124
|
-
# class Foo < ActiveRecordCompose::Base
|
|
125
|
-
# def initialize(attributes = {})
|
|
126
|
-
# @account = Account.new
|
|
127
|
-
# super
|
|
128
|
-
# end
|
|
129
|
-
#
|
|
130
|
-
# attribute :confirmation, :boolean, default: false # plain attribute
|
|
131
|
-
# delegate_attribute :name, to: :account # delegated attribute
|
|
132
|
-
#
|
|
133
|
-
# private
|
|
134
|
-
#
|
|
135
|
-
# attr_reader :account
|
|
136
|
-
# end
|
|
137
|
-
#
|
|
138
|
-
# Foo.attribute_names # Returns the merged state of plain and delegated attributes
|
|
139
|
-
# # => ["confirmation" ,"name"]
|
|
140
|
-
#
|
|
141
|
-
# foo = Foo.new
|
|
142
|
-
# foo.attribute_names # Similar behavior for instance method version
|
|
143
|
-
# # => ["confirmation", "name"]
|
|
144
|
-
#
|
|
145
|
-
# @see #attributes
|
|
146
|
-
# @return [Array<String>] array of attribute name.
|
|
147
|
-
|
|
148
|
-
# @!method attributes
|
|
149
|
-
# Returns a hash with the attribute name as key and the attribute value as value.
|
|
150
|
-
# Attributes declared with {.delegate_attribute} are also merged.
|
|
151
|
-
#
|
|
152
|
-
# class Foo < ActiveRecordCompose::Base
|
|
153
|
-
# def initialize(attributes = {})
|
|
154
|
-
# @account = Account.new
|
|
155
|
-
# super
|
|
156
|
-
# end
|
|
157
|
-
#
|
|
158
|
-
# attribute :confirmation, :boolean, default: false # plain attribute
|
|
159
|
-
# delegate_attribute :name, to: :account # delegated attribute
|
|
160
|
-
#
|
|
161
|
-
# private
|
|
162
|
-
#
|
|
163
|
-
# attr_reader :account
|
|
164
|
-
# end
|
|
165
|
-
#
|
|
166
|
-
# foo = Foo.new
|
|
167
|
-
# foo.name = "Alice"
|
|
168
|
-
# foo.confirmation = true
|
|
169
|
-
#
|
|
170
|
-
# foo.attributes # Returns the merged state of plain and delegated attributes
|
|
171
|
-
# # => { "confirmation" => true, "name" => "Alice" }
|
|
172
|
-
#
|
|
173
|
-
# @return [Hash<String, Object>] hash with the attribute name as key and the attribute value as value.
|
|
174
|
-
|
|
175
|
-
# @!method persisted?
|
|
176
|
-
# Returns true if model is persisted.
|
|
177
|
-
#
|
|
178
|
-
# By overriding this definition, you can control the callbacks that are triggered when a save is made.
|
|
179
|
-
# For example, returning false will trigger before_create, around_create and after_create,
|
|
180
|
-
# and returning true will trigger {.before_update}, {.around_update} and {.after_update}.
|
|
181
|
-
#
|
|
182
|
-
# @return [Boolean] returns true if model is persisted.
|
|
183
|
-
# @example
|
|
184
|
-
# # A model where persistence is always false
|
|
185
|
-
# class Foo < ActiveRecordCompose::Model
|
|
186
|
-
# before_save { puts "before_save called" }
|
|
187
|
-
# before_create { puts "before_create called" }
|
|
188
|
-
# before_update { puts "before_update called" }
|
|
189
|
-
# after_update { puts "after_update called" }
|
|
190
|
-
# after_create { puts "after_create called" }
|
|
191
|
-
# after_save { puts "after_save called" }
|
|
192
|
-
#
|
|
193
|
-
# def persisted? = false
|
|
194
|
-
# end
|
|
195
|
-
#
|
|
196
|
-
# # A model where persistence is always true
|
|
197
|
-
# class Bar < Foo
|
|
198
|
-
# def persisted? = true
|
|
199
|
-
# end
|
|
200
|
-
#
|
|
201
|
-
# Foo.new.save!
|
|
202
|
-
# # before_save called
|
|
203
|
-
# # before_create called
|
|
204
|
-
# # after_create called
|
|
205
|
-
# # after_save called
|
|
206
|
-
#
|
|
207
|
-
# Bar.new.save!
|
|
208
|
-
# # before_save called
|
|
209
|
-
# # before_update called
|
|
210
|
-
# # after_update called
|
|
211
|
-
# # after_save called
|
|
212
|
-
|
|
213
|
-
# @endgroup
|
|
214
|
-
|
|
215
|
-
# @group Validations
|
|
216
|
-
|
|
217
|
-
# @!method valid?(context = nil)
|
|
218
|
-
# Runs all the validations and returns the result as true or false.
|
|
219
|
-
# @param context Validation context.
|
|
220
|
-
# @return [Boolean] true on success, false on failure.
|
|
221
|
-
|
|
222
|
-
# @!method validate(context = nil)
|
|
223
|
-
# Alias for {#valid?}
|
|
224
|
-
# @see #valid? Validation context.
|
|
225
|
-
# @param context
|
|
226
|
-
# @return [Boolean] true on success, false on failure.
|
|
227
|
-
|
|
228
|
-
# @!method validate!(context = nil)
|
|
229
|
-
# @see #valid?
|
|
230
|
-
# Runs all the validations within the specified context.
|
|
231
|
-
# no errors are found, raises `ActiveRecord::RecordInvalid` otherwise.
|
|
232
|
-
# @param context Validation context.
|
|
233
|
-
# @raise ActiveRecord::RecordInvalid
|
|
234
|
-
|
|
235
|
-
# @!method errors
|
|
236
|
-
# Returns the `ActiveModel::Errors` object that holds all information about attribute error messages.
|
|
237
|
-
#
|
|
238
|
-
# The `ActiveModel::Base` implementation itself,
|
|
239
|
-
# but also aggregates error information for objects stored in {#models} when validation is performed.
|
|
240
|
-
#
|
|
241
|
-
# class Account < ActiveRecord::Base
|
|
242
|
-
# validates :name, :email, presence: true
|
|
243
|
-
# end
|
|
244
|
-
#
|
|
245
|
-
# class AccountRegistration < ActiveRecordCompose::Model
|
|
246
|
-
# def initialize(attributes = {})
|
|
247
|
-
# @account = Account.new
|
|
248
|
-
# super(attributes)
|
|
249
|
-
# models << account
|
|
250
|
-
# end
|
|
251
|
-
#
|
|
252
|
-
# attribute :confirmation, :boolean, default: false
|
|
253
|
-
# validates :confirmation, presence: true
|
|
254
|
-
#
|
|
255
|
-
# private
|
|
256
|
-
#
|
|
257
|
-
# attr_reader :account
|
|
258
|
-
# end
|
|
259
|
-
#
|
|
260
|
-
# registration = AccountRegistration
|
|
261
|
-
# registration.valid?
|
|
262
|
-
# #=> false
|
|
263
|
-
#
|
|
264
|
-
# # In addition to the model's own validation error information (`confirmation`), also aggregates
|
|
265
|
-
# # error information for objects stored in `account` (`name`, `email`) when validation is performed.
|
|
266
|
-
#
|
|
267
|
-
# registration.errors.map { _1.attribute } #=> [:name, :email, :confirmation]
|
|
268
|
-
#
|
|
269
|
-
# @return [ActiveModel::Errors]
|
|
270
|
-
|
|
271
|
-
# @endgroup
|
|
272
|
-
|
|
273
|
-
# @group Persistences
|
|
274
|
-
|
|
275
|
-
# @!method save(**options)
|
|
276
|
-
# Save the models that exist in models.
|
|
277
|
-
# Returns false if any of the targets fail, true if all succeed.
|
|
278
|
-
#
|
|
279
|
-
# The save is performed within a single transaction.
|
|
280
|
-
#
|
|
281
|
-
# Only the `:validate` option takes effect as it is required internally.
|
|
282
|
-
# However, we do not recommend explicitly specifying `validate: false` to skip validation.
|
|
283
|
-
# Additionally, the `:context` option is not accepted.
|
|
284
|
-
# The need for such a value indicates that operations from multiple contexts are being processed.
|
|
285
|
-
# If the contexts differ, we recommend separating them into different model definitions.
|
|
286
|
-
#
|
|
287
|
-
# @param options [Hash] parameters.
|
|
288
|
-
# @option options [Boolean] :validate Whether to run validations.
|
|
289
|
-
# This option is intended for internal use only.
|
|
290
|
-
# Users should avoid explicitly passing <tt>validate: false</tt>,
|
|
291
|
-
# as skipping validations can lead to unexpected behavior.
|
|
292
|
-
# @return [Boolean] returns true on success, false on failure.
|
|
293
|
-
|
|
294
|
-
# @!method save!(**options)
|
|
295
|
-
# Behavior is same to {#save}, but raises an exception prematurely on failure.
|
|
296
|
-
# @see #save
|
|
297
|
-
# @raise ActiveRecord::RecordInvalid
|
|
298
|
-
# @raise ActiveRecord::RecordNotSaved
|
|
299
|
-
|
|
300
|
-
# @!method update(attributes)
|
|
301
|
-
# Assign attributes and {#save}.
|
|
302
|
-
#
|
|
303
|
-
# @param [Hash<String, Object>] attributes
|
|
304
|
-
# new attributes.
|
|
305
|
-
# @see #save
|
|
306
|
-
# @return [Boolean] returns true on success, false on failure.
|
|
307
|
-
|
|
308
|
-
# @!method update!(attributes)
|
|
309
|
-
# Behavior is same to {#update}, but raises an exception prematurely on failure.
|
|
310
|
-
#
|
|
311
|
-
# @param [Hash<String, Object>] attributes
|
|
312
|
-
# new attributes.
|
|
313
|
-
# @see #save
|
|
314
|
-
# @see #update
|
|
315
|
-
# @raise ActiveRecord::RecordInvalid
|
|
316
|
-
# @raise ActiveRecord::RecordNotSaved
|
|
317
|
-
|
|
318
|
-
# @endgroup
|
|
319
|
-
|
|
320
|
-
# @group Callbacks
|
|
321
|
-
|
|
322
|
-
# @!method self.before_save(*args, &block)
|
|
323
|
-
# Registers a callback to be called before a model is saved.
|
|
324
|
-
|
|
325
|
-
# @!method self.around_save(*args, &block)
|
|
326
|
-
# Registers a callback to be called around the save of a model.
|
|
327
|
-
|
|
328
|
-
# @!method self.after_save(*args, &block)
|
|
329
|
-
# Registers a callback to be called after a model is saved.
|
|
330
|
-
|
|
331
|
-
# @!method self.before_create(*args, &block)
|
|
332
|
-
# Registers a callback to be called before a model is created.
|
|
333
|
-
|
|
334
|
-
# @!method self.around_create(*args, &block)
|
|
335
|
-
# Registers a callback to be called around the creation of a model.
|
|
336
|
-
|
|
337
|
-
# @!method self.after_create(*args, &block)
|
|
338
|
-
# Registers a callback to be called after a model is created.
|
|
339
|
-
|
|
340
|
-
# @!method self.before_update(*args, &block)
|
|
341
|
-
# Registers a callback to be called before a model is updated.
|
|
342
|
-
|
|
343
|
-
# @!method self.around_update(*args, &block)
|
|
344
|
-
# Registers a callback to be called around the update of a model.
|
|
345
|
-
|
|
346
|
-
# @!method self.after_update(*args, &block)
|
|
347
|
-
# Registers a callback to be called after a update is updated.
|
|
348
|
-
|
|
349
|
-
# @!method self.after_commit(*args, &block)
|
|
350
|
-
# Registers a block to be called after the transaction is fully committed.
|
|
351
|
-
|
|
352
|
-
# @!method self.after_rollback(*args, &block)
|
|
353
|
-
# Registers a block to be called after the transaction is rolled back.
|
|
354
|
-
|
|
355
|
-
# @endgroup
|
|
356
|
-
|
|
357
|
-
# @!method inspect
|
|
358
|
-
# Returns a formatted string representation of the record's attributes.
|
|
359
|
-
# It tries to replicate the inspect format provided by ActiveRecord as closely as possible.
|
|
360
|
-
#
|
|
361
|
-
# @example
|
|
362
|
-
# class Model < ActiveRecordCompose::Model
|
|
363
|
-
# def initialize(ar_model)
|
|
364
|
-
# @ar_model = ar_model
|
|
365
|
-
# super
|
|
366
|
-
# end
|
|
367
|
-
#
|
|
368
|
-
# attribute :foo, :date, default: -> { Date.today }
|
|
369
|
-
# delegate_attribute :bar, to: :ar_model
|
|
370
|
-
#
|
|
371
|
-
# private attr_reader :ar_model
|
|
372
|
-
# end
|
|
373
|
-
#
|
|
374
|
-
# m = Model.new(ar_model)
|
|
375
|
-
# m.inspect #=> #<Model:0x00007ff0fe75fe58 foo: "2025-11-14", bar: "bar">
|
|
376
|
-
#
|
|
377
|
-
# @example use {.filter_attributes}
|
|
378
|
-
# class Model < ActiveRecordCompose::Model
|
|
379
|
-
# self.filter_attributes += %i[foo]
|
|
380
|
-
#
|
|
381
|
-
# # ...
|
|
382
|
-
# end
|
|
383
|
-
#
|
|
384
|
-
# m = Model.new(ar_model)
|
|
385
|
-
# m.inspect #=> #<Model:0x00007ff0fe75fe58 foo: [FILTERED], bar: "bar">
|
|
386
|
-
#
|
|
387
|
-
# @return [String] formatted string representation of the record's attributes.
|
|
388
|
-
# @see .filter_attributes
|
|
389
|
-
|
|
390
|
-
# @!method self.filter_attributes
|
|
391
|
-
# Returns columns not to expose when invoking {#inspect}.
|
|
392
|
-
# @return [Array<Symbol>]
|
|
393
|
-
# @see #inspect
|
|
394
|
-
|
|
395
|
-
# @!method self.filter_attributes=(value)
|
|
396
|
-
# Specify columns not to expose when invoking {#inspect}.
|
|
397
|
-
# @param [Array<Symbol>] value
|
|
398
|
-
# @see #inspect
|
|
399
|
-
end
|
|
400
|
-
|
|
401
92
|
def initialize(attributes = {})
|
|
402
93
|
super
|
|
403
94
|
end
|
|
@@ -425,8 +116,6 @@ module ActiveRecordCompose
|
|
|
425
116
|
#
|
|
426
117
|
def id = nil
|
|
427
118
|
|
|
428
|
-
# @group Model Core
|
|
429
|
-
|
|
430
119
|
private
|
|
431
120
|
|
|
432
121
|
# Returns a collection of model elements to encapsulate.
|
|
@@ -439,7 +128,5 @@ module ActiveRecordCompose
|
|
|
439
128
|
# @return [ActiveRecordCompose::ComposedCollection]
|
|
440
129
|
#
|
|
441
130
|
def models = @__models ||= ActiveRecordCompose::ComposedCollection.new(self)
|
|
442
|
-
|
|
443
|
-
# @endgroup
|
|
444
131
|
end
|
|
445
132
|
end
|
|
@@ -6,7 +6,6 @@ require_relative "composed_collection"
|
|
|
6
6
|
module ActiveRecordCompose
|
|
7
7
|
using ComposedCollection::PackagePrivate
|
|
8
8
|
|
|
9
|
-
# @private
|
|
10
9
|
module Persistence
|
|
11
10
|
extend ActiveSupport::Concern
|
|
12
11
|
include ActiveRecordCompose::Callbacks
|
|
@@ -22,6 +21,11 @@ module ActiveRecordCompose
|
|
|
22
21
|
# The need for such a value indicates that operations from multiple contexts are being processed.
|
|
23
22
|
# If the contexts differ, we recommend separating them into different model definitions.
|
|
24
23
|
#
|
|
24
|
+
# @param options [Hash] parameters.
|
|
25
|
+
# @option options [Boolean] :validate Whether to run validations.
|
|
26
|
+
# This option is intended for internal use only.
|
|
27
|
+
# Users should avoid explicitly passing <tt>validate: false</tt>,
|
|
28
|
+
# as skipping validations can lead to unexpected behavior.
|
|
25
29
|
# @return [Boolean] returns true on success, false on failure.
|
|
26
30
|
def save(**options)
|
|
27
31
|
with_callbacks { save_models(**options, bang: false) }
|
|
@@ -29,38 +33,80 @@ module ActiveRecordCompose
|
|
|
29
33
|
false
|
|
30
34
|
end
|
|
31
35
|
|
|
32
|
-
#
|
|
33
|
-
# Unlike #save, an exception is raises on failure.
|
|
34
|
-
#
|
|
35
|
-
# Saving, like `#save`, is performed within a single transaction.
|
|
36
|
-
#
|
|
37
|
-
# Only the `:validate` option takes effect as it is required internally.
|
|
38
|
-
# However, we do not recommend explicitly specifying `validate: false` to skip validation.
|
|
39
|
-
# Additionally, the `:context` option is not accepted.
|
|
40
|
-
# The need for such a value indicates that operations from multiple contexts are being processed.
|
|
41
|
-
# If the contexts differ, we recommend separating them into different model definitions.
|
|
36
|
+
# Behavior is same to {#save}, but raises an exception prematurely on failure.
|
|
42
37
|
#
|
|
38
|
+
# @see #save
|
|
39
|
+
# @raise ActiveRecord::RecordInvalid
|
|
40
|
+
# @raise ActiveRecord::RecordNotSaved
|
|
43
41
|
def save!(**options)
|
|
44
42
|
with_callbacks { save_models(**options, bang: true) } || raise_on_save_error
|
|
45
43
|
end
|
|
46
44
|
|
|
47
|
-
# Assign attributes and save.
|
|
45
|
+
# Assign attributes and {#save}.
|
|
48
46
|
#
|
|
47
|
+
# @param [Hash<String, Object>] attributes
|
|
48
|
+
# new attributes.
|
|
49
|
+
# @see #save
|
|
49
50
|
# @return [Boolean] returns true on success, false on failure.
|
|
50
51
|
def update(attributes)
|
|
51
52
|
assign_attributes(attributes)
|
|
52
53
|
save
|
|
53
54
|
end
|
|
54
55
|
|
|
55
|
-
# Behavior is same to
|
|
56
|
+
# Behavior is same to {#update}, but raises an exception prematurely on failure.
|
|
56
57
|
#
|
|
58
|
+
# @param [Hash<String, Object>] attributes
|
|
59
|
+
# new attributes.
|
|
60
|
+
# @see #save
|
|
61
|
+
# @see #update
|
|
62
|
+
# @raise ActiveRecord::RecordInvalid
|
|
63
|
+
# @raise ActiveRecord::RecordNotSaved
|
|
57
64
|
def update!(attributes)
|
|
58
65
|
assign_attributes(attributes)
|
|
59
66
|
save!
|
|
60
67
|
end
|
|
61
68
|
|
|
69
|
+
# @!method persisted?
|
|
70
|
+
# Returns true if model is persisted.
|
|
71
|
+
#
|
|
72
|
+
# By overriding this definition, you can control the callbacks that are triggered when a save is made.
|
|
73
|
+
# For example, returning false will trigger before_create, around_create and after_create,
|
|
74
|
+
# and returning true will trigger {.before_update}, {.around_update} and {.after_update}.
|
|
75
|
+
#
|
|
76
|
+
# @return [Boolean] returns true if model is persisted.
|
|
77
|
+
# @example
|
|
78
|
+
# # A model where persistence is always false
|
|
79
|
+
# class Foo < ActiveRecordCompose::Model
|
|
80
|
+
# before_save { puts "before_save called" }
|
|
81
|
+
# before_create { puts "before_create called" }
|
|
82
|
+
# before_update { puts "before_update called" }
|
|
83
|
+
# after_update { puts "after_update called" }
|
|
84
|
+
# after_create { puts "after_create called" }
|
|
85
|
+
# after_save { puts "after_save called" }
|
|
86
|
+
#
|
|
87
|
+
# def persisted? = false
|
|
88
|
+
# end
|
|
89
|
+
#
|
|
90
|
+
# # A model where persistence is always true
|
|
91
|
+
# class Bar < Foo
|
|
92
|
+
# def persisted? = true
|
|
93
|
+
# end
|
|
94
|
+
#
|
|
95
|
+
# Foo.new.save!
|
|
96
|
+
# # before_save called
|
|
97
|
+
# # before_create called
|
|
98
|
+
# # after_create called
|
|
99
|
+
# # after_save called
|
|
100
|
+
#
|
|
101
|
+
# Bar.new.save!
|
|
102
|
+
# # before_save called
|
|
103
|
+
# # before_update called
|
|
104
|
+
# # after_update called
|
|
105
|
+
# # after_save called
|
|
106
|
+
|
|
62
107
|
private
|
|
63
108
|
|
|
109
|
+
# @private
|
|
64
110
|
def save_models(bang:, **options)
|
|
65
111
|
models.__wrapped_models.all? do |model|
|
|
66
112
|
if bang
|
|
@@ -71,8 +117,10 @@ module ActiveRecordCompose
|
|
|
71
117
|
end
|
|
72
118
|
end
|
|
73
119
|
|
|
120
|
+
# @private
|
|
74
121
|
def raise_on_save_error = raise ActiveRecord::RecordNotSaved.new(raise_on_save_error_message, self)
|
|
75
122
|
|
|
123
|
+
# @private
|
|
76
124
|
def raise_on_save_error_message = "Failed to save the model."
|
|
77
125
|
end
|
|
78
126
|
end
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
require "active_support/core_ext/module"
|
|
4
4
|
|
|
5
5
|
module ActiveRecordCompose
|
|
6
|
-
# @private
|
|
7
6
|
module TransactionSupport
|
|
8
7
|
extend ActiveSupport::Concern
|
|
9
8
|
|
|
@@ -11,82 +10,103 @@ module ActiveRecordCompose
|
|
|
11
10
|
define_callbacks :commit, :rollback, :before_commit, scope: [ :kind, :name ]
|
|
12
11
|
end
|
|
13
12
|
|
|
14
|
-
|
|
15
|
-
# steep:ignore:start
|
|
13
|
+
# steep:ignore:start
|
|
16
14
|
|
|
15
|
+
class_methods do
|
|
16
|
+
# @private
|
|
17
17
|
# @deprecated
|
|
18
18
|
def with_connection(...)
|
|
19
19
|
ActiveRecord.deprecator.warn("`with_connection` is deprecated. Use `ActiveRecord::Base.with_connection` instead.")
|
|
20
20
|
ActiveRecord::Base.with_connection(...)
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
+
# @private
|
|
23
24
|
# @deprecated
|
|
24
25
|
def lease_connection(...)
|
|
25
26
|
ActiveRecord.deprecator.warn("`lease_connection` is deprecated. Use `ActiveRecord::Base.lease_connection` instead.")
|
|
26
27
|
ActiveRecord::Base.lease_connection(...)
|
|
27
28
|
end
|
|
28
29
|
|
|
30
|
+
# @private
|
|
29
31
|
# @deprecated
|
|
30
32
|
def connection(...)
|
|
31
33
|
ActiveRecord.deprecator.warn("`connection` is deprecated. Use `ActiveRecord::Base.connection` instead.")
|
|
32
34
|
ActiveRecord::Base.connection(...)
|
|
33
35
|
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# steep:ignore:end
|
|
34
39
|
|
|
35
|
-
|
|
40
|
+
# steep:ignore:start
|
|
36
41
|
|
|
42
|
+
class_methods do
|
|
43
|
+
# @private
|
|
37
44
|
def before_commit(*args, &block)
|
|
38
45
|
set_options_for_callbacks!(args)
|
|
39
|
-
set_callback(:before_commit, :before, *args, &block)
|
|
46
|
+
set_callback(:before_commit, :before, *args, &block)
|
|
40
47
|
end
|
|
41
48
|
|
|
49
|
+
# Registers a block to be called after the transaction is fully committed.
|
|
50
|
+
#
|
|
42
51
|
def after_commit(*args, &block)
|
|
43
52
|
set_options_for_callbacks!(args, prepend_option)
|
|
44
|
-
set_callback(:commit, :after, *args, &block)
|
|
53
|
+
set_callback(:commit, :after, *args, &block)
|
|
45
54
|
end
|
|
46
55
|
|
|
56
|
+
# Registers a block to be called after the transaction is rolled back.
|
|
57
|
+
#
|
|
47
58
|
def after_rollback(*args, &block)
|
|
48
59
|
set_options_for_callbacks!(args, prepend_option)
|
|
49
|
-
set_callback(:rollback, :after, *args, &block)
|
|
60
|
+
set_callback(:rollback, :after, *args, &block)
|
|
50
61
|
end
|
|
51
62
|
|
|
52
63
|
private
|
|
53
64
|
|
|
65
|
+
# @private
|
|
54
66
|
def prepend_option
|
|
55
|
-
if ActiveRecord.run_after_transaction_callbacks_in_order_defined
|
|
67
|
+
if ActiveRecord.run_after_transaction_callbacks_in_order_defined
|
|
56
68
|
{ prepend: true }
|
|
57
69
|
else
|
|
58
70
|
{}
|
|
59
71
|
end
|
|
60
72
|
end
|
|
61
73
|
|
|
74
|
+
# @private
|
|
62
75
|
def set_options_for_callbacks!(args, enforced_options = {})
|
|
63
76
|
options = args.extract_options!.merge!(enforced_options)
|
|
64
77
|
args << options
|
|
65
78
|
end
|
|
66
79
|
end
|
|
67
80
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
def save!(**options) = with_transaction_returning_status { super }
|
|
81
|
+
# steep:ignore:end
|
|
71
82
|
|
|
72
83
|
concerning :SupportForActiveRecordConnectionAdaptersTransaction do
|
|
84
|
+
# @private
|
|
73
85
|
def trigger_transactional_callbacks? = true
|
|
74
86
|
|
|
87
|
+
# @private
|
|
75
88
|
def before_committed!
|
|
76
89
|
_run_before_commit_callbacks
|
|
77
90
|
end
|
|
78
91
|
|
|
92
|
+
# @private
|
|
79
93
|
def committed!(should_run_callbacks: true)
|
|
80
94
|
_run_commit_callbacks if should_run_callbacks
|
|
81
95
|
end
|
|
82
96
|
|
|
97
|
+
# @private
|
|
83
98
|
def rolledback!(force_restore_state: false, should_run_callbacks: true)
|
|
84
99
|
_run_rollback_callbacks if should_run_callbacks
|
|
85
100
|
end
|
|
86
101
|
end
|
|
87
102
|
|
|
103
|
+
def save(**options) = with_transaction_returning_status { super }
|
|
104
|
+
|
|
105
|
+
def save!(**options) = with_transaction_returning_status { super }
|
|
106
|
+
|
|
88
107
|
private
|
|
89
108
|
|
|
109
|
+
# @private
|
|
90
110
|
def with_transaction_returning_status
|
|
91
111
|
connection_pool.with_connection do |connection|
|
|
92
112
|
with_pool_transaction_isolation_level(connection) do
|
|
@@ -96,13 +116,15 @@ module ActiveRecordCompose
|
|
|
96
116
|
connection.add_transaction_record(self, ensure_finalize || has_transactional_callbacks?) # steep:ignore
|
|
97
117
|
|
|
98
118
|
yield.tap { raise ActiveRecord::Rollback unless _1 }
|
|
99
|
-
end
|
|
119
|
+
end || false
|
|
100
120
|
end
|
|
101
121
|
end
|
|
102
122
|
end
|
|
103
123
|
|
|
124
|
+
# @private
|
|
104
125
|
def default_ar_class = ActiveRecord::Base
|
|
105
126
|
|
|
127
|
+
# @private
|
|
106
128
|
def connection_pool(ar_class: default_ar_class)
|
|
107
129
|
connection_specification_name = ar_class.connection_specification_name
|
|
108
130
|
role = ar_class.current_role
|
|
@@ -114,6 +136,7 @@ module ActiveRecordCompose
|
|
|
114
136
|
connection_handler.retrieve_connection_pool(connection_specification_name, **retrieve_options)
|
|
115
137
|
end
|
|
116
138
|
|
|
139
|
+
# @private
|
|
117
140
|
def with_pool_transaction_isolation_level(connection, &block)
|
|
118
141
|
if ActiveRecord.gem_version.release >= Gem::Version.new("8.1.0")
|
|
119
142
|
isolation_level = ActiveRecord.default_transaction_isolation_level # steep:ignore
|
|
@@ -123,6 +146,7 @@ module ActiveRecordCompose
|
|
|
123
146
|
end
|
|
124
147
|
end
|
|
125
148
|
|
|
149
|
+
# @private
|
|
126
150
|
def has_transactional_callbacks?
|
|
127
151
|
_rollback_callbacks.present? || _commit_callbacks.present? || _before_commit_callbacks.present? # steep:ignore
|
|
128
152
|
end
|