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.
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