flow 0.9.3 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +177 -245
  3. data/lib/flow.rb +2 -0
  4. data/lib/flow/concerns/transaction_wrapper.rb +10 -17
  5. data/lib/flow/custom_matchers.rb +5 -3
  6. data/lib/flow/custom_matchers/define_failure.rb +21 -0
  7. data/lib/flow/custom_matchers/define_output.rb +34 -0
  8. data/lib/flow/custom_matchers/handle_error.rb +32 -0
  9. data/lib/flow/custom_matchers/have_on_state.rb +54 -0
  10. data/lib/flow/custom_matchers/use_operations.rb +13 -11
  11. data/lib/flow/custom_matchers/wrap_in_transaction.rb +60 -0
  12. data/lib/flow/flow/callbacks.rb +1 -1
  13. data/lib/flow/flow/core.rb +1 -0
  14. data/lib/flow/flow/flux.rb +0 -2
  15. data/lib/flow/flow/status.rb +0 -4
  16. data/lib/flow/flow/transactions.rb +2 -2
  17. data/lib/flow/flow/trigger.rb +1 -1
  18. data/lib/flow/flow_base.rb +13 -15
  19. data/lib/flow/operation/accessors.rb +66 -0
  20. data/lib/flow/operation/callbacks.rb +12 -10
  21. data/lib/flow/operation/core.rb +10 -8
  22. data/lib/flow/operation/error_handler.rb +18 -12
  23. data/lib/flow/operation/errors/already_executed.rb +5 -3
  24. data/lib/flow/operation/errors/already_rewound.rb +5 -3
  25. data/lib/flow/operation/execute.rb +28 -26
  26. data/lib/flow/operation/failures.rb +48 -42
  27. data/lib/flow/operation/status.rb +18 -21
  28. data/lib/flow/operation/transactions.rb +8 -6
  29. data/lib/flow/operation_base.rb +15 -13
  30. data/lib/flow/rspec_configuration.rb +5 -0
  31. data/lib/flow/spec_helper.rb +3 -0
  32. data/lib/flow/state/errors/not_validated.rb +9 -0
  33. data/lib/flow/state/output.rb +59 -0
  34. data/lib/flow/state/status.rb +22 -0
  35. data/lib/flow/state_base.rb +9 -16
  36. data/lib/flow/version.rb +1 -1
  37. data/lib/generators/flow/application_flow/templates/application_flow.rb +1 -1
  38. data/lib/generators/flow/application_operation/templates/application_operation.rb +1 -1
  39. data/lib/generators/flow/application_state/templates/application_state.rb +1 -1
  40. data/lib/generators/flow/operation/USAGE +1 -1
  41. data/lib/generators/flow/operation/templates/operation.rb.erb +0 -5
  42. data/lib/generators/flow/state/USAGE +1 -1
  43. data/lib/generators/flow/state/templates/state.rb.erb +2 -1
  44. data/lib/generators/rspec/application_flow/templates/application_flow_spec.rb +1 -1
  45. data/lib/generators/rspec/application_operation/templates/application_operation_spec.rb +1 -9
  46. data/lib/generators/rspec/application_state/templates/application_state_spec.rb +1 -1
  47. data/lib/generators/rspec/flow/templates/flow_spec.rb.erb +0 -8
  48. data/lib/generators/rspec/operation/templates/operation_spec.rb.erb +5 -11
  49. data/lib/generators/rspec/state/templates/state_spec.rb.erb +8 -10
  50. metadata +39 -52
  51. data/lib/flow/custom_matchers/define_argument.rb +0 -19
  52. data/lib/flow/custom_matchers/define_attribute.rb +0 -19
  53. data/lib/flow/custom_matchers/define_option.rb +0 -26
  54. data/lib/flow/flow/ebb.rb +0 -28
  55. data/lib/flow/flow/revert.rb +0 -18
  56. data/lib/flow/operation/rewind.rb +0 -25
  57. data/lib/flow/state/arguments.rb +0 -30
  58. data/lib/flow/state/attributes.rb +0 -31
  59. data/lib/flow/state/callbacks.rb +0 -13
  60. data/lib/flow/state/core.rb +0 -14
  61. data/lib/flow/state/options.rb +0 -45
  62. 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 Operation
