flow 0.9.3 → 0.10.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 (62) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +177 -245
  3. data/lib/flow.rb +2 -0
  4. data/lib/flow/concerns/transaction_wrapper.rb +10 -17
  5. data/lib/flow/custom_matchers.rb +5 -3
  6. data/lib/flow/custom_matchers/define_failure.rb +21 -0
  7. data/lib/flow/custom_matchers/define_output.rb +34 -0
  8. data/lib/flow/custom_matchers/handle_error.rb +32 -0
  9. data/lib/flow/custom_matchers/have_on_state.rb +54 -0
  10. data/lib/flow/custom_matchers/use_operations.rb +13 -11
  11. data/lib/flow/custom_matchers/wrap_in_transaction.rb +60 -0
  12. data/lib/flow/flow/callbacks.rb +1 -1
  13. data/lib/flow/flow/core.rb +1 -0
  14. data/lib/flow/flow/flux.rb +0 -2
  15. data/lib/flow/flow/status.rb +0 -4
  16. data/lib/flow/flow/transactions.rb +2 -2
  17. data/lib/flow/flow/trigger.rb +1 -1
  18. data/lib/flow/flow_base.rb +13 -15
  19. data/lib/flow/operation/accessors.rb +66 -0
  20. data/lib/flow/operation/callbacks.rb +12 -10
  21. data/lib/flow/operation/core.rb +10 -8
  22. data/lib/flow/operation/error_handler.rb +18 -12
  23. data/lib/flow/operation/errors/already_executed.rb +5 -3
  24. data/lib/flow/operation/errors/already_rewound.rb +5 -3
  25. data/lib/flow/operation/execute.rb +28 -26
  26. data/lib/flow/operation/failures.rb +48 -42
  27. data/lib/flow/operation/status.rb +18 -21
  28. data/lib/flow/operation/transactions.rb +8 -6
  29. data/lib/flow/operation_base.rb +15 -13
  30. data/lib/flow/rspec_configuration.rb +5 -0
  31. data/lib/flow/spec_helper.rb +3 -0
  32. data/lib/flow/state/errors/not_validated.rb +9 -0
  33. data/lib/flow/state/output.rb +59 -0
  34. data/lib/flow/state/status.rb +22 -0
  35. data/lib/flow/state_base.rb +9 -16
  36. data/lib/flow/version.rb +1 -1
  37. data/lib/generators/flow/application_flow/templates/application_flow.rb +1 -1
  38. data/lib/generators/flow/application_operation/templates/application_operation.rb +1 -1
  39. data/lib/generators/flow/application_state/templates/application_state.rb +1 -1
  40. data/lib/generators/flow/operation/USAGE +1 -1
  41. data/lib/generators/flow/operation/templates/operation.rb.erb +0 -5
  42. data/lib/generators/flow/state/USAGE +1 -1
  43. data/lib/generators/flow/state/templates/state.rb.erb +2 -1
  44. data/lib/generators/rspec/application_flow/templates/application_flow_spec.rb +1 -1
  45. data/lib/generators/rspec/application_operation/templates/application_operation_spec.rb +1 -9
  46. data/lib/generators/rspec/application_state/templates/application_state_spec.rb +1 -1
  47. data/lib/generators/rspec/flow/templates/flow_spec.rb.erb +0 -8
  48. data/lib/generators/rspec/operation/templates/operation_spec.rb.erb +5 -11
  49. data/lib/generators/rspec/state/templates/state_spec.rb.erb +8 -10
  50. metadata +39 -52
  51. data/lib/flow/custom_matchers/define_argument.rb +0 -19
  52. data/lib/flow/custom_matchers/define_attribute.rb +0 -19
  53. data/lib/flow/custom_matchers/define_option.rb +0 -26
  54. data/lib/flow/flow/ebb.rb +0 -28
  55. data/lib/flow/flow/revert.rb +0 -18
  56. data/lib/flow/operation/rewind.rb +0 -25
  57. data/lib/flow/state/arguments.rb +0 -30
  58. data/lib/flow/state/attributes.rb +0 -31
  59. data/lib/flow/state/callbacks.rb +0 -13
  60. data/lib/flow/state/core.rb +0 -14
  61. data/lib/flow/state/options.rb +0 -45
  62. data/lib/flow/state/string.rb +0 -34
