activeinteractor 1.0.0.beta.7 → 1.0.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.
Files changed (102) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +12 -0
  3. data/CHANGELOG.md +50 -158
  4. data/README.md +13 -856
  5. data/lib/active_interactor.rb +61 -4
  6. data/lib/active_interactor/base.rb +26 -20
  7. data/lib/active_interactor/config.rb +36 -18
  8. data/lib/active_interactor/configurable.rb +17 -9
  9. data/lib/active_interactor/context/attributes.rb +73 -26
  10. data/lib/active_interactor/context/base.rb +236 -65
  11. data/lib/active_interactor/context/loader.rb +20 -15
  12. data/lib/active_interactor/context/status.rb +38 -56
  13. data/lib/active_interactor/error.rb +15 -7
  14. data/lib/active_interactor/interactor/callbacks.rb +174 -160
  15. data/lib/active_interactor/interactor/context.rb +279 -87
  16. data/lib/active_interactor/interactor/perform.rb +256 -0
  17. data/lib/active_interactor/interactor/worker.rb +19 -14
  18. data/lib/active_interactor/models.rb +65 -0
  19. data/lib/active_interactor/organizer/base.rb +18 -0
  20. data/lib/active_interactor/organizer/callbacks.rb +153 -0
  21. data/lib/active_interactor/organizer/interactor_interface.rb +38 -26
  22. data/lib/active_interactor/organizer/interactor_interface_collection.rb +35 -32
  23. data/lib/active_interactor/organizer/organize.rb +67 -0
  24. data/lib/active_interactor/organizer/perform.rb +93 -0
  25. data/lib/active_interactor/rails.rb +0 -10
  26. data/lib/active_interactor/rails/orm/active_record.rb +1 -1
  27. data/lib/active_interactor/rails/railtie.rb +8 -11
  28. data/lib/active_interactor/version.rb +2 -1
  29. data/lib/rails/generators/active_interactor.rb +1 -38
  30. data/lib/rails/generators/active_interactor/application_context_generator.rb +21 -0
  31. data/lib/rails/generators/active_interactor/application_interactor_generator.rb +5 -15
  32. data/lib/rails/generators/active_interactor/application_organizer_generator.rb +21 -0
  33. data/lib/rails/generators/active_interactor/base.rb +29 -0
  34. data/lib/rails/generators/active_interactor/generator.rb +21 -0
  35. data/lib/rails/generators/active_interactor/install_generator.rb +2 -9
  36. data/lib/rails/generators/interactor/context/rspec_generator.rb +3 -10
  37. data/lib/rails/generators/interactor/context/test_unit_generator.rb +4 -11
  38. data/lib/rails/generators/interactor/context_generator.rb +7 -10
  39. data/lib/rails/generators/interactor/generates_context.rb +28 -0
  40. data/lib/rails/generators/interactor/interactor_generator.rb +8 -10
  41. data/lib/rails/generators/interactor/organizer_generator.rb +8 -12
  42. data/lib/rails/generators/interactor/rspec_generator.rb +2 -9
  43. data/lib/rails/generators/interactor/test_unit_generator.rb +3 -10
  44. data/lib/rails/generators/{active_interactor/templates/initializer.erb → templates/active_interactor.erb} +3 -11
  45. data/lib/rails/generators/{active_interactor/templates → templates}/application_context.rb +0 -0
  46. data/lib/rails/generators/{active_interactor/templates → templates}/application_interactor.rb +0 -0
  47. data/lib/rails/generators/templates/application_organizer.rb +4 -0
  48. data/lib/rails/generators/{interactor/templates → templates}/context.erb +0 -0
  49. data/lib/rails/generators/{interactor/context/templates/rspec.erb → templates/context_spec.erb} +0 -0
  50. data/lib/rails/generators/{interactor/context/templates/test_unit.erb → templates/context_test_unit.erb} +0 -0
  51. data/lib/rails/generators/{interactor/templates → templates}/interactor.erb +0 -0
  52. data/lib/rails/generators/{interactor/templates/rspec.erb → templates/interactor_spec.erb} +0 -0
  53. data/lib/rails/generators/{interactor/templates/test_unit.erb → templates/interactor_text_unit.erb} +0 -0
  54. data/lib/rails/generators/{interactor/templates → templates}/organizer.erb +0 -0
  55. data/spec/active_interactor/base_spec.rb +3 -3
  56. data/spec/active_interactor/interactor/{perform_options_spec.rb → perform/options_spec.rb} +1 -1
  57. data/spec/active_interactor/interactor/worker_spec.rb +14 -15
  58. data/spec/active_interactor/{organizer_spec.rb → organizer/base_spec.rb} +27 -17
  59. data/spec/integration/a_basic_interactor_spec.rb +106 -0
  60. data/spec/integration/a_basic_organizer_spec.rb +97 -0
  61. data/spec/integration/a_failing_interactor_spec.rb +42 -0
  62. data/spec/integration/active_record_integration_spec.rb +9 -9
  63. data/spec/integration/an_interactor_with_after_context_validation_callbacks_spec.rb +69 -0
  64. data/spec/integration/an_interactor_with_after_perform_callbacks_spec.rb +30 -0
  65. data/spec/integration/an_interactor_with_after_rollback_callbacks_spec.rb +33 -0
  66. data/spec/integration/an_interactor_with_an_existing_context_class_spec.rb +49 -0
  67. data/spec/integration/an_interactor_with_around_perform_callbacks_spec.rb +35 -0
  68. data/spec/integration/an_interactor_with_around_rollback_callbacks_spec.rb +39 -0
  69. data/spec/integration/an_interactor_with_before_perform_callbacks_spec.rb +30 -0
  70. data/spec/integration/an_interactor_with_before_rollback_callbacks_spec.rb +33 -0
  71. data/spec/integration/an_interactor_with_validations_on_called_spec.rb +40 -0
  72. data/spec/integration/an_interactor_with_validations_on_calling_spec.rb +36 -0
  73. data/spec/integration/an_interactor_with_validations_spec.rb +93 -0
  74. data/spec/integration/an_organizer_performing_in_parallel_spec.rb +48 -0
  75. data/spec/integration/an_organizer_with_after_each_callbacks_spec.rb +34 -0
  76. data/spec/integration/an_organizer_with_around_each_callbacks_spec.rb +39 -0
  77. data/spec/integration/an_organizer_with_before_each_callbacks_spec.rb +34 -0
  78. data/spec/integration/an_organizer_with_conditionally_organized_interactors_spec.rb +314 -0
  79. data/spec/spec_helper.rb +8 -12
  80. data/spec/support/coverage.rb +4 -0
  81. data/spec/support/coverage/reporters.rb +11 -0
  82. data/spec/support/coverage/reporters/codacy.rb +39 -0
  83. data/spec/support/coverage/reporters/simple_cov.rb +54 -0
  84. data/spec/support/coverage/runner.rb +66 -0
  85. data/spec/support/helpers/factories.rb +1 -1
  86. data/spec/support/shared_examples/a_class_with_interactor_callback_methods_example.rb +8 -8
  87. data/spec/support/shared_examples/a_class_with_interactor_context_methods_example.rb +5 -5
  88. data/spec/support/shared_examples/a_class_with_interactor_methods_example.rb +2 -2
  89. data/spec/support/shared_examples/a_class_with_organizer_callback_methods_example.rb +3 -3
  90. metadata +83 -40
  91. data/lib/active_interactor/interactor.rb +0 -84
  92. data/lib/active_interactor/interactor/perform_options.rb +0 -29
  93. data/lib/active_interactor/organizer.rb +0 -269
  94. data/lib/active_interactor/rails/config.rb +0 -45
  95. data/lib/active_interactor/rails/models.rb +0 -58
  96. data/lib/rails/generators/active_interactor/templates/application_organizer.rb +0 -4
  97. data/spec/active_interactor/rails/config_spec.rb +0 -29
  98. data/spec/active_interactor/rails_spec.rb +0 -24
  99. data/spec/integration/basic_callback_integration_spec.rb +0 -355
  100. data/spec/integration/basic_context_integration_spec.rb +0 -73
  101. data/spec/integration/basic_integration_spec.rb +0 -570
  102. data/spec/integration/basic_validations_integration_spec.rb +0 -204
