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