activeinteractor 2.0.0.alpha.3.0.0 → 2.0.0.alpha.4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -0
- data/lib/active_interactor/context/attribute.rb +2 -1
- data/lib/active_interactor/context/attribute_validation.rb +22 -0
- data/lib/active_interactor/context/base.rb +64 -10
- data/lib/active_interactor/context/input.rb +11 -0
- data/lib/active_interactor/context/output.rb +5 -1
- data/lib/active_interactor/context/result.rb +4 -2
- data/lib/active_interactor/context/runtime.rb +1 -0
- data/lib/active_interactor/context.rb +0 -2
- data/lib/active_interactor/errors.rb +2 -11
- data/lib/active_interactor/interactor/base.rb +271 -13
- data/lib/active_interactor/interactor/options.rb +23 -0
- data/lib/active_interactor/interactor.rb +1 -2
- data/lib/active_interactor/result.rb +110 -88
- data/lib/active_interactor/type/decleration_methods.rb +0 -4
- data/lib/active_interactor/type.rb +1 -1
- data/lib/active_interactor.rb +2 -2
- metadata +8 -11
- data/lib/active_interactor/active_model_error_methods.rb +0 -30
- data/lib/active_interactor/context/attribute_assignment.rb +0 -64
- data/lib/active_interactor/context/attribute_registration.rb +0 -27
- data/lib/active_interactor/interactor/context_methods.rb +0 -83
- data/lib/active_interactor/interactor/interaction_methods.rb +0 -38
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1ec1233daeb01ec3145d13cef57caf6c7f69a8128c311e5a0414c594c9b36f5b
|
4
|
+
data.tar.gz: e76d9a078f540d5e8eaa953cb1f88726bde6f507222f5f6f8c807a4f771dd6e6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 79f453fbfafecdf6cc59c1eebcb26229132818c18c5109977cf70efd40e94dbfb8ce7094b3e24cb9c39238a787bcf92b12f1c39716992402e7627e6c454defe1
|
7
|
+
data.tar.gz: a58f48730d1494e0a52608a8b76f07a11c53158d1f67966a4f5538965fc1e0495abd847613660f1be72cfbd9b21e720d4631a8d562c37bdd28567f1acc3e73fa
|
data/README.md
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
[![Version](https://img.shields.io/gem/v/activeinteractor.svg?logo=ruby)](https://rubygems.org/gems/activeinteractor)
|
4
4
|
[![Build](https://github.com/activeinteractor/activeinteractor/actions/workflows/build.yml/badge.svg?branch=main)](https://github.com/activeinteractor/activeinteractor/actions/workflows/build.yml)
|
5
|
+
[![Test Coverage](https://api.codeclimate.com/v1/badges/9a44100fb77f785dac8a/test_coverage)](https://codeclimate.com/github/activeinteractor/activeinteractor/test_coverage)
|
5
6
|
[![Docs](https://img.shields.io/badge/docs-blue)](https://activeinteractor.org/)
|
6
7
|
[![License](https://img.shields.io/github/license/activeinteractor/activeinteractor.svg?maxAge=300)](https://github.com/activeinteractor/activeinteractor/blob/main/LICENSE)
|
7
8
|
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveInteractor
|
4
|
+
module Context
|
5
|
+
module AttributeValidation
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
included do
|
9
|
+
validate :validate_attributes!
|
10
|
+
end
|
11
|
+
|
12
|
+
protected
|
13
|
+
|
14
|
+
def validate_attributes!
|
15
|
+
attribute_set.attributes.each do |attribute|
|
16
|
+
attribute.validate!
|
17
|
+
attribute.error_messages.each { |message| errors.add(attribute.name, message) }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -2,27 +2,81 @@
|
|
2
2
|
|
3
3
|
module ActiveInteractor
|
4
4
|
module Context
|
5
|
+
# The base class for all context objects
|
5
6
|
class Base
|
7
|
+
extend Type::DeclerationMethods::ClassMethods
|
6
8
|
include ActiveModel::Validations
|
7
|
-
include ActiveModelErrorMethods
|
8
|
-
include AttributeRegistration
|
9
|
-
include AttributeAssignment
|
10
9
|
include Type::DeclerationMethods
|
11
10
|
|
12
|
-
|
11
|
+
class << self
|
12
|
+
def method_defined?(method_name)
|
13
|
+
attribute_set.attribute_names.include?(method_name.to_s.delete('=').to_sym) || super
|
14
|
+
end
|
15
|
+
|
16
|
+
protected
|
17
|
+
|
18
|
+
def attribute_set
|
19
|
+
@attribute_set ||= AttributeSet.new(self)
|
20
|
+
end
|
21
|
+
end
|
13
22
|
|
14
23
|
def initialize(attributes = {})
|
15
|
-
|
16
|
-
|
24
|
+
attribute_set.attributes.each do |attribute|
|
25
|
+
next unless attributes.with_indifferent_access.key?(attribute.name)
|
26
|
+
|
27
|
+
assign_attribute_value(attribute.name, attributes.with_indifferent_access[attribute.name])
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def [](attribute_name)
|
32
|
+
read_attribute_value(attribute_name)
|
33
|
+
end
|
34
|
+
|
35
|
+
def []=(attribute_name, value)
|
36
|
+
assign_attribute_value(attribute_name, value)
|
17
37
|
end
|
18
38
|
|
19
39
|
protected
|
20
40
|
|
21
|
-
def
|
22
|
-
attribute_set.
|
23
|
-
|
24
|
-
|
41
|
+
def assign_attribute_value(attribute_name, value)
|
42
|
+
attribute = attribute_set.find(attribute_name)
|
43
|
+
raise NoMethodError, "unknown attribute '#{attribute_name}' for #{self.class.name}" unless attribute
|
44
|
+
|
45
|
+
attribute.assign_value(value)
|
46
|
+
end
|
47
|
+
|
48
|
+
def assignment_method_missing(method_name, *arguments)
|
49
|
+
if arguments.length != 1
|
50
|
+
raise ArgumentError,
|
51
|
+
"wrong number of arguments (given #{arguments.length}, expected 1)"
|
25
52
|
end
|
53
|
+
|
54
|
+
assign_attribute_value(method_name.to_s.delete('=').to_sym, arguments.first)
|
55
|
+
end
|
56
|
+
|
57
|
+
def attribute_set
|
58
|
+
@attribute_set ||= AttributeSet.new(self, *self.class.send(:attribute_set).attributes.map(&:dup))
|
59
|
+
end
|
60
|
+
|
61
|
+
def method_missing(method_name, *arguments)
|
62
|
+
return super unless respond_to_missing?(method_name)
|
63
|
+
return assignment_method_missing(method_name, *arguments) if method_name.to_s.end_with?('=')
|
64
|
+
|
65
|
+
read_attribute_value(method_name)
|
66
|
+
end
|
67
|
+
|
68
|
+
def read_attribute_value(attribute_name)
|
69
|
+
attribute = attribute_set.find(attribute_name)
|
70
|
+
raise NoMethodError, "unknown attribute '#{attribute_name}' for #{self.class.name}" unless attribute
|
71
|
+
|
72
|
+
attribute.value
|
73
|
+
end
|
74
|
+
|
75
|
+
def respond_to_missing?(method_name, _include_private = false)
|
76
|
+
return true if attribute_set.attribute_names.include?(method_name.to_sym)
|
77
|
+
return true if attribute_set.attribute_names.include?(method_name.to_s.delete('=').to_sym)
|
78
|
+
|
79
|
+
super
|
26
80
|
end
|
27
81
|
end
|
28
82
|
end
|
@@ -3,6 +3,11 @@
|
|
3
3
|
module ActiveInteractor
|
4
4
|
module Context
|
5
5
|
class Input < Base
|
6
|
+
delegate :as_json, to: :arguments
|
7
|
+
delegate :to_h, to: :arguments
|
8
|
+
delegate :to_hash, to: :arguments
|
9
|
+
delegate :to_json, to: :arguments
|
10
|
+
|
6
11
|
def self.argument(*attribute_args)
|
7
12
|
attribute_set.add(*attribute_args)
|
8
13
|
end
|
@@ -14,6 +19,12 @@ module ActiveInteractor
|
|
14
19
|
def self.arguments
|
15
20
|
attribute_set.attributes
|
16
21
|
end
|
22
|
+
|
23
|
+
def arguments
|
24
|
+
attribute_set.attributes.each_with_object({}) do |attribute, result|
|
25
|
+
result[attribute.name] = attribute.value
|
26
|
+
end
|
27
|
+
end
|
17
28
|
end
|
18
29
|
end
|
19
30
|
end
|
@@ -2,8 +2,12 @@
|
|
2
2
|
|
3
3
|
module ActiveInteractor
|
4
4
|
module Context
|
5
|
+
# The base class for all output context objects
|
5
6
|
class Output < Base
|
6
|
-
delegate :
|
7
|
+
delegate :as_json, to: :fields
|
8
|
+
delegate :to_h, to: :fields
|
9
|
+
delegate :to_hash, to: :fields
|
10
|
+
delegate :to_json, to: :fields
|
7
11
|
|
8
12
|
def self.returns(*attribute_args)
|
9
13
|
attribute_set.add(*attribute_args)
|
@@ -3,7 +3,9 @@
|
|
3
3
|
module ActiveInteractor
|
4
4
|
module Context
|
5
5
|
class Result
|
6
|
-
delegate :[],
|
6
|
+
delegate :[], to: :to_hash
|
7
|
+
delegate :as_json, to: :to_hash
|
8
|
+
delegate :to_json, to: :to_hash
|
7
9
|
|
8
10
|
def self.register_owner(owner)
|
9
11
|
owner.const_set(:ResultContext, Class.new(self))
|
@@ -11,7 +13,7 @@ module ActiveInteractor
|
|
11
13
|
|
12
14
|
def self.for_output_context(owner, context)
|
13
15
|
context.fields.each_key { |field| owner::ResultContext.send(:attr_reader, field) }
|
14
|
-
owner::ResultContext.new(context.fields)
|
16
|
+
owner::ResultContext.new(context.fields.deep_dup)
|
15
17
|
end
|
16
18
|
|
17
19
|
def initialize(attributes = {})
|
@@ -1,22 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ActiveInteractor
|
4
|
-
# Raised
|
4
|
+
# Raised on interactor failure
|
5
5
|
#
|
6
6
|
# @!attribute [r] result
|
7
|
-
# @return [ActiveInteractor::Result]
|
8
|
-
# {ActiveInteractor::Interactor::Base interactor} that failed
|
7
|
+
# @return [ActiveInteractor::Result] the result of the interactor that failed
|
9
8
|
class Error < StandardError
|
10
9
|
attr_reader :result
|
11
10
|
|
12
|
-
# Create a new instance of {ActiveInteractor::Error}
|
13
|
-
#
|
14
|
-
# @param result [ActiveInteractor::Result] An instance of {ActiveInteractor::Result} for the
|
15
|
-
# {ActiveInteractor::Interactor::Base interactor} that failed
|
16
|
-
# @param message [String] The error message
|
17
|
-
#
|
18
|
-
# @private
|
19
|
-
# @return [ActiveInteractor::Error]
|
20
11
|
def initialize(result, message = nil)
|
21
12
|
@result = result
|
22
13
|
super(message)
|
@@ -2,37 +2,295 @@
|
|
2
2
|
|
3
3
|
module ActiveInteractor
|
4
4
|
module Interactor
|
5
|
+
# The Base Class inherited by all Interactors
|
5
6
|
class Base
|
6
|
-
include
|
7
|
-
|
7
|
+
include ActiveSupport::Callbacks
|
8
|
+
extend Type::DeclerationMethods::ClassMethods
|
8
9
|
include Type::DeclerationMethods
|
9
10
|
|
10
|
-
|
11
|
-
|
12
|
-
|
11
|
+
define_callbacks :fail, :input_context_create, :input_context_validation, :output_context_create,
|
12
|
+
:output_context_validation, :perform, :rollback, :runtime_context_create
|
13
|
+
|
14
|
+
class << self
|
15
|
+
delegate :argument, :argument_names, :arguments, to: :input_context_class
|
16
|
+
delegate :returns, :field_names, :fields, to: :output_context_class
|
17
|
+
delegate(*ActiveModel::Validations::ClassMethods.instance_methods, to: :input_context_class, prefix: :input)
|
18
|
+
delegate(*ActiveModel::Validations::HelperMethods.instance_methods, to: :input_context_class, prefix: :input)
|
19
|
+
delegate(*ActiveModel::Validations::ClassMethods.instance_methods, to: :output_context_class, prefix: :output)
|
20
|
+
delegate(*ActiveModel::Validations::HelperMethods.instance_methods, to: :output_context_class, prefix: :output)
|
21
|
+
|
22
|
+
def accepts_arguments_matching(set_input_context_class)
|
23
|
+
@input_context_class = set_input_context_class
|
24
|
+
end
|
25
|
+
alias input_context accepts_arguments_matching
|
26
|
+
alias input_type accepts_arguments_matching
|
27
|
+
|
28
|
+
def after_fail(...)
|
29
|
+
set_callback(:fail, :after, ...)
|
30
|
+
end
|
31
|
+
|
32
|
+
def after_input_context_create(...)
|
33
|
+
set_callback(:input_context_create, :after, ...)
|
34
|
+
end
|
35
|
+
|
36
|
+
def after_input_context_validation(...)
|
37
|
+
set_callback(:input_context_validation, :after, ...)
|
38
|
+
end
|
39
|
+
|
40
|
+
def after_output_context_create(...)
|
41
|
+
set_callback(:output_context_create, :after, ...)
|
42
|
+
end
|
43
|
+
|
44
|
+
def after_output_context_validation(...)
|
45
|
+
set_callback(:output_context_validation, :after, ...)
|
46
|
+
end
|
47
|
+
|
48
|
+
def after_perform(...)
|
49
|
+
set_callback(:perform, :after, ...)
|
50
|
+
end
|
51
|
+
|
52
|
+
def after_rollback(...)
|
53
|
+
set_callback(:rollback, :after, ...)
|
54
|
+
end
|
55
|
+
|
56
|
+
def after_runtime_context_create(...)
|
57
|
+
set_callback(:runtime_context_create, :after, ...)
|
58
|
+
end
|
59
|
+
|
60
|
+
def around_fail(...)
|
61
|
+
set_callback(:fail, :around, ...)
|
62
|
+
end
|
63
|
+
|
64
|
+
def around_input_context_create(...)
|
65
|
+
set_callback(:input_context_create, :around, ...)
|
66
|
+
end
|
67
|
+
|
68
|
+
def around_input_context_validation(...)
|
69
|
+
set_callback(:input_context_validation, :around, ...)
|
70
|
+
end
|
71
|
+
|
72
|
+
def around_output_context_create(...)
|
73
|
+
set_callback(:output_context_create, :around, ...)
|
74
|
+
end
|
75
|
+
|
76
|
+
def around_output_context_validation(...)
|
77
|
+
set_callback(:output_context_validation, :around, ...)
|
78
|
+
end
|
79
|
+
|
80
|
+
def around_perform(...)
|
81
|
+
set_callback(:perform, :around, ...)
|
82
|
+
end
|
83
|
+
|
84
|
+
def around_rollback(...)
|
85
|
+
set_callback(:rollback, :around, ...)
|
86
|
+
end
|
87
|
+
|
88
|
+
def around_runtime_context_create(...)
|
89
|
+
set_callback(:runtime_context_create, :around, ...)
|
90
|
+
end
|
91
|
+
|
92
|
+
def before_fail(...)
|
93
|
+
set_callback(:fail, :before, ...)
|
94
|
+
end
|
95
|
+
|
96
|
+
def before_input_context_create(...)
|
97
|
+
set_callback(:input_context_create, :before, ...)
|
98
|
+
end
|
99
|
+
|
100
|
+
def before_input_context_validation(...)
|
101
|
+
set_callback(:input_context_validation, :before, ...)
|
102
|
+
end
|
103
|
+
|
104
|
+
def before_output_context_create(...)
|
105
|
+
set_callback(:output_context_create, :before, ...)
|
106
|
+
end
|
107
|
+
|
108
|
+
def before_output_context_validation(...)
|
109
|
+
set_callback(:output_context_validation, :before, ...)
|
110
|
+
end
|
111
|
+
|
112
|
+
def before_perform(...)
|
113
|
+
set_callback(:perform, :before, ...)
|
114
|
+
end
|
115
|
+
|
116
|
+
def before_rollback(...)
|
117
|
+
set_callback(:rollback, :before, ...)
|
118
|
+
end
|
119
|
+
|
120
|
+
def before_runtime_context_create(...)
|
121
|
+
set_callback(:runtime_context_create, :before, ...)
|
122
|
+
end
|
123
|
+
|
124
|
+
def input_context_class
|
125
|
+
@input_context_class ||= const_set(:InputContext, Class.new(Context::Input))
|
126
|
+
end
|
127
|
+
|
128
|
+
def output_context_class
|
129
|
+
@output_context_class ||= const_set(:OutputContext, Class.new(Context::Output))
|
130
|
+
end
|
131
|
+
|
132
|
+
def perform!(input_context = {}, options = {})
|
133
|
+
new(options).perform!(input_context)
|
134
|
+
end
|
135
|
+
|
136
|
+
def perform(input_context = {}, options = {})
|
137
|
+
perform!(input_context, options)
|
138
|
+
rescue Error => e
|
139
|
+
e.result
|
140
|
+
rescue StandardError => e
|
141
|
+
Result.failure(errors: e.message)
|
142
|
+
end
|
143
|
+
|
144
|
+
def returns_data_matching(set_output_context_class)
|
145
|
+
@output_context_class = set_output_context_class
|
146
|
+
end
|
147
|
+
alias output_context returns_data_matching
|
148
|
+
alias output_type returns_data_matching
|
149
|
+
|
150
|
+
def runtime_context_class
|
151
|
+
@runtime_context_class ||= begin
|
152
|
+
context_class = const_set(:RuntimeContext, Class.new(Context::Runtime))
|
153
|
+
context_class.send(:attribute_set).merge(input_context_class.send(:attribute_set).attributes)
|
154
|
+
context_class.send(:attribute_set).merge(output_context_class.send(:attribute_set).attributes)
|
155
|
+
context_class
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def with_options(options)
|
160
|
+
new(options)
|
161
|
+
end
|
162
|
+
|
163
|
+
protected
|
164
|
+
|
165
|
+
def result_context
|
166
|
+
@result_context ||= Context::Result.register_owner(self)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
def initialize(options = {})
|
171
|
+
@options = Options.new(options.deep_dup)
|
13
172
|
end
|
14
173
|
|
15
|
-
def
|
174
|
+
def interact; end
|
175
|
+
|
176
|
+
def perform!(input_context = {})
|
177
|
+
@raw_input = input_context.deep_dup
|
178
|
+
create_input_context
|
179
|
+
validate_input_context!
|
180
|
+
create_runtime_context
|
181
|
+
return execute_perform_with_callbacks unless @options.skip_perform_callbacks
|
182
|
+
|
183
|
+
execute_perform
|
184
|
+
end
|
185
|
+
|
186
|
+
def perform(input_context = {})
|
187
|
+
perform!(input_context)
|
188
|
+
rescue Error => e
|
189
|
+
e.result
|
190
|
+
rescue StandardError => e
|
191
|
+
Result.failure(errors: e.message)
|
192
|
+
end
|
193
|
+
|
194
|
+
def rollback; end
|
195
|
+
|
196
|
+
def with_options(options)
|
197
|
+
@options = Options.new(options.deep_dup)
|
198
|
+
self
|
199
|
+
end
|
200
|
+
|
201
|
+
protected
|
202
|
+
|
203
|
+
attr_reader :context
|
204
|
+
|
205
|
+
def create_input_context
|
206
|
+
run_callbacks :input_context_create do
|
207
|
+
@input_context = self.class.input_context_class.new(@raw_input.deep_dup)
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
def create_output_context
|
212
|
+
run_callbacks :output_context_create do
|
213
|
+
@output_context = self.class.output_context_class.new(@context.attributes.deep_dup)
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
def create_runtime_context
|
218
|
+
run_callbacks :runtime_context_create do
|
219
|
+
@context = self.class.runtime_context_class.new(@input_context.to_h.deep_dup)
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
def execute_perform
|
16
224
|
with_notification(:perform) do |payload|
|
17
225
|
interact
|
18
|
-
|
226
|
+
create_output_context
|
227
|
+
validate_output_context!
|
19
228
|
payload[:result] = Result.success(data: output_to_result_context!)
|
20
229
|
end
|
21
230
|
end
|
22
231
|
|
23
|
-
|
232
|
+
def execute_perform_with_callbacks
|
233
|
+
run_callbacks :perform do
|
234
|
+
execute_perform
|
235
|
+
end
|
236
|
+
end
|
24
237
|
|
25
|
-
def
|
26
|
-
|
27
|
-
with_notification(:rollback) do |payload|
|
238
|
+
def execute_rollback_with_callbacks
|
239
|
+
run_callbacks :rollback do
|
28
240
|
rollback
|
29
|
-
|
30
|
-
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
def fail!(errors = {})
|
245
|
+
run_callbacks :fail do
|
246
|
+
result = nil
|
247
|
+
with_notification(:rollback) do |payload|
|
248
|
+
perform_rollback unless @options.skip_rollback
|
249
|
+
result = Result.failure(data: @output, errors: errors)
|
250
|
+
payload[:result] = result
|
251
|
+
end
|
31
252
|
end
|
32
253
|
|
33
254
|
raise Error, result
|
34
255
|
end
|
35
256
|
|
257
|
+
def output_to_result_context!
|
258
|
+
self.class.send(:result_context).for_output_context(self.class, @output_context)
|
259
|
+
end
|
260
|
+
|
261
|
+
def perform_rollback
|
262
|
+
return execute_rollback_with_callbacks unless @options.skip_rollback_callbacks
|
263
|
+
|
264
|
+
rollback
|
265
|
+
end
|
266
|
+
|
267
|
+
def validate_input_context!
|
268
|
+
return unless @options.validate && @options.validate_input_context
|
269
|
+
|
270
|
+
run_callbacks :input_context_validation do
|
271
|
+
@input_context.valid?
|
272
|
+
end
|
273
|
+
return if @input_context.errors.empty?
|
274
|
+
|
275
|
+
raise Error,
|
276
|
+
Result.failure(errors: @input_context.errors,
|
277
|
+
status: Result::STATUS[:failed_at_input])
|
278
|
+
end
|
279
|
+
|
280
|
+
def validate_output_context!
|
281
|
+
return unless @options.validate && @options.validate_output_context
|
282
|
+
|
283
|
+
run_callbacks :output_context_validation do
|
284
|
+
@output_context.valid?
|
285
|
+
end
|
286
|
+
|
287
|
+
return if @output_context.errors.empty?
|
288
|
+
|
289
|
+
raise Error,
|
290
|
+
Result.failure(errors: @output_context.errors,
|
291
|
+
status: Result::STATUS[:failed_at_output])
|
292
|
+
end
|
293
|
+
|
36
294
|
def with_notification(action)
|
37
295
|
ActiveSupport::Notifications.instrument("#{self.class.name}::#{action.to_s.classify}") do |payload|
|
38
296
|
yield payload if block_given?
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveInteractor
|
4
|
+
module Interactor
|
5
|
+
class Options
|
6
|
+
DEFAULTS = {
|
7
|
+
skip_perform_callbacks: false,
|
8
|
+
skip_rollback: false,
|
9
|
+
skip_rollback_callbacks: false,
|
10
|
+
validate: true,
|
11
|
+
validate_input_context: true,
|
12
|
+
validate_output_context: true
|
13
|
+
}.freeze
|
14
|
+
|
15
|
+
attr_reader(*DEFAULTS.keys)
|
16
|
+
|
17
|
+
def initialize(options = {})
|
18
|
+
prepared_options = DEFAULTS.merge(options.deep_dup)
|
19
|
+
prepared_options.each_pair { |key, value| instance_variable_set(:"@#{key}", value) }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -1,64 +1,38 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ActiveInteractor
|
4
|
-
#
|
4
|
+
# An interactor result
|
5
5
|
#
|
6
6
|
# @!attribute [r] data
|
7
|
+
# @return [ActiveInteractor::Context::Result, Object, nil] the data returned by the interactor
|
7
8
|
#
|
8
|
-
# @example
|
9
|
-
#
|
10
|
-
# argument :login, String, 'The login for the User', required: true
|
11
|
-
# argument :password, String, 'The password for the User', required: true
|
12
|
-
# argument :password_confirmation, String, 'The password confirmation for the user', required: true
|
13
|
-
#
|
14
|
-
# returns :user, User, 'The created User', required: true
|
15
|
-
#
|
16
|
-
# def interact
|
17
|
-
# context.user = User.new(context)
|
18
|
-
# fail!(context.user.errors) unless context.user.save
|
19
|
-
# end
|
20
|
-
# end
|
21
|
-
#
|
22
|
-
# result = CreateUser.perform(login: 'johndoe', password: 'password', password_confirmation: 'password')
|
9
|
+
# @example
|
10
|
+
# result = CreateUser.perform(email: 'hello@aaronmallen.me', password: 'password')
|
23
11
|
# result.data.user
|
24
|
-
#
|
25
|
-
# #=> <# User @login='johndoe'>
|
26
|
-
#
|
27
|
-
# @return [ActiveInteractor::Context::Result] the data returned by the
|
28
|
-
# {ActiveInteractor::Interactor::Base interactor}
|
12
|
+
# #=> <#User @email="hello@aaronmallen.me">
|
29
13
|
#
|
30
14
|
# @!attribute [r] errors
|
31
|
-
# @return [ActiveModel::Errors] the errors
|
32
|
-
#
|
33
|
-
# @
|
15
|
+
# @return [ActiveModel::Errors] the errors from the interactor
|
16
|
+
#
|
17
|
+
# @example
|
18
|
+
# result = CreateUser.perform(
|
19
|
+
# email: 'hello@aaronmallen',
|
20
|
+
# password: 'password',
|
21
|
+
# password_confirmation: 'not password'
|
22
|
+
# )
|
23
|
+
# result.errors.full_messages
|
24
|
+
# #=> ["Password confirmation doesn't match Password"]
|
34
25
|
class Result
|
35
|
-
|
26
|
+
extend ActiveModel::Naming
|
36
27
|
|
37
|
-
|
38
|
-
|
39
|
-
#
|
40
|
-
# @example When an {ActiveInteractor::Interactor::Base interactor} succeeds
|
41
|
-
# result = CreateUser.perform(login: 'johndoe', password: 'password', password_confirmation: 'password')
|
42
|
-
# result.to_hash
|
43
|
-
#
|
44
|
-
# #=> { :success => true, :errors => {}, :data => { :login => 'johndoe' } }
|
45
|
-
#
|
46
|
-
# @example When an {ActiveInteractor::Interactor::Base interactor} fails
|
47
|
-
# result = CreateUser.perform(login: 'johndoe', password: 'password', password_confirmation: 'notpassword')
|
48
|
-
# result.to_hash
|
49
|
-
#
|
50
|
-
# #=> {
|
51
|
-
# #=> :success => false,
|
52
|
-
# #=> :errors => { :password_confirmation => ["doesn't match Password"] },
|
53
|
-
# #=> :data => { :login => 'johndoe' }
|
54
|
-
# #=> }
|
55
|
-
#
|
56
|
-
# @deprecated will be removed in version 2.0.0-alpha.3.0.0
|
57
|
-
# use {#to_hash} instead
|
58
|
-
# @return [Hash {Symbol => Boolean, Hash}]
|
28
|
+
attr_reader :data, :errors
|
29
|
+
|
30
|
+
# Return a result as a JSON string
|
59
31
|
delegate :to_json, to: :to_hash
|
60
32
|
|
61
|
-
#
|
33
|
+
# Status codes for interactor results
|
34
|
+
#
|
35
|
+
# @visibility private
|
62
36
|
STATUS = {
|
63
37
|
success: 0,
|
64
38
|
failed_at_input: 1,
|
@@ -66,15 +40,16 @@ module ActiveInteractor
|
|
66
40
|
failed_at_output: 3
|
67
41
|
}.freeze
|
68
42
|
|
69
|
-
attr_reader :data
|
70
|
-
|
71
43
|
class << self
|
72
|
-
#
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
# @
|
44
|
+
# Return a failed result
|
45
|
+
#
|
46
|
+
# @visibility private
|
47
|
+
#
|
48
|
+
# @param data [ActiveInteractor::Context::Result, Object, nil] the data returned by the interactor
|
49
|
+
# @param errors [ActiveModel::Errors, Hash, String] the errors from the interactor
|
50
|
+
# @param status [Integer] the status code for the result
|
51
|
+
#
|
52
|
+
# @return [ActiveInteractor::Result] the failed result
|
78
53
|
def failure(data: {}, errors: {}, status: STATUS[:failed_at_runtime])
|
79
54
|
result = new(status: status, data: data)
|
80
55
|
parse_errors(errors).each_pair do |attribute, messages|
|
@@ -83,6 +58,31 @@ module ActiveInteractor
|
|
83
58
|
result
|
84
59
|
end
|
85
60
|
|
61
|
+
# Needed for ActiveModel::Errors
|
62
|
+
#
|
63
|
+
# @visibility private
|
64
|
+
def human_attribute_name(attribute, _options = {})
|
65
|
+
attribute.respond_to?(:to_s) ? attribute.to_s.humanize : attribute
|
66
|
+
end
|
67
|
+
|
68
|
+
# Needed for ActiveModel::Errors
|
69
|
+
#
|
70
|
+
# @visibility private
|
71
|
+
def lookup_ancestors
|
72
|
+
[self]
|
73
|
+
end
|
74
|
+
|
75
|
+
# Return a successful result
|
76
|
+
#
|
77
|
+
# @visibility private
|
78
|
+
#
|
79
|
+
# @param data [ActiveInteractor::Context::Result, Object, nil] the data returned by the interactor
|
80
|
+
#
|
81
|
+
# @return [ActiveInteractor::Result] the successful result
|
82
|
+
def success(data: {})
|
83
|
+
new(status: STATUS[:success], data: data)
|
84
|
+
end
|
85
|
+
|
86
86
|
private
|
87
87
|
|
88
88
|
def parse_errors(errors)
|
@@ -98,68 +98,90 @@ module ActiveInteractor
|
|
98
98
|
end
|
99
99
|
|
100
100
|
private_class_method :new
|
101
|
-
|
101
|
+
|
102
|
+
# Create a new instance of {ActiveInteractor::Result}
|
103
|
+
#
|
104
|
+
# @visibility private
|
105
|
+
#
|
106
|
+
# @param status [Integer] the status code for the result
|
107
|
+
# @param data [ActiveInteractor::Context::Result, Object, nil] the data returned by the interactor
|
108
|
+
#
|
109
|
+
# @return [ActiveInteractor::Result] the result
|
102
110
|
def initialize(status:, data: {})
|
103
111
|
@status = status
|
104
112
|
@data = data
|
105
113
|
@errors = ActiveModel::Errors.new(self)
|
106
114
|
end
|
107
115
|
|
108
|
-
# Whether or not the
|
109
|
-
#
|
110
|
-
# @example When
|
111
|
-
# result = CreateUser.perform(
|
116
|
+
# Whether or not the result is a failure
|
117
|
+
#
|
118
|
+
# @example When the result is a success
|
119
|
+
# result = CreateUser.perform(
|
120
|
+
# email: 'hello@aaronmallen',
|
121
|
+
# password: 'password',
|
122
|
+
# password_confirmation: 'password'
|
123
|
+
# )
|
112
124
|
# result.failure?
|
125
|
+
# #=> false
|
113
126
|
#
|
114
|
-
#
|
115
|
-
#
|
116
|
-
# @
|
117
|
-
#
|
127
|
+
# @example When the result is a failure
|
128
|
+
# result = CreateUser.perform(
|
129
|
+
# email: 'hello@aaronmallen',
|
130
|
+
# password: 'password',
|
131
|
+
# password_confirmation: 'not password'
|
132
|
+
# )
|
118
133
|
# result.failure?
|
134
|
+
# #=> true
|
119
135
|
#
|
120
|
-
#
|
121
|
-
#
|
122
|
-
# @return [Boolean]
|
136
|
+
# @return [Boolean] whether or not the result is a failure
|
123
137
|
def failure?
|
124
138
|
!success?
|
125
139
|
end
|
126
140
|
alias failed? failure?
|
127
141
|
|
128
|
-
#
|
142
|
+
# Needed for ActiveModel::Errors
|
143
|
+
#
|
144
|
+
# @visibility private
|
129
145
|
def read_attribute_for_validation(attribute_name)
|
130
146
|
data&.send(attribute_name.to_sym)
|
131
147
|
end
|
132
148
|
|
133
|
-
# Whether or not the
|
149
|
+
# Whether or not the result is a success
|
150
|
+
#
|
151
|
+
# @example When the result is a success
|
152
|
+
# result = CreateUser.perform(
|
153
|
+
# email: 'hello@aaronmallen',
|
154
|
+
# password: 'password',
|
155
|
+
# password_confirmation: 'password'
|
156
|
+
# )
|
157
|
+
# result.success?
|
158
|
+
# #=> true
|
159
|
+
#
|
160
|
+
# @example When the result is a failure
|
161
|
+
# result = CreateUser.perform(
|
162
|
+
# email: 'hello@aaronmallen',
|
163
|
+
# password: 'password',
|
164
|
+
# password_confirmation: 'not password'
|
165
|
+
# )
|
166
|
+
# result.success?
|
167
|
+
# #=> false
|
134
168
|
#
|
135
|
-
# @return [Boolean]
|
169
|
+
# @return [Boolean] whether or not the result is a failure
|
136
170
|
def success?
|
137
171
|
@status == STATUS[:success]
|
138
172
|
end
|
139
173
|
alias successful? success?
|
140
174
|
|
141
|
-
#
|
142
|
-
#
|
143
|
-
# @example When an {ActiveInteractor::Interactor::Base interactor} succeeds
|
144
|
-
# result = CreateUser.perform(login: 'johndoe', password: 'password', password_confirmation: 'password')
|
145
|
-
# result.to_hash
|
175
|
+
# Return the result as a hash
|
146
176
|
#
|
147
|
-
#
|
148
|
-
#
|
149
|
-
# @example When an {ActiveInteractor::Interactor::Base interactor} fails
|
150
|
-
# result = CreateUser.perform(login: 'johndoe', password: 'password', password_confirmation: 'notpassword')
|
177
|
+
# @example
|
178
|
+
# result = CreateUser.perform(email: 'hello@aaronmallen', password: 'password')
|
151
179
|
# result.to_hash
|
152
|
-
#
|
153
|
-
# #=> {
|
154
|
-
# #=> :success => false,
|
155
|
-
# #=> :errors => { :password_confirmation => ["doesn't match Password"] },
|
156
|
-
# #=> :data => { :login => 'johndoe' }
|
157
|
-
# #=> }
|
158
|
-
#
|
159
|
-
# @return [Hash {Symbol => Boolean, Hash}]
|
180
|
+
# #=> { success: true, status: 0, errors: {}, data: { user: <#User> } }
|
160
181
|
def to_hash
|
161
182
|
{
|
162
183
|
success: success?,
|
184
|
+
status: @status,
|
163
185
|
errors: errors.to_hash,
|
164
186
|
data: data.to_json
|
165
187
|
}
|
data/lib/active_interactor.rb
CHANGED
@@ -33,6 +33,8 @@ require_relative 'active_interactor/errors'
|
|
33
33
|
# It has features like rich support for attributes, callbacks, and validations, and
|
34
34
|
# thread safe performance methods.
|
35
35
|
#
|
36
|
+
# {https://activeinteractor.org/docs/getting-started Getting Started}
|
37
|
+
#
|
36
38
|
# {file:CHANGELOG.md Changelog}
|
37
39
|
#
|
38
40
|
# {file:HUMANS.md Acknowledgements}
|
@@ -44,8 +46,6 @@ require_relative 'active_interactor/errors'
|
|
44
46
|
module ActiveInteractor
|
45
47
|
extend ActiveSupport::Autoload
|
46
48
|
|
47
|
-
autoload :ActiveModelErrorMethods
|
48
|
-
autoload :Base
|
49
49
|
autoload :Context
|
50
50
|
autoload :Interactor
|
51
51
|
autoload :Result
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activeinteractor
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.0.alpha.
|
4
|
+
version: 2.0.0.alpha.4.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Aaron Allen
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-01-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activemodel
|
@@ -63,12 +63,10 @@ files:
|
|
63
63
|
- LICENSE
|
64
64
|
- README.md
|
65
65
|
- lib/active_interactor.rb
|
66
|
-
- lib/active_interactor/active_model_error_methods.rb
|
67
66
|
- lib/active_interactor/context.rb
|
68
67
|
- lib/active_interactor/context/attribute.rb
|
69
|
-
- lib/active_interactor/context/attribute_assignment.rb
|
70
|
-
- lib/active_interactor/context/attribute_registration.rb
|
71
68
|
- lib/active_interactor/context/attribute_set.rb
|
69
|
+
- lib/active_interactor/context/attribute_validation.rb
|
72
70
|
- lib/active_interactor/context/base.rb
|
73
71
|
- lib/active_interactor/context/input.rb
|
74
72
|
- lib/active_interactor/context/output.rb
|
@@ -77,8 +75,7 @@ files:
|
|
77
75
|
- lib/active_interactor/errors.rb
|
78
76
|
- lib/active_interactor/interactor.rb
|
79
77
|
- lib/active_interactor/interactor/base.rb
|
80
|
-
- lib/active_interactor/interactor/
|
81
|
-
- lib/active_interactor/interactor/interaction_methods.rb
|
78
|
+
- lib/active_interactor/interactor/options.rb
|
82
79
|
- lib/active_interactor/result.rb
|
83
80
|
- lib/active_interactor/type.rb
|
84
81
|
- lib/active_interactor/type/base.rb
|
@@ -92,10 +89,10 @@ licenses:
|
|
92
89
|
- MIT
|
93
90
|
metadata:
|
94
91
|
bug_tracker_uri: https://github.com/activeinteractor/activeinteractor/issues
|
95
|
-
changelog_uri: https://github.com/activeinteractor/activeinteractor/blob/v2.0.0-alpha.
|
92
|
+
changelog_uri: https://github.com/activeinteractor/activeinteractor/blob/v2.0.0-alpha.4.0.0/CHANGELOG.md
|
96
93
|
homepage_uri: https://activeinteractor.org
|
97
|
-
source_code_uri: https://github.com/activeinteractor/activeinteractor/tree/v2.0.0-alpha.
|
98
|
-
documentation_uri: https://activeinteractor.org/api/activeinteractor/v2.0.0-alpha.
|
94
|
+
source_code_uri: https://github.com/activeinteractor/activeinteractor/tree/v2.0.0-alpha.4.0.0
|
95
|
+
documentation_uri: https://activeinteractor.org/api/activeinteractor/v2.0.0-alpha.4.0.0
|
99
96
|
wiki_uri: https://github.com/activeinteractor/activeinteractor/wiki
|
100
97
|
rubygems_mfa_required: 'true'
|
101
98
|
post_install_message:
|
@@ -113,7 +110,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
113
110
|
- !ruby/object:Gem::Version
|
114
111
|
version: 1.3.1
|
115
112
|
requirements: []
|
116
|
-
rubygems_version: 3.4.
|
113
|
+
rubygems_version: 3.4.22
|
117
114
|
signing_key:
|
118
115
|
specification_version: 4
|
119
116
|
summary: Ruby interactors with ActiveModel::Validations
|
@@ -1,30 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module ActiveInteractor
|
4
|
-
# @private
|
5
|
-
module ActiveModelErrorMethods
|
6
|
-
extend ActiveSupport::Concern
|
7
|
-
|
8
|
-
attr_reader :errors
|
9
|
-
|
10
|
-
# @private
|
11
|
-
module ClassMethods
|
12
|
-
def human_attribute_name(attribute, _options = {})
|
13
|
-
attribute.respond_to?(:to_s) ? attribute.to_s.humanize : attribute
|
14
|
-
end
|
15
|
-
|
16
|
-
def lookup_ancestors
|
17
|
-
[self]
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
included do
|
22
|
-
extend ActiveModel::Naming
|
23
|
-
extend ClassMethods
|
24
|
-
end
|
25
|
-
|
26
|
-
def read_attribute_for_validation(attribute_name)
|
27
|
-
send(attribute_name.to_sym)
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
@@ -1,64 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module ActiveInteractor
|
4
|
-
module Context
|
5
|
-
module AttributeAssignment
|
6
|
-
extend ActiveSupport::Concern
|
7
|
-
|
8
|
-
def initialize(attributes = {})
|
9
|
-
attribute_set.attributes.each do |attribute|
|
10
|
-
next unless attributes.with_indifferent_access.key?(attribute.name)
|
11
|
-
|
12
|
-
assign_attribute_value(attribute.name, attributes.with_indifferent_access[attribute.name])
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
def [](attribute_name)
|
17
|
-
read_attribute_value(attribute_name)
|
18
|
-
end
|
19
|
-
|
20
|
-
def []=(attribute_name, value)
|
21
|
-
assign_attribute_value(attribute_name, value)
|
22
|
-
end
|
23
|
-
|
24
|
-
protected
|
25
|
-
|
26
|
-
def assign_attribute_value(attribute_name, value)
|
27
|
-
attribute = attribute_set.find(attribute_name)
|
28
|
-
raise NoMethodError, "unknown attribute '#{attribute_name}' for #{self.class.name}" unless attribute
|
29
|
-
|
30
|
-
attribute.assign_value(value)
|
31
|
-
end
|
32
|
-
|
33
|
-
def assignment_method_missing(method_name, *arguments)
|
34
|
-
if arguments.length != 1
|
35
|
-
raise ArgumentError,
|
36
|
-
"wrong number of arguments (given #{arguments.length}, expected 1)"
|
37
|
-
end
|
38
|
-
|
39
|
-
assign_attribute_value(method_name.to_s.delete('=').to_sym, arguments.first)
|
40
|
-
end
|
41
|
-
|
42
|
-
def method_missing(method_name, *arguments)
|
43
|
-
return super unless respond_to_missing?(method_name)
|
44
|
-
return assignment_method_missing(method_name, *arguments) if method_name.to_s.end_with?('=')
|
45
|
-
|
46
|
-
read_attribute_value(method_name)
|
47
|
-
end
|
48
|
-
|
49
|
-
def read_attribute_value(attribute_name)
|
50
|
-
attribute = attribute_set.find(attribute_name)
|
51
|
-
raise NoMethodError, "unknown attribute '#{attribute_name}' for #{self.class.name}" unless attribute
|
52
|
-
|
53
|
-
attribute.value
|
54
|
-
end
|
55
|
-
|
56
|
-
def respond_to_missing?(method_name, _include_private = false)
|
57
|
-
return true if attribute_set.attribute_names.include?(method_name.to_sym)
|
58
|
-
return true if attribute_set.attribute_names.include?(method_name.to_s.delete('=').to_sym)
|
59
|
-
|
60
|
-
super
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
@@ -1,27 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module ActiveInteractor
|
4
|
-
module Context
|
5
|
-
module AttributeRegistration
|
6
|
-
extend ActiveSupport::Concern
|
7
|
-
|
8
|
-
module ClassMethods
|
9
|
-
protected
|
10
|
-
|
11
|
-
def attribute_set
|
12
|
-
@attribute_set ||= AttributeSet.new(self)
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
included do
|
17
|
-
extend ClassMethods
|
18
|
-
end
|
19
|
-
|
20
|
-
protected
|
21
|
-
|
22
|
-
def attribute_set
|
23
|
-
@attribute_set ||= AttributeSet.new(self, *self.class.send(:attribute_set).attributes.map(&:dup))
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
@@ -1,83 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module ActiveInteractor
|
4
|
-
module Interactor
|
5
|
-
module ContextMethods
|
6
|
-
extend ActiveSupport::Concern
|
7
|
-
|
8
|
-
module ClassMethods
|
9
|
-
delegate :argument, :argument_names, :arguments, to: :input_context_class
|
10
|
-
delegate :returns, :field_names, :fields, to: :output_context_class
|
11
|
-
delegate(*ActiveModel::Validations::ClassMethods.instance_methods, to: :input_context_class, prefix: :input)
|
12
|
-
delegate(*ActiveModel::Validations::HelperMethods.instance_methods, to: :input_context_class, prefix: :input)
|
13
|
-
delegate(*ActiveModel::Validations::ClassMethods.instance_methods, to: :output_context_class, prefix: :output)
|
14
|
-
delegate(*ActiveModel::Validations::HelperMethods.instance_methods, to: :output_context_class, prefix: :output)
|
15
|
-
|
16
|
-
def input_context_class
|
17
|
-
@input_context_class ||= const_set(:InputContext, Class.new(Context::Input))
|
18
|
-
end
|
19
|
-
|
20
|
-
def accepts_arguments_matching(set_input_context_class)
|
21
|
-
@input_context_class = set_input_context_class
|
22
|
-
end
|
23
|
-
alias input_context accepts_arguments_matching
|
24
|
-
alias input_type accepts_arguments_matching
|
25
|
-
|
26
|
-
def output_context_class
|
27
|
-
@output_context_class ||= const_set(:OutputContext, Class.new(Context::Output))
|
28
|
-
end
|
29
|
-
|
30
|
-
def returns_data_matching(set_output_context_class)
|
31
|
-
@output_context_class = set_output_context_class
|
32
|
-
end
|
33
|
-
alias output_context returns_data_matching
|
34
|
-
alias output_type returns_data_matching
|
35
|
-
|
36
|
-
def runtime_context_class
|
37
|
-
@runtime_context_class ||= begin
|
38
|
-
context_class = const_set(:RuntimeContext, Class.new(Context::Runtime))
|
39
|
-
context_class.send(:attribute_set).merge(input_context_class.send(:attribute_set).attributes)
|
40
|
-
context_class.send(:attribute_set).merge(output_context_class.send(:attribute_set).attributes)
|
41
|
-
context_class
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
protected
|
46
|
-
|
47
|
-
def result_context
|
48
|
-
@result_context ||= Context::Result.register_owner(self)
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
included do
|
53
|
-
extend ClassMethods
|
54
|
-
|
55
|
-
protected
|
56
|
-
|
57
|
-
attr_reader :context
|
58
|
-
end
|
59
|
-
|
60
|
-
protected # rubocop:disable Lint/UselessAccessModifier
|
61
|
-
|
62
|
-
def generate_and_validate_output_context!
|
63
|
-
@output = self.class.output_context_class.new(context.attributes)
|
64
|
-
@output.valid?
|
65
|
-
return if @output.errors.empty?
|
66
|
-
|
67
|
-
raise Error, Result.failure(errors: @output.errors, status: Result::STATUS[:failed_at_output])
|
68
|
-
end
|
69
|
-
|
70
|
-
def output_to_result_context!
|
71
|
-
self.class.send(:result_context).for_output_context(self.class, @output)
|
72
|
-
end
|
73
|
-
|
74
|
-
def validate_input_and_generate_runtime_context!
|
75
|
-
@input = self.class.input_context_class.new(@raw_input)
|
76
|
-
@input.valid?
|
77
|
-
return (@context = self.class.runtime_context_class.new(@raw_input)) if @input.errors.empty?
|
78
|
-
|
79
|
-
raise Error, Result.failure(errors: @input.errors, status: Result::STATUS[:failed_at_input])
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|
@@ -1,38 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module ActiveInteractor
|
4
|
-
module Interactor
|
5
|
-
module InteractionMethods
|
6
|
-
extend ActiveSupport::Concern
|
7
|
-
|
8
|
-
module ClassMethods
|
9
|
-
def perform!(input_context = {})
|
10
|
-
new(input_context).perform!
|
11
|
-
end
|
12
|
-
|
13
|
-
def perform(input_context = {})
|
14
|
-
perform!(input_context)
|
15
|
-
rescue Error => e
|
16
|
-
e.result
|
17
|
-
rescue StandardError => e
|
18
|
-
Result.failure(errors: e.message)
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
included do
|
23
|
-
extend ClassMethods
|
24
|
-
end
|
25
|
-
|
26
|
-
def perform
|
27
|
-
perform!
|
28
|
-
rescue Error => e
|
29
|
-
e.result
|
30
|
-
rescue StandardError => e
|
31
|
-
Result.failure(errors: e.message)
|
32
|
-
end
|
33
|
-
|
34
|
-
def interact; end
|
35
|
-
def rollback; end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|