flow 0.10.2 → 0.10.7.1.pre1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|