flow 0.10.2 → 0.10.7.1.pre1
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 +107 -1116
- data/lib/flow.rb +17 -2
- data/lib/flow/flow/callbacks.rb +7 -5
- data/lib/flow/flow/core.rb +16 -14
- data/lib/flow/flow/flux.rb +36 -32
- data/lib/flow/flow/operations.rb +16 -14
- data/lib/flow/flow/status.rb +15 -13
- data/lib/flow/flow/transactions.rb +7 -5
- data/lib/flow/flow/trigger.rb +26 -24
- data/lib/flow/flow_base.rb +5 -6
- data/lib/flow/malfunction/base.rb +10 -0
- data/lib/flow/operation/core.rb +12 -1
- data/lib/flow/operation/error_handler.rb +1 -1
- data/lib/flow/operation/execute.rb +1 -1
- data/lib/flow/operation_base.rb +10 -15
- data/lib/flow/{spec_helper/rspec_configuration.rb → rspec/configuration.rb} +0 -0
- data/lib/flow/{spec_helper → rspec}/custom_matchers.rb +3 -0
- data/lib/flow/rspec/custom_matchers/access_state.rb +27 -0
- data/lib/flow/{spec_helper → rspec}/custom_matchers/define_failure.rb +0 -0
- data/lib/flow/{spec_helper → rspec}/custom_matchers/define_output.rb +0 -0
- data/lib/flow/{spec_helper → rspec}/custom_matchers/handle_error.rb +0 -0
- data/lib/flow/{spec_helper → rspec}/custom_matchers/have_on_state.rb +6 -1
- data/lib/flow/rspec/custom_matchers/read_state.rb +23 -0
- data/lib/flow/{spec_helper → rspec}/custom_matchers/use_operations.rb +0 -0
- data/lib/flow/{spec_helper → rspec}/custom_matchers/wrap_in_transaction.rb +0 -0
- data/lib/flow/rspec/custom_matchers/write_state.rb +23 -0
- data/lib/flow/{spec_helper → rspec}/shared_contexts.rb +0 -0
- data/lib/flow/{spec_helper → rspec}/shared_contexts/with_failing_operation.rb +0 -0
- data/lib/flow/{spec_helper → rspec}/shoulda_matcher_helper.rb +0 -0
- data/lib/flow/spec_helper.rb +4 -4
- data/lib/flow/state/output.rb +1 -1
- data/lib/flow/state_base.rb +5 -4
- data/lib/flow/state_proxy.rb +30 -0
- data/lib/flow/version.rb +1 -1
- data/lib/generators/flow/operation/templates/operation.rb.erb +4 -0
- data/lib/generators/rspec/application_operation/templates/application_operation_spec.rb +9 -5
- data/lib/generators/rspec/operation/templates/operation_spec.rb.erb +4 -0
- metadata +73 -66
- data/lib/flow/flow/errors/state_invalid.rb +0 -7
- data/lib/flow/operation/errors/already_executed.rb +0 -9
- data/lib/flow/operation/errors/already_rewound.rb +0 -9
- data/lib/flow/state/errors/not_validated.rb +0 -9
data/lib/flow.rb
CHANGED
@@ -4,14 +4,29 @@ require "active_model"
|
|
4
4
|
require "active_record"
|
5
5
|
require "active_support"
|
6
6
|
|
7
|
-
require "
|
7
|
+
require "spicery"
|
8
|
+
require "malfunction"
|
8
9
|
|
9
10
|
require "flow/version"
|
10
11
|
|
11
12
|
require "flow/concerns/transaction_wrapper"
|
12
13
|
|
14
|
+
require "flow/malfunction/base"
|
15
|
+
|
13
16
|
require "flow/flow_base"
|
14
17
|
require "flow/operation_base"
|
15
18
|
require "flow/state_base"
|
19
|
+
require "flow/state_proxy"
|
20
|
+
|
21
|
+
module Flow
|
22
|
+
class Error < StandardError; end
|
23
|
+
|
24
|
+
class FlowError < Error; end
|
25
|
+
class StateInvalidError < FlowError; end
|
26
|
+
|
27
|
+
class OperationError < Error; end
|
28
|
+
class AlreadyExecutedError < OperationError; end
|
16
29
|
|
17
|
-
|
30
|
+
class StateError < Error; end
|
31
|
+
class NotValidatedError < StateError; end
|
32
|
+
end
|
data/lib/flow/flow/callbacks.rb
CHANGED
@@ -2,12 +2,14 @@
|
|
2
2
|
|
3
3
|
# Callbacks provide an extensible mechanism for hooking into a Flow.
|
4
4
|
module Flow
|
5
|
-
module
|
6
|
-
|
5
|
+
module Flow
|
6
|
+
module Callbacks
|
7
|
+
extend ActiveSupport::Concern
|
7
8
|
|
8
|
-
|
9
|
-
|
10
|
-
|
9
|
+
included do
|
10
|
+
include ActiveSupport::Callbacks
|
11
|
+
define_callbacks :initialize, :trigger, :flux
|
12
|
+
end
|
11
13
|
end
|
12
14
|
end
|
13
15
|
end
|
data/lib/flow/flow/core.rb
CHANGED
@@ -2,25 +2,27 @@
|
|
2
2
|
|
3
3
|
# Accepts input representing the arguments and options which define the initial state.
|
4
4
|
module Flow
|
5
|
-
module
|
6
|
-
|
5
|
+
module Flow
|
6
|
+
module Core
|
7
|
+
extend ActiveSupport::Concern
|
7
8
|
|
8
|
-
|
9
|
-
|
10
|
-
|
9
|
+
class_methods do
|
10
|
+
def state_class
|
11
|
+
conjugate(StateBase)
|
12
|
+
end
|
11
13
|
end
|
12
|
-
end
|
13
14
|
|
14
|
-
|
15
|
-
|
16
|
-
|
15
|
+
included do
|
16
|
+
delegate :state_class, to: :class
|
17
|
+
delegate :outputs, to: :state
|
17
18
|
|
18
|
-
|
19
|
-
|
19
|
+
attr_reader :state
|
20
|
+
end
|
20
21
|
|
21
|
-
|
22
|
-
|
23
|
-
|
22
|
+
def initialize(state_instance = nil, **options)
|
23
|
+
run_callbacks(:initialize) do
|
24
|
+
@state = state_instance || state_class.new(**options)
|
25
|
+
end
|
24
26
|
end
|
25
27
|
end
|
26
28
|
end
|
data/lib/flow/flow/flux.rb
CHANGED
@@ -2,52 +2,56 @@
|
|
2
2
|
|
3
3
|
# When `#trigger` is called on a Flow, `#execute` is called on Operations sequentially in their given order.
|
4
4
|
module Flow
|
5
|
-
module
|
6
|
-
|
5
|
+
module Flow
|
6
|
+
module Flux
|
7
|
+
extend ActiveSupport::Concern
|
7
8
|
|
8
|
-
|
9
|
-
|
9
|
+
included do
|
10
|
+
set_callback(:initialize, :after) { @executed_operations = [] }
|
10
11
|
|
11
|
-
|
12
|
+
attr_reader :failed_operation
|
12
13
|
|
13
|
-
|
14
|
+
delegate :operation_failure, to: :failed_operation, allow_nil: true
|
14
15
|
|
15
|
-
|
16
|
+
private
|
16
17
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
18
|
+
attr_reader :executed_operations
|
19
|
+
|
20
|
+
def _flux
|
21
|
+
executable_operations.each do |operation|
|
22
|
+
operation.execute
|
23
|
+
(@failed_operation = operation) and raise Flow::Flux::Failure if operation.failed?
|
24
|
+
executed_operations << operation
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def executable_operations
|
29
|
+
operation_instances - executed_operations
|
22
30
|
end
|
23
|
-
end
|
24
31
|
|
25
|
-
|
26
|
-
|
32
|
+
def operation_instances
|
33
|
+
_operations.map { |operation_class| operation_class.new(state) }
|
34
|
+
end
|
35
|
+
memoize :operation_instances
|
27
36
|
end
|
28
37
|
|
29
|
-
def
|
30
|
-
|
38
|
+
def failed_operation?
|
39
|
+
failed_operation.present?
|
31
40
|
end
|
32
|
-
memoize :operation_instances
|
33
|
-
end
|
34
41
|
|
35
|
-
|
36
|
-
|
37
|
-
|
42
|
+
def flux
|
43
|
+
flux!
|
44
|
+
rescue StandardError => exception
|
45
|
+
info :error_executing_operation, state: state, exception: exception
|
38
46
|
|
39
|
-
|
40
|
-
|
41
|
-
rescue StandardError => exception
|
42
|
-
error :error_executing_operation, state: state, exception: exception
|
47
|
+
raise exception unless exception.is_a? Flow::Flux::Failure
|
48
|
+
end
|
43
49
|
|
44
|
-
|
45
|
-
|
50
|
+
def flux!
|
51
|
+
run_callbacks(:flux) { _flux }
|
52
|
+
end
|
46
53
|
|
47
|
-
|
48
|
-
run_callbacks(:flux) { _flux }
|
54
|
+
class Failure < StandardError; end
|
49
55
|
end
|
50
|
-
|
51
|
-
class Failure < StandardError; end
|
52
56
|
end
|
53
57
|
end
|
data/lib/flow/flow/operations.rb
CHANGED
@@ -2,25 +2,27 @@
|
|
2
2
|
|
3
3
|
# Operations are an ordered list of the behaviors which are executed with (and possibly change) the Flow's state.
|
4
4
|
module Flow
|
5
|
-
module
|
6
|
-
|
5
|
+
module Flow
|
6
|
+
module Operations
|
7
|
+
extend ActiveSupport::Concern
|
7
8
|
|
8
|
-
|
9
|
-
|
9
|
+
included do
|
10
|
+
class_attribute :_operations, instance_writer: false, default: []
|
10
11
|
|
11
|
-
|
12
|
-
end
|
13
|
-
|
14
|
-
class_methods do
|
15
|
-
def inherited(base)
|
16
|
-
base._operations = _operations.dup
|
17
|
-
super
|
12
|
+
delegate :_operations, to: :class
|
18
13
|
end
|
19
14
|
|
20
|
-
|
15
|
+
class_methods do
|
16
|
+
def inherited(base)
|
17
|
+
base._operations = _operations.dup
|
18
|
+
super
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
21
22
|
|
22
|
-
|
23
|
-
|
23
|
+
def operations(*operations)
|
24
|
+
_operations.concat(operations.flatten)
|
25
|
+
end
|
24
26
|
end
|
25
27
|
end
|
26
28
|
end
|
data/lib/flow/flow/status.rb
CHANGED
@@ -2,23 +2,25 @@
|
|
2
2
|
|
3
3
|
# The Flow status is a summary of what has occurred during the runtime, used mainly for analysis and program flow.
|
4
4
|
module Flow
|
5
|
-
module
|
6
|
-
|
5
|
+
module Flow
|
6
|
+
module Status
|
7
|
+
extend ActiveSupport::Concern
|
7
8
|
|
8
|
-
|
9
|
-
|
10
|
-
|
9
|
+
def pending?
|
10
|
+
executed_operations.none?
|
11
|
+
end
|
11
12
|
|
12
|
-
|
13
|
-
|
14
|
-
|
13
|
+
def triggered?
|
14
|
+
executed_operations.any? || failed_operation?
|
15
|
+
end
|
15
16
|
|
16
|
-
|
17
|
-
|
18
|
-
|
17
|
+
def success?
|
18
|
+
triggered? && (operation_instances - executed_operations).none?
|
19
|
+
end
|
19
20
|
|
20
|
-
|
21
|
-
|
21
|
+
def failed?
|
22
|
+
triggered? && !success?
|
23
|
+
end
|
22
24
|
end
|
23
25
|
end
|
24
26
|
end
|
@@ -2,12 +2,14 @@
|
|
2
2
|
|
3
3
|
# Flows where no operation should be persisted unless all are successful should use a transaction.
|
4
4
|
module Flow
|
5
|
-
module
|
6
|
-
|
5
|
+
module Flow
|
6
|
+
module Transactions
|
7
|
+
extend ActiveSupport::Concern
|
7
8
|
|
8
|
-
|
9
|
-
|
10
|
-
|
9
|
+
class_methods do
|
10
|
+
def callback_name
|
11
|
+
:flux
|
12
|
+
end
|
11
13
|
end
|
12
14
|
end
|
13
15
|
end
|
data/lib/flow/flow/trigger.rb
CHANGED
@@ -2,37 +2,39 @@
|
|
2
2
|
|
3
3
|
# Triggering a Flow executes all its operations in sequential order (see `Flow::Flux`) *iff* it has a valid state.
|
4
4
|
module Flow
|
5
|
-
module
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
5
|
+
module Flow
|
6
|
+
module Trigger
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
class_methods do
|
10
|
+
def trigger!(*arguments, **options)
|
11
|
+
new(*arguments, **options).trigger!
|
12
|
+
end
|
13
|
+
|
14
|
+
def trigger(*arguments, **options)
|
15
|
+
new(*arguments, **options).trigger
|
16
|
+
end
|
11
17
|
end
|
12
18
|
|
13
|
-
|
14
|
-
|
15
|
-
end
|
16
|
-
end
|
19
|
+
included do
|
20
|
+
delegate :valid?, :validated?, to: :state, prefix: true
|
17
21
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
set_callback :trigger, :around, ->(_, block) { surveil(:trigger) { block.call } }
|
22
|
-
end
|
22
|
+
set_callback :trigger, :around, ->(_, block) { surveil(:trigger) { block.call } }
|
23
|
+
end
|
23
24
|
|
24
|
-
|
25
|
-
|
25
|
+
def trigger!
|
26
|
+
raise StateInvalidError unless state_valid?
|
26
27
|
|
27
|
-
|
28
|
+
run_callbacks(:trigger) { flux }
|
28
29
|
|
29
|
-
|
30
|
-
|
30
|
+
self
|
31
|
+
end
|
31
32
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
33
|
+
def trigger
|
34
|
+
trigger!
|
35
|
+
rescue StateInvalidError
|
36
|
+
self
|
37
|
+
end
|
36
38
|
end
|
37
39
|
end
|
38
40
|
end
|
data/lib/flow/flow_base.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative "flow/errors/state_invalid"
|
4
|
-
|
5
3
|
require_relative "flow/callbacks"
|
6
4
|
require_relative "flow/core"
|
7
5
|
require_relative "flow/flux"
|
@@ -12,10 +10,11 @@ require_relative "flow/trigger"
|
|
12
10
|
|
13
11
|
# A **Flow** is a collection of procedurally executed **Operations** sharing a common **State**.
|
14
12
|
module Flow
|
15
|
-
class FlowBase
|
16
|
-
include
|
17
|
-
|
18
|
-
|
13
|
+
class FlowBase < Spicerack::RootObject
|
14
|
+
include Conjunction::Junction
|
15
|
+
suffixed_with "Flow"
|
16
|
+
|
17
|
+
include TransactionWrapper
|
19
18
|
include Flow::Callbacks
|
20
19
|
include Flow::Core
|
21
20
|
include Flow::Flux
|
data/lib/flow/operation/core.rb
CHANGED
@@ -8,10 +8,21 @@ module Flow
|
|
8
8
|
|
9
9
|
included do
|
10
10
|
attr_reader :state
|
11
|
+
|
12
|
+
delegate :state_proxy_class, to: :class
|
13
|
+
end
|
14
|
+
|
15
|
+
class_methods do
|
16
|
+
def state_proxy_class
|
17
|
+
@state_proxy_class ||= Class.new(StateProxy).tap do |proxy_class|
|
18
|
+
delegate_method_names = _state_writers.map { |method_name| "#{method_name}=" } + _state_readers
|
19
|
+
proxy_class.delegate(*delegate_method_names, to: :_state) if delegate_method_names.any?
|
20
|
+
end
|
21
|
+
end
|
11
22
|
end
|
12
23
|
|
13
24
|
def initialize(state)
|
14
|
-
@state = state
|
25
|
+
@state = state_proxy_class.new(state)
|
15
26
|
end
|
16
27
|
end
|
17
28
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# When an exception is raised during
|
3
|
+
# When an exception is raised during execution, but a handler can rescue, it causes a failure instead.
|
4
4
|
module Flow
|
5
5
|
module Operation
|
6
6
|
module ErrorHandler
|
@@ -12,7 +12,7 @@ module Flow
|
|
12
12
|
attr_reader :operation_failure
|
13
13
|
|
14
14
|
set_callback :execute, :around, ->(_, block) { surveil(:execute) { block.call } }
|
15
|
-
set_callback :execute, :before, -> { raise
|
15
|
+
set_callback :execute, :before, -> { raise AlreadyExecutedError }, if: :executed?
|
16
16
|
end
|
17
17
|
|
18
18
|
def execute!
|
data/lib/flow/operation_base.rb
CHANGED
@@ -1,8 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative "operation/errors/already_executed"
|
4
|
-
require_relative "operation/errors/already_rewound"
|
5
|
-
|
6
3
|
require_relative "operation/accessors"
|
7
4
|
require_relative "operation/callbacks"
|
8
5
|
require_relative "operation/core"
|
@@ -14,17 +11,15 @@ require_relative "operation/transactions"
|
|
14
11
|
|
15
12
|
# An **Operation** is a service object which is executed with a **State**.
|
16
13
|
module Flow
|
17
|
-
class OperationBase
|
18
|
-
include
|
19
|
-
include
|
20
|
-
include
|
21
|
-
include
|
22
|
-
include
|
23
|
-
include
|
24
|
-
include
|
25
|
-
include
|
26
|
-
include
|
27
|
-
include Flow::Operation::Status
|
28
|
-
include Flow::Operation::Transactions
|
14
|
+
class OperationBase < Spicerack::RootObject
|
15
|
+
include TransactionWrapper
|
16
|
+
include Operation::Accessors
|
17
|
+
include Operation::Callbacks
|
18
|
+
include Operation::Core
|
19
|
+
include Operation::ErrorHandler
|
20
|
+
include Operation::Execute
|
21
|
+
include Operation::Failures
|
22
|
+
include Operation::Status
|
23
|
+
include Operation::Transactions
|
29
24
|
end
|
30
25
|
end
|