@@ -1,31 +1,36 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'active_support/core_ext/string/inflections'
4
-
5
3
  module ActiveInteractor
6
4
  module Context
5
+ # Find or create {Base context} classes for a given {ActiveInteractor::Base interactor}
6
+ #
7
7
  # @api private
8
- # Load, find or create {Context::Base} classes for a given interactor.
9
- # @author Aaron Allen <hello@aaronmallen.me>
8
+ # @author Aaron Allen <hello@aaronmallen>
10
9
  # @since 1.0.0
11
10
  module Loader
12
- # @return [Array<Class>] ActiveInteractor base classes
13
- BASE_CLASSES = [ActiveInteractor::Base, ActiveInteractor::Organizer].freeze
14
- # @return [Class] the base context class
11
+ # ActiveInteractor base classes
12
+ # @return [Array<Const>]
13
+ BASE_CLASSES = [ActiveInteractor::Base, ActiveInteractor::Organizer::Base].freeze
14
+ # The {ActiveInteractor::Context::Base} class
15
+ # @return [Const]
15
16
  BASE_CONTEXT = ActiveInteractor::Context::Base
16
17
 
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
18
+ # Create a {Base context} class for a given {ActiveInteractor::Base interactor}
19
+ #
20
+ # @param context_class_name [Symbol, String] the class name of the
21
+ # {Base context} class to create
22
+ # @param interactor_class [Const] an {ActiveInteractor::Base interactor} class
23
+ # @return [Const] a class that inherits from {Base}
21
24
  def self.create(context_class_name, interactor_class)
