active_record_compose 0.9.0 → 0.11.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: dba7d13bf8f2dc9ef449ead00c1a7ae3fff83e216f07b5d11606659a3c1f4650
4
- data.tar.gz: 883a9480ff3e6bd4c99246c12200a7b5a6e44103c0d1c967ce97c4536aa478b1
3
+ metadata.gz: eae661ba621f2e95a089f01a029aa1918af952588e2a0204a8124e1c48aeecc3
4
+ data.tar.gz: '0208e91c70ea06235a2870901fcae2dfda328b8877b4ab9d776b14cd4e1de322'
5
5
  SHA512:
6
- metadata.gz: 905bf7969733f738e00726f0dc7ed1442bce1f598485a72a0478fd67069c556b5d8299fdc5485636b1194e095649df550ba9f3c01ffb42c107a8a69afcde85ae
7
- data.tar.gz: 22f44a63d515d31682f22641e112c3bf720c5b3ff29c8fb0a30ceb777b23847a74f053eb3591211c35bfe2e2f96de345523236d08b30365059fc3a7421437bf4
6
+ metadata.gz: 0e535cd4c0cc88700fbb50846db582848dd13b612274328a60382963ace26fafa06feb8ad9c724e95f4f48d257c2877ce2d74f828671f33639d533a341be2c37
7
+ data.tar.gz: 24ad34a3fd2b608c7f266f09417f9aeca97c494dcedd9b233a83eb5f2d6bb04e2f867acf1eb7260749886dbbbdce3f458885861277cd36dd34087ac7d7b33b8e
data/.rubocop.yml CHANGED
@@ -24,6 +24,9 @@ Style/NumericPredicate:
24
24
  Style/OptionalBooleanParameter:
25
25
  Enabled: false
26
26
 
27
+ Style/ParallelAssignment:
28
+ Enabled: false
29
+
27
30
  Style/StringLiterals:
28
31
  Enabled: true
29
32
 
data/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.11.0] - 2025-05-30
4
+
5
+ - `#attribute_names` now takes into account attributes declared in `.delegate_attribute`
6
+ - Implemented query methods with a `?` suffix for each attribute.
7
+ Their evaluation behavior is consistent with ActiveRecord.
8
+ For example:
9
+ - Defining `attribute :foo` allows calling `model.foo?`.
10
+ - Defining `delegate_attribute :bar, to: :other` allows calling `model.bar?`.
11
+
12
+ ## [0.10.0] - 2025-04-07
13
+
14
+ - avoid twice validation. As a side effect, save must accept argument `#save(**options)`.
15
+ In line with this, the model to be put into models must be
16
+ at least responsive to `model.valid? && model.save(validate: false)`, not `model.save()` (no arguments).
17
+ - supports context as the first argument of `#valid?`, for example `model.valid(:custom_context)`.
18
+ At the same time, it accepts `options[:context]` in `#save(**options)`, such as `model.save(context: :custom_context)`.
19
+ However, this is a convenience support to unify the interface, not a positive one.
20
+
3
21
  ## [0.9.0] - 2025-03-16
4
22
 
5
23
  - removed `persisted_flag_callback_control` support.
data/README.md CHANGED
@@ -2,7 +2,11 @@
2
2
 
3
3
  activemodel (activerecord) form object pattern. it embraces multiple AR models and provides a transparent interface as if they were a single model.
4
4
 
