active_record_compose 0.7.0 → 0.9.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/.rubocop.yml +6 -0
- data/CHANGELOG.md +13 -0
- data/README.md +5 -54
- data/lib/active_record_compose/delegate_attribute.rb +2 -2
- data/lib/active_record_compose/model.rb +27 -154
- data/lib/active_record_compose/transaction_support.rb +1 -1
- data/lib/active_record_compose/version.rb +1 -1
- data/sig/_internal/package_private.rbs +1 -1
- data/sig/active_record_compose.rbs +3 -5
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dba7d13bf8f2dc9ef449ead00c1a7ae3fff83e216f07b5d11606659a3c1f4650
|
4
|
+
data.tar.gz: 883a9480ff3e6bd4c99246c12200a7b5a6e44103c0d1c967ce97c4536aa478b1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 905bf7969733f738e00726f0dc7ed1442bce1f598485a72a0478fd67069c556b5d8299fdc5485636b1194e095649df550ba9f3c01ffb42c107a8a69afcde85ae
|
7
|
+
data.tar.gz: 22f44a63d515d31682f22641e112c3bf720c5b3ff29c8fb0a30ceb777b23847a74f053eb3591211c35bfe2e2f96de345523236d08b30365059fc3a7421437bf4
|
data/.rubocop.yml
CHANGED
@@ -21,6 +21,9 @@ Style/DoubleNegation:
|
|
21
21
|
Style/NumericPredicate:
|
22
22
|
Enabled: false
|
23
23
|
|
24
|
+
Style/OptionalBooleanParameter:
|
25
|
+
Enabled: false
|
26
|
+
|
24
27
|
Style/StringLiterals:
|
25
28
|
Enabled: true
|
26
29
|
|
@@ -48,6 +51,9 @@ Layout/LeadingCommentSpace:
|
|
48
51
|
Layout/LineLength:
|
49
52
|
Max: 120
|
50
53
|
|
54
|
+
Lint/UselessMethodDefinition:
|
55
|
+
Enabled: false
|
56
|
+
|
51
57
|
Metrics/AbcSize:
|
52
58
|
Enabled: false
|
53
59
|
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,18 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [0.9.0] - 2025-03-16
|
4
|
+
|
5
|
+
- removed `persisted_flag_callback_control` support.
|
6
|
+
- Omitted `:private` option from `delegate_attribute` because, assuming the
|
7
|
+
behavior and use cases of `ActiveModel::Attributes.attribute`, making it private is unnecessary.
|
8
|
+
- Added the URL for the sample application to the README
|
9
|
+
|
10
|
+
## [0.8.0] - 2025-02-22
|
11
|
+
|
12
|
+
- changed `persisted_flag_callback_control` default from `false` to `true`.
|
13
|
+
- adjusted to save errors as soon as an invalid is found.
|
14
|
+
- drop support rails 6.1.x.
|
15
|
+
|
3
16
|
## [0.7.0] - 2025-02-12
|
4
17
|
|
5
18
|
- rename ActiveRecordCompose::InnerModel to ActiveRecordCompose::WrappedModel
|
data/README.md
CHANGED
@@ -15,7 +15,7 @@ activemodel (activerecord) form object pattern. it embraces multiple AR models a
|
|
15
15
|
- [I18n](#i18n)
|
16
16
|
- [Advanced Usage](#advanced-usage)
|
17
17
|
- [`destroy` option](#destroy-option)
|
18
|
-
- [Callback ordering by `#
|
18
|
+
- [Callback ordering by `#persisted?`](#callback-ordering-by-persisted)
|
19
19
|
- [Links](#links)
|
20
20
|
- [Development](#development)
|
21
21
|
- [Contributing](#contributing)
|
@@ -284,57 +284,13 @@ class AccountRegistration < ActiveRecordCompose::Model
|
|
284
284
|
end
|
285
285
|
```
|
286
286
|
|
287
|
-
### Callback ordering by `#
|
287
|
+
### Callback ordering by `#persisted?`
|
288
288
|
|
289
|
-
The behavior of `(before|after|around)_create` and `(before|after|around)_update` hooks
|
290
|
-
the
|
291
|
-
|
292
|
-
When `persisted_flag_callback_control` is set to false,
|
293
|
-
the execution of `#create`, `#update`, or `#save` determines which callbacks will be triggered.
|
294
|
-
Currently, the default value is `false`, but it will no longer be supported in the future.
|
295
|
-
|
296
|
-
```ruby
|
297
|
-
class ComposedModel < ActiveRecordCompose::Model
|
298
|
-
self.persisted_flag_callback_control = false # Currently defaults to false, but will no longer be supported in the future.
|
299
|
-
|
300
|
-
# ...
|
301
|
-
|
302
|
-
before_save { puts 'before_save called!' }
|
303
|
-
before_create { puts 'before_create called!' }
|
304
|
-
before_update { puts 'before_update called!' }
|
305
|
-
after_save { puts 'after_save called!' }
|
306
|
-
after_create { puts 'after_create called!' }
|
307
|
-
after_update { puts 'after_update called!' }
|
308
|
-
end
|
309
|
-
```
|
310
|
-
|
311
|
-
```ruby
|
312
|
-
model = ComposedModel.new
|
313
|
-
|
314
|
-
model.save
|
315
|
-
# before_save called!
|
316
|
-
# after_save called!
|
317
|
-
|
318
|
-
model.create
|
319
|
-
# before_save called!
|
320
|
-
# before_create called!
|
321
|
-
# after_create called!
|
322
|
-
# after_save called!
|
323
|
-
|
324
|
-
model.update
|
325
|
-
# before_save called!
|
326
|
-
# before_update called!
|
327
|
-
# after_update called!
|
328
|
-
# after_save called!
|
329
|
-
```
|
330
|
-
|
331
|
-
When `persisted_flag_callback_control` is set to `true`, it behaves almost like callback control in ActiveRecord.
|
332
|
-
This behavior will be the default in the future.
|
289
|
+
The behavior of `(before|after|around)_create` and `(before|after|around)_update` hooks depending on the evaluation result of `#persisted?`,
|
290
|
+
either the create-related callbacks or the update-related callbacks will be triggered.
|
333
291
|
|
334
292
|
```ruby
|
335
293
|
class ComposedModel < ActiveRecordCompose::Model
|
336
|
-
self.persisted_flag_callback_control = true # In the future, true will be the default and false will no longer be supported.
|
337
|
-
|
338
294
|
# ...
|
339
295
|
|
340
296
|
before_save { puts 'before_save called!' }
|
@@ -382,15 +338,10 @@ model.save # or `model.update` (the same callbacks will be triggered in all case
|
|
382
338
|
# after_save called!
|
383
339
|
```
|
384
340
|
|
385
|
-
When `persisted_flag_callback_control` is `true`, `#create` is not supported.
|
386
|
-
|
387
|
-
```ruby
|
388
|
-
model.create # => raises RuntimeError
|
389
|
-
```
|
390
|
-
|
391
341
|
## Links
|
392
342
|
|
393
343
|
- [Smart way to update multiple models simultaneously in Rails](https://dev.to/hamajyotan/smart-way-to-update-multiple-models-simultaneously-in-rails-51b6)
|
344
|
+
- [Sample application as an example](https://github.com/hamajyotan/active_record_compose-example)
|
394
345
|
|
395
346
|
## Development
|
396
347
|
|
@@ -45,7 +45,7 @@ module ActiveRecordCompose
|
|
45
45
|
module ClassMethods
|
46
46
|
# Defines the reader and writer for the specified attribute.
|
47
47
|
#
|
48
|
-
def delegate_attribute(*attributes, to:, allow_nil: nil
|
48
|
+
def delegate_attribute(*attributes, to:, allow_nil: nil)
|
49
49
|
delegates = attributes.flat_map do |attribute|
|
50
50
|
reader = attribute.to_s
|
51
51
|
writer = "#{attribute}="
|
@@ -53,7 +53,7 @@ module ActiveRecordCompose
|
|
53
53
|
[reader, writer]
|
54
54
|
end
|
55
55
|
|
56
|
-
delegate(*delegates, to:, allow_nil
|
56
|
+
delegate(*delegates, to:, allow_nil:)
|
57
57
|
self.delegated_attributes = delegated_attributes.to_a + attributes.map { _1.to_s }
|
58
58
|
end
|
59
59
|
end
|
@@ -15,46 +15,6 @@ module ActiveRecordCompose
|
|
15
15
|
include ActiveRecordCompose::DelegateAttribute
|
16
16
|
include ActiveRecordCompose::TransactionSupport
|
17
17
|
|
18
|
-
# This flag controls the callback sequence for models.
|
19
|
-
# The current default value is `false`, but support for `false` is planned to be discontinued in the future.
|
20
|
-
#
|
21
|
-
# When `persisted_flag_callback_control` is set to `true`,
|
22
|
-
# the occurrence of callbacks depends on the evaluation result of `#persisted?`.
|
23
|
-
# Additionally, the definition of `#persisted?` itself can be appropriately overridden in subclasses.
|
24
|
-
#
|
25
|
-
# if `#persisted?` returns `false`:
|
26
|
-
# * before_save
|
27
|
-
# * before_create
|
28
|
-
# * after_create
|
29
|
-
# * after_save
|
30
|
-
#
|
31
|
-
# if `#persisted?` returns `true`:
|
32
|
-
# * before_save
|
33
|
-
# * before_update
|
34
|
-
# * after_update
|
35
|
-
# * after_save
|
36
|
-
#
|
37
|
-
# On the other hand, when `persisted_flag_callback_control` is set to `false`,
|
38
|
-
# the invoked methods during saving operations vary depending on the method used.
|
39
|
-
#
|
40
|
-
# when performing `#save` or `#save!`:
|
41
|
-
# * before_save
|
42
|
-
# * after_save
|
43
|
-
#
|
44
|
-
# when performing `#update` or `#update!`:
|
45
|
-
# * before_save
|
46
|
-
# * before_update
|
47
|
-
# * after_update
|
48
|
-
# * after_save
|
49
|
-
#
|
50
|
-
# when performing `#create` or `#create!`:
|
51
|
-
# * before_save
|
52
|
-
# * before_create
|
53
|
-
# * after_create
|
54
|
-
# * after_save
|
55
|
-
#
|
56
|
-
class_attribute :persisted_flag_callback_control, instance_accessor: false, default: false
|
57
|
-
|
58
18
|
define_model_callbacks :save
|
59
19
|
define_model_callbacks :create
|
60
20
|
define_model_callbacks :update
|
@@ -70,16 +30,16 @@ module ActiveRecordCompose
|
|
70
30
|
#
|
71
31
|
# The save is performed within a single transaction.
|
72
32
|
#
|
33
|
+
# Options like `:validate` and `:context` are not accepted as arguments.
|
34
|
+
# The need for such values indicates that operations from multiple contexts are being handled.
|
35
|
+
# However, if the contexts are different, it is recommended to separate them into different model definitions.
|
36
|
+
#
|
73
37
|
# @return [Boolean] returns true on success, false on failure.
|
74
38
|
def save
|
75
39
|
return false if invalid?
|
76
40
|
|
77
41
|
with_transaction_returning_status do
|
78
|
-
|
79
|
-
with_callbacks { save_models(bang: false) }
|
80
|
-
else
|
81
|
-
run_callbacks(:save) { save_models(bang: false) }
|
82
|
-
end
|
42
|
+
with_callbacks { save_models(bang: false) }
|
83
43
|
rescue ActiveRecord::RecordInvalid
|
84
44
|
false
|
85
45
|
end
|
@@ -90,140 +50,53 @@ module ActiveRecordCompose
|
|
90
50
|
#
|
91
51
|
# Saving, like `#save`, is performed within a single transaction.
|
92
52
|
#
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
with_transaction_returning_status do
|
97
|
-
if self.class.persisted_flag_callback_control
|
98
|
-
with_callbacks { save_models(bang: true) }
|
99
|
-
else
|
100
|
-
run_callbacks(:save) { save_models(bang: true) }
|
101
|
-
end
|
102
|
-
end || raise_on_save_error
|
103
|
-
end
|
104
|
-
|
105
|
-
# Behavior is same to `#save`, but `before_create` and `after_create` hooks fires.
|
106
|
-
#
|
107
|
-
# class ComposedModel < ActiveRecordCompose::Model
|
108
|
-
# # ...
|
109
|
-
#
|
110
|
-
# before_save { puts 'before_save called!' }
|
111
|
-
# before_create { puts 'before_create called!' }
|
112
|
-
# before_update { puts 'before_update called!' }
|
113
|
-
# after_save { puts 'after_save called!' }
|
114
|
-
# after_create { puts 'after_create called!' }
|
115
|
-
# after_update { puts 'after_update called!' }
|
116
|
-
# end
|
53
|
+
# Options like `:validate` and `:context` are not accepted as arguments.
|
54
|
+
# The need for such values indicates that operations from multiple contexts are being handled.
|
55
|
+
# However, if the contexts are different, it is recommended to separate them into different model definitions.
|
117
56
|
#
|
118
|
-
|
119
|
-
#
|
120
|
-
# model.save
|
121
|
-
# # before_save called!
|
122
|
-
# # after_save called!
|
123
|
-
#
|
124
|
-
# model.create
|
125
|
-
# # before_save called!
|
126
|
-
# # before_create called!
|
127
|
-
# # after_create called!
|
128
|
-
# # after_save called!
|
129
|
-
#
|
130
|
-
def create(attributes = {})
|
131
|
-
if self.class.persisted_flag_callback_control
|
132
|
-
raise '`#create` cannot be called. The context for creation or update is determined by the `#persisted` flag.'
|
133
|
-
end
|
134
|
-
|
135
|
-
assign_attributes(attributes)
|
136
|
-
return false if invalid?
|
137
|
-
|
138
|
-
with_transaction_returning_status do
|
139
|
-
with_callbacks(context: :create) { save_models(bang: false) }
|
140
|
-
rescue ActiveRecord::RecordInvalid
|
141
|
-
false
|
142
|
-
end
|
143
|
-
end
|
144
|
-
|
145
|
-
# Behavior is same to `#create`, but raises an exception prematurely on failure.
|
146
|
-
#
|
147
|
-
def create!(attributes = {})
|
148
|
-
if self.class.persisted_flag_callback_control
|
149
|
-
raise '`#create` cannot be called. The context for creation or update is determined by the `#persisted` flag.'
|
150
|
-
end
|
151
|
-
|
152
|
-
assign_attributes(attributes)
|
57
|
+
def save!
|
153
58
|
valid? || raise_validation_error
|
154
59
|
|
155
60
|
with_transaction_returning_status do
|
156
|
-
with_callbacks
|
61
|
+
with_callbacks { save_models(bang: true) }
|
157
62
|
end || raise_on_save_error
|
158
63
|
end
|
159
64
|
|
160
|
-
#
|
161
|
-
#
|
162
|
-
# class ComposedModel < ActiveRecordCompose::Model
|
163
|
-
# # ...
|
164
|
-
#
|
165
|
-
# before_save { puts 'before_save called!' }
|
166
|
-
# before_create { puts 'before_create called!' }
|
167
|
-
# before_update { puts 'before_update called!' }
|
168
|
-
# after_save { puts 'after_save called!' }
|
169
|
-
# after_create { puts 'after_create called!' }
|
170
|
-
# after_update { puts 'after_update called!' }
|
171
|
-
# end
|
172
|
-
#
|
173
|
-
# model = ComposedModel.new
|
174
|
-
#
|
175
|
-
# model.save
|
176
|
-
# # before_save called!
|
177
|
-
# # after_save called!
|
178
|
-
#
|
179
|
-
# model.update
|
180
|
-
# # before_save called!
|
181
|
-
# # before_update called!
|
182
|
-
# # after_update called!
|
183
|
-
# # after_save called!
|
65
|
+
# Assign attributes and save.
|
184
66
|
#
|
67
|
+
# @return [Boolean] returns true on success, false on failure.
|
185
68
|
def update(attributes = {})
|
186
69
|
assign_attributes(attributes)
|
187
|
-
|
188
|
-
|
189
|
-
with_transaction_returning_status do
|
190
|
-
if self.class.persisted_flag_callback_control
|
191
|
-
with_callbacks { save_models(bang: false) }
|
192
|
-
else
|
193
|
-
with_callbacks(context: :update) { save_models(bang: false) }
|
194
|
-
end
|
195
|
-
rescue ActiveRecord::RecordInvalid
|
196
|
-
false
|
197
|
-
end
|
70
|
+
save
|
198
71
|
end
|
199
72
|
|
200
73
|
# Behavior is same to `#update`, but raises an exception prematurely on failure.
|
201
74
|
#
|
202
75
|
def update!(attributes = {})
|
203
76
|
assign_attributes(attributes)
|
204
|
-
|
205
|
-
|
206
|
-
with_transaction_returning_status do
|
207
|
-
if self.class.persisted_flag_callback_control
|
208
|
-
with_callbacks { save_models(bang: true) }
|
209
|
-
else
|
210
|
-
with_callbacks(context: :update) { save_models(bang: true) }
|
211
|
-
end
|
212
|
-
end || raise_on_save_error
|
77
|
+
save!
|
213
78
|
end
|
214
79
|
|
80
|
+
# Returns true if model is persisted.
|
81
|
+
#
|
82
|
+
# By overriding this definition, you can control the callbacks that are triggered when a save is made.
|
83
|
+
# For example, returning false will trigger before_create, around_create and after_create,
|
84
|
+
# and returning true will trigger before_update, around_update and after_update.
|
85
|
+
#
|
86
|
+
# @return [Boolean] returns true if model is persisted.
|
87
|
+
def persisted? = super
|
88
|
+
|
215
89
|
private
|
216
90
|
|
217
91
|
def models = @__models ||= ActiveRecordCompose::ComposedCollection.new(self)
|
218
92
|
|
219
93
|
def validate_models
|
220
|
-
models.__wrapped_models.select { _1.invalid? }.each { errors.merge!(_1) }
|
94
|
+
models.__wrapped_models.lazy.select { _1.invalid? }.each { errors.merge!(_1) }
|
221
95
|
end
|
222
96
|
|
223
|
-
def with_callbacks(
|
224
|
-
|
225
|
-
|
226
|
-
end
|
97
|
+
def with_callbacks(&block) = run_callbacks(:save) { run_callbacks(callback_context, &block) }
|
98
|
+
|
99
|
+
def callback_context = persisted? ? :update : :create
|
227
100
|
|
228
101
|
def save_models(bang:)
|
229
102
|
models.__wrapped_models.all? { bang ? _1.save! : _1.save }
|
@@ -28,6 +28,6 @@ module ActiveRecordCompose
|
|
28
28
|
def id = nil
|
29
29
|
|
30
30
|
def trigger_transactional_callbacks? = true
|
31
|
-
def restore_transaction_record_state(_force_restore_state = false) = nil
|
31
|
+
def restore_transaction_record_state(_force_restore_state = false) = nil
|
32
32
|
end
|
33
33
|
end
|
@@ -24,7 +24,7 @@ module ActiveRecordCompose
|
|
24
24
|
def delegated_attributes: () -> Array[String]
|
25
25
|
|
26
26
|
module ClassMethods : Module
|
27
|
-
def delegate_attribute: (*untyped methods, to: untyped, ?allow_nil: untyped
|
27
|
+
def delegate_attribute: (*untyped methods, to: untyped, ?allow_nil: untyped?) -> untyped
|
28
28
|
def delegated_attributes: () -> Array[String]
|
29
29
|
def delegated_attributes=: (Array[String]) -> untyped
|
30
30
|
end
|
@@ -69,10 +69,7 @@ module ActiveRecordCompose
|
|
69
69
|
def self.after_commit: (*callback[instance], ?if: condition[instance], ?unless: condition[instance], **untyped) ?{ () [self: instance] -> void } -> void
|
70
70
|
def self.after_rollback: (*callback[instance], ?if: condition[instance], ?unless: condition[instance], **untyped) ?{ () [self: instance] -> void } -> void
|
71
71
|
|
72
|
-
def self.
|
73
|
-
def self.persisted_flag_callback_control=: (boolish) -> untyped
|
74
|
-
|
75
|
-
def self.delegate_attribute: (*untyped methods, to: untyped, ?allow_nil: untyped?, ?private: untyped?) -> untyped
|
72
|
+
def self.delegate_attribute: (*untyped methods, to: untyped, ?allow_nil: untyped?) -> untyped
|
76
73
|
def self.connection: -> ActiveRecord::ConnectionAdapters::AbstractAdapter
|
77
74
|
def self.lease_connection: -> ActiveRecord::ConnectionAdapters::AbstractAdapter
|
78
75
|
def self.with_connection: [T] () { () -> T } -> T
|
@@ -88,7 +85,8 @@ module ActiveRecordCompose
|
|
88
85
|
|
89
86
|
private
|
90
87
|
def models: -> ComposedCollection
|
91
|
-
def with_callbacks:
|
88
|
+
def with_callbacks: { () -> bool } -> bool
|
89
|
+
def callback_context: -> (:create | :update)
|
92
90
|
def validate_models: -> void
|
93
91
|
def save_models: (bang: bool) -> bool
|
94
92
|
def raise_validation_error: -> bot
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: active_record_compose
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.9.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- hamajyotan
|
8
8
|
bindir: bin
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-
|
10
|
+
date: 2025-03-16 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: activerecord
|
@@ -15,14 +15,14 @@ dependencies:
|
|
15
15
|
requirements:
|
16
16
|
- - ">="
|
17
17
|
- !ruby/object:Gem::Version
|
18
|
-
version: '
|
18
|
+
version: '7.0'
|
19
19
|
type: :runtime
|
20
20
|
prerelease: false
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
22
22
|
requirements:
|
23
23
|
- - ">="
|
24
24
|
- !ruby/object:Gem::Version
|
25
|
-
version: '
|
25
|
+
version: '7.0'
|
26
26
|
description: activemodel form object pattern. it embraces multiple AR models and provides
|
27
27
|
a transparent interface as if they were a single model.
|
28
28
|
email:
|
@@ -52,7 +52,7 @@ metadata:
|
|
52
52
|
homepage_uri: https://github.com/hamajyotan/active_record_compose
|
53
53
|
source_code_uri: https://github.com/hamajyotan/active_record_compose
|
54
54
|
changelog_uri: https://github.com/hamajyotan/active_record_compose/blob/main/CHANGELOG.md
|
55
|
-
documentation_uri: https://www.rubydoc.info/gems/active_record_compose/0.
|
55
|
+
documentation_uri: https://www.rubydoc.info/gems/active_record_compose/0.9.0
|
56
56
|
rubygems_mfa_required: 'true'
|
57
57
|
rdoc_options: []
|
58
58
|
require_paths:
|