22
25
  interactor_class.const_set(context_class_name.to_s.classify, Class.new(BASE_CONTEXT))
23
26
  end
24
27
 
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
28
+ # Find or create a {Base context} class for a given {ActiveInteractor::Base interactor}. If a class exists
29
+ # following the pattern of `InteractorNameContext` or `InteractorName::Context` then that class will be returned
30
+ # otherwise a new class will be created with the pattern `InteractorName::Context`.
31
+ #
32
+ # @param interactor_class [Const] an {ActiveInteractor::Base interactor} class
33
+ # @return [Const] a class that inherits from {Base}
29
34
  def self.find_or_create(interactor_class)
30
35
  return BASE_CONTEXT if BASE_CLASSES.include?(interactor_class)
31
36
 
@@ -2,24 +2,26 @@
2
2
 
3
3
  module ActiveInteractor
4
4
  module Context
5
- # Context status methods included by all {Context::Base}
5
+ # Context status methods. Because {Status} is a module classes should include {Status} rather than inherit from it.
6
+ #
6
7
  # @author Aaron Allen <hello@aaronmallen.me>
7
8
  # @since 1.0.0
8
9
  module Status
9
- # @api private
10
- # Track that an Interactor has been called. The {#called!} method
11
- # is used by the interactor being invoked with this context. After an
12
- # interactor is successfully called, the interactor instance is tracked in
13
- # the context for the purpose of potential future rollback
14
- # @param interactor [ActiveInteractor::Base] the called interactor
15
- # @return [Array<ActiveInteractor::Base>] all called interactors
10
+ # Add an instance of {ActiveInteractor::Base interactor} to the list of {ActiveInteractor::Base interactors}
11
+ # called on the {Base context}. This list is used when {#rollback!} is called on a {Base context} instance.
12
+ #
13
+ # @since 0.1.0
14
+ #
15
+ # @param interactor [Class] an {ActiveInteractor::Base interactor} instance
16
+ # @return [Array<Class>] the list of called {ActiveInteractor::Base interactors}
16
17
  def called!(interactor)
17
18
  _called << interactor
18
19
  end
19
20
 
20
- # Fail the context instance. Failing a context raises an error
21
- # that may be rescued by the calling interactor. The context is also flagged
22
- # as having failed
21
+ # Fail the {Base context} instance. Failing an instance raises an error that may be rescued by the calling
22
+ # {ActiveInteractor::Base interactor}. The instance is also flagged as having failed.
23
+ #
24
+ # @since 0.1.0
23
25
  #
24
26
  # @example Fail an interactor context
25
27
  # class MyInteractor < ActiveInteractor::Base
@@ -29,60 +31,42 @@ module ActiveInteractor
29
31
  # end
30
32
  #
31
33
  # MyInteractor.perform!
