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.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +107 -1116
  3. data/lib/flow.rb +17 -2
  4. data/lib/flow/flow/callbacks.rb +7 -5
  5. data/lib/flow/flow/core.rb +16 -14
  6. data/lib/flow/flow/flux.rb +36 -32
  7. data/lib/flow/flow/operations.rb +16 -14
  8. data/lib/flow/flow/status.rb +15 -13
  9. data/lib/flow/flow/transactions.rb +7 -5
  10. data/lib/flow/flow/trigger.rb +26 -24
  11. data/lib/flow/flow_base.rb +5 -6
  12. data/lib/flow/malfunction/base.rb +10 -0
  13. data/lib/flow/operation/core.rb +12 -1
  14. data/lib/flow/operation/error_handler.rb +1 -1
  15. data/lib/flow/operation/execute.rb +1 -1
  16. data/lib/flow/operation_base.rb +10 -15
  17. data/lib/flow/{spec_helper/rspec_configuration.rb → rspec/configuration.rb} +0 -0
  18. data/lib/flow/{spec_helper → rspec}/custom_matchers.rb +3 -0
  19. data/lib/flow/rspec/custom_matchers/access_state.rb +27 -0
  20. data/lib/flow/{spec_helper → rspec}/custom_matchers/define_failure.rb +0 -0
  21. data/lib/flow/{spec_helper → rspec}/custom_matchers/define_output.rb +0 -0
  22. data/lib/flow/{spec_helper → rspec}/custom_matchers/handle_error.rb +0 -0
  23. data/lib/flow/{spec_helper → rspec}/custom_matchers/have_on_state.rb +6 -1
  24. data/lib/flow/rspec/custom_matchers/read_state.rb +23 -0
  25. data/lib/flow/{spec_helper → rspec}/custom_matchers/use_operations.rb +0 -0
  26. data/lib/flow/{spec_helper → rspec}/custom_matchers/wrap_in_transaction.rb +0 -0
  27. data/lib/flow/rspec/custom_matchers/write_state.rb +23 -0
  28. data/lib/flow/{spec_helper → rspec}/shared_contexts.rb +0 -0
  29. data/lib/flow/{spec_helper → rspec}/shared_contexts/with_failing_operation.rb +0 -0
  30. data/lib/flow/{spec_helper → rspec}/shoulda_matcher_helper.rb +0 -0
  31. data/lib/flow/spec_helper.rb +4 -4
  32. data/lib/flow/state/output.rb +1 -1
  33. data/lib/flow/state_base.rb +5 -4
  34. data/lib/flow/state_proxy.rb +30 -0
  35. data/lib/flow/version.rb +1 -1
  36. data/lib/generators/flow/operation/templates/operation.rb.erb +4 -0
  37. data/lib/generators/rspec/application_operation/templates/application_operation_spec.rb +9 -5
  38. data/lib/generators/rspec/operation/templates/operation_spec.rb.erb +4 -0
  39. metadata +73 -66
  40. data/lib/flow/flow/errors/state_invalid.rb +0 -7
  41. data/lib/flow/operation/errors/already_executed.rb +0 -9
  42. data/lib/flow/operation/errors/already_rewound.rb +0 -9
  43. 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 "spicerack"
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
- module Flow; end
30
+ class StateError < Error; end
31
+ class NotValidatedError < StateError; end
32
+ end
@@ -2,12 +2,14 @@
2
2
 
3
3
  # Callbacks provide an extensible mechanism for hooking into a Flow.
4
4
  module Flow
5
- module Callbacks
6
- extend ActiveSupport::Concern
5
+ module Flow
6
+ module Callbacks
7
+ extend ActiveSupport::Concern
7
8
 
8
- included do
9
- include ActiveSupport::Callbacks
10
- define_callbacks :initialize, :trigger, :flux
9
+ included do
10
+ include ActiveSupport::Callbacks
11
+ define_callbacks :initialize, :trigger, :flux
12
+ end
11
13
  end
12
14
  end
13
15
  end
@@ -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 Core
6
- extend ActiveSupport::Concern
5
+ module Flow
6
+ module Core
7
+ extend ActiveSupport::Concern
7
8
 
8
- class_methods do
9
- def state_class
10
- "#{name.chomp("Flow")}State".constantize
9
+ class_methods do
10
+ def state_class
11
+ conjugate(StateBase)
12
+ end
11
13
  end
12
- end
13
14
 
14
- included do
15
- delegate :state_class, to: :class
16
- delegate :outputs, to: :state
15
+ included do
16
+ delegate :state_class, to: :class
17
+ delegate :outputs, to: :state
17
18
 
18
- attr_reader :state
19
- end
19
+ attr_reader :state
20
+ end
20
21
 
21
- def initialize(state_instance = nil, **options)
22
- run_callbacks(:initialize) do
23
- @state = state_instance || state_class.new(**options)
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
@@ -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 Flux
6
- extend ActiveSupport::Concern
5
+ module Flow
6
+ module Flux
7
+ extend ActiveSupport::Concern
7
8
 
