flows 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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