flow 0.9.3 → 0.10.0

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 (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,19 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # RSpec matcher for flow state arguments.
4
- #
5
- # Usage:
6
- #
7
- # RSpec.describe ExampleState, type: :state do
8
- # subject { described_class.new(**input) }
9
- #
10
- # let(:input) { {} }
11
- #
12
- # it { is_expected.to define_argument :foo }
13
- # end
14
-
15
- RSpec::Matchers.define :define_argument do |argument|
16
- match { |state| expect(state._arguments).to include argument }
17
- description { "has argument #{argument}" }
18
- failure_message { |state| "expected #{state.class.name}# to have argument #{argument}" }
19
- end
@@ -1,19 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # RSpec matcher for flow state attributes.
4
- #
5
- # Usage:
6
- #
7
- # RSpec.describe ExampleState, type: :state do
8
- # subject { described_class.new(**input) }
9
- #
10
- # let(:input) { {} }
11
- #
12
- # it { is_expected.to define_attribute :foo }
13
- # end
14
-
15
- RSpec::Matchers.define :define_attribute do |attribute|
16
- match { |state| expect(state.class._attributes).to include attribute }
17
- description { "has attribute #{attribute}" }
18
- failure_message { |state| "expected #{state.class.name}# to have attribute #{attribute}" }
19
- end
@@ -1,26 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # RSpec matcher for flow state options.
4
- #
5
- # Usage:
6
- #
7
- # RSpec.describe ExampleState, type: :state do
8
- # subject { described_class.new(**input) }
9
- #
10
- # let(:input) { {} }
11
- #
12
- # it { is_expected.to define_option :foo }
13
- # it { is_expected.to define_option :foo, default_value }
14
- # end
15
-
16
- RSpec::Matchers.define :define_option do |option, default_value = nil|
17
- match { |state| expect(state._options[option].default_value).to eq default_value }
18
- description { "has option #{option}" }
19
- failure_message { |state| "expected #{state.class.name}# to have option #{option}, #{for_default(default_value)}" }
20
-
21
- def for_default(default_value)
22
- return "without a default value" if default_value.nil?
23
-
24
- "with default value #{default_value}"
25
- end
26
- end
@@ -1,28 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # When a `#revert` is called on a Flow, `#rewind` is called on Operations in reverse of the order they were executed.
4
- module Flow
5
- module Ebb
6
- extend ActiveSupport::Concern
7
-
8
- included do
9
- set_callback(:initialize, :after) { @rewound_operations = [] }
10
-
11
- private
12
-
13
- attr_reader :rewound_operations
14
-
15
- def _ebb
16
- rewindable_operations.reverse_each { |executed_operation| rewound_operations << executed_operation.rewind }
17
- end
18
-
19
- def rewindable_operations
20
- executed_operations - rewound_operations
21
- end
22
- end
23
-
24
- def ebb
25
- run_callbacks(:ebb) { _ebb }
26
- end
27
- end
28
- end
@@ -1,18 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Reverting a Flow rewinds all its executed operations in reverse order (see `Flow::Ebb`).
4
- module Flow
5
- module Revert
6
- extend ActiveSupport::Concern
7
-
8
- included do
9
- set_callback :revert, :around, ->(_, block) { surveil(:revert) { block.call } }
10
- end
11
-
12
- def revert
13
- run_callbacks(:revert) { ebb }
14
-
15
- state
16
- end
17
- end
18
- end
@@ -1,25 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # When something goes wrong in Flow, `#rewind` is called on all executed Operations to `#undo` their behavior.
4
- module Operation
5
- module Rewind
6
- extend ActiveSupport::Concern
7
-
8
- included do
9
- set_callback :rewind, :around, ->(_, block) { surveil(:rewind) { block.call } }
10
- set_callback :rewind, :before, -> { raise Operation::Errors::AlreadyRewound }, if: :rewound?
11
- end
12
-
13
- def rewind
14
- run_callbacks(:rewind) do
15
- run_callbacks(:undo) { undo }
16
- end
17
-
18
- self
19
- end
20
-
21
- def undo
22
- # abstract method which should be defined by descendants to undo the functionality of the `#behavior` method
23
- end
24
- end
25
- end
@@ -1,30 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Arguments describe input required to define the initial state.
4
- module State
5
- module Arguments
6
- extend ActiveSupport::Concern
7
-
8
- included do
9
- class_attribute :_arguments, instance_writer: false, default: []
10
- set_callback :initialize, :after do
11
- missing = _arguments.select { |argument| public_send(argument).nil? }
12
- raise ArgumentError, "Missing #{"argument".pluralize(missing.length)}: #{missing.join(", ")}" if missing.any?
13
- end
14
- end
15
-
16
- class_methods do
17
- def inherited(base)
18
- base._arguments = _arguments.dup
19
- super
20
- end
21
-
22
- private
23
-
24
- def argument(argument)
25
- _arguments << argument
26
- define_attribute argument
27
- end
28
- end
29
- end
30
- end
@@ -1,31 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # A state's attributes provide accessors to the input data it was initialized with.
4
- module State
5
- module Attributes
6
- extend ActiveSupport::Concern
7
-
8
- included do
9
- include ActiveModel::AttributeMethods
10
-
11
- class_attribute :_attributes, instance_writer: false, default: []
12
- end
13
-
14
- class_methods do
15
- def inherited(base)
16
- base._attributes = _attributes.dup
17
- super
18
- end
19
-
20
- private
21
-
22
- def define_attribute(attribute)
23
- _attributes << attribute
24
-
25
- attr_accessor attribute
26
- define_attribute_methods attribute
27
- end
28
- alias_method :attribute, :define_attribute
29
- end
30
- end
31
- end
@@ -1,13 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Callbacks provide an extensible mechanism for hooking into a State.
4
- module State
5
- module Callbacks
6
- extend ActiveSupport::Concern
7
-
8
- included do
9
- include ActiveSupport::Callbacks
10
- define_callbacks :initialize
11
- end
12
- end
13
- end
@@ -1,14 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # A state accepts input represented by arguments and options which initialize it.
4
- module State
5
- module Core
6
- extend ActiveSupport::Concern
7
-
8
- def initialize(**input)
9
- run_callbacks(:initialize) do
10
- input.each { |key, value| __send__("#{key}=".to_sym, value) }
11
- end
12
- end
13
- end
14
- end
@@ -1,45 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Options describe input which may be provided to define or override the initial state.
4
- module State
5
- module Options
6
- extend ActiveSupport::Concern
7
-
8
- included do
9
- class_attribute :_options, instance_writer: false, default: {}
10
-
11
- set_callback :initialize, :after do
12
- _options.each do |option, info|
13
- __send__("#{option}=".to_sym, info.default_value.dup) if public_send(option).nil?
14
- end
15
- end
16
- end
17
-
18
- class_methods do
19
- def inherited(base)
20
- dup = _options.dup
21
- base._options = dup.each { |k, v| dup[k] = v.dup }
22
- super
23
- end
24
-
25
- private
26
-
27
- def option(option, default: nil, &block)
28
- _options[option] = Option.new(default: default, &block)
29
- define_attribute option
30
- end
31
- end
32
-
33
- class Option
34
- def initialize(default:, &block)
35
- @default_value = (default.nil? && block_given?) ? block : default
36
- end
37
-
38
- def default_value
39
- return instance_eval(&@default_value) if @default_value.respond_to?(:call)
40
-
41
- @default_value
42
- end
43
- end
44
- end
45
- end
@@ -1,34 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Formats the state as a string.
4
- module State
5
- module String
6
- extend ActiveSupport::Concern
7
-
8
- def to_s
9
- string_for(__method__)
10
- end
11
-
12
- def inspect
13
- string_for(__method__)
14
- end
15
-
16
- private
17
-
18
- def stringable_attributes
19
- self.class._attributes
20
- end
21
-
22
- def string_for(method)
23
- "#<#{self.class.name} #{attribute_string(method)}>"
24
- end
25
-
26
- def attribute_string(method)
27
- stringable_attribute_values.map { |attribute, value| "#{attribute}=#{value.public_send(method)}" }.join(" ")
28
- end
29
-
30
- def stringable_attribute_values
31
- stringable_attributes.each_with_object({}) { |attribute, result| result[attribute] = public_send(attribute) }
32
- end
33
- end
34
- end