@@ -1,18 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Callbacks provide an extensible mechanism for hooking into a Operation.
4
- module Operation
5
- module Callbacks
6
- extend ActiveSupport::Concern
4
+ module Flow
5
+ module Operation
6
+ module Callbacks
7
+ extend ActiveSupport::Concern
7
8
 
8
- included do
9
- include ActiveSupport::Callbacks
10
- define_callbacks :failure, :execute, :behavior, :rewind, :undo
11
- end
9
+ included do
10
+ include ActiveSupport::Callbacks
11
+ define_callbacks :failure, :execute, :behavior
12
+ end
12
13
 
13
- class_methods do
14
- def on_failure(*filters, &block)
15
- set_callback(:failure, :before, *filters, &block)
14
+ class_methods do
15
+ def on_failure(*filters, &block)
16
+ set_callback(:failure, :before, *filters, &block)
17
+ end
16
18
  end
17
19
  end
18
20
  end
@@ -1,16 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Operations take a state as input.
4
- module Operation
5
- module Core
6
- extend ActiveSupport::Concern
4
+ module Flow
5
+ module Operation
6
+ module Core
7
+ extend ActiveSupport::Concern
7
8
 
8
- included do
9
- attr_reader :state
10
- end
9
+ included do
10
+ attr_reader :state
11
+ end
11
12
 
12
- def initialize(state)
13
- @state = state
13
+ def initialize(state)
14
+ @state = state
15
+ end
14
16
  end
15
17
  end
16
18
  end
@@ -1,22 +1,28 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # When an exception is raised during during execution, but a handler can rescue, it causes a failure instead.
4
- module Operation
5
- module ErrorHandler
6
- extend ActiveSupport::Concern
4
+ module Flow
5
+ module Operation
6
+ module ErrorHandler
7
+ extend ActiveSupport::Concern
7
8
 
8
- class_methods do
9
- private
9
+ class_methods do
10
+ private
10
11
 
11
- def handle_error(error_class, problem: error_class.name.demodulize.underscore, with: nil, &block)
12
- failure problem
12
+ def handle_error(error_class, problem: error_class.name.demodulize.underscore, with: nil, &block)
13
+ failure problem
13
14
 
14
- rescue_from(error_class) { |exception| fail!(problem.to_sym, exception: exception) }
15
+ rescue_from(error_class) { |exception| fail!(problem.to_sym, exception: exception) }
15
16
 
16
- if with.present?
17
- rescue_from(error_class, with: with)
18
- elsif block_given?
19
- rescue_from(error_class, &block)
17
+ if with.present?
18
+ rescue_from(error_class, with: with)
19
+ elsif block_given?
20
+ rescue_from(error_class, &block)
21
+ end
22
+ end
23
+
24
+ def handle_errors(*errors)
25
+ errors.flatten.each(&method(:handle_error))
20
26
  end
21
27
  end
22
28
  end
@@ -1,7 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Operation
4
- module Errors
5
- class AlreadyExecuted < StandardError; end
3
+ module Flow
4
+ module Operation
5
+ module Errors
6
+ class AlreadyExecuted < StandardError; end
7
+ end
6
8
  end
7
9
  end
@@ -1,7 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Operation
4
- module Errors
5
- class AlreadyRewound < StandardError; end
3
+ module Flow
4
+ module Operation
5
+ module Errors
6
+ class AlreadyRewound < StandardError; end
7
+ end
6
8
  end
7
9
  end
@@ -1,41 +1,43 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Operations define a `#behavior` that occurs when `#execute` is called.
4
- module Operation
5
- module Execute
6
- extend ActiveSupport::Concern
4
+ module Flow
5
+ module Operation
6
+ module Execute
7
+ extend ActiveSupport::Concern
7
8
 
8
- included do
9
- include ActiveSupport::Rescuable
9
+ included do
10
+ include ActiveSupport::Rescuable
10
11
 
11
- attr_reader :operation_failure
12
+ attr_reader :operation_failure
12
13
 
13
- set_callback :execute, :around, ->(_, block) { surveil(:execute) { block.call } }
14
- set_callback :execute, :before, -> { raise Operation::Errors::AlreadyExecuted }, if: :executed?
15
- end
16
-
17
- def execute!
18
- run_callbacks(:execute) do
19
- run_callbacks(:behavior) { behavior }
14
+ set_callback :execute, :around, ->(_, block) { surveil(:execute) { block.call } }
15
+ set_callback :execute, :before, -> { raise Operation::Errors::AlreadyExecuted }, if: :executed?
20
16
  end
21
17
 
22
- self
23
- rescue StandardError => exception
24
- rescue_with_handler(exception) || raise
18
+ def execute!
19
+ run_callbacks(:execute) do
20
+ run_callbacks(:behavior) { behavior }
21
+ end
25
22
 
26
- self
27
- end
23
+ self
24
+ rescue StandardError => exception
25
+ rescue_with_handler(exception) || raise
26
+
27
+ self
28
+ end
28
29
 
29
- def execute
30
- execute!
31
- rescue Operation::Failures::OperationFailure => exception
32
- @operation_failure = exception
30
+ def execute
31
+ execute!
32
+ rescue Operation::Failures::OperationFailure => exception
33
+ @operation_failure = exception
33
34
 
34
- self
35
- end
35
+ self
36
+ end
36
37
 
37
- def behavior
38
- # abstract method which should be defined by descendants with the functionality of the given operation
38
+ def behavior
39
+ # abstract method which should be defined by descendants with the functionality of the given operation
40
+ end
39
41
  end
40
42
  end
41
43
  end
@@ -1,64 +1,70 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # When `#execute` is unsuccessful, expected problems are **failures** and unexpected problems are **Exceptions**.
4
- module Operation
5
- module Failures
6
- extend ActiveSupport::Concern
4
+ module Flow
5
+ module Operation
6
+ module Failures
7
+ extend ActiveSupport::Concern
7
8
 
8
- included do
9
- class_attribute :_failures, instance_writer: false, default: []
9
+ included do
10
+ class_attribute :_failures, instance_writer: false, default: []
10
11
 
11
- delegate :_failures, to: :class
12
- end
13
-
14
- class_methods do
15
- def inherited(base)
16
- base._failures = _failures.dup
17
- super
12
+ delegate :_failures, to: :class
18
13
  end
19
14
 
20
- private
15
+ class_methods do
16
+ def inherited(base)
17
+ base._failures = _failures.dup
18
+ super
19
+ end
21
20
 
22
- def failure(problem)
23
- problem = problem.to_s.to_sym
24
- warn(:problem_already_defined) if _failures.include? problem
21
+ private
25
22
 
26
- _failures << problem
27
- define_callbacks problem
28
- define_on_failure_for_problem(problem)
29
- define_fail_method_for_problem(problem)
30
- end
23
+ def failure(problem, **options)
24
+ problem = problem.to_s.to_sym
25
+ _failures << problem
26
+ define_callbacks problem
27
+ define_on_failure_for_problem(problem)
28
+ define_fail_method_for_problem(problem)
29
+ define_proactive_failure_for_problem(problem, **options)
30
+ end
31
31
 
32
- def define_on_failure_for_problem(problem)
33
- on_failure_name = "on_#{problem}_failure".to_sym
34
- return if respond_to?(on_failure_name)
32
+ def define_on_failure_for_problem(problem)
33
+ on_failure_name = "on_#{problem}_failure".to_sym
34
+ return if respond_to?(on_failure_name)
35
35
 
36
- define_singleton_method(on_failure_name) { |*filters, &block| set_callback(problem, :before, *filters, &block) }
37
- end
36
+ define_singleton_method(on_failure_name) { |*filters, &block| set_callback(problem, :before, *filters, &block) }
37
+ end
38
38
 
39
- def define_fail_method_for_problem(problem)
40
- problem_failure_name = "#{problem}_failure!".to_sym
41
- return if method_defined?(problem_failure_name)
39
+ def define_fail_method_for_problem(problem)
40
+ problem_failure_name = "#{problem}_failure!".to_sym
41
+ return if method_defined?(problem_failure_name)
42
42
 
43
- define_method(problem_failure_name) { |**details| fail!(problem, **details) }
43
+ define_method(problem_failure_name) { |**details| fail!(problem, **details) }
44
+ end
45
+
46
+ def define_proactive_failure_for_problem(problem, **options)
47
+ conditional_options = options.slice(:if, :unless)
48
+ set_callback(:execute, :before, -> { fail!(problem) }, **conditional_options) if conditional_options.present?
49
+ end
44
50
  end
45
- end
46
51
 
47
- def fail!(problem, **details)
48
- run_callbacks(problem) do
49
- run_callbacks(:failure) do
50
- error! OperationFailure.new(problem, **details), **details
52
+ def fail!(problem, **details)
53
+ run_callbacks(problem) do
54
+ run_callbacks(:failure) do
55
+ error! OperationFailure.new(problem, **details), **details
56
+ end
51
57
  end
