flows 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/build.yml +43 -0
- data/.mdlrc +1 -0
- data/.rubocop.yml +25 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +80 -25
- data/README.md +170 -44
- data/bin/benchmark +65 -42
- data/bin/examples.rb +37 -1
- data/bin/profile_10steps +48 -6
- data/docs/.nojekyll +0 -0
- data/docs/CNAME +1 -0
- data/docs/README.md +197 -0
- data/docs/_sidebar.md +26 -0
- data/docs/contributing/benchmarks_profiling.md +3 -0
- data/docs/contributing/local_development.md +3 -0
- data/docs/flow/direct_usage.md +3 -0
- data/docs/flow/general_idea.md +3 -0
- data/docs/index.html +30 -0
- data/docs/operation/basic_usage.md +1 -0
- data/docs/operation/inject_steps.md +3 -0
- data/docs/operation/lambda_steps.md +3 -0
- data/docs/operation/result_shapes.md +3 -0
- data/docs/operation/routing_tracks.md +3 -0
- data/docs/operation/wrapping_steps.md +3 -0
- data/docs/overview/performance.md +336 -0
- data/docs/railway/basic_usage.md +232 -0
- data/docs/result_objects/basic_usage.md +196 -0
- data/docs/result_objects/do_notation.md +139 -0
- data/flows.gemspec +2 -0
- data/forspell.dict +8 -0
- data/lefthook.yml +12 -0
- data/lib/flows.rb +2 -0
- data/lib/flows/flow.rb +1 -1
- data/lib/flows/operation.rb +1 -3
- data/lib/flows/operation/builder.rb +2 -2
- data/lib/flows/operation/dsl.rb +21 -0
- data/lib/flows/railway.rb +48 -0
- data/lib/flows/railway/builder.rb +68 -0
- data/lib/flows/railway/dsl.rb +28 -0
- data/lib/flows/railway/errors.rb +21 -0
- data/lib/flows/railway/executor.rb +23 -0
- data/lib/flows/result.rb +1 -0
- data/lib/flows/result/do.rb +30 -0
- data/lib/flows/result_router.rb +1 -1
- data/lib/flows/version.rb +1 -1
- metadata +59 -3
- data/.travis.yml +0 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: eed3e3ba742be2b25c1ae3ca7b52d2db3418183e0950616409ef8d05232365de
|
4
|
+
data.tar.gz: c2f340f63e6e55178426c28fcfe29aa6d6c9af7d9a4b539d43461f068f4a6082
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 44a6ac9cdcedb674aab92373647582e038602c9459361d1ccbc6502cd78556835042fb81004c2329f1c1a95108371040dddb829fbd11a8eaa20bf310817163f3
|
7
|
+
data.tar.gz: 333ff1f85e39a794c5e4073e7deeca887562a0568497cd480d75b26338aff242dc8c5d1a8f67ce822083e894beecfe033b7abcffe25e931490de53db7e52cf29
|
@@ -0,0 +1,43 @@
|
|
1
|
+
name: Build
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
branches:
|
6
|
+
- master
|
7
|
+
pull_request:
|
8
|
+
branches:
|
9
|
+
- master
|
10
|
+
schedule:
|
11
|
+
- cron: 0 2 * * 1-5
|
12
|
+
|
13
|
+
jobs:
|
14
|
+
build:
|
15
|
+
runs-on: ubuntu-latest
|
16
|
+
strategy:
|
17
|
+
fail-fast: false
|
18
|
+
matrix:
|
19
|
+
ruby:
|
20
|
+
- 2.5.x
|
21
|
+
- 2.6.x
|
22
|
+
steps:
|
23
|
+
- uses: actions/checkout@v1
|
24
|
+
- name: Set up Ruby
|
25
|
+
uses: actions/setup-ruby@v1
|
26
|
+
with:
|
27
|
+
ruby-version: ${{ matrix.ruby }}
|
28
|
+
- name: Install Bundler
|
29
|
+
run: gem install bundler
|
30
|
+
- name: Install Hunspell
|
31
|
+
run: sudo apt-get install hunspell
|
32
|
+
- name: Install Deps
|
33
|
+
run: bundle install --jobs 4 --retry 3
|
34
|
+
- name: Rubocop
|
35
|
+
run: bundle exec rubocop
|
36
|
+
- name: RSpec
|
37
|
+
env:
|
38
|
+
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
39
|
+
run: bundle exec rspec
|
40
|
+
- name: Markdown Linter (docs)
|
41
|
+
run: bundle exec mdl docs/**/*.md README.md
|
42
|
+
- name: Spelling Check (docs & code)
|
43
|
+
run: bundle exec forspell
|
data/.mdlrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rules "~MD013", "~MD033"
|
data/.rubocop.yml
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require:
|
2
2
|
- rubocop-rspec
|
3
3
|
- rubocop-performance
|
4
|
+
- rubocop-md
|
4
5
|
|
5
6
|
AllCops:
|
6
7
|
TargetRubyVersion: 2.6
|
@@ -20,3 +21,27 @@ Metrics/ParameterLists:
|
|
20
21
|
|
21
22
|
Style/WhileUntilModifier:
|
22
23
|
Enabled: false
|
24
|
+
|
25
|
+
Naming/UncommunicativeMethodParamName:
|
26
|
+
Exclude:
|
27
|
+
- '**/*.md'
|
28
|
+
|
29
|
+
Style/MixinUsage:
|
30
|
+
Exclude:
|
31
|
+
- '**/*.md'
|
32
|
+
|
33
|
+
Lint/UnusedMethodArgument:
|
34
|
+
Exclude:
|
35
|
+
- '**/*.md'
|
36
|
+
|
37
|
+
Lint/UnusedBlockArgument:
|
38
|
+
Exclude:
|
39
|
+
- '**/*.md'
|
40
|
+
|
41
|
+
Lint/UnreachableCode:
|
42
|
+
Exclude:
|
43
|
+
- 'docs/result_objects/do_notation.md'
|
44
|
+
|
45
|
+
Lint/Void:
|
46
|
+
Exclude:
|
47
|
+
- 'docs/result_objects/do_notation.md'
|
data/Gemfile
CHANGED
@@ -2,3 +2,9 @@ source 'https://rubygems.org'
|
|
2
2
|
|
3
3
|
# Specify your gem's dependencies in flows.gemspec
|
4
4
|
gemspec
|
5
|
+
|
6
|
+
# temporary dev dependencies,
|
7
|
+
# move it to gemspec after PRs will be merged into realesed version of a gem
|
8
|
+
group :development do
|
9
|
+
gem 'mdl', github: 'ffloyd/markdownlint', branch: 'update-kramdown-dep'
|
10
|
+
end
|
data/Gemfile.lock
CHANGED
@@ -1,12 +1,24 @@
|
|
1
|
+
GIT
|
2
|
+
remote: git://github.com/ffloyd/markdownlint.git
|
3
|
+
revision: 801cb12f645d894e0d262bb4908d8086b6dd23bc
|
4
|
+
branch: update-kramdown-dep
|
5
|
+
specs:
|
6
|
+
mdl (0.5.0)
|
7
|
+
kramdown (~> 2.0)
|
8
|
+
kramdown-parser-gfm (~> 1.0)
|
9
|
+
mixlib-cli (~> 1.7, >= 1.7.0)
|
10
|
+
mixlib-config (~> 2.2, >= 2.2.1)
|
11
|
+
|
1
12
|
PATH
|
2
13
|
remote: .
|
3
14
|
specs:
|
4
|
-
flows (0.
|
15
|
+
flows (0.2.0)
|
5
16
|
|
6
17
|
GEM
|
7
18
|
remote: https://rubygems.org/
|
8
19
|
specs:
|
9
20
|
ast (2.4.0)
|
21
|
+
backports (3.15.0)
|
10
22
|
benchmark-ips (2.7.2)
|
11
23
|
codecov (0.1.14)
|
12
24
|
json
|
@@ -14,8 +26,9 @@ GEM
|
|
14
26
|
url
|
15
27
|
coderay (1.1.2)
|
16
28
|
concurrent-ruby (1.1.5)
|
29
|
+
crass (1.0.4)
|
17
30
|
diff-lcs (1.3)
|
18
|
-
docile (1.3.
|
31
|
+
docile (1.3.2)
|
19
32
|
dry-configurable (0.8.3)
|
20
33
|
concurrent-ruby (~> 1.0)
|
21
34
|
dry-core (~> 0.4, >= 0.4.7)
|
@@ -29,9 +42,9 @@ GEM
|
|
29
42
|
concurrent-ruby (~> 1.0)
|
30
43
|
dry-core (~> 0.4)
|
31
44
|
dry-equalizer (~> 0.2)
|
32
|
-
dry-matcher (0.8.
|
33
|
-
dry-core (>= 0.4.
|
34
|
-
dry-monads (1.3.
|
45
|
+
dry-matcher (0.8.2)
|
46
|
+
dry-core (>= 0.4.8)
|
47
|
+
dry-monads (1.3.1)
|
35
48
|
concurrent-ruby (~> 1.0)
|
36
49
|
dry-core (~> 0.4, >= 0.4.4)
|
37
50
|
dry-equalizer
|
@@ -40,60 +53,99 @@ GEM
|
|
40
53
|
dry-events (>= 0.1.0)
|
41
54
|
dry-matcher (>= 0.7.0)
|
42
55
|
dry-monads (>= 0.4.0)
|
43
|
-
|
56
|
+
equatable (0.6.1)
|
57
|
+
ffi (1.11.1)
|
58
|
+
ffi-hunspell (0.4.0)
|
59
|
+
ffi (~> 1.0)
|
60
|
+
forspell (0.0.8)
|
61
|
+
backports (~> 3.0)
|
62
|
+
ffi-hunspell
|
63
|
+
highline
|
64
|
+
kramdown (~> 2.0)
|
65
|
+
kramdown-parser-gfm (~> 1.0)
|
66
|
+
parser
|
67
|
+
pastel
|
68
|
+
ruby-progressbar
|
69
|
+
sanitize (~> 5.0)
|
70
|
+
slop (~> 4.6)
|
71
|
+
yard
|
72
|
+
highline (2.0.2)
|
73
|
+
jaro_winkler (1.5.3)
|
44
74
|
json (2.2.0)
|
75
|
+
kramdown (2.1.0)
|
76
|
+
kramdown-parser-gfm (1.1.0)
|
77
|
+
kramdown (~> 2.0)
|
45
78
|
method_source (0.9.2)
|
79
|
+
mini_portile2 (2.4.0)
|
80
|
+
mixlib-cli (1.7.0)
|
81
|
+
mixlib-config (2.2.18)
|
82
|
+
tomlrb
|
83
|
+
nokogiri (1.10.4)
|
84
|
+
mini_portile2 (~> 2.4.0)
|
85
|
+
nokogumbo (2.0.1)
|
86
|
+
nokogiri (~> 1.8, >= 1.8.4)
|
46
87
|
parallel (1.17.0)
|
47
|
-
parser (2.6.
|
88
|
+
parser (2.6.4.1)
|
48
89
|
ast (~> 2.4.0)
|
90
|
+
pastel (0.7.3)
|
91
|
+
equatable (~> 0.6)
|
92
|
+
tty-color (~> 0.5)
|
49
93
|
pry (0.12.2)
|
50
94
|
coderay (~> 1.1.0)
|
51
95
|
method_source (~> 0.9.0)
|
52
|
-
psych (3.1.0)
|
53
96
|
rainbow (3.0.0)
|
54
97
|
rake (10.5.0)
|
55
98
|
rspec (3.8.0)
|
56
99
|
rspec-core (~> 3.8.0)
|
57
100
|
rspec-expectations (~> 3.8.0)
|
58
101
|
rspec-mocks (~> 3.8.0)
|
59
|
-
rspec-core (3.8.
|
102
|
+
rspec-core (3.8.2)
|
60
103
|
rspec-support (~> 3.8.0)
|
61
|
-
rspec-expectations (3.8.
|
104
|
+
rspec-expectations (3.8.4)
|
62
105
|
diff-lcs (>= 1.2.0, < 2.0)
|
63
106
|
rspec-support (~> 3.8.0)
|
64
|
-
rspec-mocks (3.8.
|
107
|
+
rspec-mocks (3.8.1)
|
65
108
|
diff-lcs (>= 1.2.0, < 2.0)
|
66
109
|
rspec-support (~> 3.8.0)
|
67
|
-
rspec-support (3.8.
|
68
|
-
rubocop (0.
|
110
|
+
rspec-support (3.8.2)
|
111
|
+
rubocop (0.74.0)
|
69
112
|
jaro_winkler (~> 1.5.1)
|
70
113
|
parallel (~> 1.10)
|
71
|
-
parser (>= 2.
|
72
|
-
psych (>= 3.1.0)
|
114
|
+
parser (>= 2.6)
|
73
115
|
rainbow (>= 2.2.2, < 4.0)
|
74
116
|
ruby-progressbar (~> 1.7)
|
75
|
-
unicode-display_width (>= 1.4.0, < 1.
|
76
|
-
rubocop-
|
77
|
-
rubocop (
|
78
|
-
rubocop-
|
117
|
+
unicode-display_width (>= 1.4.0, < 1.7)
|
118
|
+
rubocop-md (0.3.0)
|
119
|
+
rubocop (~> 0.60)
|
120
|
+
rubocop-performance (1.4.1)
|
121
|
+
rubocop (>= 0.71.0)
|
122
|
+
rubocop-rspec (1.35.0)
|
79
123
|
rubocop (>= 0.60.0)
|
80
124
|
ruby-prof (1.0.0)
|
81
|
-
ruby-progressbar (1.10.
|
82
|
-
|
125
|
+
ruby-progressbar (1.10.1)
|
126
|
+
sanitize (5.1.0)
|
127
|
+
crass (~> 1.0.2)
|
128
|
+
nokogiri (>= 1.8.0)
|
129
|
+
nokogumbo (~> 2.0)
|
130
|
+
simplecov (0.17.1)
|
83
131
|
docile (~> 1.1)
|
84
132
|
json (>= 1.8, < 3)
|
85
133
|
simplecov-html (~> 0.10.0)
|
86
134
|
simplecov-html (0.10.2)
|
135
|
+
slop (4.7.0)
|
87
136
|
stackprof (0.2.12)
|
88
|
-
|
137
|
+
tomlrb (1.2.8)
|
138
|
+
trailblazer-activity (0.9.0)
|
89
139
|
trailblazer-context (>= 0.1.4)
|
90
|
-
trailblazer-activity-dsl-linear (0.
|
91
|
-
trailblazer-activity (>= 0.
|
140
|
+
trailblazer-activity-dsl-linear (0.2.0)
|
141
|
+
trailblazer-activity (>= 0.9.0, < 1.0.0)
|
92
142
|
trailblazer-context (0.1.4)
|
93
143
|
trailblazer-operation (0.5.2)
|
94
144
|
trailblazer-activity-dsl-linear (>= 0.1.6, < 1.0.0)
|
95
|
-
|
145
|
+
tty-color (0.5.0)
|
146
|
+
unicode-display_width (1.6.0)
|
96
147
|
url (0.3.2)
|
148
|
+
yard (0.9.20)
|
97
149
|
|
98
150
|
PLATFORMS
|
99
151
|
ruby
|
@@ -104,10 +156,13 @@ DEPENDENCIES
|
|
104
156
|
codecov
|
105
157
|
dry-transaction
|
106
158
|
flows!
|
159
|
+
forspell (~> 0.0.8)
|
160
|
+
mdl!
|
107
161
|
pry
|
108
162
|
rake (~> 10.0)
|
109
163
|
rspec (~> 3.0)
|
110
164
|
rubocop
|
165
|
+
rubocop-md
|
111
166
|
rubocop-performance
|
112
167
|
rubocop-rspec
|
113
168
|
ruby-prof
|
data/README.md
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
# Flows
|
2
2
|
|
3
|
-
[![Build Status](https://
|
3
|
+
[![Build Status](https://github.com/ffloyd/flows/workflows/Build/badge.svg)](https://github.com/ffloyd/flows/actions)
|
4
4
|
[![codecov](https://codecov.io/gh/ffloyd/flows/branch/master/graph/badge.svg)](https://codecov.io/gh/ffloyd/flows)
|
5
5
|
[![Gem Version](https://badge.fury.io/rb/flows.svg)](https://badge.fury.io/rb/flows)
|
6
6
|
|
7
7
|
Small and fast ruby framework for implementing railway-like operations.
|
8
|
-
By design it close to [Trailblazer::Operation](http://trailblazer.to/gems/operation/2.0/) and [Dry::Transaction](https://dry-rb.org/gems/dry-transaction/),
|
9
|
-
but has
|
8
|
+
By design it is close to [Trailblazer::Operation](http://trailblazer.to/gems/operation/2.0/) and [Dry::Transaction](https://dry-rb.org/gems/dry-transaction/),
|
9
|
+
but has simpler and flexible DSL for defining operations and matching results. Also `flows` is faster, see [Performance](#performance).
|
10
10
|
|
11
|
-
`flows` has no production dependencies so
|
11
|
+
`flows` has no production dependencies so it can be used with any framework.
|
12
12
|
|
13
13
|
## Installation
|
14
14
|
|
@@ -20,11 +20,15 @@ gem 'flows'
|
|
20
20
|
|
21
21
|
And then execute:
|
22
22
|
|
23
|
-
|
23
|
+
```sh
|
24
|
+
bundle
|
25
|
+
```
|
24
26
|
|
25
27
|
Or install it yourself as:
|
26
28
|
|
27
|
-
|
29
|
+
```sh
|
30
|
+
gem install flows
|
31
|
+
```
|
28
32
|
|
29
33
|
## Usage
|
30
34
|
|
@@ -51,7 +55,7 @@ Basic usage:
|
|
51
55
|
|
52
56
|
```ruby
|
53
57
|
# create successful result with data {a: 1, b: 2}
|
54
|
-
result_ok = Flows::Result::Ok.new(a:1, b: 2)
|
58
|
+
result_ok = Flows::Result::Ok.new(a: 1, b: 2)
|
55
59
|
|
56
60
|
# get `:a` from result
|
57
61
|
result_ok.unwrap[:a] # 1
|
@@ -73,7 +77,7 @@ result_ok_custom = Flows::Result::Ok.new({ a: 1, b: 2 }, status: :custom)
|
|
73
77
|
result_ok_custom.status # :custom
|
74
78
|
|
75
79
|
# create failure result with data {a: 1, b: 2}
|
76
|
-
result_err = Flows::Result::Err.new(a:1, b: 2)
|
80
|
+
result_err = Flows::Result::Err.new(a: 1, b: 2)
|
77
81
|
|
78
82
|
# get `:a` from result
|
79
83
|
result_err.unwrap[:a] # raises exception
|
@@ -101,29 +105,33 @@ Mixin `Flows::Result::Helpers` contains tools for simpler generating and matchin
|
|
101
105
|
include Flows::Result::Helpers
|
102
106
|
|
103
107
|
# create successful result with data {a: 1, b: 2}
|
104
|
-
result_ok = ok(a:1, b: 2)
|
108
|
+
result_ok = ok(a: 1, b: 2)
|
105
109
|
|
106
110
|
# create successful result with data {a: 1, b: 2} and status `:custom`
|
107
111
|
result_ok_custom = ok(:custom, a: 1, b: 2)
|
108
112
|
|
109
113
|
# create failure result with data {a: 1, b: 2}
|
110
|
-
result_err = err(a:1, b: 2)
|
114
|
+
result_err = err(a: 1, b: 2)
|
111
115
|
|
112
116
|
# create failure result with data {a: 1, b: 2} and status `:custom`
|
113
117
|
result_err_custom = err(:custom, a: 1, b: 2)
|
114
118
|
|
115
119
|
# matching helpers
|
116
|
-
result =
|
120
|
+
result = SomeOperation.new.call
|
117
121
|
|
118
122
|
case result
|
119
123
|
when match_ok(:custom)
|
120
124
|
# matches only successful results with status :custom
|
125
|
+
do_something
|
121
126
|
when match_ok
|
122
127
|
# matches only successful results with any status
|
128
|
+
do_something
|
123
129
|
when match_err(:custom)
|
124
130
|
# matches only failure results with status :custom
|
131
|
+
do_something
|
125
132
|
when match_err
|
126
133
|
# matches only failure results with any status
|
134
|
+
do_something
|
127
135
|
end
|
128
136
|
```
|
129
137
|
|
@@ -141,7 +149,7 @@ class Summator
|
|
141
149
|
# It adds DSL, initializer and call method.
|
142
150
|
# Also it includes Flows::Result::Helper both on DSL and instance level.
|
143
151
|
include Flows::Operation
|
144
|
-
|
152
|
+
|
145
153
|
# This is step definitions.
|
146
154
|
# In simplest form step defined by its name and
|
147
155
|
# step implementation expected to be in a method
|
@@ -151,39 +159,38 @@ class Summator
|
|
151
159
|
step :validate
|
152
160
|
step :calc_sum
|
153
161
|
step :calc_square
|
154
|
-
|
162
|
+
|
155
163
|
# Which keys of operation data we want to expose on success
|
156
164
|
ok_shape :sum, :sum_square
|
157
|
-
|
158
|
-
# Which keys of operation data we want to expose on failure
|
165
|
+
|
166
|
+
# Which keys of operation data we want to expose on failure
|
159
167
|
err_shape :message
|
160
|
-
|
168
|
+
|
161
169
|
# Step implementation receives execution context as keyword arguments.
|
162
170
|
# For the first step context equals to operation arguments.
|
163
171
|
#
|
164
172
|
# Step implementation must return Result Object.
|
165
173
|
# Result Objects's data will be merged into operation context.
|
166
|
-
#
|
174
|
+
#
|
167
175
|
# If result is successful - next step will be executed.
|
168
176
|
# If not - operation terminates and returns failure.
|
169
177
|
def validate(a:, b:, **)
|
170
|
-
err(message: 'a is not a number')
|
171
|
-
err(message: 'b is not a number')
|
172
|
-
|
178
|
+
err(message: 'a is not a number') unless a.is_a?(Number)
|
179
|
+
err(message: 'b is not a number') unless b.is_a?(Number)
|
180
|
+
|
173
181
|
ok
|
174
182
|
end
|
175
|
-
|
183
|
+
|
176
184
|
def calc_sum(a:, b:, **)
|
177
185
|
ok(sum: a + b)
|
178
186
|
end
|
179
|
-
|
187
|
+
|
180
188
|
# We may get data from previous steps because all results' data are merged to context.
|
181
189
|
def calc_square(sum:, **)
|
182
|
-
ok(sum_square:
|
190
|
+
ok(sum_square: sum * sum)
|
183
191
|
end
|
184
192
|
end
|
185
193
|
|
186
|
-
|
187
194
|
# prepare operation
|
188
195
|
operation = Summator.new
|
189
196
|
|
@@ -193,7 +200,6 @@ result = operation.call(a: 1, b: 2)
|
|
193
200
|
result.ok? # true
|
194
201
|
result.unwrap # { sum: 3, sum_square: 9 } - only keys from success shape present
|
195
202
|
|
196
|
-
|
197
203
|
result = operation.call(a: nil, b: nil)
|
198
204
|
|
199
205
|
result.ok? # false
|
@@ -202,8 +208,7 @@ result.error # { message: 'a is not a number' } - only keys from error shape pre
|
|
202
208
|
|
203
209
|
#### Result Shapes
|
204
210
|
|
205
|
-
You may limit list of exposed fields by defining success and failure shapes. _After_ step definitions use `ok_shape` to define shapes of success result,
|
206
|
-
and `err_shape` to define shapes of failure result. Examples:
|
211
|
+
You may limit list of exposed fields by defining success and failure shapes. _After_ step definitions use `ok_shape` to define shapes of success result, and `err_shape` to define shapes of failure result. Examples:
|
207
212
|
|
208
213
|
```ruby
|
209
214
|
# Set exposed keys for :success status of successful result.
|
@@ -216,13 +221,13 @@ ok_shape :key1, :key2
|
|
216
221
|
# Set different exposed keys for different statuses.
|
217
222
|
#
|
218
223
|
# Operation result status is a status of last executed step result.
|
219
|
-
ok_shape status1: [
|
220
|
-
|
221
|
-
|
224
|
+
ok_shape status1: %i[key1 key2],
|
225
|
+
status2: [:key3]
|
226
|
+
|
222
227
|
# Failure shapes defined in the same way:
|
223
228
|
err_shape :key1, :key2
|
224
|
-
err_shape status1: [
|
225
|
-
|
229
|
+
err_shape status1: %i[key1 key2],
|
230
|
+
status2: [:key3]
|
226
231
|
```
|
227
232
|
|
228
233
|
Operation definition should have exact one `ok_shape` DSL-call and zero or one `err_shape` DSL-call. If you want to disable shaping
|
@@ -247,18 +252,19 @@ end
|
|
247
252
|
step :outer_2
|
248
253
|
```
|
249
254
|
|
250
|
-
In definition above tracks will not be used because there is no routes to this tracks. You may define routing like this:
|
255
|
+
In definition above tracks will not be used because there is no routes to this tracks. You may define routing like this:
|
251
256
|
|
252
257
|
```ruby
|
253
|
-
# if result is successful and has status :to_some_track - next step will be inner_1
|
258
|
+
# if result is successful and has status :to_some_track - next step will be inner_1
|
254
259
|
# for any other successful results - outer_2
|
255
|
-
step :outer_1,
|
256
|
-
|
260
|
+
step :outer_1, routes(
|
261
|
+
when_ok(:to_some_track) => :some_track
|
262
|
+
)
|
257
263
|
|
258
264
|
track :some_track do
|
259
|
-
step :
|
265
|
+
step :inner * 1, routes(when_err => :inner_track) # redirect to inner_track on any failure result
|
260
266
|
track :inner_track do
|
261
|
-
step :deep_1,
|
267
|
+
step :deep_1, routes(when_ok(:some_status) => :outer_2) # you may redirect to steps too
|
262
268
|
step :deep_2
|
263
269
|
end
|
264
270
|
step :inner_2
|
@@ -267,6 +273,25 @@ end
|
|
267
273
|
step :outer_2
|
268
274
|
```
|
269
275
|
|
276
|
+
You also can use less verbose, but shorter form of definition:
|
277
|
+
|
278
|
+
```ruby
|
279
|
+
step :name,
|
280
|
+
match_ok(:status) => :track_name,
|
281
|
+
match_ok => :track_name
|
282
|
+
```
|
283
|
+
|
284
|
+
Step has default routes:
|
285
|
+
|
286
|
+
```ruby
|
287
|
+
routes(
|
288
|
+
when_ok => next_step_name,
|
289
|
+
when_err => :term
|
290
|
+
)
|
291
|
+
```
|
292
|
+
|
293
|
+
Custom routes have bigger priority than default ones. Moreover, default routes can be overriden.
|
294
|
+
|
270
295
|
#### Lambda Steps
|
271
296
|
|
272
297
|
You can use lambda for in-place step implementation:
|
@@ -282,15 +307,15 @@ You can override or inject step implementation on initialization:
|
|
282
307
|
```ruby
|
283
308
|
class Summator
|
284
309
|
include Flows::Operation
|
285
|
-
|
310
|
+
|
286
311
|
step :sum
|
287
|
-
|
312
|
+
|
288
313
|
ok_shape :sum
|
289
314
|
end
|
290
315
|
|
291
316
|
summator = Summator.new(deps: {
|
292
|
-
|
293
|
-
})
|
317
|
+
sum: ->(a:, b:, **) { ok(sum: a + b) }
|
318
|
+
})
|
294
319
|
|
295
320
|
summator.call(a: 1, b: 2).unwrap[:sum] # 3
|
296
321
|
```
|
@@ -306,7 +331,7 @@ wrap :wrapper do
|
|
306
331
|
step :wrapped
|
307
332
|
end
|
308
333
|
|
309
|
-
def wrapper(**
|
334
|
+
def wrapper(**_context)
|
310
335
|
# do smth
|
311
336
|
result = yield # execute wrapped steps
|
312
337
|
# do smth or modify result
|
@@ -320,6 +345,107 @@ There is routing limitation when you use wrap:
|
|
320
345
|
* you may route wrapped steps only to wrapped steps in the same wrap block
|
321
346
|
* you cannot route to wrapped steps from outside
|
322
347
|
|
348
|
+
## Performance
|
349
|
+
|
350
|
+
You can compare performance for some cases by executing `bin/benchmark`. Examples for benchmark are presented in `bin/examples.rb`.
|
351
|
+
|
352
|
+
`Flows::Operation` and `Dry::Trancation` may be executed in two ways:
|
353
|
+
|
354
|
+
_Build once:_ when we create operation instance once (build operation):
|
355
|
+
|
356
|
+
```ruby
|
357
|
+
operation = OperationClass.new
|
358
|
+
|
359
|
+
10_000.times { operation.call }
|
360
|
+
```
|
361
|
+
|
362
|
+
_Build each time:_ when we create operation instance each execution:
|
363
|
+
|
364
|
+
```ruby
|
365
|
+
10_000.times { OperationClass.new.call }
|
366
|
+
```
|
367
|
+
|
368
|
+
`flows` and `dry` are much faster in _build once_ way of using. Note that Trailblazer gives you only one way to execute operation.
|
369
|
+
|
370
|
+
### Benchmark Results
|
371
|
+
|
372
|
+
Host:
|
373
|
+
|
374
|
+
* MacBook Pro (13-inch, 2017, Four Thunderbolt 3 Ports)
|
375
|
+
* 3.1 GHz Intel Core i5
|
376
|
+
* 8 GB 2133 MHz LPDDR3
|
377
|
+
|
378
|
+
Results:
|
379
|
+
|
380
|
+
```text
|
381
|
+
--------------------------------------------------
|
382
|
+
- task: A + B, one step implementation
|
383
|
+
--------------------------------------------------
|
384
|
+
Warming up --------------------------------------
|
385
|
+
Flows::Operation (build each time)
|
386
|
+
9.147k i/100ms
|
387
|
+
Flows::Operation (build once)
|
388
|
+
25.738k i/100ms
|
389
|
+
Dry::Transaction (build each time)
|
390
|
+
2.294k i/100ms
|
391
|
+
Dry::Transaction (build once)
|
392
|
+
21.836k i/100ms
|
393
|
+
Trailblazer::Operation
|
394
|
+
5.057k i/100ms
|
395
|
+
Calculating -------------------------------------
|
396
|
+
Flows::Operation (build each time)
|
397
|
+
96.095k (± 2.3%) i/s - 484.791k in 5.047684s
|
398
|
+
Flows::Operation (build once)
|
399
|
+
281.248k (± 1.7%) i/s - 1.416M in 5.034728s
|
400
|
+
Dry::Transaction (build each time)
|
401
|
+
23.683k (± 1.7%) i/s - 119.288k in 5.038506s
|
402
|
+
Dry::Transaction (build once)
|
403
|
+
237.379k (± 3.3%) i/s - 1.201M in 5.066073s
|
404
|
+
Trailblazer::Operation
|
405
|
+
52.676k (± 1.5%) i/s - 268.021k in 5.089306s
|
406
|
+
|
407
|
+
Comparison:
|
408
|
+
Flows::Operation (build once): 281248.4 i/s
|
409
|
+
Dry::Transaction (build once): 237378.7 i/s - 1.18x slower
|
410
|
+
Flows::Operation (build each time): 96094.9 i/s - 2.93x slower
|
411
|
+
Trailblazer::Operation: 52676.3 i/s - 5.34x slower
|
412
|
+
Dry::Transaction (build each time): 23682.9 i/s - 11.88x slower
|
413
|
+
|
414
|
+
|
415
|
+
--------------------------------------------------
|
416
|
+
- task: ten steps returns successful result
|
417
|
+
--------------------------------------------------
|
418
|
+
Warming up --------------------------------------
|
419
|
+
Flows::Operation (build each time)
|
420
|
+
1.496k i/100ms
|
421
|
+
Flows::Operation (build once)
|
422
|
+
3.847k i/100ms
|
423
|
+
Dry::Transaction (build each time)
|
424
|
+
274.000 i/100ms
|
425
|
+
Dry::Transaction (build once)
|
426
|
+
2.992k i/100ms
|
427
|
+
Trailblazer::Operation
|
428
|
+
1.082k i/100ms
|
429
|
+
Calculating -------------------------------------
|
430
|
+
Flows::Operation (build each time)
|
431
|
+
15.013k (± 3.8%) i/s - 76.296k in 5.089734s
|
432
|
+
Flows::Operation (build once)
|
433
|
+
39.239k (± 1.6%) i/s - 196.197k in 5.001538s
|
434
|
+
Dry::Transaction (build each time)
|
435
|
+
2.743k (± 3.7%) i/s - 13.700k in 5.002847s
|
436
|
+
Dry::Transaction (build once)
|
437
|
+
30.441k (± 1.8%) i/s - 152.592k in 5.014565s
|
438
|
+
Trailblazer::Operation
|
439
|
+
11.022k (± 1.4%) i/s - 55.182k in 5.007543s
|
440
|
+
|
441
|
+
Comparison:
|
442
|
+
Flows::Operation (build once): 39238.6 i/s
|
443
|
+
Dry::Transaction (build once): 30440.5 i/s - 1.29x slower
|
444
|
+
Flows::Operation (build each time): 15012.7 i/s - 2.61x slower
|
445
|
+
Trailblazer::Operation: 11022.1 i/s - 3.56x slower
|
446
|
+
Dry::Transaction (build each time): 2743.0 i/s - 14.30x slower
|
447
|
+
```
|
448
|
+
|
323
449
|
## Development
|
324
450
|
|
325
451
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
@@ -328,7 +454,7 @@ To install this gem onto your local machine, run `bundle exec rake install`.
|
|
328
454
|
|
329
455
|
## Contributing
|
330
456
|
|
331
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/ffloyd/flows. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
457
|
+
Bug reports and pull requests are welcome on GitHub at [ffloyd/fflows](https://github.com/ffloyd/flows). This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
332
458
|
|
333
459
|
## License
|
334
460
|
|