5
+ [![Gem Version](https://badge.fury.io/rb/active_record_compose.svg)](https://badge.fury.io/rb/active_record_compose)
5
6
  ![CI](https://github.com/hamajyotan/active_record_compose/workflows/CI/badge.svg)
7
+ [![DeepWiki](https://img.shields.io/badge/DeepWiki-hamajyotan%2Factive__record__compose-blue.svg?logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAyCAYAAAAnWDnqAAAAAXNSR0IArs4c6QAAA05JREFUaEPtmUtyEzEQhtWTQyQLHNak2AB7ZnyXZMEjXMGeK/AIi+QuHrMnbChYY7MIh8g01fJoopFb0uhhEqqcbWTp06/uv1saEDv4O3n3dV60RfP947Mm9/SQc0ICFQgzfc4CYZoTPAswgSJCCUJUnAAoRHOAUOcATwbmVLWdGoH//PB8mnKqScAhsD0kYP3j/Yt5LPQe2KvcXmGvRHcDnpxfL2zOYJ1mFwrryWTz0advv1Ut4CJgf5uhDuDj5eUcAUoahrdY/56ebRWeraTjMt/00Sh3UDtjgHtQNHwcRGOC98BJEAEymycmYcWwOprTgcB6VZ5JK5TAJ+fXGLBm3FDAmn6oPPjR4rKCAoJCal2eAiQp2x0vxTPB3ALO2CRkwmDy5WohzBDwSEFKRwPbknEggCPB/imwrycgxX2NzoMCHhPkDwqYMr9tRcP5qNrMZHkVnOjRMWwLCcr8ohBVb1OMjxLwGCvjTikrsBOiA6fNyCrm8V1rP93iVPpwaE+gO0SsWmPiXB+jikdf6SizrT5qKasx5j8ABbHpFTx+vFXp9EnYQmLx02h1QTTrl6eDqxLnGjporxl3NL3agEvXdT0WmEost648sQOYAeJS9Q7bfUVoMGnjo4AZdUMQku50McDcMWcBPvr0SzbTAFDfvJqwLzgxwATnCgnp4wDl6Aa+Ax283gghmj+vj7feE2KBBRMW3FzOpLOADl0Isb5587h/U4gGvkt5v60Z1VLG8BhYjbzRwyQZemwAd6cCR5/XFWLYZRIMpX39AR0tjaGGiGzLVyhse5C9RKC6ai42ppWPKiBagOvaYk8lO7DajerabOZP46Lby5wKjw1HCRx7p9sVMOWGzb/vA1hwiWc6jm3MvQDTogQkiqIhJV0nBQBTU+3okKCFDy9WwferkHjtxib7t3xIUQtHxnIwtx4mpg26/HfwVNVDb4oI9RHmx5WGelRVlrtiw43zboCLaxv46AZeB3IlTkwouebTr1y2NjSpHz68WNFjHvupy3q8TFn3Hos2IAk4Ju5dCo8B3wP7VPr/FGaKiG+T+v+TQqIrOqMTL1VdWV1DdmcbO8KXBz6esmYWYKPwDL5b5FA1a0hwapHiom0r/cKaoqr+27/XcrS5UwSMbQAAAABJRU5ErkJggg==)](https://deepwiki.com/hamajyotan/active_record_compose)
8
+
9
+
6
10
 
7
11
  ## Table of Contents
8
12
 
@@ -16,6 +20,8 @@ activemodel (activerecord) form object pattern. it embraces multiple AR models a
16
20
  - [Advanced Usage](#advanced-usage)
17
21
  - [`destroy` option](#destroy-option)
18
22
  - [Callback ordering by `#persisted?`](#callback-ordering-by-persisted)
23
+ - [`#save` with custom context option](#save-with-custom-context-option)
24
+ - [Sample application as an example](#sample-application-as-an-example)
19
25
  - [Links](#links)
20
26
  - [Development](#development)
21
27
  - [Contributing](#contributing)
@@ -338,10 +344,70 @@ model.save # or `model.update` (the same callbacks will be triggered in all case
338
344
  # after_save called!
339
345
  ```
340
346
 
347
+ ### `#save` with custom context option
348
+
349
+ The interface remains consistent with standard ActiveModel and ActiveRecord models, so the :context option works with #save.
350
+
351
+ ```ruby
352
+ composed_model.valid?(:custom_context)
353
+
354
+ composed_model.save(context: :custom_context)
355
+ ```
356
+
357
+ However, this may not be ideal from a design perspective.
358
+ If your application requires complex context-specific validations, consider separating models by context.
359
+
360
+ ```ruby
361
+ class Account < ActiveRecord::Base
362
+ validates :name, presence: true
363
+ validates :email, presence: true
364
+ validates :email, format: { with: /\.edu\z/ }, on: :education
365
+ end
366
+
367
+ class Registration < ActiveRecordCompose::Model
368
+ def initialize(attributes = {})
369
+ models.push(@account = Account.new)
370
+ super(attributes)
371
+ end
372
+
373
+ attribute :accept, :boolean
374
+ validates :accept, presence: true, on: :education
375
+
376
+ delegate_attribute :name, :email, to: :account
377
+
378
+ private
379
+
380
+ attr_reader :account
381
+ end
382
+ ```
383
+ ```ruby
384
+ r = Registration.new(name: 'foo', email: 'example@example.com', accept: false)
385
+ r.valid?
386
+ => true
387
+
388
+ r.valid?(:education)
389
+ => false
390
+ r.errors.map { [_1.attribute, _1.type] }
391
+ => [[:email, :invalid], [:accept, :blank]]
392
+
393
+ r.email = 'example@example.edu'
394
+ r.accept = true
395
+
396
+ r.valid?(:education)
397
+ => true
398
+ r.save(context: :education)
399
+ => true
400
+ ```
401
+
402
+ ## Sample application as an example
403
+
404
+ With Github Codespaces, it can also be run directly in the browser. Naturally, a local environment is also possible.
405
+
406
+ - https://github.com/hamajyotan/active_record_compose-example
407
+
341
408
  ## Links
342
409
 
343
410
  - [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)
345
411
 
346
412
  ## Development
347
413
 
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecordCompose
4
+ # = Attribute \Querying
5
+ #
6
+ # This provides predicate methods based on the attributes.
7
+ #
8
+ # class AccountRegistration < ActiveRecordCompose::Model
9
+ # def initialize
10
+ # @account = Account.new
11
+ # super()
12
+ # models << account
13
+ # end
14
+ #
15
+ # attribute :original_attr
16
+ # delegate_attribute :name, :email, to: :account
17
+ #
18
+ # private
19
+ #
20
+ # attr_reader :account
21
+ # end
22
+ #
23
+ # model = AccountRegistration.new
24
+ #
25
+ # model.name #=> nil
26
+ # model.name? #=> false
27
+ # model.name = 'Alice'
28
+ # model.name? #=> true
29
+ #
30
+ # model.original_attr = "Bob"
31
+ # model.original_attr? #=> true
32
+ # model.original_attr = ""
33
+ # model.original_attr? #=> false
34
+ #
35
+ # # If the value is numeric, it returns the result of checking whether it is zero or not.
36
+ # # This behavior is consistent with `ActiveRecord::AttributeMethods::Query`.
37
+ # model.original_attr = 123
38
+ # model.original_attr? #=> true
39
+ # model.original_attr = 0
40
+ # model.original_attr? #=> false
41
+ #
42
+ module AttributeQuerying
43
+ extend ActiveSupport::Concern
44
+
45
+ included do
46
+ attribute_method_suffix '?', parameters: false
47
+ end
48
+
49
+ private
50
+
51
+ def attribute?(attr_name)
52
+ value = public_send(attr_name)
53
+
54
+ case value
55
+ when true then true
56
+ when false, nil then false
57
+ else
58
+ if value.respond_to?(:zero?)
59
+ !value.zero?
60
+ else
61
+ value.present?
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecordCompose
4
+ module Callbacks
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ define_model_callbacks :save
9
+ define_model_callbacks :create
10
+ define_model_callbacks :update
11
+ end
12
+
13
+ private
14
+
15
+ def with_callbacks(&block) = run_callbacks(:save) { run_callbacks(callback_context, &block) }
16
+
17
+ def callback_context = persisted? ? :update : :create
18
+ end
19
+ end
@@ -58,6 +58,12 @@ module ActiveRecordCompose
58
58
  end
59
59
  end
60
60
 
61
+ # Returns a array of attribute name.
62
+ # Attributes declared with `delegate_attribute` are also merged.
63
+ #
64
+ # @return [Array<String>] array of attribute name.
65
+ def attribute_names = super + delegated_attributes
66
+
61
67
  # Returns a hash with the attribute name as key and the attribute value as value.
62
68
  # Attributes declared with `delegate_attribute` are also merged.
63
69
  #
@@ -1,8 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'active_record_compose/callbacks'
4
+ require 'active_record_compose/attribute_querying'
3
5
  require 'active_record_compose/composed_collection'
4
6
  require 'active_record_compose/delegate_attribute'
5
7
  require 'active_record_compose/transaction_support'
8
+ require 'active_record_compose/validations'
6
9
 
7
10
  module ActiveRecordCompose
8
11
  using ComposedCollection::PackagePrivate
@@ -12,12 +15,11 @@ module ActiveRecordCompose
12
15
  include ActiveModel::Validations::Callbacks
13
16
  include ActiveModel::Attributes
14
17
 
18
+ include ActiveRecordCompose::AttributeQuerying
19
+ include ActiveRecordCompose::Callbacks
15
20
  include ActiveRecordCompose::DelegateAttribute
16
21
  include ActiveRecordCompose::TransactionSupport
17
-
18
- define_model_callbacks :save
19
- define_model_callbacks :create
20
- define_model_callbacks :update
22
+ prepend ActiveRecordCompose::Validations
21
23
 
22
24
  validate :validate_models
23
25
 
@@ -30,16 +32,16 @@ module ActiveRecordCompose
30
32
  #
31
33
  # The save is performed within a single transaction.
32
34
  #
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.
35
+ # Only the `:validate` option takes effect as it is required internally.
36
+ # However, we do not recommend explicitly specifying `validate: false` to skip validation.
37
+ # Additionally, the `:context` option is not accepted.
38
+ # The need for such a value indicates that operations from multiple contexts are being processed.
39
+ # If the contexts differ, we recommend separating them into different model definitions.
36
40
  #
37
41
  # @return [Boolean] returns true on success, false on failure.
38
- def save
39
- return false if invalid?
40
-
42
+ def save(**options)
41
43
  with_transaction_returning_status do
42
- with_callbacks { save_models(bang: false) }
44
+ with_callbacks { save_models(**options, bang: false) }
43
45
  rescue ActiveRecord::RecordInvalid
44
46
  false
45
47
  end
@@ -50,15 +52,15 @@ module ActiveRecordCompose
50
52
  #
51
53
  # Saving, like `#save`, is performed within a single transaction.
52
54
  #
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.
55
+ # Only the `:validate` option takes effect as it is required internally.
56
+ # However, we do not recommend explicitly specifying `validate: false` to skip validation.
57
+ # Additionally, the `:context` option is not accepted.
58
+ # The need for such a value indicates that operations from multiple contexts are being processed.
59
+ # If the contexts differ, we recommend separating them into different model definitions.
56
60
  #
57
- def save!
58
- valid? || raise_validation_error
59
-
61
+ def save!(**options)
60
62
  with_transaction_returning_status do
61
- with_callbacks { save_models(bang: true) }
63
+ with_callbacks { save_models(**options, bang: true) }
62
64
  end || raise_on_save_error
63
65
  end
64
66
 
@@ -66,15 +68,19 @@ module ActiveRecordCompose
66
68
  #
67
69
  # @return [Boolean] returns true on success, false on failure.
68
70
  def update(attributes = {})
69
- assign_attributes(attributes)
70
- save
71
+ with_transaction_returning_status do
72
+ assign_attributes(attributes)
73
+ save
74
+ end
71
75
  end
72
76
 
73
77
  # Behavior is same to `#update`, but raises an exception prematurely on failure.
74
78
  #
75
79
  def update!(attributes = {})
76
- assign_attributes(attributes)
77
- save!
80
+ with_transaction_returning_status do
81
+ assign_attributes(attributes)
82
+ save!
83
+ end
78
84
  end
79
85
 
80
86
  # Returns true if model is persisted.
@@ -90,20 +96,10 @@ module ActiveRecordCompose
90
96
 
91
97
  def models = @__models ||= ActiveRecordCompose::ComposedCollection.new(self)
92
98
 
93
- def validate_models
94
- models.__wrapped_models.lazy.select { _1.invalid? }.each { errors.merge!(_1) }
95
- end
96
-
97
- def with_callbacks(&block) = run_callbacks(:save) { run_callbacks(callback_context, &block) }
98
-
99
- def callback_context = persisted? ? :update : :create
100
-
101
- def save_models(bang:)
102
- models.__wrapped_models.all? { bang ? _1.save! : _1.save }
99
+ def save_models(bang:, **options)
100
+ models.__wrapped_models.all? { bang ? _1.save!(**options, validate: false) : _1.save(**options, validate: false) }
103
101
  end
104
102
 
105
- def raise_validation_error = raise ActiveRecord::RecordInvalid, self
106
-
107
103
  def raise_on_save_error = raise ActiveRecord::RecordNotSaved.new(raise_on_save_error_message, self)
108
104
 
109
105
  def raise_on_save_error_message = 'Failed to save the model.'
@@ -18,7 +18,7 @@ module ActiveRecordCompose
18
18
 
19
19
  def with_connection(&) = ar_class.with_connection(&) # steep:ignore
20
20
 
21
- def composite_primary_key? = false
21
+ def composite_primary_key? = false # steep:ignore
22
22
 
23
23
  private
24
24
 
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecordCompose
4
+ using ComposedCollection::PackagePrivate
5
+
6
+ module Validations
7
+ def save(**options)
8
+ perform_validations(options) ? super : false
9
+ end
10
+
11
+ def save!(**options)
12
+ perform_validations(options) ? super : raise_validation_error
13
+ end
14
+
15
+ def valid?(context = nil) = context_for_override_validation.with_override(context) { super }
16
+
17
+ private
18
+
19
+ def validate_models
20
+ context = override_validation_context
21
+ models.__wrapped_models.lazy.select { _1.invalid?(context) }.each { errors.merge!(_1) }
22
+ end
23
+
24
+ def perform_validations(options)
25
+ options[:validate] == false || valid?(options[:context])
26
+ end
27
+
28
+ def raise_validation_error = raise ActiveRecord::RecordInvalid, self
29
+
30
+ def context_for_override_validation
31
+ @context_for_override_validation ||= OverrideValidationContext.new
32
+ end
33
+
34
+ def override_validation_context = context_for_override_validation.context
35
+
36
+ class OverrideValidationContext
37
+ attr_reader :context
38
+
39
+ def with_override(context)
40
+ @context, original = context, @context
41
+ yield
42
+ ensure
43
+ @context = original # steep:ignore
44
+ end
45
+ end
46
+ private_constant :OverrideValidationContext
47
+ end
48
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveRecordCompose
4
- VERSION = '0.9.0'
4
+ VERSION = '0.11.0'
5
5
  end
@@ -60,7 +60,7 @@ module ActiveRecordCompose
60
60
  # Whether save or destroy is executed depends on the value of `#destroy_context?`.
61
61
  #
62
62
  # @return [Boolean] returns true on success, false on failure.
63
- def save
63
+ def save(**options)
64
64
  # While errors caused by the type check are avoided,
65
65
  # it is important to note that an error can still occur
66
66
  # if `#destroy_context?` returns true but ar_like does not implement `#destroy`.
@@ -70,14 +70,14 @@ module ActiveRecordCompose
70
70
  m.destroy
71
71
  else
72
72
  # @type var m: ActiveRecordCompose::_ARLike
73
- m.save
73
+ m.save(**options)
74
74
  end
75
75
  end
76
76
 
77
77
  # Execute save or destroy. Unlike #save, an exception is raises on failure.
78
78
  # Whether save or destroy is executed depends on the value of `#destroy_context?`.
79
79
  #
80
- def save!
80
+ def save!(**options)
81
81
  # While errors caused by the type check are avoided,
82
82
  # it is important to note that an error can still occur
83
83
  # if `#destroy_context?` returns true but ar_like does not implement `#destroy`.
@@ -87,15 +87,15 @@ module ActiveRecordCompose
87
87
  m.destroy!
88
88
  else
89
89
  # @type var model: ActiveRecordCompose::_ARLike
90
- m.save!
90
+ m.save!(**options)
91
91
  end
92
92
  end
93
93
 
94
94
  # @return [Boolean]
95
- def invalid? = destroy_context? ? false : model.invalid?
95
+ def invalid?(context = nil) = !valid?(context)
96
96
 
97
97
  # @return [Boolean]
98
- def valid? = !invalid?
98
+ def valid?(context = nil) = destroy_context? || model.valid?(context)
99
99
 
100
100
  # Returns true if equivalent.
101
101
  #
@@ -1,4 +1,23 @@
1
1
  module ActiveRecordCompose
2
+ module AttributeQuerying
3
+ extend ActiveSupport::Concern
4
+ extend ActiveModel::AttributeMethods::ClassMethods
5
+
6
+ private
7
+ def attribute?: (attribute_name) -> untyped
8
+ end
9
+
10
+ module Callbacks
11
+ include ActiveModel::Model
12
+ include ActiveModel::Validations::Callbacks
13
+ extend ActiveSupport::Concern
14
+ extend ActiveModel::Callbacks
15
+
16
+ private
17
+ def with_callbacks: { () -> bool } -> bool
18
+ def callback_context: -> (:create | :update)
19
+ end
20
+
2
21
  class ComposedCollection
3
22
  def initialize: (Model) -> void
4
23
 
@@ -20,7 +39,6 @@ module ActiveRecordCompose
20
39
  module DelegateAttribute : ActiveModel::Attributes
21
40
  extend ActiveSupport::Concern
22
41
 
23
- def attributes: -> Hash[String, untyped]
24
42
  def delegated_attributes: () -> Array[String]
25
43
 
26
44
  module ClassMethods : Module
@@ -35,8 +53,13 @@ module ActiveRecordCompose
35
53
  extend DelegateAttribute::ClassMethods
36
54
  include TransactionSupport
37
55
  extend TransactionSupport::ClassMethods
56
+ include ActiveRecordCompose::Callbacks
38
57
 
39
58
  @__models: ComposedCollection
59
+
60
+ private
61
+ def validate_models: -> void
62
+ def override_validation_context: -> validation_context
40
63
  end
41
64
 
42
65
  module TransactionSupport
@@ -54,14 +77,37 @@ module ActiveRecordCompose
54
77
  end
55
78
  end
56
79
 
80
+ module Validations : Model
81
+ def save: (**untyped options) -> bool
82
+ def save!: (**untyped options) -> untyped
83
+ def valid?: (?validation_context context) -> bool
84
+
85
+ @context_for_override_validation: OverrideValidationContext
86
+
87
+ private
88
+
89
+ def perform_validations: (::Hash[untyped, untyped]) -> bool
90
+ def raise_validation_error: -> bot
91
+ def context_for_override_validation: -> OverrideValidationContext
92
+ def override_validation_context: -> validation_context
93
+
94
+ class OverrideValidationContext
95
+ @context: validation_context
96
+
97
+ attr_reader context: validation_context
98
+
99
+ def with_override: [T] (validation_context) { () -> T } -> T
100
+ end
101
+ end
102
+
57
103
  class WrappedModel
58
104
  def initialize: (ar_like, ?destroy: (bool | destroy_context_type), ?if: (nil | condition_type)) -> void
59
105
  def destroy_context?: -> bool
60
106
  def ignore?: -> bool
61
- def save: -> bool
62
- def save!: -> untyped
63
- def invalid?: -> bool
64
- def valid?: -> bool
107
+ def save: (**untyped options) -> bool
108
+ def save!: (**untyped options) -> untyped
109
+ def invalid?: (?validation_context context) -> bool
110
+ def valid?: (?validation_context context) -> bool
65
111
  def is_a?: (untyped) -> bool
66
112
  def ==: (untyped) -> bool
67
113
 
@@ -5,27 +5,29 @@ module ActiveRecordCompose
5
5
  VERSION: String
6
6
 
7
7
  interface _ARLike
8
- def save: -> bool
9
- def save!: -> untyped
10
- def invalid?: -> bool
11
- def valid?: -> bool
8
+ def save: (**untyped options) -> bool
9
+ def save!: (**untyped options) -> untyped
10
+ def invalid?: (?validation_context context) -> bool
11
+ def valid?: (?validation_context context) -> bool
12
12
  def errors: -> untyped
13
13
  def is_a?: (untyped) -> bool
14
14
  def ==: (untyped) -> bool
15
15
  end
16
16
  interface _ARLikeWithDestroy
17
- def save: -> bool
18
- def save!: -> untyped
17
+ def save: (**untyped options) -> bool
18
+ def save!: (**untyped options) -> untyped
19
19
  def destroy: -> bool
20
20
  def destroy!: -> untyped
21
- def invalid?: -> bool
22
- def valid?: -> bool
21
+ def invalid?: (?validation_context context) -> bool
22
+ def valid?: (?validation_context context) -> bool
23
23
  def errors: -> untyped
24
24
  def is_a?: (untyped) -> bool
25
25
  def ==: (untyped) -> bool
26
26
  end
27
27
  type ar_like = (_ARLike | _ARLikeWithDestroy)
28
28
 
29
+ type validation_context = nil | Symbol | Array[Symbol]
30
+
29
31
  type condition[T] = Symbol | ^(T) [self: T] -> boolish
30
32
  type callback[T] = Symbol | ^(T) [self: T] -> void
31
33
  type around_callback[T] = Symbol | ^(T, Proc) [self: T] -> void
@@ -75,21 +77,15 @@ module ActiveRecordCompose
75
77
  def self.with_connection: [T] () { () -> T } -> T
76
78
 
77
79
  def initialize: (?Hash[attribute_name, untyped]) -> void
78
- def save: -> bool
79
- def save!: -> untyped
80
- def create: (?Hash[attribute_name, untyped]) -> bool
81
- def create!: (?Hash[attribute_name, untyped]) -> untyped
80
+ def save: (**untyped options) -> bool
81
+ def save!: (**untyped options) -> untyped
82
82
  def update: (?Hash[attribute_name, untyped]) -> bool
83
83
  def update!: (?Hash[attribute_name, untyped]) -> untyped
84
84
  def id: -> untyped
85
85
 
86
86
  private
87
87
  def models: -> ComposedCollection
88
- def with_callbacks: { () -> bool } -> bool
89
- def callback_context: -> (:create | :update)
90
- def validate_models: -> void
91
- def save_models: (bang: bool) -> bool
92
- def raise_validation_error: -> bot
88
+ def save_models: (bang: bool, **untyped options) -> bool
93
89
  def raise_on_save_error: -> bot
94
90
  def raise_on_save_error_message: -> String
95
91
  end
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.9.0
4
+ version: 0.11.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - hamajyotan
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-03-16 00:00:00.000000000 Z
10
+ date: 2025-05-29 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: activerecord
@@ -37,10 +37,13 @@ files:
37
37
  - LICENSE.txt
38
38
  - README.md
39
39
  - lib/active_record_compose.rb
40
+ - lib/active_record_compose/attribute_querying.rb
41
+ - lib/active_record_compose/callbacks.rb
40
42
  - lib/active_record_compose/composed_collection.rb
41
43
  - lib/active_record_compose/delegate_attribute.rb
42
44
  - lib/active_record_compose/model.rb
43
45
  - lib/active_record_compose/transaction_support.rb
46
+ - lib/active_record_compose/validations.rb
44
47
  - lib/active_record_compose/version.rb
45
48
  - lib/active_record_compose/wrapped_model.rb
46
49
  - sig/_internal/package_private.rbs
@@ -52,7 +55,7 @@ metadata:
52
55
  homepage_uri: https://github.com/hamajyotan/active_record_compose
53
56
  source_code_uri: https://github.com/hamajyotan/active_record_compose
54
57
  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.9.0
58
+ documentation_uri: https://www.rubydoc.info/gems/active_record_compose/0.11.0
56
59
  rubygems_mfa_required: 'true'
57
60
  rdoc_options: []
58
61
  require_paths: