flows 0.1.0 → 0.2.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/.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
|
-
[](https://github.com/ffloyd/flows/actions)
|
|
4
4
|
[](https://codecov.io/gh/ffloyd/flows)
|
|
5
5
|
[](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
|
|