52
58
  end
53
- end
54
59
 
55
- class OperationFailure < StandardError
56
- attr_reader :problem, :details
60
+ class OperationFailure < StandardError
61
+ attr_reader :problem, :details
57
62
 
58
- def initialize(problem = nil, **details)
59
- super(problem)
60
- @problem = problem
61
- @details = OpenStruct.new(details)
63
+ def initialize(problem = nil, **details)
64
+ super(problem)
65
+ @problem = problem
66
+ @details = OpenStruct.new(details)
67
+ end
62
68
  end
63
69
  end
64
70
  end
@@ -1,33 +1,30 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # The Operation status is used by the Flow calling it to determine what to do next; continue on or stop and rollback.
4
- module Operation
5
- module Status
6
- extend ActiveSupport::Concern
4
+ module Flow
5
+ module Operation
6
+ module Status
7
+ extend ActiveSupport::Concern
7
8
 
8
- included do
9
- set_callback(:execute, :before) { self.was_executed = true }
10
- set_callback(:rewind, :before) { self.was_rewound = true }
9
+ included do
10
+ set_callback(:execute, :before) { self.was_executed = true }
11
11
 
12
- private
12
+ private
13
13
 
14
- attr_accessor :was_executed, :was_rewound
15
- end
14
+ attr_accessor :was_executed
15
+ end
16
16
 
17
- def executed?
18
- was_executed.present?
19
- end
17
+ def executed?
18
+ was_executed.present?
19
+ end
20
20
 
21
- def rewound?
22
- was_rewound.present?
23
- end
24
-
25
- def failed?
26
- operation_failure.present?
27
- end
21
+ def failed?
22
+ operation_failure.present?
23
+ end
28
24
 
29
- def success?
30
- executed? && !failed?
25
+ def success?
26
+ executed? && !failed?
27
+ end
31
28
  end
32
29
  end
33
30
  end
@@ -1,13 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Operations which modify several persisted objects together should use a transaction.
4
- module Operation
5
- module Transactions
6
- extend ActiveSupport::Concern
4
+ module Flow
5
+ module Operation
6
+ module Transactions
7
+ extend ActiveSupport::Concern
7
8
 
8
- class_methods do
9
- def callbacks_for_transaction
10
- %i[behavior undo].freeze
9
+ class_methods do
10
+ def callback_name
11
+ :behavior
12
+ end
11
13
  end
12
14
  end
13
15
  end
@@ -3,26 +3,28 @@
3
3
  require_relative "operation/errors/already_executed"
4
4
  require_relative "operation/errors/already_rewound"
5
5
 
6
+ require_relative "operation/accessors"
6
7
  require_relative "operation/callbacks"
7
8
  require_relative "operation/core"
8
9
  require_relative "operation/error_handler"
9
10
  require_relative "operation/execute"
10
11
  require_relative "operation/failures"
11
- require_relative "operation/rewind"
12
12
  require_relative "operation/status"
13
13
  require_relative "operation/transactions"
14
14
 
15
15
  # An **Operation** is a service object which is executed with a **State**.
16
- class OperationBase
17
- include ShortCircuIt
18
- include Technologic
19
- include TransactionWrapper
20
- include Operation::Callbacks
21
- include Operation::Core
22
- include Operation::ErrorHandler
23
- include Operation::Execute
24
- include Operation::Failures
25
- include Operation::Rewind
26
- include Operation::Status
27
- include Operation::Transactions
16
+ module Flow
17
+ class OperationBase
18
+ include ShortCircuIt
19
+ include Technologic
20
+ include Flow::TransactionWrapper
21
+ include Flow::Operation::Accessors
22
+ include Flow::Operation::Callbacks
23
+ include Flow::Operation::Core
24
+ include Flow::Operation::ErrorHandler
25
+ include Flow::Operation::Execute
26
+ include Flow::Operation::Failures
27
+ include Flow::Operation::Status
28
+ include Flow::Operation::Transactions
29
+ end
28
30
  end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.configure do |config|
4
+ config.include CustomMatchers
5
+ end
@@ -1,4 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "instructor/spec_helper"
4
+
3
5
  require_relative "custom_matchers"
4
6
  require_relative "shoulda_matcher_helper"
7
+ require_relative "rspec_configuration"
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Flow
4
+ module State
5
+ module Errors
6
+ class NotValidated < StandardError; end
7
+ end
8
+ end
9
+ end