32
- # #=> ActiveInteractor::Error::ContextFailure: <#MyInteractor::Context>
33
- # @param errors [ActiveModel::Errors|nil] errors to add to the context on failure
34
+ # ActiveInteractor::Error::ContextFailure "<#MyInteractor::Context>"
35
+ #
36
+ # @param errors [ActiveModel::Errors, String] error messages for the failure
34
37
  # @see https://api.rubyonrails.org/classes/ActiveModel/Errors.html ActiveModel::Errors
35
38
  # @raise [Error::ContextFailure]
36
39
  def fail!(errors = nil)
37
40
  merge_errors!(errors) if errors
38
41
  @_failed = true
39
- raise Error::ContextFailure, self
42
+ raise ActiveInteractor::Error::ContextFailure, self
40
43
  end
41
44
 
42
- # Whether the context instance has failed. By default, a new
43
- # context is successful and only changes when explicitly failed
45
+ # Whether the {Base context} instance has {#fail! failed}. By default, a new instance is successful and only
46
+ # changes when explicitly {#fail! failed}.
47
+ #
48
+ # @since 0.1.0
44
49
  # @note The {#failure?} method is the inverse of the {#success?} method
45
- # @example Check if a context has failed
46
- # class MyInteractor < ActiveInteractor::Base
47
- # def perform; end
48
- # end
49
50
  #
51
+ # @example Check if a context has failed
50
52
  # result = MyInteractor.perform
51
- # #=> <#MyInteractor::Context>
52
- #
53
53
  # result.failure?
54
54
  # #=> false
55
- # @return [Boolean] `false` by default or `true` if failed
55
+ #
56
+ # @return [Boolean] `false` by default or `true` if {#fail! failed}.
56
57
  def failure?
57
58
  @_failed || false
58
59
  end
59
60
  alias fail? failure?
60
61
 
61
- # Roll back an interactor context. Any interactors to which this
62
- # context has been passed and which have been successfully called are asked
63
- # to roll themselves back by invoking their
64
- # {ActiveInteractor::Base#rollback} instance methods.
65
- # @example Rollback an interactor's context
66
- # class MyInteractor < ActiveInteractor::Base
67
- # def perform
68
- # context.fail!
69
- # end
62
+ # {#rollback! Rollback} an instance of {Base context}. Any {ActiveInteractor::Base interactors} the instance has
63
+ # been passed via the {#called!} method are asked to roll themselves back by invoking their
64
+ # {Interactor::Perform#rollback #rollback} methods. The instance is also flagged as rolled back.
70
65
  #
71
- # def rollback
72
- # context.user&.destroy
73
- # end
74
- # end
66
+ # @since 0.1.0
75
67
  #
76
- # user = User.create
77
- # #=> <#User>
78
- #
79
- # result = MyInteractor.perform(user: user)
80
- # #=> <#MyInteractor::Context user=<#User>>
81
- #
82
- # result.user.destroyed?
83
- # #=> true
84
- # @return [Boolean] `true` if rolled back successfully or `false` if already
85
- # rolled back
68
+ # @return [Boolean] `true` if {#rollback! rolled back} successfully or `false` if already
69
+ # {#rollback! rolled back}
86
70
  def rollback!
87
71
  return false if @_rolled_back
88
72
 
@@ -90,20 +74,18 @@ module ActiveInteractor
90
74
  @_rolled_back = true
91
75
  end
92
76
 
93
- # Whether the context instance is successful. By default, a new
94
- # context is successful and only changes when explicitly failed
95
- # @note the {#success?} method is the inverse of the {#failure?} method
96
- # @example Check if a context is successful
97
- # class MyInteractor < ActiveInteractor::Base
98
- # def perform; end
99
- # end
77
+ # Whether the {Base context} instance is successful. By default, a new instance is successful and only changes
78
+ # when explicitly {#fail! failed}.
100
79
  #
101
- # result = MyInteractor.perform
102
- # #=> <#MyInteractor::Context>
80
+ # @since 0.1.0
81
+ # @note The {#success?} method is the inverse of the {#failure?} method
103
82
  #
83
+ # @example Check if a context has failed
84
+ # result = MyInteractor.perform
104
85
  # result.success?
105
86
  # #=> true
106
- # @return [Boolean] `true` by default or `false` if failed
87
+ #
88
+ # @return [Boolean] `true` by default or `false` if {#fail! failed}
107
89
  def success?
108
90
  !failure?
109
91
  end
@@ -2,17 +2,23 @@
2
2
 
3
3
  module ActiveInteractor
4
4
  # ActiveInteractor errors
5
+ #
5
6
  # @author Aaron Allen <hello@aaronmallen.me>
6
7
  # @since 0.1.5
7
8
  module Error
8
- # Raised when an interactor context fails.
9
+ # Raised when an {Base interactor} {Context::Base context} {Context::Status#fail! fails}.
10
+ #
9
11
  # @!attribute [r] context
12
+ # An instance of {Context::Base context} used for debugging.
13
+ #
10
14
  # @return [Context::Base] an instance of {Context::Base}
11
15
  class ContextFailure < StandardError
12
16
  attr_reader :context
13
17
 
14
- # @param context [Context::Base] the failed context instance.
15
- # @return [ContextFailure] a new instance of {ContextFailure}.
18
+ # Initialize a new instance of {ContextFailure}
19
+ #
20
+ # @param context [Class, nil] an instance of {Context::Base context}
21
+ # @return [ContextFailure] a new instance of {ContextFailure}
16
22
  def initialize(context = nil)
17
23
  @context = context
18
24
  context_class_name = context&.class&.name || 'Context'
@@ -20,15 +26,17 @@ module ActiveInteractor
20
26
  end
21
27
  end
22
28
 
23
- # Raised when an invalid context class is assigned to an interactor.
29
+ # Raised when an invalid {Context::Base context} class is assigned to an {Base interactor}.
30
+ #
24
31
  # @since 1.0.0
32
+ #
25
33
  # @!attribute [r] class_name
26
- # @return [String|nil] the class name of the context
34
+ # The class name of the {Context::Base context} used for debugging.
35
+ #
36
+ # @return [String|nil] the class name of the {Context::Base context}
27
37
  class InvalidContextClass < StandardError
28
38
  attr_reader :class_name
29
39
 
30
- # @param class_name [String] the context class name
31
- # @return [InvalidContextClass] a new instance of {InvalidContextClass}
32
40
  def initialize(class_name = nil)
33
41
  @class_name = class_name
34
42
  super("invalid context class #{class_name}")
@@ -1,245 +1,250 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'active_support/callbacks'
4
- require 'active_support/core_ext/array/extract_options'
5
- require 'active_support/core_ext/class/attribute'
6
-
7
3
  module ActiveInteractor
8
4
  module Interactor
9
- # Interactor callback methods included by all {Base}
5
+ # Interactor callback methods. Because {Callbacks} is a module classes should include {Callbacks} rather than
6
+ # inherit from it.
7
+ #
10
8
  # @author Aaron Allen <hello@aaronmallen.me>
11
- # @since 0.0.1
9
+ # @since 0.1.0
10
+ # @see https://github.com/aaronmallen/activeinteractor/wiki/Callbacks Callbacks
11
+ # @see https://api.rubyonrails.org/classes/ActiveSupport/Callbacks.html ActiveSupport::Callbacks
12
12
  module Callbacks
13
- def self.included(base)
14
- base.class_eval do
15
- extend ClassMethods
16
- include ActiveSupport::Callbacks
17
-
18
- define_callbacks :validation,
19
- skip_after_callbacks_if_terminated: true,
20
- scope: %i[kind name]
21
- define_callbacks :perform, :rollback
22
- end
23
- end
24
-
25
- # Interactor callback class methods extended by all {Base}
13
+ # Interactor callback class methods. Because {ClassMethods} is a module classes should extend {ClassMethods}
14
+ # rather than inherit from it.
15
+ #
16
+ # @author Aaron Allen
17
+ # @since 0.1.0
26
18
  module ClassMethods
27
- # Define a callback to call after {ActiveInteractor::Context::Base#valid? #valid?} has been invoked on an
28
- # interactor's context
29
- # @example Implement an after_context_validation callback
30
- # class MyInteractor < ActiveInteractor::Base
31
- # after_context_validation :downcase_name
19
+ # Define a callback to call after validation has been run on an {Base interactor} instance's
20
+ # {ActiveInteractor::Context::Base context} instance.
32
21
  #