5
- module Callbacks
6
- extend ActiveSupport::Concern
4
+ module Flow
5
+ module Operation
6
+ module Callbacks
7
+ extend ActiveSupport::Concern
7
8
 
8
- included do
9
- include ActiveSupport::Callbacks
10
- define_callbacks :failure, :execute, :behavior, :rewind, :undo
11
- end
9
+ included do
10
+ include ActiveSupport::Callbacks
11
+ define_callbacks :failure, :execute, :behavior
12
+ end
12
13
 
13
- class_methods do
14
- def on_failure(*filters, &block)
15
- set_callback(:failure, :before, *filters, &block)
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
@@ -1,16 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Operations take a state as input.
4
- module Operation
5
- module Core
6
- extend ActiveSupport::Concern
4
+ module Flow
5
+ module Operation
6
+ module Core
7
+ extend ActiveSupport::Concern
7
8
 
8
- included do
9
- attr_reader :state
10
- end
9
+ included do
10
+ attr_reader :state
11
+ end
11
12
 
12
- def initialize(state)
13
- @state = state
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 Operation
5
- module ErrorHandler
6
- extend ActiveSupport::Concern
4
+ module Flow
5
+ module Operation
6
+ module ErrorHandler
7
+ extend ActiveSupport::Concern
7
8
 
8
- class_methods do
9
- private
9
+ class_methods do
10
+ private
10
11
 
11
- def handle_error(error_class, problem: error_class.name.demodulize.underscore, with: nil, &block)
12
- failure problem
12
+ def handle_error(error_class, problem: error_class.name.demodulize.underscore, with: nil, &block)
13
+ failure problem
13
14
 
14
- rescue_from(error_class) { |exception| fail!(problem.to_sym, exception: exception) }
15
+ rescue_from(error_class) { |exception| fail!(problem.to_sym, exception: exception) }
15
16
 
16
- if with.present?
17
- rescue_from(error_class, with: with)
18
- elsif block_given?
19
- rescue_from(error_class, &block)
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,7 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Operation
4
- module Errors
5
- class AlreadyExecuted < StandardError; end
3
+ module Flow
4
+ module Operation
5
+ module Errors
6
+ class AlreadyExecuted < StandardError; end
7
+ end
6
8
  end
7
9
  end
@@ -1,7 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Operation
4
- module Errors
5
- class AlreadyRewound < StandardError; end
3
+ module Flow
4
+ module Operation
5
+ module Errors
6
+ class AlreadyRewound < StandardError; end
7
+ end
6
8
  end
7
9
  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 Operation
5
- module Execute
6
- extend ActiveSupport::Concern
4
+ module Flow
5
+ module Operation
6
+ module Execute
7
+ extend ActiveSupport::Concern
7
8
 
8
- included do
9
- include ActiveSupport::Rescuable
9
+ included do
10
+ include ActiveSupport::Rescuable
10
11
 
11
- attr_reader :operation_failure
12
+ attr_reader :operation_failure
12
13
 
13
- set_callback :execute, :around, ->(_, block) { surveil(:execute) { block.call } }
14
- set_callback :execute, :before, -> { raise Operation::Errors::AlreadyExecuted }, if: :executed?
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
- self
23
- rescue StandardError => exception
24
- rescue_with_handler(exception) || raise
18
+ def execute!
19
+ run_callbacks(:execute) do
20
+ run_callbacks(:behavior) { behavior }
21
+ end
25
22
 
26
- self
27
- end
23
+ self
24
+ rescue StandardError => exception
25
+ rescue_with_handler(exception) || raise
26
+
27
+ self
28
+ end
28
29
 
29
- def execute
30
- execute!
31
- rescue Operation::Failures::OperationFailure => exception
32
- @operation_failure = exception
30
+ def execute
31
+ execute!
32
+ rescue Operation::Failures::OperationFailure => exception
33
+ @operation_failure = exception
33
34
 
34
- self
35
- end
35
+ self
36
+ end
36
37
 
37
- def behavior
38
- # abstract method which should be defined by descendants with the functionality of the given operation
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 Operation
5
- module Failures
6
- extend ActiveSupport::Concern
4
+ module Flow
5
+ module Operation
6
+ module Failures
7
+ extend ActiveSupport::Concern
7
8
 
