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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ab0bb2d4ff8813ad8fb9a4830c2b1d3887aaa29702f11561c9c8624719a5082f
4
- data.tar.gz: a3fcfb870aaf5a0825a218556423cc1af16644b41ec22f0b4d656cb42cd33790
3
+ metadata.gz: dba7d13bf8f2dc9ef449ead00c1a7ae3fff83e216f07b5d11606659a3c1f4650
4
+ data.tar.gz: 883a9480ff3e6bd4c99246c12200a7b5a6e44103c0d1c967ce97c4536aa478b1
5
5
  SHA512:
6
- metadata.gz: d5a1ea42bd83a986437faac38b2543ee48aff7f2003afa6267a7adcd325db1bbb0506f4f1f25e2aaa13b733df7956ebd820627821f19101fc36dd4f046a51c43
7
- data.tar.gz: 4a9cc98fddbcf1068720387207469cb527b96e536a9f109a6174b5a439daa383f54a1625e916c731d01883cf360c6e01281858a238796b5cb64b92665c2405c3
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 `#save`, `#create` and `#update`](#callback-ordering-by-save-create-and-update)
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 `#save`, `#create` and `#update`.
287
+ ### Callback ordering by `#persisted?`
288
288
 
289
- The behavior of `(before|after|around)_create` and `(before|after|around)_update` hooks depends on
290
- the state of the `persisted_flag_callback_control` setting.
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, private: 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:, private:)
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
- if self.class.persisted_flag_callback_control
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
- def save!
94
- valid? || raise_validation_error
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
- # model = ComposedModel.new
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(context: :create) { save_models(bang: true) }
61
+ with_callbacks { save_models(bang: true) }
157
62
  end || raise_on_save_error
158
63
  end
159
64
 
160
- # Behavior is same to `#save`, but `before_update` and `after_update` hooks fires.
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
- return false if invalid?
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
- valid? || raise_validation_error
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(context: nil, &block)
224
- context ||= persisted? ? :update : :create
225
- run_callbacks(:save) { run_callbacks(context, &block) }
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 # rubocop:disable Style/OptionalBooleanParameter
31
+ def restore_transaction_record_state(_force_restore_state = false) = nil
32
32
  end
33
33
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveRecordCompose
4
- VERSION = '0.7.0'
4
+ VERSION = '0.9.0'
5
5
  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?, ?private: untyped?) -> 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.persisted_flag_callback_control: () -> boolish
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: (?context: (nil | :create | :update)) { () -> bool } -> bool
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.7.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-02-12 00:00:00.000000000 Z
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: '6.1'
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: '6.1'
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.7.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: