active_record_compose 0.3.4 → 0.4.1
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 +16 -0
- data/README.md +25 -2
- data/lib/active_record_compose/inner_model.rb +69 -46
- data/lib/active_record_compose/inner_model_collection.rb +38 -10
- data/lib/active_record_compose/model.rb +1 -1
- data/lib/active_record_compose/version.rb +1 -1
- data/lib/active_record_compose.rb +0 -2
- data/sig/active_record_compose.rbs +10 -9
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5fe788a613275af9e9784ba51b84af314792e0a5761e0c8d5e985602dfe7d586
|
4
|
+
data.tar.gz: 0621ff28f43377240efc94a97b12ffde861e71142f6ba2ac349d4d9a165d6401
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d67bf25365ccad6f049a17cbc311cbf19c48ee4719e51afefc149fb0227989ec8f1c55684ab26328839ae7c79897fed27ac6b5c7db90c42bf0b81e3bc4437c97
|
7
|
+
data.tar.gz: ab71a4b2bd61619136f8685b0b1346d4ad111fec0717a1527ce714985f5eaee91f0c91dd9713ba835a2633acbdc2a197f2ae2c77767d2028b13eb4bcb5f21887
|
data/.rubocop.yml
CHANGED
@@ -12,6 +12,9 @@ Style/CommentedKeyword:
|
|
12
12
|
Style/Documentation:
|
13
13
|
Enabled: false
|
14
14
|
|
15
|
+
Style/DoubleNegation:
|
16
|
+
Enabled: false
|
17
|
+
|
15
18
|
Style/NumericPredicate:
|
16
19
|
Enabled: false
|
17
20
|
|
@@ -39,6 +42,9 @@ Layout/LeadingCommentSpace:
|
|
39
42
|
Layout/LineLength:
|
40
43
|
Max: 120
|
41
44
|
|
45
|
+
Metrics/AbcSize:
|
46
|
+
Enabled: false
|
47
|
+
|
42
48
|
Metrics/BlockLength:
|
43
49
|
Enabled: false
|
44
50
|
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,21 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [0.4.1] - 2024-09-20
|
4
|
+
|
5
|
+
- Omitted optional argument for `InnerModelCollection#destroy`.
|
6
|
+
`InnerModel` equivalence is always performed based on the instance of the inner `model`.
|
7
|
+
Since there are no use cases that depend on the original behavior.
|
8
|
+
|
9
|
+
## [0.4.0] - 2024-09-15
|
10
|
+
|
11
|
+
- support `destrpy` option. and deprecated `context` option.
|
12
|
+
`:context` will be removed in 0.5.0. Use `:destroy` option instead.
|
13
|
+
for example,
|
14
|
+
- `models.push(model, context: :destroy)` is replaced by `models.push(model, destroy: true)`
|
15
|
+
- `models.push(model, context: -> { foo? ? :destroy : :save })` is replaced by `models.push(model, destroy: -> { foo? })`
|
16
|
+
- `models.push(model, context: ->(m) { m.bar? ? :destroy : :save })` is replaced by `models.push(model, destroy: ->(m) { m.bar? })`
|
17
|
+
- `destroy` option can now be specified with a `Symbol` representing the method name.
|
18
|
+
|
3
19
|
## [0.3.4] - 2024-09-01
|
4
20
|
|
5
21
|
- ci: removed sqlite3 version specifing for new AR.
|
data/README.md
CHANGED
@@ -156,7 +156,7 @@ Account.count #=> 1
|
|
156
156
|
Profile.count #=> 1
|
157
157
|
```
|
158
158
|
|
159
|
-
By adding to the `models` array while specifying `
|
159
|
+
By adding to the `models` array while specifying `destroy: true`, you can perform a delete instead of a save on the model at `#save` time.
|
160
160
|
|
161
161
|
```ruby
|
162
162
|
class AccountResignation < ActiveRecordCompose::Model
|
@@ -164,7 +164,7 @@ class AccountResignation < ActiveRecordCompose::Model
|
|
164
164
|
@account = account
|
165
165
|
@profile = account.profile # Suppose that Account has_one Profile.
|
166
166
|
models.push(account)
|
167
|
-
models.push(profile,
|
167
|
+
models.push(profile, destroy: true)
|
168
168
|
end
|
169
169
|
|
170
170
|
attr_reader :account, :profile
|
@@ -193,6 +193,29 @@ account.resigned_at.present? #=> Tue, 02 Jan 2024 22:58:01.991008870 JST +09:00
|
|
193
193
|
account.profile.blank? #=> true
|
194
194
|
```
|
195
195
|
|
196
|
+
Conditional destroy (or save) can be written like this.
|
197
|
+
|
198
|
+
```ruby
|
199
|
+
class AccountRegistration < ActiveRecordCompose::Model
|
200
|
+
def initialize(account, attributes = {})
|
201
|
+
@account = account
|
202
|
+
@profile = account.profile || account.build_profile
|
203
|
+
super(attributes)
|
204
|
+
models.push(account)
|
205
|
+
models.push(profile, destroy: :all_blank?) # destroy if all blank, otherwise save.
|
206
|
+
end
|
207
|
+
|
208
|
+
delegate_attribute :name, :email, to: :account
|
209
|
+
delegate_attribute :firstname, :lastname, :age, to: :profile
|
210
|
+
|
211
|
+
private
|
212
|
+
|
213
|
+
attr_reader :account, :profile
|
214
|
+
|
215
|
+
def all_blank? = firstname.blank && lastname.blank? && age.blank?
|
216
|
+
end
|
217
|
+
```
|
218
|
+
|
196
219
|
### `delegate_attribute`
|
197
220
|
|
198
221
|
It provides a macro description that expresses access to the attributes of the AR model through delegation.
|
@@ -5,67 +5,82 @@ require 'active_support/core_ext/object'
|
|
5
5
|
module ActiveRecordCompose
|
6
6
|
class InnerModel
|
7
7
|
# @param model [Object] the model instance.
|
8
|
-
# @param
|
9
|
-
# @param
|
10
|
-
def initialize(model, context:
|
8
|
+
# @param destroy [Boolean] given true, destroy model.
|
9
|
+
# @param destroy [Proc] when proc returning true, destroy model.
|
10
|
+
def initialize(model, destroy: false, context: nil)
|
11
11
|
@model = model
|
12
|
-
@
|
13
|
-
|
12
|
+
@destroy_context_type =
|
13
|
+
if context
|
14
|
+
c = context
|
14
15
|
|
15
|
-
|
16
|
+
if c.is_a?(Proc)
|
17
|
+
# @type var c: ((^() -> (context)) | (^(_ARLike) -> (context)))
|
18
|
+
if c.arity == 0
|
19
|
+
deprecator.warn(
|
20
|
+
'`:context` will be removed in 0.5.0. Use `:destroy` option instead. ' \
|
21
|
+
'for example, `models.push(model, context: -> { foo? ? :destroy : :save })` ' \
|
22
|
+
'is replaced by `models.push(model, destroy: -> { foo? })`.',
|
23
|
+
)
|
24
|
+
|
25
|
+
# @type var c: ^() -> (context)
|
26
|
+
-> { c.call == :destroy }
|
27
|
+
else
|
28
|
+
deprecator.warn(
|
29
|
+
'`:context` will be removed in 0.5.0. Use `:destroy` option instead. ' \
|
30
|
+
'for example, `models.push(model, context: ->(m) { m.bar? ? :destroy : :save })` ' \
|
31
|
+
'is replaced by `models.push(model, destroy: ->(m) { m.bar? })`.',
|
32
|
+
)
|
16
33
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
34
|
+
# @type var c: ^(_ARLike) -> (context)
|
35
|
+
->(model) { c.call(model) == :destroy }
|
36
|
+
end
|
37
|
+
elsif %i[save destroy].include?(c)
|
38
|
+
deprecator.warn(
|
39
|
+
'`:context` will be removed in 0.5.0. Use `:destroy` option instead. ' \
|
40
|
+
"for example, `models.push(model, context: #{c.inspect})` " \
|
41
|
+
"is replaced by `models.push(model, destroy: #{(c == :destroy).inspect})`.",
|
42
|
+
)
|
43
|
+
|
44
|
+
# @type var c: (:save | :destory)
|
45
|
+
c == :destroy
|
25
46
|
else
|
26
|
-
|
27
|
-
c.call(model)
|
47
|
+
c
|
28
48
|
end
|
29
49
|
else
|
30
|
-
|
50
|
+
destroy
|
31
51
|
end
|
32
|
-
ret.presence_in(%i[save destroy]) || :save
|
33
52
|
end
|
34
53
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
54
|
+
delegate :errors, to: :model
|
55
|
+
|
56
|
+
def destroy_context?
|
57
|
+
d = destroy_context_type
|
58
|
+
if d.is_a?(Proc)
|
59
|
+
if d.arity == 0
|
60
|
+
# @type var d: ^() -> (bool | context)
|
61
|
+
!!d.call
|
62
|
+
else
|
63
|
+
# @type var d: ^(_ARLike) -> (bool | context)
|
64
|
+
!!d.call(model)
|
65
|
+
end
|
43
66
|
else
|
44
|
-
|
67
|
+
!!d
|
45
68
|
end
|
46
69
|
end
|
47
70
|
|
71
|
+
# Execute save or destroy. Returns true on success, false on failure.
|
72
|
+
# Whether save or destroy is executed depends on the value of `#destroy_context?`.
|
73
|
+
#
|
74
|
+
# @return [Boolean] returns true on success, false on failure.
|
75
|
+
def save = destroy_context? ? model.destroy : model.save
|
76
|
+
|
48
77
|
# Execute save or destroy. Unlike #save, an exception is raises on failure.
|
49
|
-
# Whether save or destroy is executed depends on the value of
|
78
|
+
# Whether save or destroy is executed depends on the value of `#destroy_context?`.
|
50
79
|
#
|
51
|
-
def save!
|
52
|
-
case context
|
53
|
-
when :destroy
|
54
|
-
model.destroy!
|
55
|
-
else
|
56
|
-
model.save!
|
57
|
-
end
|
58
|
-
end
|
80
|
+
def save! = destroy_context? ? model.destroy! : model.save!
|
59
81
|
|
60
82
|
# @return [Boolean]
|
61
|
-
def invalid?
|
62
|
-
case context
|
63
|
-
when :destroy
|
64
|
-
false
|
65
|
-
else
|
66
|
-
model.invalid?
|
67
|
-
end
|
68
|
-
end
|
83
|
+
def invalid? = destroy_context? ? false : model.invalid?
|
69
84
|
|
70
85
|
# @return [Boolean]
|
71
86
|
def valid? = !invalid?
|
@@ -77,11 +92,11 @@ module ActiveRecordCompose
|
|
77
92
|
def ==(other)
|
78
93
|
return false unless self.class == other.class
|
79
94
|
return false unless __raw_model == other.__raw_model # steep:ignore
|
80
|
-
return false unless context == other.context
|
81
95
|
|
82
96
|
true
|
83
97
|
end
|
84
98
|
|
99
|
+
# @private
|
85
100
|
# Returns a model instance of raw, but it should
|
86
101
|
# be noted that application developers are not expected to use this interface.
|
87
102
|
#
|
@@ -90,6 +105,14 @@ module ActiveRecordCompose
|
|
90
105
|
|
91
106
|
private
|
92
107
|
|
93
|
-
attr_reader :model
|
108
|
+
attr_reader :model, :destroy_context_type
|
109
|
+
|
110
|
+
def deprecator
|
111
|
+
if ActiveRecord.respond_to?(:deprecator)
|
112
|
+
ActiveRecord.deprecator
|
113
|
+
else # for rails 7.0.x or lower
|
114
|
+
ActiveSupport::Deprecation
|
115
|
+
end
|
116
|
+
end
|
94
117
|
end
|
95
118
|
end
|
@@ -6,6 +6,11 @@ module ActiveRecordCompose
|
|
6
6
|
class InnerModelCollection
|
7
7
|
include Enumerable
|
8
8
|
|
9
|
+
def initialize(owner)
|
10
|
+
@owner = owner
|
11
|
+
@models = []
|
12
|
+
end
|
13
|
+
|
9
14
|
# Enumerates model objects.
|
10
15
|
#
|
11
16
|
# @yieldparam [Object] the model instance
|
@@ -23,17 +28,19 @@ module ActiveRecordCompose
|
|
23
28
|
# @param model [Object] the model instance
|
24
29
|
# @return [self] returns itself.
|
25
30
|
def <<(model)
|
26
|
-
models << wrap(model,
|
31
|
+
models << wrap(model, destroy: false)
|
27
32
|
self
|
28
33
|
end
|
29
34
|
|
30
35
|
# Appends model to collection.
|
31
36
|
#
|
32
37
|
# @param model [Object] the model instance
|
33
|
-
# @param
|
38
|
+
# @param destroy [Boolean] given true, destroy model.
|
39
|
+
# @param destroy [Proc] when proc returning true, destroy model.
|
40
|
+
# @param destroy [Symbol] applies boolean value of result of sending a message to `owner` to evaluation.
|
34
41
|
# @return [self] returns itself.
|
35
|
-
def push(model, context:
|
36
|
-
models << wrap(model, context:)
|
42
|
+
def push(model, destroy: false, context: nil)
|
43
|
+
models << wrap(model, destroy:, context:)
|
37
44
|
self
|
38
45
|
end
|
39
46
|
|
@@ -54,16 +61,25 @@ module ActiveRecordCompose
|
|
54
61
|
# Returns nil if the deletion fails, self if it succeeds.
|
55
62
|
#
|
56
63
|
# @param model [Object] the model instance
|
57
|
-
# @param context [Symbol] :save or :destroy
|
58
64
|
# @return [self] Successful deletion
|
59
65
|
# @return [nil] If deletion fails
|
60
|
-
def delete(model, context:
|
61
|
-
|
66
|
+
def delete(model, destroy: nil, context: nil)
|
67
|
+
if !destroy.nil? || !context.nil?
|
68
|
+
# steep:ignore:start
|
69
|
+
deprecator.warn(
|
70
|
+
'In `InnerModelConnection#destroy`, the option values `destroy` and `context` are ignored. ' \
|
71
|
+
'These options will be removed in 0.5.0.',
|
72
|
+
)
|
73
|
+
# steep:ignore:end
|
74
|
+
end
|
75
|
+
|
76
|
+
wrapped = wrap(model)
|
62
77
|
return nil unless models.delete(wrapped)
|
63
78
|
|
64
79
|
self
|
65
80
|
end
|
66
81
|
|
82
|
+
# @private
|
67
83
|
# Enumerates model objects, but it should be noted that
|
68
84
|
# application developers are not expected to use this interface.
|
69
85
|
#
|
@@ -79,15 +95,27 @@ module ActiveRecordCompose
|
|
79
95
|
|
80
96
|
private
|
81
97
|
|
82
|
-
|
98
|
+
attr_reader :owner, :models
|
83
99
|
|
84
|
-
def wrap(model, context:)
|
100
|
+
def wrap(model, destroy: false, context: nil)
|
85
101
|
if model.is_a?(ActiveRecordCompose::InnerModel) # steep:ignore
|
86
102
|
# @type var model: ActiveRecordCompose::InnerModel
|
87
103
|
model
|
88
104
|
else
|
105
|
+
if destroy.is_a?(Symbol)
|
106
|
+
method = destroy
|
107
|
+
destroy = -> { owner.__send__(method) }
|
108
|
+
end
|
89
109
|
# @type var model: ActiveRecordCompose::_ARLike
|
90
|
-
ActiveRecordCompose::InnerModel.new(model, context:)
|
110
|
+
ActiveRecordCompose::InnerModel.new(model, destroy:, context:)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def deprecator
|
115
|
+
if ActiveRecord.respond_to?(:deprecator)
|
116
|
+
ActiveRecord.deprecator # steep:ignore
|
117
|
+
else # for rails 7.0.x or lower
|
118
|
+
ActiveSupport::Deprecation
|
91
119
|
end
|
92
120
|
end
|
93
121
|
end
|
@@ -148,7 +148,7 @@ module ActiveRecordCompose
|
|
148
148
|
|
149
149
|
private
|
150
150
|
|
151
|
-
def models = @__models ||= ActiveRecordCompose::InnerModelCollection.new
|
151
|
+
def models = @__models ||= ActiveRecordCompose::InnerModelCollection.new(self)
|
152
152
|
|
153
153
|
def wrapped_models = models.__each_by_wrapped # steep:ignore
|
154
154
|
|
@@ -3,8 +3,6 @@
|
|
3
3
|
require 'active_record'
|
4
4
|
|
5
5
|
require_relative 'active_record_compose/version'
|
6
|
-
require_relative 'active_record_compose/inner_model'
|
7
|
-
require_relative 'active_record_compose/inner_model_collection'
|
8
6
|
require_relative 'active_record_compose/model'
|
9
7
|
|
10
8
|
module ActiveRecordCompose
|
@@ -17,6 +17,7 @@ module ActiveRecordCompose
|
|
17
17
|
type attribute_name = (String | Symbol)
|
18
18
|
type context = (:save | :destroy)
|
19
19
|
type context_proc = ((^() -> context) | (^(_ARLike) -> context))
|
20
|
+
type destroy_context_type = (bool | Symbol | (^() -> boolish) | (^(_ARLike) -> boolish))
|
20
21
|
|
21
22
|
module DelegateAttribute
|
22
23
|
extend ActiveSupport::Concern
|
@@ -30,25 +31,24 @@ module ActiveRecordCompose
|
|
30
31
|
|
31
32
|
class InnerModelCollection
|
32
33
|
include ::Enumerable[_ARLike]
|
33
|
-
@models: Array[InnerModel]
|
34
34
|
|
35
|
+
def initialize: (Model) -> void
|
35
36
|
def each: () { (_ARLike) -> void } -> InnerModelCollection | () -> Enumerator[_ARLike, self]
|
36
37
|
def <<: (_ARLike) -> self
|
37
|
-
def push: (_ARLike, ?context: (context | context_proc)) -> self
|
38
|
+
def push: (_ARLike, ?destroy: destroy_context_type, ?context: (nil | context | context_proc)) -> self
|
38
39
|
def empty?: -> bool
|
39
40
|
def clear: -> self
|
40
|
-
def delete: (_ARLike | InnerModel, ?context: (context | context_proc)) -> InnerModelCollection?
|
41
|
+
def delete: (_ARLike | InnerModel, ?destroy: (nil | destroy_context_type), ?context: (nil | context | context_proc)) -> InnerModelCollection?
|
41
42
|
|
42
43
|
private
|
43
|
-
|
44
|
-
|
44
|
+
attr_reader owner: Model
|
45
|
+
attr_reader models: Array[InnerModel]
|
46
|
+
def wrap: (_ARLike | InnerModel, ?destroy: destroy_context_type, ?context: (nil | context | context_proc)) -> InnerModel
|
45
47
|
end
|
46
48
|
|
47
49
|
class InnerModel
|
48
|
-
|
49
|
-
|
50
|
-
def initialize: (_ARLike, ?context: (context | context_proc)) -> void
|
51
|
-
def context: -> context
|
50
|
+
def initialize: (_ARLike, ?destroy: destroy_context_type, ?context: (nil | context | context_proc)) -> void
|
51
|
+
def destroy_context?: -> bool
|
52
52
|
def save: -> bool
|
53
53
|
def save!: -> untyped
|
54
54
|
def invalid?: -> bool
|
@@ -57,6 +57,7 @@ module ActiveRecordCompose
|
|
57
57
|
|
58
58
|
private
|
59
59
|
attr_reader model: _ARLike
|
60
|
+
attr_reader destroy_context_type: destroy_context_type
|
60
61
|
end
|
61
62
|
|
62
63
|
class Model
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
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.4.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- hamajyotan
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-09-
|
11
|
+
date: 2024-09-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -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.4.1
|
56
56
|
rubygems_mfa_required: 'true'
|
57
57
|
post_install_message:
|
58
58
|
rdoc_options: []
|
@@ -69,7 +69,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
69
69
|
- !ruby/object:Gem::Version
|
70
70
|
version: '0'
|
71
71
|
requirements: []
|
72
|
-
rubygems_version: 3.5.
|
72
|
+
rubygems_version: 3.5.18
|
73
73
|
signing_key:
|
74
74
|
specification_version: 4
|
75
75
|
summary: activemodel form object pattern
|