active_record_compose 0.3.3 → 0.4.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 +19 -0
- data/README.md +25 -2
- data/lib/active_record_compose/delegate_attribute.rb +2 -2
- data/lib/active_record_compose/inner_model.rb +80 -47
- data/lib/active_record_compose/inner_model_collection.rb +15 -7
- data/lib/active_record_compose/model.rb +1 -1
- data/lib/active_record_compose/version.rb +1 -1
- data/sig/active_record_compose.rbs +11 -6
- 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: 8d9e12c3cb3d2e8571d418957576a8cf5b5d3dbedb9e83e2bcd25f172def43f3
|
4
|
+
data.tar.gz: a991715192e6cb62e8b794860422667811b1fb1f985089e415b444f0b7bc0d4a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 837b3d35c903eb0ff4400e5c83c438255d9ee02515407792fd19d24ab9a5a63cea1f9faa1d12a5fd5184d52fee2d662625e7c0d0b2a79e27ebb0866054a6b2a4
|
7
|
+
data.tar.gz: e245d8380a2d20f0c9bbbc1f24712f1d7982697bf90b172886dcd2c9bd666ace8ddd3b29a20b9bcc709d8a236a1395a41c0f53ccdb3869f66a527d5ad6be08dc
|
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,24 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [0.4.0] - 2024-09-15
|
4
|
+
|
5
|
+
- support `destrpy` option. and deprecated `context` option.
|
6
|
+
`:context` will be removed in 0.5.0. Use `:destroy` option instead.
|
7
|
+
for example,
|
8
|
+
- `models.push(model, context: :destroy)` is replaced by `models.push(model, destroy: true)`
|
9
|
+
- `models.push(model, context: -> { foo? ? :destroy : :save })` is replaced by `models.push(model, destroy: -> { foo? })`
|
10
|
+
- `models.push(model, context: ->(m) { m.bar? ? :destroy : :save })` is replaced by `models.push(model, destroy: ->(m) { m.bar? })`
|
11
|
+
- `destroy` option can now be specified with a `Symbol` representing the method name.
|
12
|
+
|
13
|
+
## [0.3.4] - 2024-09-01
|
14
|
+
|
15
|
+
- ci: removed sqlite3 version specifing for new AR.
|
16
|
+
- `delegate_attribute` options are now specific and do not accept `prefix`
|
17
|
+
|
18
|
+
## [0.3.3] - 2024-06-24
|
19
|
+
|
20
|
+
- use steep:ignore
|
21
|
+
|
3
22
|
## [0.3.2] - 2024-04-10
|
4
23
|
|
5
24
|
- support `ActiveRecord::Base#with_connection`
|
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.
|
@@ -44,7 +44,7 @@ module ActiveRecordCompose
|
|
44
44
|
module ClassMethods
|
45
45
|
# Defines the reader and writer for the specified attribute.
|
46
46
|
#
|
47
|
-
def delegate_attribute(*attributes, to:,
|
47
|
+
def delegate_attribute(*attributes, to:, allow_nil: nil, private: nil)
|
48
48
|
delegates = attributes.flat_map do |attribute|
|
49
49
|
reader = attribute.to_s
|
50
50
|
writer = "#{attribute}="
|
@@ -52,7 +52,7 @@ module ActiveRecordCompose
|
|
52
52
|
[reader, writer]
|
53
53
|
end
|
54
54
|
|
55
|
-
delegate(*delegates, to:,
|
55
|
+
delegate(*delegates, to:, allow_nil:, private:) # steep:ignore
|
56
56
|
delegated_attributes = (self.delegated_attributes ||= []) # steep:ignore
|
57
57
|
attributes.each { delegated_attributes.push(_1.to_s) }
|
58
58
|
end
|
@@ -5,67 +5,85 @@ 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(owner, model, destroy: false, context: nil)
|
11
|
+
@owner = owner
|
11
12
|
@model = model
|
12
|
-
@
|
13
|
-
|
13
|
+
@destroy =
|
14
|
+
if context
|
15
|
+
c = context
|
14
16
|
|
15
|
-
|
17
|
+
if c.is_a?(Proc)
|
18
|
+
# @type var c: ((^() -> (context)) | (^(_ARLike) -> (context)))
|
19
|
+
if c.arity == 0
|
20
|
+
deprecator.warn(
|
21
|
+
'`:context` will be removed in 0.5.0. Use `:destroy` option instead. ' \
|
22
|
+
'for example, `models.push(model, context: -> { foo? ? :destroy : :save })` ' \
|
23
|
+
'is replaced by `models.push(model, destroy: -> { foo? })`.',
|
24
|
+
)
|
25
|
+
|
26
|
+
# @type var c: ^() -> (context)
|
27
|
+
-> { c.call == :destroy }
|
28
|
+
else
|
29
|
+
deprecator.warn(
|
30
|
+
'`:context` will be removed in 0.5.0. Use `:destroy` option instead. ' \
|
31
|
+
'for example, `models.push(model, context: ->(m) { m.bar? ? :destroy : :save })` ' \
|
32
|
+
'is replaced by `models.push(model, destroy: ->(m) { m.bar? })`.',
|
33
|
+
)
|
34
|
+
|
35
|
+
# @type var c: ^(_ARLike) -> (context)
|
36
|
+
->(model) { c.call(model) == :destroy }
|
37
|
+
end
|
38
|
+
elsif %i[save destroy].include?(c)
|
39
|
+
deprecator.warn(
|
40
|
+
'`:context` will be removed in 0.5.0. Use `:destroy` option instead. ' \
|
41
|
+
"for example, `models.push(model, context: #{c.inspect})` " \
|
42
|
+
"is replaced by `models.push(model, destroy: #{(c == :destroy).inspect})`.",
|
43
|
+
)
|
16
44
|
|
17
|
-
|
18
|
-
|
19
|
-
c = @context
|
20
|
-
ret =
|
21
|
-
if c.is_a?(Proc)
|
22
|
-
if c.arity == 0
|
23
|
-
# @type var c: ^() -> context
|
24
|
-
c.call
|
45
|
+
# @type var c: (:save | :destory)
|
46
|
+
c == :destroy
|
25
47
|
else
|
26
|
-
|
27
|
-
c.call(model)
|
48
|
+
c
|
28
49
|
end
|
29
50
|
else
|
30
|
-
|
51
|
+
destroy
|
31
52
|
end
|
32
|
-
ret.presence_in(%i[save destroy]) || :save
|
33
53
|
end
|
34
54
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
55
|
+
delegate :errors, to: :model
|
56
|
+
|
57
|
+
def destroy_context?
|
58
|
+
d = destroy
|
59
|
+
if d.is_a?(Proc)
|
60
|
+
if d.arity == 0
|
61
|
+
# @type var d: ^() -> (bool | context)
|
62
|
+
d.call
|
63
|
+
else
|
64
|
+
# @type var d: ^(_ARLike) -> (bool | context)
|
65
|
+
d.call(model)
|
66
|
+
end
|
67
|
+
elsif d.is_a?(Symbol)
|
68
|
+
owner.send(d)
|
43
69
|
else
|
44
|
-
|
70
|
+
!!d
|
45
71
|
end
|
46
72
|
end
|
47
73
|
|
74
|
+
# Execute save or destroy. Returns true on success, false on failure.
|
75
|
+
# Whether save or destroy is executed depends on the value of `#destroy_context?`.
|
76
|
+
#
|
77
|
+
# @return [Boolean] returns true on success, false on failure.
|
78
|
+
def save = destroy_context? ? model.destroy : model.save
|
79
|
+
|
48
80
|
# Execute save or destroy. Unlike #save, an exception is raises on failure.
|
49
|
-
# Whether save or destroy is executed depends on the value of
|
81
|
+
# Whether save or destroy is executed depends on the value of `#destroy_context?`.
|
50
82
|
#
|
51
|
-
def save!
|
52
|
-
case context
|
53
|
-
when :destroy
|
54
|
-
model.destroy!
|
55
|
-
else
|
56
|
-
model.save!
|
57
|
-
end
|
58
|
-
end
|
83
|
+
def save! = destroy_context? ? model.destroy! : model.save!
|
59
84
|
|
60
85
|
# @return [Boolean]
|
61
|
-
def invalid?
|
62
|
-
case context
|
63
|
-
when :destroy
|
64
|
-
false
|
65
|
-
else
|
66
|
-
model.invalid?
|
67
|
-
end
|
68
|
-
end
|
86
|
+
def invalid? = destroy_context? ? false : model.invalid?
|
69
87
|
|
70
88
|
# @return [Boolean]
|
71
89
|
def valid? = !invalid?
|
@@ -76,8 +94,8 @@ module ActiveRecordCompose
|
|
76
94
|
# @return [Boolean]
|
77
95
|
def ==(other)
|
78
96
|
return false unless self.class == other.class
|
79
|
-
return false unless __raw_model == other.__raw_model
|
80
|
-
return false unless
|
97
|
+
return false unless __raw_model == other.__raw_model
|
98
|
+
return false unless __destroy == other.__destroy
|
81
99
|
|
82
100
|
true
|
83
101
|
end
|
@@ -88,8 +106,23 @@ module ActiveRecordCompose
|
|
88
106
|
# @return [Object] raw model instance
|
89
107
|
def __raw_model = model
|
90
108
|
|
109
|
+
# Returns a model instance of raw, but it should
|
110
|
+
# be noted that application developers are not expected to use this interface.
|
111
|
+
#
|
112
|
+
# @return [Boolean] raw destroy instance
|
113
|
+
# @return [Proc] raw destroy instance
|
114
|
+
def __destroy = destroy
|
115
|
+
|
91
116
|
private
|
92
117
|
|
93
|
-
attr_reader :model
|
118
|
+
attr_reader :owner, :model, :destroy
|
119
|
+
|
120
|
+
def deprecator
|
121
|
+
if ActiveRecord.respond_to?(:deprecator)
|
122
|
+
ActiveRecord.deprecator
|
123
|
+
else # for rails 7.0.x or lower
|
124
|
+
ActiveSupport::Deprecation
|
125
|
+
end
|
126
|
+
end
|
94
127
|
end
|
95
128
|
end
|
@@ -6,6 +6,10 @@ module ActiveRecordCompose
|
|
6
6
|
class InnerModelCollection
|
7
7
|
include Enumerable
|
8
8
|
|
9
|
+
def initialize(owner)
|
10
|
+
@owner = owner
|
11
|
+
end
|
12
|
+
|
9
13
|
# Enumerates model objects.
|
10
14
|
#
|
11
15
|
# @yieldparam [Object] the model instance
|
@@ -23,17 +27,18 @@ module ActiveRecordCompose
|
|
23
27
|
# @param model [Object] the model instance
|
24
28
|
# @return [self] returns itself.
|
25
29
|
def <<(model)
|
26
|
-
models << wrap(model,
|
30
|
+
models << wrap(model, destroy: false)
|
27
31
|
self
|
28
32
|
end
|
29
33
|
|
30
34
|
# Appends model to collection.
|
31
35
|
#
|
32
36
|
# @param model [Object] the model instance
|
37
|
+
# @param destroy [Boolean] given true, destroy model.
|
33
38
|
# @param context [Symbol] :save or :destroy
|
34
39
|
# @return [self] returns itself.
|
35
|
-
def push(model, context:
|
36
|
-
models << wrap(model, context:)
|
40
|
+
def push(model, destroy: false, context: nil)
|
41
|
+
models << wrap(model, destroy:, context:)
|
37
42
|
self
|
38
43
|
end
|
39
44
|
|
@@ -54,11 +59,12 @@ module ActiveRecordCompose
|
|
54
59
|
# Returns nil if the deletion fails, self if it succeeds.
|
55
60
|
#
|
56
61
|
# @param model [Object] the model instance
|
62
|
+
# @param destroy [Boolean] given true, destroy model.
|
57
63
|
# @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
|
-
wrapped = wrap(model, context:)
|
66
|
+
def delete(model, destroy: false, context: nil)
|
67
|
+
wrapped = wrap(model, destroy:, context:)
|
62
68
|
return nil unless models.delete(wrapped)
|
63
69
|
|
64
70
|
self
|
@@ -79,15 +85,17 @@ module ActiveRecordCompose
|
|
79
85
|
|
80
86
|
private
|
81
87
|
|
88
|
+
attr_reader :owner
|
89
|
+
|
82
90
|
def models = @models ||= []
|
83
91
|
|
84
|
-
def wrap(model, context:)
|
92
|
+
def wrap(model, destroy:, context: nil)
|
85
93
|
if model.is_a?(ActiveRecordCompose::InnerModel) # steep:ignore
|
86
94
|
# @type var model: ActiveRecordCompose::InnerModel
|
87
95
|
model
|
88
96
|
else
|
89
97
|
# @type var model: ActiveRecordCompose::_ARLike
|
90
|
-
ActiveRecordCompose::InnerModel.new(model, context:)
|
98
|
+
ActiveRecordCompose::InnerModel.new(owner, model, destroy:, context:)
|
91
99
|
end
|
92
100
|
end
|
93
101
|
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
|
|
@@ -24,31 +24,35 @@ module ActiveRecordCompose
|
|
24
24
|
def attributes: -> Hash[String, untyped]
|
25
25
|
|
26
26
|
module ClassMethods
|
27
|
-
def delegate_attribute: (*untyped methods, to: untyped?,
|
27
|
+
def delegate_attribute: (*untyped methods, to: untyped?, ?allow_nil: untyped?, ?private: untyped?) -> untyped
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
31
31
|
class InnerModelCollection
|
32
32
|
include ::Enumerable[_ARLike]
|
33
|
+
@owner: Model
|
33
34
|
@models: Array[InnerModel]
|
34
35
|
|
36
|
+
def initialize: (Model) -> void
|
35
37
|
def each: () { (_ARLike) -> void } -> InnerModelCollection | () -> Enumerator[_ARLike, self]
|
36
38
|
def <<: (_ARLike) -> self
|
37
|
-
def push: (_ARLike, ?context: (context | context_proc)) -> self
|
39
|
+
def push: (_ARLike, ?destroy: bool, ?context: (nil | context | context_proc)) -> self
|
38
40
|
def empty?: -> bool
|
39
41
|
def clear: -> self
|
40
|
-
def delete: (_ARLike | InnerModel, ?context: (context | context_proc)) -> InnerModelCollection?
|
42
|
+
def delete: (_ARLike | InnerModel, ?destroy: bool, ?context: (nil | context | context_proc)) -> InnerModelCollection?
|
41
43
|
|
42
44
|
private
|
45
|
+
attr_reader owner: Model
|
43
46
|
def models: -> Array[InnerModel]
|
44
|
-
def wrap: (_ARLike | InnerModel, context: (context | context_proc)) -> InnerModel
|
47
|
+
def wrap: (_ARLike | InnerModel, destroy: bool, ?context: (nil | context | context_proc)) -> InnerModel
|
45
48
|
end
|
46
49
|
|
47
50
|
class InnerModel
|
51
|
+
@owner: Model
|
48
52
|
@context: (context | context_proc)
|
49
53
|
|
50
|
-
def initialize: (_ARLike, ?context: (context | context_proc)) -> void
|
51
|
-
def
|
54
|
+
def initialize: (Model, _ARLike, ?destroy: bool, ?context: (nil | context | context_proc)) -> void
|
55
|
+
def destroy_context?: -> bool
|
52
56
|
def save: -> bool
|
53
57
|
def save!: -> untyped
|
54
58
|
def invalid?: -> bool
|
@@ -56,6 +60,7 @@ module ActiveRecordCompose
|
|
56
60
|
def ==: (untyped) -> bool
|
57
61
|
|
58
62
|
private
|
63
|
+
attr_reader owner: Model
|
59
64
|
attr_reader model: _ARLike
|
60
65
|
end
|
61
66
|
|
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.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- hamajyotan
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-09-14 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.0
|
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
|