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.
- checksums.yaml +4 -4
- data/README.md +177 -245
- data/lib/flow.rb +2 -0
- data/lib/flow/concerns/transaction_wrapper.rb +10 -17
- data/lib/flow/custom_matchers.rb +5 -3
- data/lib/flow/custom_matchers/define_failure.rb +21 -0
- data/lib/flow/custom_matchers/define_output.rb +34 -0
- data/lib/flow/custom_matchers/handle_error.rb +32 -0
- data/lib/flow/custom_matchers/have_on_state.rb +54 -0
- data/lib/flow/custom_matchers/use_operations.rb +13 -11
- data/lib/flow/custom_matchers/wrap_in_transaction.rb +60 -0
- data/lib/flow/flow/callbacks.rb +1 -1
- data/lib/flow/flow/core.rb +1 -0
- data/lib/flow/flow/flux.rb +0 -2
- data/lib/flow/flow/status.rb +0 -4
- data/lib/flow/flow/transactions.rb +2 -2
- data/lib/flow/flow/trigger.rb +1 -1
- data/lib/flow/flow_base.rb +13 -15
- data/lib/flow/operation/accessors.rb +66 -0
- data/lib/flow/operation/callbacks.rb +12 -10
- data/lib/flow/operation/core.rb +10 -8
- data/lib/flow/operation/error_handler.rb +18 -12
- data/lib/flow/operation/errors/already_executed.rb +5 -3
- data/lib/flow/operation/errors/already_rewound.rb +5 -3
- data/lib/flow/operation/execute.rb +28 -26
- data/lib/flow/operation/failures.rb +48 -42
- data/lib/flow/operation/status.rb +18 -21
- data/lib/flow/operation/transactions.rb +8 -6
- data/lib/flow/operation_base.rb +15 -13
- data/lib/flow/rspec_configuration.rb +5 -0
- data/lib/flow/spec_helper.rb +3 -0
- data/lib/flow/state/errors/not_validated.rb +9 -0
- data/lib/flow/state/output.rb +59 -0
- data/lib/flow/state/status.rb +22 -0
- data/lib/flow/state_base.rb +9 -16
- data/lib/flow/version.rb +1 -1
- data/lib/generators/flow/application_flow/templates/application_flow.rb +1 -1
- data/lib/generators/flow/application_operation/templates/application_operation.rb +1 -1
- data/lib/generators/flow/application_state/templates/application_state.rb +1 -1
- data/lib/generators/flow/operation/USAGE +1 -1
- data/lib/generators/flow/operation/templates/operation.rb.erb +0 -5
- data/lib/generators/flow/state/USAGE +1 -1
- data/lib/generators/flow/state/templates/state.rb.erb +2 -1
- data/lib/generators/rspec/application_flow/templates/application_flow_spec.rb +1 -1
- data/lib/generators/rspec/application_operation/templates/application_operation_spec.rb +1 -9
- data/lib/generators/rspec/application_state/templates/application_state_spec.rb +1 -1
- data/lib/generators/rspec/flow/templates/flow_spec.rb.erb +0 -8
- data/lib/generators/rspec/operation/templates/operation_spec.rb.erb +5 -11
- data/lib/generators/rspec/state/templates/state_spec.rb.erb +8 -10
- metadata +39 -52
- data/lib/flow/custom_matchers/define_argument.rb +0 -19
- data/lib/flow/custom_matchers/define_attribute.rb +0 -19
- data/lib/flow/custom_matchers/define_option.rb +0 -26
- data/lib/flow/flow/ebb.rb +0 -28
- data/lib/flow/flow/revert.rb +0 -18
- data/lib/flow/operation/rewind.rb +0 -25
- data/lib/flow/state/arguments.rb +0 -30
- data/lib/flow/state/attributes.rb +0 -31
- data/lib/flow/state/callbacks.rb +0 -13
- data/lib/flow/state/core.rb +0 -14
- data/lib/flow/state/options.rb +0 -45
- 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
|
5
|
-
module
|
6
|
-
|
4
|
+
module Flow
|
5
|
+
module Operation
|
6
|
+
module Callbacks
|
7
|
+
extend ActiveSupport::Concern
|
7
8
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
included do
|
10
|
+
include ActiveSupport::Callbacks
|
11
|
+
define_callbacks :failure, :execute, :behavior
|
12
|
+
end
|
12
13
|
|
13
|
-
|
14
|
-
|
15
|
-
|
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
|
data/lib/flow/operation/core.rb
CHANGED
@@ -1,16 +1,18 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Operations take a state as input.
|
4
|
-
module
|
5
|
-
module
|
6
|
-
|
4
|
+
module Flow
|
5
|
+
module Operation
|
6
|
+
module Core
|
7
|
+
extend ActiveSupport::Concern
|
7
8
|
|
8
|
-
|
9
|
-
|
10
|
-
|
9
|
+
included do
|
10
|
+
attr_reader :state
|
11
|
+
end
|
11
12
|
|
12
|
-
|
13
|
-
|
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
|
5
|
-
module
|
6
|
-
|
4
|
+
module Flow
|
5
|
+
module Operation
|
6
|
+
module ErrorHandler
|
7
|
+
extend ActiveSupport::Concern
|
7
8
|
|
8
|
-
|
9
|
-
|
9
|
+
class_methods do
|
10
|
+
private
|
10
11
|
|
11
|
-
|
12
|
-
|
12
|
+
def handle_error(error_class, problem: error_class.name.demodulize.underscore, with: nil, &block)
|
13
|
+
failure problem
|
13
14
|
|
14
|
-
|
15
|
+
rescue_from(error_class) { |exception| fail!(problem.to_sym, exception: exception) }
|
15
16
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
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,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
|
5
|
-
module
|
6
|
-
|
4
|
+
module Flow
|
5
|
+
module Operation
|
6
|
+
module Execute
|
7
|
+
extend ActiveSupport::Concern
|
7
8
|
|
8
|
-
|
9
|
-
|
9
|
+
included do
|
10
|
+
include ActiveSupport::Rescuable
|
10
11
|
|
11
|
-
|
12
|
+
attr_reader :operation_failure
|
12
13
|
|
13
|
-
|
14
|
-
|
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
|
-
|
23
|
-
|
24
|
-
|
18
|
+
def execute!
|
19
|
+
run_callbacks(:execute) do
|
20
|
+
run_callbacks(:behavior) { behavior }
|
21
|
+
end
|
25
22
|
|
26
|
-
|
27
|
-
|
23
|
+
self
|
24
|
+
rescue StandardError => exception
|
25
|
+
rescue_with_handler(exception) || raise
|
26
|
+
|
27
|
+
self
|
28
|
+
end
|
28
29
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
30
|
+
def execute
|
31
|
+
execute!
|
32
|
+
rescue Operation::Failures::OperationFailure => exception
|
33
|
+
@operation_failure = exception
|
33
34
|
|
34
|
-
|
35
|
-
|
35
|
+
self
|
36
|
+
end
|
36
37
|
|
37
|
-
|
38
|
-
|
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
|
5
|
-
module
|
6
|
-
|
4
|
+
module Flow
|
5
|
+
module Operation
|
6
|
+
module Failures
|
7
|
+
extend ActiveSupport::Concern
|
7
8
|
|
8
|
-
|
9
|
-
|
9
|
+
included do
|
10
|
+
class_attribute :_failures, instance_writer: false, default: []
|
10
11
|
|
11
|
-
|
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
|
-
|
15
|
+
class_methods do
|
16
|
+
def inherited(base)
|
17
|
+
base._failures = _failures.dup
|
18
|
+
super
|
19
|
+
end
|
21
20
|
|
22
|
-
|
23
|
-
problem = problem.to_s.to_sym
|
24
|
-
warn(:problem_already_defined) if _failures.include? problem
|
21
|
+
private
|
25
22
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
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
|
-
|
33
|
-
|
34
|
-
|
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
|
-
|
37
|
-
|
36
|
+
define_singleton_method(on_failure_name) { |*filters, &block| set_callback(problem, :before, *filters, &block) }
|
37
|
+
end
|
38
38
|
|
39
|
-
|
40
|
-
|
41
|
-
|
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
|
-
|
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
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
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
|
-
|
56
|
-
|
60
|
+
class OperationFailure < StandardError
|
61
|
+
attr_reader :problem, :details
|
57
62
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
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
|
5
|
-
module
|
6
|
-
|
4
|
+
module Flow
|
5
|
+
module Operation
|
6
|
+
module Status
|
7
|
+
extend ActiveSupport::Concern
|
7
8
|
|
8
|
-
|
9
|
-
|
10
|
-
set_callback(:rewind, :before) { self.was_rewound = true }
|
9
|
+
included do
|
10
|
+
set_callback(:execute, :before) { self.was_executed = true }
|
11
11
|
|
12
|
-
|
12
|
+
private
|
13
13
|
|
14
|
-
|
15
|
-
|
14
|
+
attr_accessor :was_executed
|
15
|
+
end
|
16
16
|
|
17
|
-
|
18
|
-
|
19
|
-
|
17
|
+
def executed?
|
18
|
+
was_executed.present?
|
19
|
+
end
|
20
20
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
def failed?
|
26
|
-
operation_failure.present?
|
27
|
-
end
|
21
|
+
def failed?
|
22
|
+
operation_failure.present?
|
23
|
+
end
|
28
24
|
|
29
|
-
|
30
|
-
|
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
|
5
|
-
module
|
6
|
-
|
4
|
+
module Flow
|
5
|
+
module Operation
|
6
|
+
module Transactions
|
7
|
+
extend ActiveSupport::Concern
|
7
8
|
|
8
|
-
|
9
|
-
|
10
|
-
|
9
|
+
class_methods do
|
10
|
+
def callback_name
|
11
|
+
:behavior
|
12
|
+
end
|
11
13
|
end
|
12
14
|
end
|
13
15
|
end
|
data/lib/flow/operation_base.rb
CHANGED
@@ -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
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
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
|
data/lib/flow/spec_helper.rb
CHANGED