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.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/build.yml +43 -0
  3. data/.mdlrc +1 -0
  4. data/.rubocop.yml +25 -0
  5. data/Gemfile +6 -0
  6. data/Gemfile.lock +80 -25
  7. data/README.md +170 -44
  8. data/bin/benchmark +65 -42
  9. data/bin/examples.rb +37 -1
  10. data/bin/profile_10steps +48 -6
  11. data/docs/.nojekyll +0 -0
  12. data/docs/CNAME +1 -0
  13. data/docs/README.md +197 -0
  14. data/docs/_sidebar.md +26 -0
  15. data/docs/contributing/benchmarks_profiling.md +3 -0
  16. data/docs/contributing/local_development.md +3 -0
  17. data/docs/flow/direct_usage.md +3 -0
  18. data/docs/flow/general_idea.md +3 -0
  19. data/docs/index.html +30 -0
  20. data/docs/operation/basic_usage.md +1 -0
  21. data/docs/operation/inject_steps.md +3 -0
  22. data/docs/operation/lambda_steps.md +3 -0
  23. data/docs/operation/result_shapes.md +3 -0
  24. data/docs/operation/routing_tracks.md +3 -0
  25. data/docs/operation/wrapping_steps.md +3 -0
  26. data/docs/overview/performance.md +336 -0
  27. data/docs/railway/basic_usage.md +232 -0
  28. data/docs/result_objects/basic_usage.md +196 -0
  29. data/docs/result_objects/do_notation.md +139 -0
  30. data/flows.gemspec +2 -0
  31. data/forspell.dict +8 -0
  32. data/lefthook.yml +12 -0
  33. data/lib/flows.rb +2 -0
  34. data/lib/flows/flow.rb +1 -1
  35. data/lib/flows/operation.rb +1 -3
  36. data/lib/flows/operation/builder.rb +2 -2
  37. data/lib/flows/operation/dsl.rb +21 -0
  38. data/lib/flows/railway.rb +48 -0
  39. data/lib/flows/railway/builder.rb +68 -0
  40. data/lib/flows/railway/dsl.rb +28 -0
  41. data/lib/flows/railway/errors.rb +21 -0
  42. data/lib/flows/railway/executor.rb +23 -0
  43. data/lib/flows/result.rb +1 -0
  44. data/lib/flows/result/do.rb +30 -0
  45. data/lib/flows/result_router.rb +1 -1
  46. data/lib/flows/version.rb +1 -1
  47. metadata +59 -3
  48. data/.travis.yml +0 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3a69e7b183fcc1a9a0e0eabeaac0e66aff9fc55ea458fa34a5c37de86c7970ab
4
- data.tar.gz: 20dbb58536ba20c0f7c7032757679ea968088965890851e93af81e2b4cb148f4
3
+ metadata.gz: eed3e3ba742be2b25c1ae3ca7b52d2db3418183e0950616409ef8d05232365de
4
+ data.tar.gz: c2f340f63e6e55178426c28fcfe29aa6d6c9af7d9a4b539d43461f068f4a6082
5
5
  SHA512:
6
- metadata.gz: b606d7df548e65d694f4700ecf6991667bca8f6626a41111bce6cbe9f8949ad0c5cfc58830fd1596b1fc3c170eaca7aed2b4f8f83092a5a7d174979978ece70b
7
- data.tar.gz: 4fbfd1e9422aca8b7800b4930d32a7a3a4f4eca67eceab08cf0b20fcde24ef628d677f5bc40ffa1f7a1c9c3af6f1f80de78a6e248adf145f23f63d3d4ae6c77c
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"
@@ -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
@@ -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.1.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.1)
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.1)
33
- dry-core (>= 0.4.7)
34
- dry-monads (1.3.0)
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
- jaro_winkler (1.5.2)
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.2.0)
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.0)
102
+ rspec-core (3.8.2)
60
103
  rspec-support (~> 3.8.0)
61
- rspec-expectations (3.8.2)
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.0)
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.0)
68
- rubocop (0.67.1)
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.5, != 2.5.1.1)
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.6)
76
- rubocop-performance (1.0.0)
77
- rubocop (>= 0.58.0)
78
- rubocop-rspec (1.32.0)
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.0)
82
- simplecov (0.16.1)
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
- trailblazer-activity (0.8.4)
137
+ tomlrb (1.2.8)
138
+ trailblazer-activity (0.9.0)
89
139
  trailblazer-context (>= 0.1.4)
90
- trailblazer-activity-dsl-linear (0.1.8)
91
- trailblazer-activity (>= 0.8.3, < 1.0.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
- unicode-display_width (1.5.0)
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://travis-ci.com/ffloyd/flows.svg?branch=master)](https://travis-ci.com/ffloyd/flows)
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 more simpler and flexible DSL for defining operations and matching results. Also `flows` is significantly faster.
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 you can use it with any framework.
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
- $ bundle
23
+ ```sh
24
+ bundle
25
+ ```
24
26
 
25
27
  Or install it yourself as:
26
28
 
27
- $ gem install flows
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') if !a.is_a?(Number)
171
- err(message: 'b is not a number') if !b.is_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: a * b)
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: [:key1, :key2],
220
- status2: [:key3]
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: [:key1, :key2],
225
- status2: [:key3]
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
- match_ok(:to_some_track) => :some_track
260
+ step :outer_1, routes(
261
+ when_ok(:to_some_track) => :some_track
262
+ )
257
263
 
258
264
  track :some_track do
259
- step :inner_1, match_err => :inner_track # redirect to inner_track on failure result
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, match_ok(:some_status) => :outer_2 # you may redirect to steps too
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
- sum: ->(a:, b:, **) { ok(sum: a + b) }
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(**context)
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