33
- # private
22
+ # @example
23
+ # class MyInteractor < ActiveInteractor::Base
24
+ # after_context_validation :ensure_name_is_aaron
25
+ # context_validates :name, presence: true
34
26
  #
35
- # def downcase_name
36
- # context.name.downcase!
37
- # end
38
- # end
27
+ # private
39
28
  #
40
- # result = MyInteractor.perform(name: 'Aaron')
41
- # #=> <MyInteractor::Context name='aaron'>
29
+ # def ensure_name_is_aaron
30
+ # context.name = 'Aaron'
31
+ # end
32
+ # end
42
33
  #
43
- # result.valid?
44
- # #=> true
34
+ # result = MyInteractor.perform(name: 'Bob')
35
+ # result.name
36
+ # #=> 'Aaron'
45
37
  #
46
- # result.name
47
- # #=> 'aaron'
38
+ # result = MyInteractor.perform({ name: 'Bob' }, { validate: false })
39
+ # result.name
40
+ # #=> 'Bob'
48
41
  def after_context_validation(*args, &block)
49
42
  options = normalize_options(args.extract_options!.dup.merge(prepend: true))
50
43
  set_callback(:validation, :after, *args, options, &block)
51
44
  end
52
45
 
53
- # Define a callback to call after {Interactor#perform #perform} has been invoked
46
+ # Define a callback to call after {Interactor::Perform#perform #perform} has been called on an
47
+ # {Base interactor} instance.
48
+ #
54
49
  # @example
55
- # class MyInteractor < ActiveInteractor::Base
56
- # after_perform :print_done
50
+ # class MyInteractor < ActiveInteractor::Base
51
+ # after_perform :print_done
57
52
  #
58
- # def perform
59
- # puts 'Performing'
60
- # end
53
+ # def perform
54
+ # puts 'Performing'
55
+ # end
61
56
  #
62
- # private
57
+ # private
63
58
  #
64
- # def print_done
65
- # puts 'Done'
66
- # end
67
- # end
59
+ # def print_done
60
+ # puts 'Done'
61
+ # end
62
+ # end
68
63
  #
69
- # MyInteractor.perform(name: 'Aaron')
70
- # "Performing"
71
- # "Done"
72
- # #=> <MyInteractor::Context name='Aaron'>
64
+ # MyInteractor.perform
65
+ # "Performing"
66
+ # "Done"
67
+ # #=> <#MyInteractor::Context>
73
68
  def after_perform(*filters, &block)
74
69
  set_callback(:perform, :after, *filters, &block)
75
70
  end
76
71
 
77
- # Define a callback to call after {Interactor#rollback #rollback} has been invoked
78
- # @example
79
- # class MyInteractor < ActiveInteractor::Base
80
- # after_rollback :print_done
72
+ # Define a callback to call after {Interactor::Perform#rollback #rollback} has been called on an
73
+ # {Base interactor} instance.
81
74
  #
82
- # def perform
83
- # context.fail!
84
- # end
75
+ # @example
76
+ # class MyInteractor < ActiveInteractor::Base
77
+ # after_rollback :print_done
85
78
  #
86
- # def rollback
87
- # puts 'Rolling back'
88
- # end
79
+ # def perform
80
+ # context.fail!
81
+ # end
89
82
  #
90
- # private
83
+ # def rollback
84
+ # puts 'Rolling Back'
85
+ # end
91
86
  #
92
- # def print_done
93
- # puts 'Done'
94
- # end
95
- # end
87
+ # private
96
88
  #
97
- # context = MyInteractor.perform(name: 'Aaron')
98
- # #=> <MyInteractor::Context name='Aaron'>
89
+ # def print_done
90
+ # puts 'Done'
91
+ # end
92
+ # end
99
93
  #
100
- # context.rollback!
101
- # "Rolling back"
102
- # "Done"
94
+ # MyInteractor.perform
95
+ # "Rolling Back"
96
+ # "Done"
97
+ # #=> <MyInteractor::Context>
103
98
  def after_rollback(*filters, &block)
104
99
  set_callback(:rollback, :after, *filters, &block)
105
100
  end
106
101
 