8
- included do
9
- set_callback(:initialize, :after) { @executed_operations = [] }
9
+ included do
10
+ set_callback(:initialize, :after) { @executed_operations = [] }
10
11
 
11
- attr_reader :failed_operation
12
+ attr_reader :failed_operation
12
13
 
13
- private
14
+ delegate :operation_failure, to: :failed_operation, allow_nil: true
14
15
 
15
- attr_reader :executed_operations
16
+ private
16
17
 
17
- def _flux
18
- executable_operations.each do |operation|
19
- operation.execute
20
- (@failed_operation = operation) and raise Flow::Flux::Failure if operation.failed?
21
- executed_operations << operation
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
- def executable_operations
26
- operation_instances - executed_operations
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 operation_instances
30
- _operations.map { |operation_class| operation_class.new(state) }
38
+ def failed_operation?
39
+ failed_operation.present?
31
40
  end
32
- memoize :operation_instances
33
- end
34
41
 
35
- def failed_operation?
36
- failed_operation.present?
37
- end
42
+ def flux
43
+ flux!
44
+ rescue StandardError => exception
45
+ info :error_executing_operation, state: state, exception: exception
38
46
 
39
- def flux
40
- flux!
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
- raise exception unless exception.is_a? Flow::Flux::Failure
45
- end
50
+ def flux!
51
+ run_callbacks(:flux) { _flux }
52
+ end
46
53
 
47
- def flux!
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
@@ -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 Operations
6
- extend ActiveSupport::Concern
5
+ module Flow
6
+ module Operations
7
+ extend ActiveSupport::Concern
7
8
 
8
- included do
9
- class_attribute :_operations, instance_writer: false, default: []
9
+ included do
10
+ class_attribute :_operations, instance_writer: false, default: []
10
11
 
11
- delegate :_operations, to: :class
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
- private
15
+ class_methods do
16
+ def inherited(base)
17
+ base._operations = _operations.dup
18
+ super
19
+ end
20
+
21
+ private
21
22
 
22
- def operations(*operations)
23
- _operations.concat(operations.flatten)
23
+ def operations(*operations)
24
+ _operations.concat(operations.flatten)
25
+ end
24
26
  end
25
27
  end
26
28
  end
@@ -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 Status
6
- extend ActiveSupport::Concern
5
+ module Flow
6
+ module Status
7
+ extend ActiveSupport::Concern
7
8
 
8
- def pending?
9
- executed_operations.none?
10
- end
9
+ def pending?
10
+ executed_operations.none?
11
+ end
11
12
 
12
- def triggered?
13
- executed_operations.any? || failed_operation?
14
- end
13
+ def triggered?
14
+ executed_operations.any? || failed_operation?
15
+ end
15
16
 
16
- def success?
17
- triggered? && (operation_instances - executed_operations).none?
18
- end
17
+ def success?
18
+ triggered? && (operation_instances - executed_operations).none?
19
+ end
19
20
 
20
- def failed?
21
- triggered? && !success?
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 Transactions
6
- extend ActiveSupport::Concern
5
+ module Flow
6
+ module Transactions
7
+ extend ActiveSupport::Concern
7
8
 
8
- class_methods do
9
- def callback_name
10
- :flux
9
+ class_methods do
10
+ def callback_name
11
+ :flux
12
+ end
11
13
  end
12
14
  end
13
15
  end
@@ -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 Trigger
6
- extend ActiveSupport::Concern
7
-
8
- class_methods do
9
- def trigger!(*arguments)
10
- new(*arguments).trigger!
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
- def trigger(*arguments)
14
- new(*arguments).trigger
15
- end
16
- end
19
+ included do
20
+ delegate :valid?, :validated?, to: :state, prefix: true
17
21
 
18
- included do
19
- delegate :valid?, :validated?, to: :state, prefix: true
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
- def trigger!
25
- raise Flow::Errors::StateInvalid unless state_valid?
25
+ def trigger!
26
+ raise StateInvalidError unless state_valid?
26
27
 
27
- run_callbacks(:trigger) { flux }
28
+ run_callbacks(:trigger) { flux }
28
29
 
29
- self
30
- end
30
+ self
31
+ end
31
32
 
32
- def trigger
33
- trigger!
34
- rescue Flow::Errors::StateInvalid
35
- self
33
+ def trigger
34
+ trigger!
35
+ rescue StateInvalidError
36
+ self
37
+ end
36
38
  end
37
39
  end
38
40
  end
@@ -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 ShortCircuIt
17
- include Technologic
18
- include Flow::TransactionWrapper
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
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Flow
4
+ module Malfunction
5
+ class Base < ::Malfunction::MalfunctionBase
6
+ prefixed_with "Flow::Malfunction::"
7
+ suffixed_with nil
8
+ end
9
+ end
10
+ end
@@ -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 during execution, but a handler can rescue, it causes a failure instead.
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 Operation::Errors::AlreadyExecuted }, if: :executed?
15
+ set_callback :execute, :before, -> { raise AlreadyExecutedError }, if: :executed?
16
16
  end
17
17
 
18
18
  def execute!
@@ -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 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
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