activeinteractor 0.1.7 → 1.0.0.beta.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/CHANGELOG.md +36 -1
- data/README.md +397 -395
- data/lib/active_interactor.rb +12 -30
- data/lib/active_interactor/base.rb +18 -8
- data/lib/active_interactor/context.rb +4 -181
- data/lib/active_interactor/context/attributes.rb +37 -149
- data/lib/active_interactor/context/base.rb +141 -0
- data/lib/active_interactor/context/loader.rb +45 -0
- data/lib/active_interactor/error.rb +22 -15
- data/lib/active_interactor/interactor.rb +24 -57
- data/lib/active_interactor/interactor/callbacks.rb +64 -76
- data/lib/active_interactor/interactor/context.rb +97 -63
- data/lib/active_interactor/interactor/worker.rb +22 -65
- data/lib/active_interactor/organizer.rb +180 -164
- data/lib/active_interactor/version.rb +2 -3
- data/lib/rails/generators/active_interactor.rb +2 -37
- data/lib/rails/generators/active_interactor/application_interactor_generator.rb +23 -0
- data/lib/rails/generators/active_interactor/install_generator.rb +8 -12
- data/lib/rails/generators/active_interactor/templates/application_context.rb +4 -0
- data/lib/rails/generators/{templates/application_interactor.erb → active_interactor/templates/application_interactor.rb} +0 -0
- data/lib/rails/generators/active_interactor/templates/application_organizer.rb +4 -0
- data/lib/rails/generators/active_interactor/templates/initializer.erb +5 -0
- data/lib/rails/generators/interactor/context/rspec_generator.rb +19 -0
- data/lib/rails/generators/interactor/context/templates/rspec.erb +7 -0
- data/lib/rails/generators/interactor/context/templates/test_unit.erb +9 -0
- data/lib/rails/generators/interactor/context/test_unit_generator.rb +19 -0
- data/lib/rails/generators/interactor/context_generator.rb +19 -0
- data/lib/rails/generators/interactor/interactor_generator.rb +8 -3
- data/lib/rails/generators/interactor/organizer_generator.rb +8 -3
- data/lib/rails/generators/interactor/rspec_generator.rb +4 -3
- data/lib/rails/generators/interactor/templates/context.erb +4 -0
- data/lib/rails/generators/{templates → interactor/templates}/interactor.erb +0 -0
- data/lib/rails/generators/{templates → interactor/templates}/organizer.erb +1 -1
- data/lib/rails/generators/{templates → interactor/templates}/rspec.erb +0 -0
- data/lib/rails/generators/{templates → interactor/templates}/test_unit.erb +0 -0
- data/lib/rails/generators/interactor/test_unit_generator.rb +4 -3
- data/spec/active_interactor/base_spec.rb +51 -0
- data/spec/active_interactor/context/base_spec.rb +229 -0
- data/spec/active_interactor/error_spec.rb +43 -0
- data/spec/active_interactor/interactor/worker_spec.rb +89 -0
- data/spec/active_interactor/organizer_spec.rb +178 -0
- data/spec/active_interactor_spec.rb +26 -0
- data/spec/integration/basic_callback_integration_spec.rb +355 -0
- data/spec/integration/basic_context_integration_spec.rb +73 -0
- data/spec/integration/basic_integration_spec.rb +220 -0
- data/spec/integration/basic_validations_integration_spec.rb +204 -0
- data/spec/spec_helper.rb +44 -0
- data/spec/support/helpers/factories.rb +41 -0
- data/spec/support/shared_examples/a_class_with_interactor_callback_methods_example.rb +99 -0
- data/spec/support/shared_examples/a_class_with_interactor_context_methods_example.rb +58 -0
- data/spec/support/shared_examples/a_class_with_interactor_methods_example.rb +21 -0
- data/spec/support/shared_examples/a_class_with_organizer_callback_methods_example.rb +39 -0
- data/spec/support/spec_helpers.rb +7 -0
- metadata +68 -138
- data/lib/active_interactor/configuration.rb +0 -38
- data/lib/active_interactor/interactor/execution.rb +0 -24
- data/lib/rails/generators/templates/initializer.erb +0 -15
@@ -1,96 +1,130 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'active_model'
|
4
|
+
require 'active_support/core_ext/string/inflections'
|
5
|
+
|
3
6
|
module ActiveInteractor
|
4
7
|
module Interactor
|
5
|
-
#
|
6
|
-
#
|
7
|
-
# @api private
|
8
|
+
# Interactor context methods included by all {Base}
|
8
9
|
# @author Aaron Allen <hello@aaronmallen.me>
|
9
10
|
# @since 0.0.1
|
10
|
-
#
|
11
|
+
# @!attribute [rw] context
|
12
|
+
# @api private
|
13
|
+
# @return [Context::Base] an instance of the interactor's context class
|
11
14
|
module Context
|
12
|
-
|
15
|
+
# @!method context_fail!(errors = nil)
|
16
|
+
# Fail the interactor's context
|
17
|
+
# @since 1.0.0
|
18
|
+
# @see ActiveInteractor::Context::Base#fail!
|
19
|
+
|
20
|
+
# @!method context_rollback!
|
21
|
+
# Rollback the interactor's context
|
22
|
+
# @since 1.0.0
|
23
|
+
# @see ActiveInteractor::Context::Base#rollback!
|
24
|
+
|
25
|
+
# @!method context_valid?(context = nil)
|
26
|
+
# Whether or not the interactor's context is valid
|
27
|
+
# @since 1.0.0
|
28
|
+
# @see ActiveInteractor::Context::Base#valid?
|
29
|
+
def self.included(base)
|
30
|
+
base.class_eval do
|
31
|
+
extend ClassMethods
|
32
|
+
delegate :fail!, :rollback!, to: :context, prefix: true
|
33
|
+
delegate(*ActiveModel::Validations.instance_methods, to: :context, prefix: true)
|
34
|
+
delegate(*ActiveModel::Validations::HelperMethods.instance_methods, to: :context, prefix: true)
|
13
35
|
|
14
|
-
|
15
|
-
extend ClassMethods
|
16
|
-
include Callbacks
|
36
|
+
private
|
17
37
|
|
18
|
-
|
19
|
-
|
38
|
+
attr_accessor :context
|
39
|
+
end
|
20
40
|
end
|
21
41
|
|
42
|
+
# Interactor context class methods extended by all {Base}
|
22
43
|
module ClassMethods
|
23
44
|
delegate(*ActiveModel::Validations::ClassMethods.instance_methods, to: :context_class, prefix: :context)
|
24
45
|
delegate(*ActiveModel::Validations::HelperMethods.instance_methods, to: :context_class, prefix: :context)
|
46
|
+
# @!method context_attributes(*attributes)
|
47
|
+
# Set or get attributes defined on the interactor's context class
|
48
|
+
# @since 0.0.1
|
49
|
+
# @see ActiveInteractor::Context::Attributes#attributes
|
50
|
+
delegate :attributes, to: :context_class, prefix: :context
|
25
51
|
|
26
|
-
#
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
#
|
32
|
-
#
|
33
|
-
# @example
|
34
|
-
# class MyInteractor
|
35
|
-
# context_attributes :first_name, :last_name
|
52
|
+
# The context class of the interactor
|
53
|
+
# @note If a context class is not defined on the interactor
|
54
|
+
# with {#contextualize_with} ActiveInteractor will attempt
|
55
|
+
# to find a class with the naming conventions:
|
56
|
+
# `MyInteractor::Context` or `MyInteractorContext`
|
57
|
+
# if no matching context class is found a context class
|
58
|
+
# will be created with the naming convention: `MyInteractor::Context`.
|
59
|
+
# @example With an existing class named `MyInteractor::Context`
|
60
|
+
# class MyInteractor::Context < ActiveInteractor::Context::Base
|
36
61
|
# end
|
37
62
|
#
|
38
|
-
# MyInteractor::
|
39
|
-
# #=> [:first_name, :last_name]
|
40
|
-
# @param attributes [Array] the attributes to assign to the context
|
41
|
-
def context_attributes(*attributes)
|
42
|
-
context_class.attributes = attributes
|
43
|
-
end
|
44
|
-
|
45
|
-
# Assign attribute aliases to the context class of the interactor
|
46
|
-
# any aliases passed to the context will be assigned to the
|
47
|
-
# key of the aliases hash
|
48
|
-
#
|
49
|
-
# @example Assign attribute aliases to the context class
|
50
|
-
# class MyInteractor > ActiveInteractor::Base
|
51
|
-
# context_attributes :first_name, :last_name
|
52
|
-
# context_attribute_aliases last_name: :sir_name
|
63
|
+
# class MyInteractor < ActiveInteractor::Base
|
53
64
|
# end
|
54
65
|
#
|
55
|
-
# MyInteractor
|
56
|
-
# #=>
|
57
|
-
#
|
58
|
-
# class
|
59
|
-
# context_attributes :first_name, :last_name
|
60
|
-
# context_attribute_aliases last_name: %i[sir_name, sirname]
|
66
|
+
# MyInteractor.context_class
|
67
|
+
# #=> MyInteractor::Context
|
68
|
+
# @example With an existing class named `MyInteractorContext`
|
69
|
+
# class MyInteractorContext < ActiveInteractor::Context::Base
|
61
70
|
# end
|
62
71
|
#
|
63
|
-
# MyInteractor::
|
64
|
-
#
|
65
|
-
#
|
66
|
-
# context = MyInteractor.perform(first_name: 'Aaron', sir_name: 'Allen')
|
67
|
-
# #=> <#MyInteractor::Context first_name='Aaron', last_name='Allen')
|
68
|
-
#
|
69
|
-
# context.sir_name
|
70
|
-
# #=> nil
|
71
|
-
#
|
72
|
-
# context.last_name
|
73
|
-
# #=> 'Allen'
|
74
|
-
#
|
75
|
-
# @param aliases [Hash{Symbol => Symbol, Array<Symbol>}] the attribute aliases
|
76
|
-
#
|
77
|
-
# @return [Hash{Symbol => Array<Symbol>}] the attribute aliases
|
78
|
-
def context_attribute_aliases(aliases = {})
|
79
|
-
context_class.alias_attributes(aliases)
|
80
|
-
end
|
81
|
-
|
82
|
-
# The context class of the interactor
|
72
|
+
# class MyInteractor < ActiveInteractor::Base
|
73
|
+
# end
|
83
74
|
#
|
84
|
-
#
|
75
|
+
# MyInteractor.context_class
|
76
|
+
# #=> MyInteractorContext
|
77
|
+
# @example With no existing context class
|
85
78
|
# class MyInteractor < ActiveInteractor::Base
|
86
79
|
# end
|
87
80
|
#
|
88
81
|
# MyInteractor.context_class
|
89
82
|
# #=> MyInteractor::Context
|
83
|
+
# @return [Class] the interactor's context class
|
90
84
|
def context_class
|
91
|
-
|
85
|
+
@context_class ||= ActiveInteractor::Context::Loader.find_or_create(self)
|
86
|
+
end
|
87
|
+
|
88
|
+
# Manual define an interactor's context class
|
89
|
+
# @since 1.0.0
|
90
|
+
# @example
|
91
|
+
# class AGenericContext < ActiveInteractor::Context::Base
|
92
|
+
# end
|
93
|
+
#
|
94
|
+
# class MyInteractor < ActiveInteractor::Base
|
95
|
+
# contextualize_with :a_generic_context
|
96
|
+
# end
|
97
|
+
#
|
98
|
+
# MyInteractor.context_class
|
99
|
+
# #=> AGenericContext
|
100
|
+
# @param klass [String|Symbol|Class] the context class
|
101
|
+
# @raise [Error::InvalidContextClass] if no matching class can be found
|
102
|
+
# @return [Class] the context class
|
103
|
+
def contextualize_with(klass)
|
104
|
+
@context_class = begin
|
105
|
+
context_class = klass.to_s.classify.safe_constantize
|
106
|
+
raise(Error::InvalidContextClass, klass) unless context_class
|
107
|
+
|
108
|
+
context_class
|
109
|
+
end
|
92
110
|
end
|
93
111
|
end
|
112
|
+
|
113
|
+
# @api private
|
114
|
+
# @param context [Context::Base|Hash] attributes to assign to the context
|
115
|
+
# @return [Base] a new instane of {Base}
|
116
|
+
def initialize(context = {})
|
117
|
+
@context = self.class.context_class.new(context)
|
118
|
+
end
|
119
|
+
|
120
|
+
# @api private
|
121
|
+
# Mark the interactor's context as called and return the context
|
122
|
+
# @since 1.0.0
|
123
|
+
# @return [Context::Base] an instance of the interactor's context class
|
124
|
+
def finalize_context!
|
125
|
+
context.called!(self)
|
126
|
+
context
|
127
|
+
end
|
94
128
|
end
|
95
129
|
end
|
96
130
|
end
|
@@ -2,39 +2,40 @@
|
|
2
2
|
|
3
3
|
module ActiveInteractor
|
4
4
|
module Interactor
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
5
|
+
# @api private
|
6
|
+
# Thread safe execution of interactor {Interactor#perform #perform} and
|
7
|
+
# {Interactor#rollback #rollback} methods.
|
8
8
|
# @author Aaron Allen <hello@aaronmallen.me>
|
9
9
|
# @since 0.0.2
|
10
|
-
# @version 0.1
|
11
10
|
class Worker
|
12
11
|
delegate :run_callbacks, to: :interactor
|
13
12
|
|
14
|
-
#
|
15
|
-
# @
|
16
|
-
# @return [ActiveInteractor::Interactor::Worker] a new instance of {Worker}
|
13
|
+
# @param interactor [Base] an instance of interactor
|
14
|
+
# @return [Worker] a new instance of {Worker}
|
17
15
|
def initialize(interactor)
|
18
|
-
@interactor =
|
16
|
+
@interactor = interactor.dup
|
19
17
|
end
|
20
18
|
|
21
|
-
# Calls {#execute_perform!} and rescues {
|
22
|
-
# @return [
|
19
|
+
# Calls {#execute_perform!} and rescues {Error::ContextFailure}
|
20
|
+
# @return [Context::Base] an instance of {Context::Base}
|
23
21
|
def execute_perform
|
24
22
|
execute_perform!
|
25
|
-
rescue
|
26
|
-
|
23
|
+
rescue Error::ContextFailure => e
|
24
|
+
ActiveInteractor.logger.error("ActiveInteractor: #{e}")
|
27
25
|
context
|
28
26
|
end
|
29
27
|
|
30
28
|
# Calls {Interactor#perform} with callbacks and context validation
|
31
|
-
# @raise [
|
32
|
-
# @return [
|
29
|
+
# @raise [Error::ContextFailure] if the context fails
|
30
|
+
# @return [Context::Base] an instance of {Context::Base}
|
33
31
|
def execute_perform!
|
34
32
|
run_callbacks :perform do
|
35
33
|
execute_context!
|
36
|
-
|
37
|
-
|
34
|
+
@context = interactor.finalize_context!
|
35
|
+
rescue StandardError
|
36
|
+
@context = interactor.finalize_context!
|
37
|
+
execute_rollback
|
38
|
+
raise
|
38
39
|
end
|
39
40
|
end
|
40
41
|
|
@@ -43,67 +44,23 @@ module ActiveInteractor
|
|
43
44
|
# rolled back
|
44
45
|
def execute_rollback
|
45
46
|
run_callbacks :rollback do
|
46
|
-
interactor.
|
47
|
+
interactor.context_rollback!
|
47
48
|
end
|
48
49
|
end
|
49
50
|
|
50
51
|
private
|
51
52
|
|
52
|
-
attr_reader :interactor
|
53
|
-
|
54
|
-
def clone_interactor(interactor)
|
55
|
-
cloned = interactor.clone
|
56
|
-
cloned.send(:context=, cloned.send(:context).clone)
|
57
|
-
cloned
|
58
|
-
end
|
59
|
-
|
60
|
-
def context
|
61
|
-
interactor.send(:context)
|
62
|
-
end
|
53
|
+
attr_reader :context, :interactor
|
63
54
|
|
64
55
|
def execute_context!
|
65
|
-
|
66
|
-
context
|
67
|
-
ensure
|
68
|
-
finalize_context!
|
69
|
-
end
|
70
|
-
|
71
|
-
def fail_on_invalid_context!(validation_context = nil)
|
72
|
-
context.fail! if should_fail_on_invalid_context?(validation_context)
|
73
|
-
end
|
74
|
-
|
75
|
-
def finalize_context!
|
76
|
-
context.clean! if interactor.should_clean_context?
|
77
|
-
context.called!
|
78
|
-
end
|
79
|
-
|
80
|
-
def log_context_failure(exception)
|
81
|
-
ActiveInteractor.logger.error("ActiveInteractor: #{exception}")
|
82
|
-
end
|
83
|
-
|
84
|
-
def perform!
|
85
|
-
fail_on_invalid_context!(:calling)
|
56
|
+
interactor.context_fail! unless validate_context(:calling)
|
86
57
|
interactor.perform
|
87
|
-
|
88
|
-
end
|
89
|
-
|
90
|
-
def rollback_context_and_raise!(exception)
|
91
|
-
context.rollback!
|
92
|
-
context.send(:mark_failed!)
|
93
|
-
raise exception
|
94
|
-
end
|
95
|
-
|
96
|
-
def should_fail_on_invalid_context?(validation_context)
|
97
|
-
!validate_context(validation_context) && interactor.fail_on_invalid_context?
|
58
|
+
interactor.context_fail! unless validate_context(:called)
|
98
59
|
end
|
99
60
|
|
100
61
|
def validate_context(validation_context = nil)
|
101
62
|
run_callbacks :validation do
|
102
|
-
|
103
|
-
context.validation_context = validation_context || init_validation_context
|
104
|
-
context.valid?
|
105
|
-
ensure
|
106
|
-
context.validation_context = init_validation_context
|
63
|
+
interactor.context_valid?(validation_context)
|
107
64
|
end
|
108
65
|
end
|
109
66
|
end
|
@@ -1,190 +1,206 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'active_support/core_ext/class/attribute'
|
4
|
+
require 'active_support/core_ext/string/inflections'
|
5
|
+
|
3
6
|
module ActiveInteractor
|
4
|
-
#
|
5
|
-
#
|
7
|
+
# A base Organizer class. All organizers should inherit from
|
8
|
+
# {Organizer}.
|
6
9
|
# @author Aaron Allen <hello@aaronmallen.me>
|
7
10
|
# @since 0.0.1
|
8
|
-
#
|
9
|
-
class
|
11
|
+
# @!attribute [r] organized
|
12
|
+
# @!scope class
|
13
|
+
# @return [Array<Base>] the organized interactors
|
14
|
+
# @example a basic organizer
|
15
|
+
# class MyInteractor1 < ActiveInteractor::Base
|
16
|
+
# def perform
|
17
|
+
# context.interactor1 = true
|
18
|
+
# end
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# class MyInteractor2 < ActiveInteractor::Base
|
22
|
+
# def perform
|
23
|
+
# context.interactor2 = true
|
24
|
+
# end
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# class MyOrganizer < ActiveInteractor::Organizer
|
28
|
+
# organize MyInteractor1, MyInteractor2
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# MyOrganizer.perform
|
32
|
+
# #=> <MyOrganizer::Context interactor1=true interactor2=true>
|
33
|
+
class Organizer < Base
|
34
|
+
class_attribute :organized, instance_writer: false, default: []
|
10
35
|
define_callbacks :each_perform
|
11
36
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
set_callback(:each_perform, :after, *filters, &block)
|
53
|
-
end
|
54
|
-
|
55
|
-
# Define a callback to call around each organized interactor's
|
56
|
-
# {ActiveInteractor::Base.perform} has been invokation
|
57
|
-
#
|
58
|
-
# @example
|
59
|
-
# class MyInteractor1 < ActiveInteractor::Base
|
60
|
-
# before_perform :print_name
|
61
|
-
#
|
62
|
-
# def perform
|
63
|
-
# puts 'MyInteractor1'
|
64
|
-
# end
|
65
|
-
# end
|
66
|
-
#
|
67
|
-
# class MyInteractor2 < ActiveInteractor::Base
|
68
|
-
# before_perform :print_name
|
69
|
-
#
|
70
|
-
# def perform
|
71
|
-
# puts 'MyInteractor2'
|
72
|
-
# end
|
73
|
-
# end
|
74
|
-
#
|
75
|
-
# class MyOrganizer < ActiveInteractor::Organizer
|
76
|
-
# around_each_perform :print_time
|
77
|
-
#
|
78
|
-
# organized MyInteractor1, MyInteractor2
|
79
|
-
#
|
80
|
-
# private
|
81
|
-
#
|
82
|
-
# def print_time
|
83
|
-
# puts Time.now.utc
|
84
|
-
# yield
|
85
|
-
# puts Time.now.utc
|
86
|
-
# end
|
87
|
-
# end
|
88
|
-
#
|
89
|
-
# MyOrganizer.perform(name: 'Aaron')
|
90
|
-
# "2019-04-01 00:00:00 UTC"
|
91
|
-
# "MyInteractor1"
|
92
|
-
# "2019-04-01 00:00:01 UTC"
|
93
|
-
# "2019-04-01 00:00:02 UTC"
|
94
|
-
# "MyInteractor2"
|
95
|
-
# "2019-04-01 00:00:03 UTC"
|
96
|
-
# #=> <MyOrganizer::Context name='Aaron'>
|
97
|
-
def around_each_perform(*filters, &block)
|
98
|
-
set_callback(:each_perform, :around, *filters, &block)
|
99
|
-
end
|
37
|
+
# Define a callback to call after each organized interactor's
|
38
|
+
# {Base.perform} has been invoked
|
39
|
+
# @example
|
40
|
+
# class MyInteractor1 < ActiveInteractor::Base
|
41
|
+
# before_perform :print_name
|
42
|
+
#
|
43
|
+
# def perform
|
44
|
+
# puts 'MyInteractor1'
|
45
|
+
# end
|
46
|
+
# end
|
47
|
+
#
|
48
|
+
# class MyInteractor2 < ActiveInteractor::Base
|
49
|
+
# before_perform :print_name
|
50
|
+
#
|
51
|
+
# def perform
|
52
|
+
# puts 'MyInteractor2'
|
53
|
+
# end
|
54
|
+
# end
|
55
|
+
#
|
56
|
+
# class MyOrganizer < ActiveInteractor::Organizer
|
57
|
+
# after_each_perform :print_done
|
58
|
+
#
|
59
|
+
# organized MyInteractor1, MyInteractor2
|
60
|
+
#
|
61
|
+
# private
|
62
|
+
#
|
63
|
+
# def print_done
|
64
|
+
# puts "done"
|
65
|
+
# end
|
66
|
+
# end
|
67
|
+
#
|
68
|
+
# MyOrganizer.perform(name: 'Aaron')
|
69
|
+
# "MyInteractor1"
|
70
|
+
# "Done"
|
71
|
+
# "MyInteractor2"
|
72
|
+
# "Done"
|
73
|
+
# #=> <MyOrganizer::Context name='Aaron'>
|
74
|
+
def self.after_each_perform(*filters, &block)
|
75
|
+
set_callback(:each_perform, :after, *filters, &block)
|
76
|
+
end
|
100
77
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
78
|
+
# Define a callback to call around each organized interactor's
|
79
|
+
# {Base.perform} has been invokation
|
80
|
+
#
|
81
|
+
# @example
|
82
|
+
# class MyInteractor1 < ActiveInteractor::Base
|
83
|
+
# before_perform :print_name
|
84
|
+
#
|
85
|
+
# def perform
|
86
|
+
# puts 'MyInteractor1'
|
87
|
+
# end
|
88
|
+
# end
|
89
|
+
#
|
90
|
+
# class MyInteractor2 < ActiveInteractor::Base
|
91
|
+
# before_perform :print_name
|
92
|
+
#
|
93
|
+
# def perform
|
94
|
+
# puts 'MyInteractor2'
|
95
|
+
# end
|
96
|
+
# end
|
97
|
+
#
|
98
|
+
# class MyOrganizer < ActiveInteractor::Organizer
|
99
|
+
# around_each_perform :print_time
|
100
|
+
#
|
101
|
+
# organized MyInteractor1, MyInteractor2
|
102
|
+
#
|
103
|
+
# private
|
104
|
+
#
|
105
|
+
# def print_time
|
106
|
+
# puts Time.now.utc
|
107
|
+
# yield
|
108
|
+
# puts Time.now.utc
|
109
|
+
# end
|
110
|
+
# end
|
111
|
+
#
|
112
|
+
# MyOrganizer.perform(name: 'Aaron')
|
113
|
+
# "2019-04-01 00:00:00 UTC"
|
114
|
+
# "MyInteractor1"
|
115
|
+
# "2019-04-01 00:00:01 UTC"
|
116
|
+
# "2019-04-01 00:00:02 UTC"
|
117
|
+
# "MyInteractor2"
|
118
|
+
# "2019-04-01 00:00:03 UTC"
|
119
|
+
# #=> <MyOrganizer::Context name='Aaron'>
|
120
|
+
def self.around_each_perform(*filters, &block)
|
121
|
+
set_callback(:each_perform, :around, *filters, &block)
|
122
|
+
end
|
142
123
|
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
124
|
+
# Define a callback to call before each organized interactor's
|
125
|
+
# {Base.perform} has been invoked
|
126
|
+
#
|
127
|
+
# @example
|
128
|
+
# class MyInteractor1 < ActiveInteractor::Base
|
129
|
+
# before_perform :print_name
|
130
|
+
#
|
131
|
+
# def perform
|
132
|
+
# puts 'MyInteractor1'
|
133
|
+
# end
|
134
|
+
# end
|
135
|
+
#
|
136
|
+
# class MyInteractor2 < ActiveInteractor::Base
|
137
|
+
# before_perform :print_name
|
138
|
+
#
|
139
|
+
# def perform
|
140
|
+
# puts 'MyInteractor2'
|
141
|
+
# end
|
142
|
+
# end
|
143
|
+
#
|
144
|
+
# class MyOrganizer < ActiveInteractor::Organizer
|
145
|
+
# before_each_perform :print_start
|
146
|
+
#
|
147
|
+
# organized MyInteractor1, MyInteractor2
|
148
|
+
#
|
149
|
+
# private
|
150
|
+
#
|
151
|
+
# def print_start
|
152
|
+
# puts "Start"
|
153
|
+
# end
|
154
|
+
# end
|
155
|
+
#
|
156
|
+
# MyOrganizer.perform(name: 'Aaron')
|
157
|
+
# "Start"
|
158
|
+
# "MyInteractor1"
|
159
|
+
# "Start"
|
160
|
+
# "MyInteractor2"
|
161
|
+
# #=> <MyOrganizer::Context name='Aaron'>
|
162
|
+
def self.before_each_perform(*filters, &block)
|
163
|
+
set_callback(:each_perform, :before, *filters, &block)
|
164
|
+
end
|
160
165
|
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
+
# Declare Interactors to be invoked as part of the
|
167
|
+
# organizer's invocation. These interactors are invoked in
|
168
|
+
# the order in which they are declared
|
169
|
+
#
|
170
|
+
# @example
|
171
|
+
# class MyFirstOrganizer < ActiveInteractor::Organizer
|
172
|
+
# organize InteractorOne, InteractorTwo
|
173
|
+
# end
|
174
|
+
#
|
175
|
+
# class MySecondOrganizer < ActiveInteractor::Organizer
|
176
|
+
# organize [InteractorThree, InteractorFour]
|
177
|
+
# end
|
178
|
+
#
|
179
|
+
# @param interactors [Array<Base>] the interactors to call
|
180
|
+
def self.organize(*interactors)
|
181
|
+
self.organized = interactors.flatten.map do |interactor|
|
182
|
+
interactor.to_s.classify.safe_constantize
|
183
|
+
end.compact
|
166
184
|
end
|
167
185
|
|
168
186
|
# Invoke the organized interactors. An organizer is
|
169
|
-
# expected not to define its own {
|
187
|
+
# expected not to define its own {Base#perform} method
|
170
188
|
# in favor of this default implementation.
|
171
189
|
def perform
|
172
190
|
self.class.organized.each do |interactor|
|
173
|
-
execute_interactor_perform_with_callbacks!(interactor)
|
191
|
+
self.context = execute_interactor_perform_with_callbacks!(interactor)
|
174
192
|
end
|
193
|
+
rescue Error::ContextFailure => e
|
194
|
+
self.context = e.context
|
195
|
+
ensure
|
196
|
+
self.context = self.class.context_class.new(context)
|
175
197
|
end
|
176
198
|
|
177
199
|
private
|
178
200
|
|
179
|
-
def execute_interactor_perform!(interactor)
|
180
|
-
self.context = interactor.new(context)
|
181
|
-
.tap(&:skip_clean_context!)
|
182
|
-
.execute_perform!
|
183
|
-
end
|
184
|
-
|
185
201
|
def execute_interactor_perform_with_callbacks!(interactor)
|
186
202
|
run_callbacks :each_perform do
|
187
|
-
|
203
|
+
interactor.new(context).execute_perform!
|
188
204
|
end
|
189
205
|
end
|
190
206
|
end
|