flows 0.0.2 → 0.1.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.
- checksums.yaml +4 -4
- data/.gitignore +4 -0
- data/Gemfile.lock +41 -1
- data/README.md +296 -2
- data/bin/benchmark +88 -0
- data/bin/demo +4 -4
- data/bin/examples.rb +159 -0
- data/bin/profile_10steps +64 -0
- data/bin/ruby_benchmarks +26 -0
- data/flows.gemspec +10 -1
- data/lib/flows/operation/builder/build_router.rb +37 -0
- data/lib/flows/operation/builder.rb +78 -23
- data/lib/flows/operation/dsl.rb +57 -18
- data/lib/flows/operation/errors.rb +10 -0
- data/lib/flows/operation/executor.rb +14 -9
- data/lib/flows/operation.rb +11 -7
- data/lib/flows/result/err.rb +5 -5
- data/lib/flows/result/ok.rb +5 -5
- data/lib/flows/result.rb +2 -6
- data/lib/flows/result_router.rb +14 -0
- data/lib/flows/router.rb +5 -9
- data/lib/flows/version.rb +1 -1
- data/lib/flows.rb +1 -0
- data/profile/.keep +0 -0
- metadata +79 -2
data/bin/profile_10steps
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# rubocop:disable all
|
3
|
+
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'json'
|
6
|
+
require 'ruby-prof'
|
7
|
+
require 'stackprof'
|
8
|
+
|
9
|
+
require_relative './examples'
|
10
|
+
|
11
|
+
flows_ten_steps = FlowsTenSteps.new
|
12
|
+
|
13
|
+
build_output_name = '10steps_build_10k_times'
|
14
|
+
exec_output_name = '10steps_execution_10k_times'
|
15
|
+
|
16
|
+
#
|
17
|
+
# RubyProf
|
18
|
+
#
|
19
|
+
RubyProf.measure_mode = RubyProf::WALL_TIME
|
20
|
+
|
21
|
+
puts 'Build with RubyProf...'
|
22
|
+
result = RubyProf.profile do
|
23
|
+
10_000.times do
|
24
|
+
FlowsTenSteps.new
|
25
|
+
end
|
26
|
+
end
|
27
|
+
printer = RubyProf::MultiPrinter.new(result)
|
28
|
+
printer.print(path: 'profile', profile: build_output_name)
|
29
|
+
|
30
|
+
puts 'Execution with RubyProf...'
|
31
|
+
result = RubyProf.profile do
|
32
|
+
10_000.times {
|
33
|
+
flows_ten_steps.call
|
34
|
+
}
|
35
|
+
end
|
36
|
+
printer = RubyProf::MultiPrinter.new(result)
|
37
|
+
printer.print(path: 'profile', profile: exec_output_name)
|
38
|
+
|
39
|
+
#
|
40
|
+
# StackProf
|
41
|
+
#
|
42
|
+
|
43
|
+
puts 'Build with StackProf...'
|
44
|
+
result = StackProf.run(mode: :wall, raw: true) do
|
45
|
+
10_000.times do
|
46
|
+
FlowsTenSteps.new
|
47
|
+
end
|
48
|
+
end
|
49
|
+
File.write("profile/#{build_output_name}.json", JSON.generate(result))
|
50
|
+
|
51
|
+
puts 'Execution with StackProf...'
|
52
|
+
result = StackProf.run(mode: :wall, raw: true) do
|
53
|
+
10_000.times do
|
54
|
+
flows_ten_steps.call
|
55
|
+
end
|
56
|
+
end
|
57
|
+
File.write("profile/#{exec_output_name}.json", JSON.generate(result))
|
58
|
+
|
59
|
+
puts
|
60
|
+
puts 'Install speedscope:'
|
61
|
+
puts ' npm i -g speedscope'
|
62
|
+
puts
|
63
|
+
puts "speedscope profile/#{build_output_name}.json"
|
64
|
+
puts "speedscope profile/#{exec_output_name}.json"
|
data/bin/ruby_benchmarks
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# rubocop:disable all
|
3
|
+
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'benchmark/ips'
|
6
|
+
|
7
|
+
puts '-' * 50
|
8
|
+
puts '- method execution'
|
9
|
+
puts '-' * 50
|
10
|
+
|
11
|
+
class OneMethod
|
12
|
+
def meth
|
13
|
+
:ok
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
one_method = OneMethod.new
|
18
|
+
method_obj = one_method.method(:meth)
|
19
|
+
|
20
|
+
Benchmark.ips do |b|
|
21
|
+
b.report('native call') { one_method.meth }
|
22
|
+
b.report('send(...)') { one_method.send(:meth) }
|
23
|
+
b.report('Method#call') { method_obj.call }
|
24
|
+
|
25
|
+
b.compare!
|
26
|
+
end
|
data/flows.gemspec
CHANGED
@@ -2,7 +2,7 @@ lib = File.expand_path('lib', __dir__)
|
|
2
2
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
3
|
require 'flows/version'
|
4
4
|
|
5
|
-
Gem::Specification.new do |spec|
|
5
|
+
Gem::Specification.new do |spec| # rubocop:disable Metrics/BlockLength
|
6
6
|
spec.name = 'flows'
|
7
7
|
spec.version = Flows::VERSION
|
8
8
|
spec.authors = ['Roman Kolesnev']
|
@@ -33,4 +33,13 @@ Gem::Specification.new do |spec|
|
|
33
33
|
|
34
34
|
spec.add_development_dependency 'codecov'
|
35
35
|
spec.add_development_dependency 'simplecov'
|
36
|
+
|
37
|
+
# benchmarking tools
|
38
|
+
spec.add_development_dependency 'benchmark-ips'
|
39
|
+
spec.add_development_dependency 'ruby-prof'
|
40
|
+
spec.add_development_dependency 'stackprof'
|
41
|
+
|
42
|
+
# alternatives for comparison in benchmarking
|
43
|
+
spec.add_development_dependency 'dry-transaction'
|
44
|
+
spec.add_development_dependency 'trailblazer-operation'
|
36
45
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Flows
|
2
|
+
module Operation
|
3
|
+
class Builder
|
4
|
+
# Router builder
|
5
|
+
module BuildRouter
|
6
|
+
class << self
|
7
|
+
def call(custom_routes, next_step, step_names)
|
8
|
+
if custom_routes
|
9
|
+
custom_router(custom_routes, next_step, step_names)
|
10
|
+
else
|
11
|
+
Flows::ResultRouter.new(next_step, :term)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def custom_router(custom_routes, next_step, step_names)
|
18
|
+
check_custom_routes(custom_routes, step_names)
|
19
|
+
|
20
|
+
custom_routes[Flows::Result::Ok] ||= next_step
|
21
|
+
custom_routes[Flows::Result::Err] ||= :term
|
22
|
+
|
23
|
+
Flows::Router.new(custom_routes)
|
24
|
+
end
|
25
|
+
|
26
|
+
def check_custom_routes(custom_routes, step_names)
|
27
|
+
custom_routes.values.each do |target|
|
28
|
+
next if step_names.include?(target) || target == :term
|
29
|
+
|
30
|
+
raise(::Flows::Operation::NoStepDefinedError, target)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -1,10 +1,17 @@
|
|
1
|
+
require_relative './builder/build_router'
|
2
|
+
|
1
3
|
module Flows
|
2
4
|
module Operation
|
3
5
|
# Flow builder
|
4
6
|
class Builder
|
5
|
-
|
7
|
+
attr_reader :steps, :method_source, :deps
|
8
|
+
|
9
|
+
def initialize(steps:, method_source:, deps:)
|
6
10
|
@method_source = method_source
|
7
11
|
@steps = steps
|
12
|
+
@deps = deps
|
13
|
+
|
14
|
+
@step_names = @steps.map { |s| s[:name] }
|
8
15
|
end
|
9
16
|
|
10
17
|
def call
|
@@ -17,39 +24,96 @@ module Flows
|
|
17
24
|
|
18
25
|
private
|
19
26
|
|
20
|
-
def resolve_wiring!
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
27
|
+
def resolve_wiring! # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
28
|
+
# we have to disable some linters for performance reasons
|
29
|
+
# this method can be simplified using `map.with_index`, but while loops is about
|
30
|
+
# 2x faster for such cases.
|
31
|
+
index = 0
|
32
|
+
|
33
|
+
while index < @steps.length
|
34
|
+
current_step = @steps[index]
|
35
|
+
next_step_name = nil
|
36
|
+
|
37
|
+
inner_index = index + 1
|
38
|
+
while inner_index < @steps.length
|
39
|
+
candidate = @steps[inner_index]
|
40
|
+
candidate_last_track = candidate[:track_path].last
|
41
|
+
|
42
|
+
if candidate[:track_path] == [] || current_step[:track_path].include?(candidate_last_track)
|
43
|
+
next_step_name = candidate[:name]
|
44
|
+
break
|
45
|
+
end
|
46
|
+
|
47
|
+
inner_index += 1
|
48
|
+
end
|
49
|
+
|
50
|
+
current_step[:next_step] = next_step_name || :term
|
51
|
+
|
52
|
+
index += 1
|
25
53
|
end
|
26
54
|
end
|
27
55
|
|
28
56
|
def resolve_bodies!
|
29
57
|
@steps.map! do |step|
|
30
|
-
unless @method_source.respond_to?(step[:name])
|
31
|
-
raise ::Flows::Operation::NoStepImplementationError, step[:name]
|
32
|
-
end
|
33
|
-
|
34
58
|
step.merge(
|
35
|
-
body:
|
59
|
+
body: step[:custom_body] || resolve_body_from_source(step[:name])
|
36
60
|
)
|
37
61
|
end
|
38
62
|
end
|
39
63
|
|
64
|
+
def resolve_body_from_source(name)
|
65
|
+
return @deps[name] if @deps.key?(name)
|
66
|
+
|
67
|
+
raise(::Flows::Operation::NoStepImplementationError, name) unless @method_source.respond_to?(name)
|
68
|
+
|
69
|
+
@method_source.method(name)
|
70
|
+
end
|
71
|
+
|
40
72
|
def build_nodes
|
41
73
|
@nodes = @steps.map do |step|
|
42
74
|
Flows::Node.new(
|
43
75
|
name: step[:name],
|
44
|
-
body: step
|
76
|
+
body: build_final_body(step),
|
45
77
|
preprocessor: method(:node_preprocessor),
|
46
78
|
postprocessor: method(:node_postprocessor),
|
47
|
-
router:
|
48
|
-
meta:
|
79
|
+
router: BuildRouter.call(step[:custom_routes], step[:next_step], @step_names),
|
80
|
+
meta: build_meta(step)
|
49
81
|
)
|
50
82
|
end
|
51
83
|
end
|
52
84
|
|
85
|
+
def build_final_body(step)
|
86
|
+
case step[:type]
|
87
|
+
when :step
|
88
|
+
step[:body]
|
89
|
+
when :wrapper
|
90
|
+
build_wrapper_body(step[:body], step[:block])
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def build_wrapper_body(wrapper, block)
|
95
|
+
suboperation_class = Class.new do
|
96
|
+
include ::Flows::Operation
|
97
|
+
end
|
98
|
+
|
99
|
+
suboperation_class.instance_exec(&block)
|
100
|
+
suboperation_class.no_shape
|
101
|
+
|
102
|
+
suboperation = suboperation_class.new(method_source: @method_source, deps: @deps)
|
103
|
+
|
104
|
+
lambda do |**options|
|
105
|
+
wrapper.call(**options) { suboperation.call(**options) }
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def build_meta(step)
|
110
|
+
{
|
111
|
+
type: step[:type],
|
112
|
+
name: step[:name],
|
113
|
+
track_path: step[:track_path]
|
114
|
+
}
|
115
|
+
end
|
116
|
+
|
53
117
|
def node_preprocessor(_input, context, _meta)
|
54
118
|
context[:data]
|
55
119
|
end
|
@@ -61,15 +125,6 @@ module Flows
|
|
61
125
|
|
62
126
|
output
|
63
127
|
end
|
64
|
-
|
65
|
-
def make_router(step_definition)
|
66
|
-
routes = step_definition[:custom_routes]
|
67
|
-
|
68
|
-
routes[Flows::Result::Ok] ||= step_definition[:next_step]
|
69
|
-
routes[Flows::Result::Err] ||= :term
|
70
|
-
|
71
|
-
Flows::Router.new(routes)
|
72
|
-
end
|
73
128
|
end
|
74
129
|
end
|
75
130
|
end
|
data/lib/flows/operation/dsl.rb
CHANGED
@@ -2,31 +2,70 @@ module Flows
|
|
2
2
|
module Operation
|
3
3
|
# DSL methods for operation
|
4
4
|
module DSL
|
5
|
-
attr_reader :steps, :
|
5
|
+
attr_reader :steps, :ok_shapes, :err_shapes
|
6
6
|
|
7
7
|
include Flows::Result::Helpers
|
8
8
|
|
9
|
-
def step(name, custom_routes =
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
9
|
+
def step(name, custom_body_or_routes = nil, custom_routes = nil)
|
10
|
+
if custom_routes
|
11
|
+
custom_body = custom_body_or_routes
|
12
|
+
elsif custom_body_or_routes.is_a? Hash
|
13
|
+
custom_routes = custom_body_or_routes
|
14
|
+
custom_body = nil
|
15
|
+
else
|
16
|
+
custom_routes = nil
|
17
|
+
custom_body = custom_body_or_routes
|
18
|
+
end
|
19
|
+
|
20
|
+
@steps << make_step(name, custom_routes: custom_routes, custom_body: custom_body)
|
21
|
+
end
|
22
|
+
|
23
|
+
def track(name, &block)
|
24
|
+
track_path_before = @track_path
|
25
|
+
@track_path += [name]
|
26
|
+
|
27
|
+
@steps << make_step(name, custom_body: ->(**) { ok })
|
28
|
+
instance_exec(&block)
|
29
|
+
|
30
|
+
@track_path = track_path_before
|
31
|
+
end
|
32
|
+
|
33
|
+
def wrap(name, custom_body = nil, &block)
|
34
|
+
@steps << make_step(name, type: :wrapper, custom_body: custom_body, block: block)
|
35
|
+
end
|
36
|
+
|
37
|
+
def ok_shape(*keys, **code_keys_map)
|
38
|
+
@ok_shapes = if keys.empty?
|
39
|
+
code_keys_map
|
40
|
+
else
|
41
|
+
{ success: keys }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def err_shape(*keys, **code_keys_map)
|
46
|
+
@err_shapes = if keys.empty?
|
47
|
+
code_keys_map
|
48
|
+
else
|
49
|
+
{ failure: keys }
|
50
|
+
end
|
14
51
|
end
|
15
52
|
|
16
|
-
def
|
17
|
-
@
|
18
|
-
|
19
|
-
else
|
20
|
-
{ success: keys }
|
21
|
-
end
|
53
|
+
def no_shape
|
54
|
+
@ok_shapes = :no_shapes
|
55
|
+
@err_shapes = :no_shapes
|
22
56
|
end
|
23
57
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
58
|
+
private
|
59
|
+
|
60
|
+
def make_step(name, type: :step, custom_routes: {}, custom_body: nil, block: nil)
|
61
|
+
{
|
62
|
+
type: type,
|
63
|
+
name: name,
|
64
|
+
custom_routes: custom_routes,
|
65
|
+
custom_body: custom_body,
|
66
|
+
block: block,
|
67
|
+
track_path: @track_path
|
68
|
+
}
|
30
69
|
end
|
31
70
|
end
|
32
71
|
end
|
@@ -29,6 +29,16 @@ module Flows
|
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
|
+
class NoStepDefinedError < ::Flows::Error
|
33
|
+
def initialize(step_name)
|
34
|
+
@step_name = step_name
|
35
|
+
end
|
36
|
+
|
37
|
+
def message
|
38
|
+
"Missing step or track definition: #{@step_name}"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
32
42
|
class MissingOutputError < ::Flows::Error
|
33
43
|
def initialize(required_keys, actual_keys)
|
34
44
|
@missing_keys = required_keys - actual_keys
|
@@ -4,11 +4,11 @@ module Flows
|
|
4
4
|
class Executor
|
5
5
|
include ::Flows::Result::Helpers
|
6
6
|
|
7
|
-
def initialize(flow:,
|
7
|
+
def initialize(flow:, ok_shapes:, err_shapes:, class_name:)
|
8
8
|
@flow = flow
|
9
9
|
|
10
|
-
@
|
11
|
-
@
|
10
|
+
@ok_shapes = ok_shapes
|
11
|
+
@err_shapes = err_shapes
|
12
12
|
@operation_class_name = class_name
|
13
13
|
end
|
14
14
|
|
@@ -31,8 +31,10 @@ module Flows
|
|
31
31
|
end
|
32
32
|
|
33
33
|
def build_success_result(status, context)
|
34
|
-
|
35
|
-
|
34
|
+
return ok(status, context[:data]) if @ok_shapes == :no_shapes
|
35
|
+
|
36
|
+
shape = @ok_shapes[status]
|
37
|
+
raise ::Flows::Operation::UnexpectedSuccessStatusError.new(status, @ok_shapes.keys) if shape.nil?
|
36
38
|
|
37
39
|
data = extract_data(context[:data], shape)
|
38
40
|
|
@@ -40,13 +42,16 @@ module Flows
|
|
40
42
|
end
|
41
43
|
|
42
44
|
def build_failure_result(status, context, last_result)
|
43
|
-
raise ::Flows::Operation::NoFailureShapeError if @
|
45
|
+
raise ::Flows::Operation::NoFailureShapeError if @err_shapes.nil?
|
46
|
+
|
47
|
+
meta = build_meta(context, last_result)
|
44
48
|
|
45
|
-
|
46
|
-
|
49
|
+
return Flows::Result::Err.new(context[:data], status: status, meta: meta) if @err_shapes == :no_shapes
|
50
|
+
|
51
|
+
shape = @err_shapes[status]
|
52
|
+
raise ::Flows::Operation::UnexpectedFailureStatusError.new(status, @err_shapes.keys) if shape.nil?
|
47
53
|
|
48
54
|
data = extract_data(context[:data], shape)
|
49
|
-
meta = build_meta(context, last_result)
|
50
55
|
|
51
56
|
Flows::Result::Err.new(data, status: status, meta: meta)
|
52
57
|
end
|
data/lib/flows/operation.rb
CHANGED
@@ -9,15 +9,18 @@ module Flows
|
|
9
9
|
module Operation
|
10
10
|
def self.included(mod)
|
11
11
|
mod.instance_variable_set(:@steps, [])
|
12
|
+
mod.instance_variable_set(:@track_path, [])
|
12
13
|
mod.extend ::Flows::Operation::DSL
|
13
14
|
end
|
14
15
|
|
15
16
|
include ::Flows::Result::Helpers
|
16
17
|
|
17
|
-
def initialize
|
18
|
+
def initialize(method_source: nil, deps: {})
|
18
19
|
_flows_do_checks
|
19
20
|
|
20
|
-
|
21
|
+
flow = _flows_make_flow(method_source || self, deps)
|
22
|
+
|
23
|
+
@_flows_executor = _flows_make_executor(flow)
|
21
24
|
end
|
22
25
|
|
23
26
|
def call(**params)
|
@@ -28,21 +31,22 @@ module Flows
|
|
28
31
|
|
29
32
|
def _flows_do_checks
|
30
33
|
raise NoStepsError if self.class.steps.empty?
|
31
|
-
raise NoSuccessShapeError, self if self.class.
|
34
|
+
raise NoSuccessShapeError, self if self.class.ok_shapes.nil?
|
32
35
|
end
|
33
36
|
|
34
|
-
def _flows_make_flow
|
37
|
+
def _flows_make_flow(method_source, deps)
|
35
38
|
::Flows::Operation::Builder.new(
|
36
39
|
steps: self.class.steps,
|
37
|
-
method_source:
|
40
|
+
method_source: method_source,
|
41
|
+
deps: deps
|
38
42
|
).call
|
39
43
|
end
|
40
44
|
|
41
45
|
def _flows_make_executor(flow)
|
42
46
|
::Flows::Operation::Executor.new(
|
43
47
|
flow: flow,
|
44
|
-
|
45
|
-
|
48
|
+
ok_shapes: self.class.ok_shapes,
|
49
|
+
err_shapes: self.class.err_shapes,
|
46
50
|
class_name: self.class.name
|
47
51
|
)
|
48
52
|
end
|
data/lib/flows/result/err.rb
CHANGED
@@ -2,8 +2,12 @@ module Flows
|
|
2
2
|
class Result
|
3
3
|
# Wrapper for failure results
|
4
4
|
class Err < Result
|
5
|
+
attr_reader :error
|
6
|
+
|
5
7
|
def initialize(data, status: :failure, meta: {})
|
6
|
-
|
8
|
+
@error = data
|
9
|
+
@status = status
|
10
|
+
@meta = meta
|
7
11
|
end
|
8
12
|
|
9
13
|
def ok?
|
@@ -17,10 +21,6 @@ module Flows
|
|
17
21
|
def unwrap
|
18
22
|
raise UnwrapError.new(@status, @data, @meta)
|
19
23
|
end
|
20
|
-
|
21
|
-
def error
|
22
|
-
@data
|
23
|
-
end
|
24
24
|
end
|
25
25
|
end
|
26
26
|
end
|
data/lib/flows/result/ok.rb
CHANGED
@@ -2,8 +2,12 @@ module Flows
|
|
2
2
|
class Result
|
3
3
|
# Wrapper for successful results
|
4
4
|
class Ok < Result
|
5
|
+
attr_reader :unwrap
|
6
|
+
|
5
7
|
def initialize(data, status: :success, meta: {})
|
6
|
-
|
8
|
+
@unwrap = data
|
9
|
+
@status = status
|
10
|
+
@meta = meta
|
7
11
|
end
|
8
12
|
|
9
13
|
def ok?
|
@@ -14,10 +18,6 @@ module Flows
|
|
14
18
|
false
|
15
19
|
end
|
16
20
|
|
17
|
-
def unwrap
|
18
|
-
@data
|
19
|
-
end
|
20
|
-
|
21
21
|
def error
|
22
22
|
raise NoErrorError.new(@status, @data)
|
23
23
|
end
|
data/lib/flows/result.rb
CHANGED
@@ -3,12 +3,8 @@ module Flows
|
|
3
3
|
class Result
|
4
4
|
attr_reader :status, :meta
|
5
5
|
|
6
|
-
def initialize(
|
7
|
-
|
8
|
-
@status = status
|
9
|
-
@meta = meta
|
10
|
-
|
11
|
-
raise 'Use Flows::Result::Ok or Flows::Result::Err for build result objects' if self.class == Result
|
6
|
+
def initialize(**)
|
7
|
+
raise 'Use Flows::Result::Ok or Flows::Result::Err for build result objects'
|
12
8
|
end
|
13
9
|
end
|
14
10
|
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Flows
|
2
|
+
# Node router for simple case when result must be a `Flows::Result`
|
3
|
+
# and we don't care about resukt status key
|
4
|
+
class ResultRouter
|
5
|
+
def initialize(success_route, failure_route)
|
6
|
+
@success_route = success_route
|
7
|
+
@failure_route = failure_route
|
8
|
+
end
|
9
|
+
|
10
|
+
def call(output, **)
|
11
|
+
output.ok? ? @success_route : @failure_route
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
data/lib/flows/router.rb
CHANGED
@@ -4,23 +4,19 @@ module Flows
|
|
4
4
|
class Error < Flows::Error; end
|
5
5
|
class NoRouteError < Error; end
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
def initialize(route_hash, preprocessor: DEFAULT_PREPROCESSOR)
|
7
|
+
def initialize(route_hash, preprocessor: nil)
|
10
8
|
@route_def = route_hash
|
11
9
|
@preprocessor = preprocessor
|
12
10
|
end
|
13
11
|
|
14
12
|
def call(output, context:, meta:)
|
15
|
-
data = @preprocessor.call(output, context, meta)
|
13
|
+
data = @preprocessor ? @preprocessor.call(output, context, meta) : output
|
16
14
|
|
17
|
-
|
18
|
-
predicate === data # rubocop:disable Style/CaseEquality
|
15
|
+
@route_def.each_pair do |predicate, route|
|
16
|
+
return route if predicate === data # rubocop:disable Style/CaseEquality
|
19
17
|
end
|
20
18
|
|
21
|
-
raise NoRouteError, "no route found found for output: #{output.inspect}"
|
22
|
-
|
23
|
-
matched_entry[1]
|
19
|
+
raise NoRouteError, "no route found found for output: #{output.inspect}"
|
24
20
|
end
|
25
21
|
end
|
26
22
|
end
|
data/lib/flows/version.rb
CHANGED
data/lib/flows.rb
CHANGED
data/profile/.keep
ADDED
File without changes
|