active_record_compose 0.9.0 → 0.10.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 +3 -0
- data/CHANGELOG.md +9 -0
- data/README.md +56 -0
- data/lib/active_record_compose/callbacks.rb +19 -0
- data/lib/active_record_compose/model.rb +28 -34
- data/lib/active_record_compose/transaction_support.rb +1 -1
- data/lib/active_record_compose/validations.rb +48 -0
- data/lib/active_record_compose/version.rb +1 -1
- data/lib/active_record_compose/wrapped_model.rb +6 -6
- data/sig/_internal/package_private.rbs +43 -4
- data/sig/active_record_compose.rbs +13 -17
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c78fcb982e3f8f36a5497483ae835eaba9724d72342efc0866ca596c25fa45a8
|
4
|
+
data.tar.gz: 515f08b1def287d06c1d8436a0c3cb466ef75016094e4778b0e9ba992c95cc30
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1976cef02cc6dbf8d9d543b339254136733c580f116f2ad38c1239fd7a18bdf9a2bde27d7a22be4f041c0eb2576f1bbc97d6e18cfd5859dae52fb2a90624ab69
|
7
|
+
data.tar.gz: 71130efd7792fa18dc33903d12bd6eb7ac17ac59bff95ceeedb2200ce128c3f34fdc7229866f8b5fbeea68f1136bb84dd35a17e2e0167b5990d2b26723022ba3
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,14 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [0.10.0] - 2025-04-07
|
4
|
+
|
5
|
+
- avoid twice validation. As a side effect, save must accept argument `#save(**options)`.
|
6
|
+
In line with this, the model to be put into models must be
|
7
|
+
at least responsive to `model.valid? && model.save(validate: false)`, not `model.save()` (no arguments).
|
8
|
+
- supports context as the first argument of `#valid?`, for example `model.valid(:custom_context)`.
|
9
|
+
At the same time, it accepts `options[:context]` in `#save(**options)`, such as `model.save(context: :custom_context)`.
|
10
|
+
However, this is a convenience support to unify the interface, not a positive one.
|
11
|
+
|
3
12
|
## [0.9.0] - 2025-03-16
|
4
13
|
|
5
14
|
- removed `persisted_flag_callback_control` support.
|
data/README.md
CHANGED
@@ -16,6 +16,7 @@ activemodel (activerecord) form object pattern. it embraces multiple AR models a
|
|
16
16
|
- [Advanced Usage](#advanced-usage)
|
17
17
|
- [`destroy` option](#destroy-option)
|
18
18
|
- [Callback ordering by `#persisted?`](#callback-ordering-by-persisted)
|
19
|
+
- [`#save` with custom context option](#save-with-custom-context-option)
|
19
20
|
- [Links](#links)
|
20
21
|
- [Development](#development)
|
21
22
|
- [Contributing](#contributing)
|
@@ -338,6 +339,61 @@ model.save # or `model.update` (the same callbacks will be triggered in all case
|
|
338
339
|
# after_save called!
|
339
340
|
```
|
340
341
|
|
342
|
+
### `#save` with custom context option
|
343
|
+
|
344
|
+
The interface remains consistent with standard ActiveModel and ActiveRecord models, so the :context option works with #save.
|
345
|
+
|
346
|
+
```ruby
|
347
|
+
composed_model.valid?(:custom_context)
|
348
|
+
|
349
|
+
composed_model.save(context: :custom_context)
|
350
|
+
```
|
351
|
+
|
352
|
+
However, this may not be ideal from a design perspective.
|
353
|
+
If your application requires complex context-specific validations, consider separating models by context.
|
354
|
+
|
355
|
+
```ruby
|
356
|
+
class Account < ActiveRecord::Base
|
357
|
+
validates :name, presence: true
|
358
|
+
validates :email, presence: true
|
359
|
+
validates :email, format: { with: /\.edu\z/ }, on: :education
|
360
|
+
end
|
361
|
+
|
362
|
+
class Registration < ActiveRecordCompose::Model
|
363
|
+
def initialize(attributes = {})
|
364
|
+
models.push(@account = Account.new)
|
365
|
+
super(attributes)
|
366
|
+
end
|
367
|
+
|
368
|
+
attribute :accept, :boolean
|
369
|
+
validates :accept, presence: true, on: :education
|
370
|
+
|
371
|
+
delegate_attribute :name, :email, to: :account
|
372
|
+
|
373
|
+
private
|
374
|
+
|
375
|
+
attr_reader :account
|
376
|
+
end
|
377
|
+
```
|
378
|
+
```ruby
|
379
|
+
r = Registration.new(name: 'foo', email: 'example@example.com', accept: false)
|
380
|
+
r.valid?
|
381
|
+
=> true
|
382
|
+
|
383
|
+
r.valid?(:education)
|
384
|
+
=> false
|
385
|
+
r.errors.map { [_1.attribute, _1.type] }
|
386
|
+
=> [[:email, :invalid], [:accept, :blank]]
|
387
|
+
|
388
|
+
r.email = 'example@example.edu'
|
389
|
+
r.accept = true
|
390
|
+
|
391
|
+
r.valid?(:education)
|
392
|
+
=> true
|
393
|
+
r.save(context: :education)
|
394
|
+
=> true
|
395
|
+
```
|
396
|
+
|
341
397
|
## Links
|
342
398
|
|
343
399
|
- [Smart way to update multiple models simultaneously in Rails](https://dev.to/hamajyotan/smart-way-to-update-multiple-models-simultaneously-in-rails-51b6)
|
@@ -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
|
@@ -1,8 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'active_record_compose/callbacks'
|
3
4
|
require 'active_record_compose/composed_collection'
|
4
5
|
require 'active_record_compose/delegate_attribute'
|
5
6
|
require 'active_record_compose/transaction_support'
|
7
|
+
require 'active_record_compose/validations'
|
6
8
|
|
7
9
|
module ActiveRecordCompose
|
8
10
|
using ComposedCollection::PackagePrivate
|
@@ -12,12 +14,10 @@ module ActiveRecordCompose
|
|
12
14
|
include ActiveModel::Validations::Callbacks
|
13
15
|
include ActiveModel::Attributes
|
14
16
|
|
17
|
+
include ActiveRecordCompose::Callbacks
|
15
18
|
include ActiveRecordCompose::DelegateAttribute
|
16
19
|
include ActiveRecordCompose::TransactionSupport
|
17
|
-
|
18
|
-
define_model_callbacks :save
|
19
|
-
define_model_callbacks :create
|
20
|
-
define_model_callbacks :update
|
20
|
+
prepend ActiveRecordCompose::Validations
|
21
21
|
|
22
22
|
validate :validate_models
|
23
23
|
|
@@ -30,16 +30,16 @@ module ActiveRecordCompose
|
|
30
30
|
#
|
31
31
|
# The save is performed within a single transaction.
|
32
32
|
#
|
33
|
-
#
|
34
|
-
#
|
35
|
-
#
|
33
|
+
# Only the `:validate` option takes effect as it is required internally.
|
34
|
+
# However, we do not recommend explicitly specifying `validate: false` to skip validation.
|
35
|
+
# Additionally, the `:context` option is not accepted.
|
36
|
+
# The need for such a value indicates that operations from multiple contexts are being processed.
|
37
|
+
# If the contexts differ, we recommend separating them into different model definitions.
|
36
38
|
#
|
37
39
|
# @return [Boolean] returns true on success, false on failure.
|
38
|
-
def save
|
39
|
-
return false if invalid?
|
40
|
-
|
40
|
+
def save(**options)
|
41
41
|
with_transaction_returning_status do
|
42
|
-
with_callbacks { save_models(bang: false) }
|
42
|
+
with_callbacks { save_models(**options, bang: false) }
|
43
43
|
rescue ActiveRecord::RecordInvalid
|
44
44
|
false
|
45
45
|
end
|
@@ -50,15 +50,15 @@ module ActiveRecordCompose
|
|
50
50
|
#
|
51
51
|
# Saving, like `#save`, is performed within a single transaction.
|
52
52
|
#
|
53
|
-
#
|
54
|
-
#
|
55
|
-
#
|
53
|
+
# Only the `:validate` option takes effect as it is required internally.
|
54
|
+
# However, we do not recommend explicitly specifying `validate: false` to skip validation.
|
55
|
+
# Additionally, the `:context` option is not accepted.
|
56
|
+
# The need for such a value indicates that operations from multiple contexts are being processed.
|
57
|
+
# If the contexts differ, we recommend separating them into different model definitions.
|
56
58
|
#
|
57
|
-
def save!
|
58
|
-
valid? || raise_validation_error
|
59
|
-
|
59
|
+
def save!(**options)
|
60
60
|
with_transaction_returning_status do
|
61
|
-
with_callbacks { save_models(bang: true) }
|
61
|
+
with_callbacks { save_models(**options, bang: true) }
|
62
62
|
end || raise_on_save_error
|
63
63
|
end
|
64
64
|
|
@@ -66,15 +66,19 @@ module ActiveRecordCompose
|
|
66
66
|
#
|
67
67
|
# @return [Boolean] returns true on success, false on failure.
|
68
68
|
def update(attributes = {})
|
69
|
-
|
70
|
-
|
69
|
+
with_transaction_returning_status do
|
70
|
+
assign_attributes(attributes)
|
71
|
+
save
|
72
|
+
end
|
71
73
|
end
|
72
74
|
|
73
75
|
# Behavior is same to `#update`, but raises an exception prematurely on failure.
|
74
76
|
#
|
75
77
|
def update!(attributes = {})
|
76
|
-
|
77
|
-
|
78
|
+
with_transaction_returning_status do
|
79
|
+
assign_attributes(attributes)
|
80
|
+
save!
|
81
|
+
end
|
78
82
|
end
|
79
83
|
|
80
84
|
# Returns true if model is persisted.
|
@@ -90,20 +94,10 @@ module ActiveRecordCompose
|
|
90
94
|
|
91
95
|
def models = @__models ||= ActiveRecordCompose::ComposedCollection.new(self)
|
92
96
|
|
93
|
-
def
|
94
|
-
models.__wrapped_models.
|
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 }
|
97
|
+
def save_models(bang:, **options)
|
98
|
+
models.__wrapped_models.all? { bang ? _1.save!(**options, validate: false) : _1.save(**options, validate: false) }
|
103
99
|
end
|
104
100
|
|
105
|
-
def raise_validation_error = raise ActiveRecord::RecordInvalid, self
|
106
|
-
|
107
101
|
def raise_on_save_error = raise ActiveRecord::RecordNotSaved.new(raise_on_save_error_message, self)
|
108
102
|
|
109
103
|
def raise_on_save_error_message = 'Failed to save the model.'
|
@@ -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
|
@@ -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? =
|
95
|
+
def invalid?(context = nil) = !valid?(context)
|
96
96
|
|
97
97
|
# @return [Boolean]
|
98
|
-
def valid? =
|
98
|
+
def valid?(context = nil) = destroy_context? || model.valid?(context)
|
99
99
|
|
100
100
|
# Returns true if equivalent.
|
101
101
|
#
|
@@ -1,4 +1,15 @@
|
|
1
1
|
module ActiveRecordCompose
|
2
|
+
module Callbacks
|
3
|
+
include ActiveModel::Model
|
4
|
+
include ActiveModel::Validations::Callbacks
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
extend ActiveModel::Callbacks
|
7
|
+
|
8
|
+
private
|
9
|
+
def with_callbacks: { () -> bool } -> bool
|
10
|
+
def callback_context: -> (:create | :update)
|
11
|
+
end
|
12
|
+
|
2
13
|
class ComposedCollection
|
3
14
|
def initialize: (Model) -> void
|
4
15
|
|
@@ -35,8 +46,13 @@ module ActiveRecordCompose
|
|
35
46
|
extend DelegateAttribute::ClassMethods
|
36
47
|
include TransactionSupport
|
37
48
|
extend TransactionSupport::ClassMethods
|
49
|
+
include ActiveRecordCompose::Callbacks
|
38
50
|
|
39
51
|
@__models: ComposedCollection
|
52
|
+
|
53
|
+
private
|
54
|
+
def validate_models: -> void
|
55
|
+
def override_validation_context: -> validation_context
|
40
56
|
end
|
41
57
|
|
42
58
|
module TransactionSupport
|
@@ -54,14 +70,37 @@ module ActiveRecordCompose
|
|
54
70
|
end
|
55
71
|
end
|
56
72
|
|
73
|
+
module Validations : Model
|
74
|
+
def save: (**untyped options) -> bool
|
75
|
+
def save!: (**untyped options) -> untyped
|
76
|
+
def valid?: (?validation_context context) -> bool
|
77
|
+
|
78
|
+
@context_for_override_validation: OverrideValidationContext
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
def perform_validations: (::Hash[untyped, untyped]) -> bool
|
83
|
+
def raise_validation_error: -> bot
|
84
|
+
def context_for_override_validation: -> OverrideValidationContext
|
85
|
+
def override_validation_context: -> validation_context
|
86
|
+
|
87
|
+
class OverrideValidationContext
|
88
|
+
@context: validation_context
|
89
|
+
|
90
|
+
attr_reader context: validation_context
|
91
|
+
|
92
|
+
def with_override: [T] (validation_context) { () -> T } -> T
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
57
96
|
class WrappedModel
|
58
97
|
def initialize: (ar_like, ?destroy: (bool | destroy_context_type), ?if: (nil | condition_type)) -> void
|
59
98
|
def destroy_context?: -> bool
|
60
99
|
def ignore?: -> bool
|
61
|
-
def save: -> bool
|
62
|
-
def save!: -> untyped
|
63
|
-
def invalid?: -> bool
|
64
|
-
def valid?: -> bool
|
100
|
+
def save: (**untyped options) -> bool
|
101
|
+
def save!: (**untyped options) -> untyped
|
102
|
+
def invalid?: (?validation_context context) -> bool
|
103
|
+
def valid?: (?validation_context context) -> bool
|
65
104
|
def is_a?: (untyped) -> bool
|
66
105
|
def ==: (untyped) -> bool
|
67
106
|
|
@@ -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
|
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.
|
4
|
+
version: 0.10.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-04-07 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: activerecord
|
@@ -37,10 +37,12 @@ files:
|
|
37
37
|
- LICENSE.txt
|
38
38
|
- README.md
|
39
39
|
- lib/active_record_compose.rb
|
40
|
+
- lib/active_record_compose/callbacks.rb
|
40
41
|
- lib/active_record_compose/composed_collection.rb
|
41
42
|
- lib/active_record_compose/delegate_attribute.rb
|
42
43
|
- lib/active_record_compose/model.rb
|
43
44
|
- lib/active_record_compose/transaction_support.rb
|
45
|
+
- lib/active_record_compose/validations.rb
|
44
46
|
- lib/active_record_compose/version.rb
|
45
47
|
- lib/active_record_compose/wrapped_model.rb
|
46
48
|
- sig/_internal/package_private.rbs
|
@@ -52,7 +54,7 @@ metadata:
|
|
52
54
|
homepage_uri: https://github.com/hamajyotan/active_record_compose
|
53
55
|
source_code_uri: https://github.com/hamajyotan/active_record_compose
|
54
56
|
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.
|
57
|
+
documentation_uri: https://www.rubydoc.info/gems/active_record_compose/0.10.0
|
56
58
|
rubygems_mfa_required: 'true'
|
57
59
|
rdoc_options: []
|
58
60
|
require_paths:
|