flow 0.9.3 → 0.10.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +177 -245
- data/lib/flow.rb +2 -0
- data/lib/flow/concerns/transaction_wrapper.rb +10 -17
- data/lib/flow/custom_matchers.rb +5 -3
- data/lib/flow/custom_matchers/define_failure.rb +21 -0
- data/lib/flow/custom_matchers/define_output.rb +34 -0
- data/lib/flow/custom_matchers/handle_error.rb +32 -0
- data/lib/flow/custom_matchers/have_on_state.rb +54 -0
- data/lib/flow/custom_matchers/use_operations.rb +13 -11
- data/lib/flow/custom_matchers/wrap_in_transaction.rb +60 -0
- data/lib/flow/flow/callbacks.rb +1 -1
- data/lib/flow/flow/core.rb +1 -0
- data/lib/flow/flow/flux.rb +0 -2
- data/lib/flow/flow/status.rb +0 -4
- data/lib/flow/flow/transactions.rb +2 -2
- data/lib/flow/flow/trigger.rb +1 -1
- data/lib/flow/flow_base.rb +13 -15
- data/lib/flow/operation/accessors.rb +66 -0
- data/lib/flow/operation/callbacks.rb +12 -10
- data/lib/flow/operation/core.rb +10 -8
- data/lib/flow/operation/error_handler.rb +18 -12
- data/lib/flow/operation/errors/already_executed.rb +5 -3
- data/lib/flow/operation/errors/already_rewound.rb +5 -3
- data/lib/flow/operation/execute.rb +28 -26
- data/lib/flow/operation/failures.rb +48 -42
- data/lib/flow/operation/status.rb +18 -21
- data/lib/flow/operation/transactions.rb +8 -6
- data/lib/flow/operation_base.rb +15 -13
- data/lib/flow/rspec_configuration.rb +5 -0
- data/lib/flow/spec_helper.rb +3 -0
- data/lib/flow/state/errors/not_validated.rb +9 -0
- data/lib/flow/state/output.rb +59 -0
- data/lib/flow/state/status.rb +22 -0
- data/lib/flow/state_base.rb +9 -16
- data/lib/flow/version.rb +1 -1
- data/lib/generators/flow/application_flow/templates/application_flow.rb +1 -1
- data/lib/generators/flow/application_operation/templates/application_operation.rb +1 -1
- data/lib/generators/flow/application_state/templates/application_state.rb +1 -1
- data/lib/generators/flow/operation/USAGE +1 -1
- data/lib/generators/flow/operation/templates/operation.rb.erb +0 -5
- data/lib/generators/flow/state/USAGE +1 -1
- data/lib/generators/flow/state/templates/state.rb.erb +2 -1
- data/lib/generators/rspec/application_flow/templates/application_flow_spec.rb +1 -1
- data/lib/generators/rspec/application_operation/templates/application_operation_spec.rb +1 -9
- data/lib/generators/rspec/application_state/templates/application_state_spec.rb +1 -1
- data/lib/generators/rspec/flow/templates/flow_spec.rb.erb +0 -8
- data/lib/generators/rspec/operation/templates/operation_spec.rb.erb +5 -11
- data/lib/generators/rspec/state/templates/state_spec.rb.erb +8 -10
- metadata +39 -52
- data/lib/flow/custom_matchers/define_argument.rb +0 -19
- data/lib/flow/custom_matchers/define_attribute.rb +0 -19
- data/lib/flow/custom_matchers/define_option.rb +0 -26
- data/lib/flow/flow/ebb.rb +0 -28
- data/lib/flow/flow/revert.rb +0 -18
- data/lib/flow/operation/rewind.rb +0 -25
- data/lib/flow/state/arguments.rb +0 -30
- data/lib/flow/state/attributes.rb +0 -31
- data/lib/flow/state/callbacks.rb +0 -13
- data/lib/flow/state/core.rb +0 -14
- data/lib/flow/state/options.rb +0 -45
- 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
|
data/lib/flow/flow/ebb.rb
DELETED
@@ -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
|
data/lib/flow/flow/revert.rb
DELETED
@@ -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
|
data/lib/flow/state/arguments.rb
DELETED
@@ -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
|
data/lib/flow/state/callbacks.rb
DELETED
@@ -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
|
data/lib/flow/state/core.rb
DELETED
@@ -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
|
data/lib/flow/state/options.rb
DELETED
@@ -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
|
data/lib/flow/state/string.rb
DELETED
@@ -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
|