107
- # Define a callback to call around {Interactor#perform #perform} invokation
108
- # @example
109
- # class MyInteractor < ActiveInteractor::Base
110
- # around_perform :track_time
102
+ # Define a callback to call around {Interactor::Perform#perform #perform} invokation on an {Base interactor}
103
+ # instance.
111
104
  #
112
- # def perform
113
- # sleep(1)
114
- # end
105
+ # @example
106
+ # class MyInteractor < ActiveInteractor::Base
107
+ # around_perform :track_time
115
108
  #
116
- # private
109
+ # def perform
110
+ # sleep(1)
111
+ # end
117
112
  #
118
- # def track_time
119
- # context.start_time = Time.now.utc
120
- # yield
121
- # context.end_time = Time.now.utc
122
- # end
123
- # end
113
+ # private
124
114
  #
125
- # result = MyInteractor.perform(name: 'Aaron')
126
- # #=> <MyInteractor::Context name='Aaron'>
115
+ # def track_time
116
+ # context.start_time = Time.now.utc
117
+ # yield
118
+ # context.end_time = Time.now.utc
119
+ # end
120
+ # end
127
121
  #
128
- # result.start_time
129
- # #=> 2019-01-01 00:00:00 UTC
122
+ # result = MyInteractor.perform
123
+ # result.start_time
124
+ # #=> 2019-01-01 00:00:00 UTC
130
125
  #
131
- # result.end_time
132
- # #=> 2019-01-01 00:00:01 UTC
126
+ # result.end_time
127
+ # #=> 2019-01-01 00:00:01 UTC
133
128
  def around_perform(*filters, &block)
134
129
  set_callback(:perform, :around, *filters, &block)
135
130
  end
136
131
 
137
- # Define a callback to call around {Interactor#rollback #rollback} invokation
138
- # @example
139
- # class MyInteractor < ActiveInteractor::Base
140
- # around_rollback :track_time
132
+ # Define a callback to call around {Interactor::Perform#rollback #rollback} invokation on an {Base interactor}
133
+ # instance.
141
134
  #
142
- # def rollback
143
- # sleep(1)
144
- # end
135
+ # @example
136
+ # class MyInteractor < ActiveInteractor::Base
137
+ # around_rollback :track_time
145
138
  #
146
- # private
139
+ # def perform
140
+ # context.fail!
141
+ # end
147
142
  #
148
- # def track_time
149
- # context.start_time = Time.now.utc
150
- # yield
151
- # context.end_time = Time.now.utc
152
- # end
153
- # end
143
+ # def rollback
144
+ # sleep(1)
145
+ # end
154
146
  #
155
- # result = MyInteractor.perform(name: 'Aaron')
156
- # #=> <MyInteractor::Context name='Aaron'>
147
+ # private
157
148
  #
158
- # result.rollback!
159
- # #=> true
149
+ # def track_time
150
+ # context.start_time = Time.now.utc
151
+ # yield
152
+ # context.end_time = Time.now.utc
153
+ # end
154
+ # end
160
155
  #
161
- # result.start_time
162
- # #=> 2019-01-01 00:00:00 UTC
156
+ # result = MyInteractor.perform
157
+ # result.start_time
158
+ # #=> 2019-01-01 00:00:00 UTC
163
159
  #
164
- # result.end_time
165
- # #=> 2019-01-01 00:00:01 UTC
160
+ # result.end_time
161
+ # #=> 2019-01-01 00:00:01 UTC
166
162
  def around_rollback(*filters, &block)
167
163
  set_callback(:rollback, :around, *filters, &block)
168
164
  end
169
165
 
170
- # Define a callback to call before {ActiveInteractor::Context::Base#valid? #valid?} has been invoked on an
171
- # interactor's context
172
- # @example Implement an after_context_validation callback
173
- # class MyInteractor < ActiveInteractor::Base
174
- # before_context_validation :downcase_name
166
+ # Define a callback to call before validation has been run on an {Base interactor} instance's
167
+ # {ActiveInteractor::Context::Base context} instance.
168
+ #
169
+ # @example
170
+ # class MyInteractor < ActiveInteractor::Base
171
+ # before_context_validation :ensure_name_is_aaron
172
+ # context_validates :name, inclusion: { in: %w[Aaron] }
175
173
  #
176
- # private
174
+ # private
177
175
  #
