activeinteractor 0.1.7 → 1.0.0.beta.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
@@ -0,0 +1,141 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_model'
|
4
|
+
require 'ostruct'
|
5
|
+
|
6
|
+
require 'active_interactor/context/attributes'
|
7
|
+
|
8
|
+
module ActiveInteractor
|
9
|
+
module Context
|
10
|
+
# The base context class. All interactor contexts should inherit from
|
11
|
+
# {Context::Base}.
|
12
|
+
# @author Aaron Allen <hello@aaronmallen.me>
|
13
|
+
# @since 0.0.1
|
14
|
+
class Base < OpenStruct
|
15
|
+
include ActiveModel::Validations
|
16
|
+
include Attributes
|
17
|
+
|
18
|
+
# @!method valid?(context = nil)
|
19
|
+
# @see
|
20
|
+
# https://github.com/rails/rails/blob/master/activemodel/lib/active_model/validations.rb#L305
|
21
|
+
# ActiveModel::Validations#valid?
|
22
|
+
|
23
|
+
# @api private
|
24
|
+
# Track that an Interactor has been called. The {#called!} method
|
25
|
+
# is used by the interactor being invoked with this context. After an
|
26
|
+
# interactor is successfully called, the interactor instance is tracked in
|
27
|
+
# the context for the purpose of potential future rollback
|
28
|
+
# @param interactor [ActiveInteractor::Base] the called interactor
|
29
|
+
# @return [Array<ActiveInteractor::Base>] all called interactors
|
30
|
+
def called!(interactor)
|
31
|
+
_called << interactor
|
32
|
+
end
|
33
|
+
|
34
|
+
# Fail the context instance. Failing a context raises an error
|
35
|
+
# that may be rescued by the calling interactor. The context is also flagged
|
36
|
+
# as having failed
|
37
|
+
#
|
38
|
+
# @example Fail an interactor context
|
39
|
+
# class MyInteractor < ActiveInteractor::Base
|
40
|
+
# def perform
|
41
|
+
# context.fail!
|
42
|
+
# end
|
43
|
+
# end
|
44
|
+
#
|
45
|
+
# MyInteractor.perform!
|
46
|
+
# #=> ActiveInteractor::Error::ContextFailure: <#MyInteractor::Context>
|
47
|
+
# @param errors [ActiveModel::Errors|nil] errors to add to the context on failure
|
48
|
+
# @see https://api.rubyonrails.org/classes/ActiveModel/Errors.html ActiveModel::Errors
|
49
|
+
# @raise [Error::ContextFailure]
|
50
|
+
def fail!(errors = nil)
|
51
|
+
merge_errors!(errors) if errors
|
52
|
+
@_failed = true
|
53
|
+
raise Error::ContextFailure, self
|
54
|
+
end
|
55
|
+
|
56
|
+
# Whether the context instance has failed. By default, a new
|
57
|
+
# context is successful and only changes when explicitly failed
|
58
|
+
# @note The {#failure?} method is the inverse of the {#success?} method
|
59
|
+
# @example Check if a context has failed
|
60
|
+
# class MyInteractor < ActiveInteractor::Base
|
61
|
+
# def perform; end
|
62
|
+
# end
|
63
|
+
#
|
64
|
+
# result = MyInteractor.perform
|
65
|
+
# #=> <#MyInteractor::Context>
|
66
|
+
#
|
67
|
+
# result.failure?
|
68
|
+
# #=> false
|
69
|
+
# @return [Boolean] `false` by default or `true` if failed
|
70
|
+
def failure?
|
71
|
+
@_failed || false
|
72
|
+
end
|
73
|
+
alias fail? failure?
|
74
|
+
|
75
|
+
# Roll back an interactor context. Any interactors to which this
|
76
|
+
# context has been passed and which have been successfully called are asked
|
77
|
+
# to roll themselves back by invoking their
|
78
|
+
# {ActiveInteractor::Base#rollback} instance methods.
|
79
|
+
# @example Rollback an interactor's context
|
80
|
+
# class MyInteractor < ActiveInteractor::Base
|
81
|
+
# def perform
|
82
|
+
# context.fail!
|
83
|
+
# end
|
84
|
+
#
|
85
|
+
# def rollback
|
86
|
+
# context.user&.destroy
|
87
|
+
# end
|
88
|
+
# end
|
89
|
+
#
|
90
|
+
# user = User.create
|
91
|
+
# #=> <#User>
|
92
|
+
#
|
93
|
+
# result = MyInteractor.perform(user: user)
|
94
|
+
# #=> <#MyInteractor::Context user=<#User>>
|
95
|
+
#
|
96
|
+
# result.user.destroyed?
|
97
|
+
# #=> true
|
98
|
+
# @return [Boolean] `true` if rolled back successfully or `false` if already
|
99
|
+
# rolled back
|
100
|
+
def rollback!
|
101
|
+
return false if @_rolled_back
|
102
|
+
|
103
|
+
_called.reverse_each(&:rollback)
|
104
|
+
@_rolled_back = true
|
105
|
+
end
|
106
|
+
|
107
|
+
# Whether the context instance is successful. By default, a new
|
108
|
+
# context is successful and only changes when explicitly failed
|
109
|
+
# @note the {#success?} method is the inverse of the {#failure?} method
|
110
|
+
# @example Check if a context is successful
|
111
|
+
# class MyInteractor < ActiveInteractor::Base
|
112
|
+
# def perform; end
|
113
|
+
# end
|
114
|
+
#
|
115
|
+
# result = MyInteractor.perform
|
116
|
+
# #=> <#MyInteractor::Context>
|
117
|
+
#
|
118
|
+
# result.success?
|
119
|
+
# #=> true
|
120
|
+
# @return [Boolean] `true` by default or `false` if failed
|
121
|
+
def success?
|
122
|
+
!failure?
|
123
|
+
end
|
124
|
+
alias successful? success?
|
125
|
+
|
126
|
+
private
|
127
|
+
|
128
|
+
def _called
|
129
|
+
@_called ||= []
|
130
|
+
end
|
131
|
+
|
132
|
+
def merge_errors!(errors)
|
133
|
+
if errors.is_a? String
|
134
|
+
self.errors.add(:context, errors)
|
135
|
+
else
|
136
|
+
self.errors.merge!(errors)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_support/core_ext/string/inflections'
|
4
|
+
|
5
|
+
module ActiveInteractor
|
6
|
+
module Context
|
7
|
+
# @api private
|
8
|
+
# Load, find or create {Context::Base} classes for a given interactor.
|
9
|
+
# @author Aaron Allen <hello@aaronmallen.me>
|
10
|
+
# @since 1.0.0
|
11
|
+
module Loader
|
12
|
+
# @return [Array<Class>] ActiveInteractor base classes
|
13
|
+
BASE_CLASSES = [ActiveInteractor::Base, ActiveInteractor::Organizer].freeze
|
14
|
+
# @return [Class] the base context class
|
15
|
+
BASE_CONTEXT = ActiveInteractor::Context::Base
|
16
|
+
|
17
|
+
# Create a new context class on an interactor
|
18
|
+
# @param context_class_name [String] the name of the class to create
|
19
|
+
# @param interactor_class [Class] the interactor class to create the context for
|
20
|
+
# @return [Class] the created context class
|
21
|
+
def self.create(context_class_name, interactor_class)
|
22
|
+
interactor_class.const_set(context_class_name.to_s.classify, Class.new(BASE_CONTEXT))
|
23
|
+
end
|
24
|
+
|
25
|
+
# Find or create a new context class for an interactor
|
26
|
+
# @note the loader will attempt to find an existing context given the naming conventions:
|
27
|
+
# `MyInteractor::Context` or `MyInteractorContext`
|
28
|
+
# @param interactor_class [Class] the interactor class to find or create the context for
|
29
|
+
def self.find_or_create(interactor_class)
|
30
|
+
return BASE_CONTEXT if BASE_CLASSES.include?(interactor_class)
|
31
|
+
|
32
|
+
klass = possible_classes(interactor_class).first
|
33
|
+
klass || create('Context', interactor_class)
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.possible_classes(interactor_class)
|
37
|
+
["#{interactor_class.name}::Context", "#{interactor_class.name}Context"]
|
38
|
+
.map(&:safe_constantize)
|
39
|
+
.compact
|
40
|
+
.reject { |klass| klass&.name&.include?('ActiveInteractor') }
|
41
|
+
end
|
42
|
+
private_class_method :possible_classes
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -1,30 +1,37 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ActiveInteractor
|
4
|
-
# ActiveInteractor
|
5
|
-
#
|
4
|
+
# ActiveInteractor errors
|
6
5
|
# @author Aaron Allen <hello@aaronmallen.me>
|
7
6
|
# @since 0.1.5
|
8
|
-
# @version 0.1
|
9
7
|
module Error
|
10
|
-
# Raised when an interactor context fails
|
11
|
-
#
|
12
|
-
# @author Aaron Allen <hello@aaronmallen.me>
|
13
|
-
# @since 0.1.5
|
14
|
-
# @version 0.1
|
15
|
-
#
|
8
|
+
# Raised when an interactor context fails.
|
16
9
|
# @!attribute [r] context
|
17
|
-
# @return [Base] an instance of {Base}
|
10
|
+
# @return [Context::Base] an instance of {Context::Base}
|
18
11
|
class ContextFailure < StandardError
|
19
12
|
attr_reader :context
|
20
13
|
|
21
|
-
#
|
22
|
-
# @
|
23
|
-
# instance of {ActiveInteractor::Context::Base}
|
24
|
-
# @return [ContextFailure] a new instance of {ContextFailure}
|
14
|
+
# @param context [Context::Base] the failed context instance.
|
15
|
+
# @return [ContextFailure] a new instance of {ContextFailure}.
|
25
16
|
def initialize(context = nil)
|
26
17
|
@context = context
|
27
|
-
|
18
|
+
context_class_name = context&.class&.name || 'Context'
|
19
|
+
super("#{context_class_name} failed!")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Raised when an invalid context class is assigned to an interactor.
|
24
|
+
# @since 1.0.0
|
25
|
+
# @!attribute [r] class_name
|
26
|
+
# @return [String|nil] the class name of the context
|
27
|
+
class InvalidContextClass < StandardError
|
28
|
+
attr_reader :class_name
|
29
|
+
|
30
|
+
# @param class_name [String] the context class name
|
31
|
+
# @return [InvalidContextClass] a new instance of {InvalidContextClass}
|
32
|
+
def initialize(class_name = nil)
|
33
|
+
@class_name = class_name
|
34
|
+
super("invalid context class #{class_name}")
|
28
35
|
end
|
29
36
|
end
|
30
37
|
end
|
@@ -1,65 +1,48 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ActiveInteractor
|
4
|
-
#
|
5
|
-
#
|
4
|
+
# Interactor methods. {Base} includes {Interactor} all interactors
|
5
|
+
# should inherit from {Base}.
|
6
6
|
# @author Aaron Allen <hello@aaronmallen.me>
|
7
7
|
# @since 0.0.1
|
8
|
-
# @version 0.1
|
9
8
|
module Interactor
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
private
|
19
|
-
|
20
|
-
attr_accessor :context
|
9
|
+
def self.included(base)
|
10
|
+
base.class_eval do
|
11
|
+
extend ClassMethods
|
12
|
+
include Callbacks
|
13
|
+
include Context
|
14
|
+
delegate :execute_perform, :execute_perform!, :execute_rollback, to: :worker
|
15
|
+
end
|
21
16
|
end
|
22
|
-
|
17
|
+
# Interactor class methods.
|
23
18
|
module ClassMethods
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
19
|
+
# Run an interactor context. This it the primary API method for
|
20
|
+
# interacting with interactors.
|
27
21
|
# @example Run an interactor
|
28
22
|
# MyInteractor.perform(name: 'Aaron')
|
29
23
|
# #=> <#MyInteractor::Context name='Aaron'>
|
30
|
-
#
|
31
|
-
#
|
32
|
-
# @return [
|
24
|
+
# @param context [Hash|Context::Base] properties to assign to
|
25
|
+
# an interactor's context.
|
26
|
+
# @return [Context::Base] an instance of context.
|
33
27
|
def perform(context = {})
|
34
28
|
new(context).execute_perform
|
35
29
|
end
|
36
30
|
|
37
|
-
#
|
31
|
+
# Run an interactor context. The {.perform!} method behaves identically to
|
38
32
|
# the {.perform} method with one notable exception. If the context is failed
|
39
|
-
# during invocation of the interactor, the {
|
33
|
+
# during invocation of the interactor, the {Error::ContextFailure}
|
40
34
|
# is raised.
|
41
|
-
#
|
42
35
|
# @example Run an interactor
|
43
36
|
# MyInteractor.perform!(name: 'Aaron')
|
44
37
|
# #=> <#MyInteractor::Context name='Aaron'>
|
45
|
-
#
|
46
|
-
# @
|
47
|
-
# @return [
|
38
|
+
# @param context [Hash|Context::Base] properties to assign to the interactor context.
|
39
|
+
# @raise [Error::ContextFailure] if the context fails.
|
40
|
+
# @return [Context::Base] an instance of context.
|
48
41
|
def perform!(context = {})
|
49
42
|
new(context).execute_perform!
|
50
43
|
end
|
51
44
|
end
|
52
45
|
|
53
|
-
# Whether or not the context should fail when invalid
|
54
|
-
# this will return false if
|
55
|
-
# {Interactor::Callbacks::ClassMethods#allow_context_to_be_invalid}
|
56
|
-
# has been invoked on the class.
|
57
|
-
# @return [Boolean] `true` if the context should fail
|
58
|
-
# `false` if it should not.
|
59
|
-
def fail_on_invalid_context?
|
60
|
-
self.class.__fail_on_invalid_context
|
61
|
-
end
|
62
|
-
|
63
46
|
# Invoke an Interactor instance without any hooks, tracking, or rollback
|
64
47
|
# @abstract It is expected that the {#perform} method is overwritten
|
65
48
|
# for each interactor class.
|
@@ -70,28 +53,12 @@ module ActiveInteractor
|
|
70
53
|
# failure is expected to overwrite the {#rollback} method.
|
71
54
|
def rollback; end
|
72
55
|
|
73
|
-
|
74
|
-
# if {#skip_clean_context!} has not been invoked on the instance
|
75
|
-
# and {Interactor::Callbacks::ClassMethods#clean_context_on_completion}
|
76
|
-
# is invoked on the class this will return `true`.
|
77
|
-
#
|
78
|
-
# @return [Boolean] `true` if the context should be cleaned
|
79
|
-
# `false` if it should not be cleaned.
|
80
|
-
def should_clean_context?
|
81
|
-
@should_clean_context.nil? && self.class.__clean_after_perform
|
82
|
-
end
|
56
|
+
private
|
83
57
|
|
84
|
-
|
85
|
-
|
86
|
-
# This method is meant to be invoked by organizer interactors
|
87
|
-
# to ensure contexts are approriately passed between interactors.
|
88
|
-
#
|
89
|
-
# @return [Boolean] `true` if the context should be cleaned
|
90
|
-
# `false` if it should not.
|
91
|
-
def skip_clean_context!
|
92
|
-
@should_clean_context = false
|
58
|
+
def worker
|
59
|
+
Worker.new(self)
|
93
60
|
end
|
94
61
|
end
|
95
62
|
end
|
96
63
|
|
97
|
-
Dir[File.expand_path('interactor/*.rb', __dir__)].each { |file| require file }
|
64
|
+
Dir[File.expand_path('interactor/*.rb', __dir__)].sort.each { |file| require file }
|
@@ -1,62 +1,56 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'active_support/callbacks'
|
3
4
|
require 'active_support/core_ext/array/extract_options'
|
4
5
|
require 'active_support/core_ext/class/attribute'
|
5
6
|
|
6
7
|
module ActiveInteractor
|
7
8
|
module Interactor
|
8
|
-
#
|
9
|
-
#
|
9
|
+
# Interactor callback methods included by all {Base}
|
10
10
|
# @author Aaron Allen <hello@aaronmallen.me>
|
11
11
|
# @since 0.0.1
|
12
|
-
# @version 0.1
|
13
12
|
module Callbacks
|
14
|
-
|
13
|
+
def self.included(base)
|
14
|
+
base.class_eval do
|
15
|
+
extend ClassMethods
|
16
|
+
include ActiveSupport::Callbacks
|
15
17
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
class_attribute :__fail_on_invalid_context, instance_writer: false, default: true
|
22
|
-
define_callbacks :validation,
|
23
|
-
skip_after_callbacks_if_terminated: true,
|
24
|
-
scope: %i[kind name]
|
25
|
-
define_callbacks :perform, :rollback
|
18
|
+
define_callbacks :validation,
|
19
|
+
skip_after_callbacks_if_terminated: true,
|
20
|
+
scope: %i[kind name]
|
21
|
+
define_callbacks :perform, :rollback
|
22
|
+
end
|
26
23
|
end
|
27
24
|
|
25
|
+
# Interactor callback class methods extended by all {Base}
|
28
26
|
module ClassMethods
|
29
|
-
# Define a callback to call after
|
27
|
+
# Define a callback to call after {ActiveInteractor::Context::Base#valid? #valid?} has been invoked on an
|
30
28
|
# interactor's context
|
31
|
-
#
|
32
29
|
# @example Implement an after_context_validation callback
|
33
30
|
# class MyInteractor < ActiveInteractor::Base
|
34
|
-
# after_context_validation :
|
35
|
-
#
|
31
|
+
# after_context_validation :downcase_name
|
32
|
+
#
|
33
|
+
# private
|
36
34
|
#
|
37
|
-
# def
|
38
|
-
# context.name
|
35
|
+
# def downcase_name
|
36
|
+
# context.name.downcase!
|
39
37
|
# end
|
40
38
|
# end
|
41
39
|
#
|
42
|
-
#
|
43
|
-
# #=> <MyInteractor::Context name='
|
40
|
+
# result = MyInteractor.perform(name: 'Aaron')
|
41
|
+
# #=> <MyInteractor::Context name='aaron'>
|
44
42
|
#
|
45
|
-
#
|
46
|
-
# #=> false
|
47
|
-
#
|
48
|
-
# context.name
|
49
|
-
# #=> 'Aaron'
|
50
|
-
#
|
51
|
-
# context.valid?
|
43
|
+
# result.valid?
|
52
44
|
# #=> true
|
45
|
+
#
|
46
|
+
# result.name
|
47
|
+
# #=> 'aaron'
|
53
48
|
def after_context_validation(*args, &block)
|
54
49
|
options = normalize_options(args.extract_options!.dup.merge(prepend: true))
|
55
50
|
set_callback(:validation, :after, *args, options, &block)
|
56
51
|
end
|
57
52
|
|
58
|
-
# Define a callback to call after {
|
59
|
-
#
|
53
|
+
# Define a callback to call after {Interactor#perform #perform} has been invoked
|
60
54
|
# @example
|
61
55
|
# class MyInteractor < ActiveInteractor::Base
|
62
56
|
# after_perform :print_done
|
@@ -65,6 +59,8 @@ module ActiveInteractor
|
|
65
59
|
# puts 'Performing'
|
66
60
|
# end
|
67
61
|
#
|
62
|
+
# private
|
63
|
+
#
|
68
64
|
# def print_done
|
69
65
|
# puts 'Done'
|
70
66
|
# end
|
@@ -78,16 +74,21 @@ module ActiveInteractor
|
|
78
74
|
set_callback(:perform, :after, *filters, &block)
|
79
75
|
end
|
80
76
|
|
81
|
-
# Define a callback to call after {
|
82
|
-
#
|
77
|
+
# Define a callback to call after {Interactor#rollback #rollback} has been invoked
|
83
78
|
# @example
|
84
79
|
# class MyInteractor < ActiveInteractor::Base
|
85
80
|
# after_rollback :print_done
|
86
81
|
#
|
82
|
+
# def perform
|
83
|
+
# context.fail!
|
84
|
+
# end
|
85
|
+
#
|
87
86
|
# def rollback
|
88
87
|
# puts 'Rolling back'
|
89
88
|
# end
|
90
89
|
#
|
90
|
+
# private
|
91
|
+
#
|
91
92
|
# def print_done
|
92
93
|
# puts 'Done'
|
93
94
|
# end
|
@@ -103,17 +104,7 @@ module ActiveInteractor
|
|
103
104
|
set_callback(:rollback, :after, *filters, &block)
|
104
105
|
end
|
105
106
|
|
106
|
-
#
|
107
|
-
# invalid before or after the {ActiveInteractor::Base.perform} method
|
108
|
-
# is invoked. Calling this method on an interactor class
|
109
|
-
# will not invoke {ActiveInteractor::Context::Base#fail!} if the
|
110
|
-
# context is invalid.
|
111
|
-
def allow_context_to_be_invalid
|
112
|
-
self.__fail_on_invalid_context = false
|
113
|
-
end
|
114
|
-
|
115
|
-
# Define a callback to call around {ActiveInteractor::Base.perform} invokation
|
116
|
-
#
|
107
|
+
# Define a callback to call around {Interactor#perform #perform} invokation
|
117
108
|
# @example
|
118
109
|
# class MyInteractor < ActiveInteractor::Base
|
119
110
|
# around_perform :track_time
|
@@ -122,6 +113,8 @@ module ActiveInteractor
|
|
122
113
|
# sleep(1)
|
123
114
|
# end
|
124
115
|
#
|
116
|
+
# private
|
117
|
+
#
|
125
118
|
# def track_time
|
126
119
|
# context.start_time = Time.now.utc
|
127
120
|
# yield
|
@@ -129,20 +122,19 @@ module ActiveInteractor
|
|
129
122
|
# end
|
130
123
|
# end
|
131
124
|
#
|
132
|
-
#
|
125
|
+
# result = MyInteractor.perform(name: 'Aaron')
|
133
126
|
# #=> <MyInteractor::Context name='Aaron'>
|
134
127
|
#
|
135
|
-
#
|
128
|
+
# result.start_time
|
136
129
|
# #=> 2019-01-01 00:00:00 UTC
|
137
130
|
#
|
138
|
-
#
|
131
|
+
# result.end_time
|
139
132
|
# #=> 2019-01-01 00:00:01 UTC
|
140
133
|
def around_perform(*filters, &block)
|
141
134
|
set_callback(:perform, :around, *filters, &block)
|
142
135
|
end
|
143
136
|
|
144
|
-
# Define a callback to call around {
|
145
|
-
#
|
137
|
+
# Define a callback to call around {Interactor#rollback #rollback} invokation
|
146
138
|
# @example
|
147
139
|
# class MyInteractor < ActiveInteractor::Base
|
148
140
|
# around_rollback :track_time
|
@@ -151,6 +143,8 @@ module ActiveInteractor
|
|
151
143
|
# sleep(1)
|
152
144
|
# end
|
153
145
|
#
|
146
|
+
# private
|
147
|
+
#
|
154
148
|
# def track_time
|
155
149
|
# context.start_time = Time.now.utc
|
156
150
|
# yield
|
@@ -158,49 +152,48 @@ module ActiveInteractor
|
|
158
152
|
# end
|
159
153
|
# end
|
160
154
|
#
|
161
|
-
#
|
155
|
+
# result = MyInteractor.perform(name: 'Aaron')
|
162
156
|
# #=> <MyInteractor::Context name='Aaron'>
|
163
157
|
#
|
164
|
-
#
|
158
|
+
# result.rollback!
|
165
159
|
# #=> true
|
166
160
|
#
|
167
|
-
#
|
161
|
+
# result.start_time
|
168
162
|
# #=> 2019-01-01 00:00:00 UTC
|
169
163
|
#
|
170
|
-
#
|
164
|
+
# result.end_time
|
171
165
|
# #=> 2019-01-01 00:00:01 UTC
|
172
166
|
def around_rollback(*filters, &block)
|
173
167
|
set_callback(:rollback, :around, *filters, &block)
|
174
168
|
end
|
175
169
|
|
176
|
-
# Define a callback to call before
|
170
|
+
# Define a callback to call before {ActiveInteractor::Context::Base#valid? #valid?} has been invoked on an
|
177
171
|
# interactor's context
|
178
|
-
#
|
179
172
|
# @example Implement an after_context_validation callback
|
180
173
|
# class MyInteractor < ActiveInteractor::Base
|
181
|
-
# before_context_validation :
|
182
|
-
# context_validates :name, inclusion: { in: %w[Aaron] }
|
174
|
+
# before_context_validation :downcase_name
|
183
175
|
#
|
184
|
-
#
|
185
|
-
#
|
176
|
+
# private
|
177
|
+
#
|
178
|
+
# def downcase_name
|
179
|
+
# context.name.downcase!
|
186
180
|
# end
|
187
181
|
# end
|
188
182
|
#
|
189
|
-
#
|
190
|
-
# #=> <MyInteractor::Context name='
|
183
|
+
# result = MyInteractor.perform(name: 'Aaron')
|
184
|
+
# #=> <MyInteractor::Context name='aaron'>
|
191
185
|
#
|
192
|
-
#
|
186
|
+
# result.valid?
|
193
187
|
# #=> true
|
194
188
|
#
|
195
|
-
#
|
196
|
-
# #=> '
|
189
|
+
# result.name
|
190
|
+
# #=> 'aaron'
|
197
191
|
def before_context_validation(*args, &block)
|
198
192
|
options = normalize_options(args.extract_options!.dup)
|
199
193
|
set_callback(:validation, :before, *args, options, &block)
|
200
194
|
end
|
201
195
|
|
202
|
-
# Define a callback to call before {
|
203
|
-
#
|
196
|
+
# Define a callback to call before {Interactor#perform #perform} has been invoked
|
204
197
|
# @example
|
205
198
|
# class MyInteractor < ActiveInteractor::Base
|
206
199
|
# before_perform :print_start
|
@@ -209,6 +202,8 @@ module ActiveInteractor
|
|
209
202
|
# puts 'Performing'
|
210
203
|
# end
|
211
204
|
#
|
205
|
+
# private
|
206
|
+
#
|
212
207
|
# def print_start
|
213
208
|
# puts 'Start'
|
214
209
|
# end
|
@@ -222,8 +217,7 @@ module ActiveInteractor
|
|
222
217
|
set_callback(:perform, :before, *filters, &block)
|
223
218
|
end
|
224
219
|
|
225
|
-
# Define a callback to call before {
|
226
|
-
#
|
220
|
+
# Define a callback to call before {Interactor#rollback #rollback} has been invoked
|
227
221
|
# @example
|
228
222
|
# class MyInteractor < ActiveInteractor::Base
|
229
223
|
# before_rollback :print_start
|
@@ -232,6 +226,8 @@ module ActiveInteractor
|
|
232
226
|
# puts 'Rolling Back'
|
233
227
|
# end
|
234
228
|
#
|
229
|
+
# private
|
230
|
+
#
|
235
231
|
# def print_start
|
236
232
|
# puts 'Start'
|
237
233
|
# end
|
@@ -248,14 +244,6 @@ module ActiveInteractor
|
|
248
244
|
set_callback(:rollback, :before, *filters, &block)
|
249
245
|
end
|
250
246
|
|
251
|
-
# Calling this method on an interactor class will invoke
|
252
|
-
# {ActiveInteractor::Context::Base#clean!} on the interactor's
|
253
|
-
# context instance after {ActiveInteractor::Base.perform}
|
254
|
-
# is invoked.
|
255
|
-
def clean_context_on_completion
|
256
|
-
self.__clean_after_perform = true
|
257
|
-
end
|
258
|
-
|
259
247
|
private
|
260
248
|
|
261
249
|
def normalize_options(options)
|