8
- included do
9
- class_attribute :_failures, instance_writer: false, default: []
9
+ included do
10
+ class_attribute :_failures, instance_writer: false, default: []
10
11
 
11
- delegate :_failures, to: :class
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
- private
15
+ class_methods do
16
+ def inherited(base)
17
+ base._failures = _failures.dup
18
+ super
19
+ end
21
20
 
22
- def failure(problem)
23
- problem = problem.to_s.to_sym
24
- warn(:problem_already_defined) if _failures.include? problem
21
+ private
25
22
 
26
- _failures << problem
27
- define_callbacks problem
28
- define_on_failure_for_problem(problem)
29
- define_fail_method_for_problem(problem)
30
- end
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
- def define_on_failure_for_problem(problem)
33
- on_failure_name = "on_#{problem}_failure".to_sym
34
- return if respond_to?(on_failure_name)
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
- define_singleton_method(on_failure_name) { |*filters, &block| set_callback(problem, :before, *filters, &block) }
37
- end
36
+ define_singleton_method(on_failure_name) { |*filters, &block| set_callback(problem, :before, *filters, &block) }
37
+ end
38
38
 
39
- def define_fail_method_for_problem(problem)
40
- problem_failure_name = "#{problem}_failure!".to_sym
41
- return if method_defined?(problem_failure_name)
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
- define_method(problem_failure_name) { |**details| fail!(problem, **details) }
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
- def fail!(problem, **details)
48
- run_callbacks(problem) do
49
- run_callbacks(:failure) do
50
- error! OperationFailure.new(problem, **details), **details
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
- class OperationFailure < StandardError
56
- attr_reader :problem, :details
60
+ class OperationFailure < StandardError
61
+ attr_reader :problem, :details
57
62
 
58
- def initialize(problem = nil, **details)
59
- super(problem)
60
- @problem = problem
61
- @details = OpenStruct.new(details)
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 Operation
5
- module Status
6
- extend ActiveSupport::Concern
4
+ module Flow
5
+ module Operation
6
+ module Status
7
+ extend ActiveSupport::Concern
7
8
 
8
- included do
9
- set_callback(:execute, :before) { self.was_executed = true }
10
- set_callback(:rewind, :before) { self.was_rewound = true }
9
+ included do
10
+ set_callback(:execute, :before) { self.was_executed = true }
11
11
 
12
- private
12
+ private
13
13
 
14
- attr_accessor :was_executed, :was_rewound
15
- end
14
+ attr_accessor :was_executed
15
+ end
16
16
 
17
- def executed?
18
- was_executed.present?
19
- end
17
+ def executed?
18
+ was_executed.present?
19
+ end
20
20
 
21
- def rewound?
22
- was_rewound.present?
23
- end
24
-
25
- def failed?
26
- operation_failure.present?
27
- end
21
+ def failed?
22
+ operation_failure.present?
23
+ end
28
24
 
29
- def success?
30
- executed? && !failed?
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 Operation
5
- module Transactions
6
- extend ActiveSupport::Concern
4
+ module Flow
5
+ module Operation
6
+ module Transactions
7
+ extend ActiveSupport::Concern
7
8
 
8
- class_methods do
9
- def callbacks_for_transaction
10
- %i[behavior undo].freeze
9
+ class_methods do
10
+ def callback_name
11
+ :behavior
12
+ end
11
13
  end
12
14
  end
13
15
  end
@@ -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
- class OperationBase
17
- include ShortCircuIt
18
- include Technologic
19
- include TransactionWrapper
20
- include Operation::Callbacks
21
- include Operation::Core
22
- include Operation::ErrorHandler
23
- include Operation::Execute
24
- include Operation::Failures
25
- include Operation::Rewind
26
- include Operation::Status
27
- include Operation::Transactions
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
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.configure do |config|
4
+ config.include CustomMatchers
5
+ end
@@ -1,4 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "instructor/spec_helper"
4
+
3
5
  require_relative "custom_matchers"
4
6
  require_relative "shoulda_matcher_helper"
7
+ require_relative "rspec_configuration"
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Flow
4
+ module State
5
+ module Errors
6
+ class NotValidated < StandardError; end
7
+ end
8
+ end
9
+ end