178
- # def downcase_name
179
- # context.name.downcase!
180
- # end
181
- # end
176
+ # def ensure_name_is_aaron
177
+ # context.name = 'Aaron'
178
+ # end
179
+ # end
182
180
  #
183
- # result = MyInteractor.perform(name: 'Aaron')
184
- # #=> <MyInteractor::Context name='aaron'>
181
+ # result = MyInteractor.perform(name: 'Bob')
182
+ # result.successful?
183
+ # #=> true
185
184
  #
186
- # result.valid?
187
- # #=> true
185
+ # result.name
186
+ # #=> 'Aaron'
188
187
  #
189
- # result.name
190
- # #=> 'aaron'
188
+ # result = MyInteractor.perform({ name: 'Bob' }, { validate: false })
189
+ # result.name
190
+ # #=> 'Bob'
191
191
  def before_context_validation(*args, &block)
192
192
  options = normalize_options(args.extract_options!.dup)
193
193
  set_callback(:validation, :before, *args, options, &block)
194
194
  end
195
195
 
196
- # Define a callback to call before {Interactor#perform #perform} has been invoked
196
+ # Define a callback to call before {Interactor::Perform#perform #perform} has been called on an
197
+ # {Base interactor} instance
198
+ #
197
199
  # @example
198
- # class MyInteractor < ActiveInteractor::Base
199
- # before_perform :print_start
200
+ # class MyInteractor < ActiveInteractor::Base
201
+ # before_perform :print_starting
200
202
  #
201
- # def perform
202
- # puts 'Performing'
203
- # end
203
+ # def perform
204
+ # puts 'Performing'
205
+ # end
204
206
  #
205
- # private
207
+ # private
206
208
  #
207
- # def print_start
208
- # puts 'Start'
209
- # end
210
- # end
209
+ # def print_starting
210
+ # puts 'Starting'
211
+ # end
212
+ # end
211
213
  #
212
- # MyInteractor.perform(name: 'Aaron')
213
- # "Start"
214
- # "Performing"
215
- # #=> <MyInteractor::Context name='Aaron'>
214
+ # MyInteractor.perform
215
+ # "Starting"
216
+ # "Performing"
217
+ # #=> <#MyInteractor::Context>
216
218
  def before_perform(*filters, &block)
217
219
  set_callback(:perform, :before, *filters, &block)
218
220
  end
219
221
 
220
- # Define a callback to call before {Interactor#rollback #rollback} has been invoked
222
+ # Define a callback to call before {Interactor::Perform#rollback #rollback} has been called on an
223
+ # {Base interactor} instance.
224
+ #
221
225
  # @example
222
- # class MyInteractor < ActiveInteractor::Base
223
- # before_rollback :print_start
226
+ # class MyInteractor < ActiveInteractor::Base
227
+ # before_rollback :print_starting
224
228
  #
225
- # def rollback
226
- # puts 'Rolling Back'
227
- # end
229
+ # def perform
230
+ # context.fail!
231
+ # end
228
232
  #
229
- # private
233
+ # def rollback
234
+ # puts 'Rolling Back'
235
+ # end
230
236
  #
231
- # def print_start
232
- # puts 'Start'
233
- # end
234
- # end
237
+ # private
235
238
  #
236
- # context = MyInteractor.perform(name: 'Aaron')
237
- # #=> <MyInteractor::Context name='Aaron'>
239
+ # def print_starting
240
+ # puts 'Starting'
241
+ # end
242
+ # end
238
243
  #
239
- # context.rollback!
240
- # "Start"
241
- # "Rolling Back"
242
- # #=> true
244
+ # MyInteractor.perform
245
+ # "Starting"
246
+ # "Rolling Back"
247
+ # #=> <#MyInteractor::Context>
243
248
  def before_rollback(*filters, &block)
244
249
  set_callback(:rollback, :before, *filters, &block)
245
250
  end
@@ -256,6 +261,15 @@ module ActiveInteractor
256
261
  options
257
262
  end
258
263
  end
264
+
265
+ def self.included(base)
266
+ base.class_eval do
267
+ define_callbacks :validation,
268
+ skip_after_callbacks_if_terminated: true,
269
+ scope: %i[kind name]
270
+ define_callbacks :perform, :rollback
271
+ end
272
+ end
259
273
  end
260
274
  end
261
275
  end