flows 0.2.0 → 0.6.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 (166) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/{build.yml → test.yml} +5 -10
  3. data/.gitignore +9 -1
  4. data/.mdlrc +1 -1
  5. data/.reek.yml +54 -0
  6. data/.rubocop.yml +26 -7
  7. data/.rubocop_todo.yml +27 -0
  8. data/.ruby-version +1 -1
  9. data/.yardopts +1 -0
  10. data/CHANGELOG.md +81 -0
  11. data/Gemfile +0 -6
  12. data/README.md +167 -363
  13. data/Rakefile +35 -1
  14. data/bin/.rubocop.yml +5 -0
  15. data/bin/all_the_errors +55 -0
  16. data/bin/benchmark +73 -105
  17. data/bin/benchmark_cli/compare.rb +118 -0
  18. data/bin/benchmark_cli/compare/a_plus_b.rb +22 -0
  19. data/bin/benchmark_cli/compare/base.rb +45 -0
  20. data/bin/benchmark_cli/compare/command.rb +47 -0
  21. data/bin/benchmark_cli/compare/ten_steps.rb +22 -0
  22. data/bin/benchmark_cli/examples.rb +23 -0
  23. data/bin/benchmark_cli/examples/.rubocop.yml +22 -0
  24. data/bin/benchmark_cli/examples/a_plus_b/dry_do.rb +23 -0
  25. data/bin/benchmark_cli/examples/a_plus_b/dry_transaction.rb +17 -0
  26. data/bin/benchmark_cli/examples/a_plus_b/flows_do.rb +22 -0
  27. data/bin/benchmark_cli/examples/a_plus_b/flows_railway.rb +13 -0
  28. data/bin/benchmark_cli/examples/a_plus_b/flows_scp.rb +13 -0
  29. data/bin/benchmark_cli/examples/a_plus_b/flows_scp_mut.rb +13 -0
  30. data/bin/benchmark_cli/examples/a_plus_b/flows_scp_oc.rb +21 -0
  31. data/bin/benchmark_cli/examples/a_plus_b/trailblazer.rb +15 -0
  32. data/bin/benchmark_cli/examples/ten_steps/dry_do.rb +70 -0
  33. data/bin/benchmark_cli/examples/ten_steps/dry_transaction.rb +64 -0
  34. data/bin/benchmark_cli/examples/ten_steps/flows_do.rb +69 -0
  35. data/bin/benchmark_cli/examples/ten_steps/flows_railway.rb +58 -0
  36. data/bin/benchmark_cli/examples/ten_steps/flows_scp.rb +58 -0
  37. data/bin/benchmark_cli/examples/ten_steps/flows_scp_mut.rb +58 -0
  38. data/bin/benchmark_cli/examples/ten_steps/flows_scp_oc.rb +66 -0
  39. data/bin/benchmark_cli/examples/ten_steps/trailblazer.rb +60 -0
  40. data/bin/benchmark_cli/helpers.rb +12 -0
  41. data/bin/benchmark_cli/ruby.rb +15 -0
  42. data/bin/benchmark_cli/ruby/command.rb +38 -0
  43. data/bin/benchmark_cli/ruby/method_exec.rb +71 -0
  44. data/bin/benchmark_cli/ruby/self_class.rb +69 -0
  45. data/bin/benchmark_cli/ruby/structs.rb +90 -0
  46. data/bin/console +1 -0
  47. data/bin/docserver +7 -0
  48. data/bin/errors +138 -0
  49. data/bin/errors_cli/contract_error_demo.rb +49 -0
  50. data/bin/errors_cli/di_error_demo.rb +38 -0
  51. data/bin/errors_cli/flow_error_demo.rb +22 -0
  52. data/bin/errors_cli/flows_router_error_demo.rb +15 -0
  53. data/bin/errors_cli/interface_error_demo.rb +17 -0
  54. data/bin/errors_cli/oc_error_demo.rb +40 -0
  55. data/bin/errors_cli/railway_error_demo.rb +10 -0
  56. data/bin/errors_cli/result_error_demo.rb +13 -0
  57. data/bin/errors_cli/scp_error_demo.rb +17 -0
  58. data/docs/README.md +3 -187
  59. data/docs/_sidebar.md +0 -24
  60. data/docs/index.html +1 -1
  61. data/flows.gemspec +27 -2
  62. data/forspell.dict +9 -0
  63. data/lefthook.yml +9 -0
  64. data/lib/flows.rb +11 -5
  65. data/lib/flows/contract.rb +402 -0
  66. data/lib/flows/contract/array.rb +55 -0
  67. data/lib/flows/contract/case_eq.rb +43 -0
  68. data/lib/flows/contract/compose.rb +77 -0
  69. data/lib/flows/contract/either.rb +53 -0
  70. data/lib/flows/contract/error.rb +24 -0
  71. data/lib/flows/contract/hash.rb +75 -0
  72. data/lib/flows/contract/hash_of.rb +70 -0
  73. data/lib/flows/contract/helpers.rb +22 -0
  74. data/lib/flows/contract/predicate.rb +34 -0
  75. data/lib/flows/contract/transformer.rb +50 -0
  76. data/lib/flows/contract/tuple.rb +70 -0
  77. data/lib/flows/flow.rb +96 -7
  78. data/lib/flows/flow/errors.rb +29 -0
  79. data/lib/flows/flow/node.rb +132 -0
  80. data/lib/flows/flow/router.rb +29 -0
  81. data/lib/flows/flow/router/custom.rb +59 -0
  82. data/lib/flows/flow/router/errors.rb +11 -0
  83. data/lib/flows/flow/router/simple.rb +25 -0
  84. data/lib/flows/plugin.rb +15 -0
  85. data/lib/flows/plugin/dependency_injector.rb +170 -0
  86. data/lib/flows/plugin/dependency_injector/dependency.rb +24 -0
  87. data/lib/flows/plugin/dependency_injector/dependency_definition.rb +16 -0
  88. data/lib/flows/plugin/dependency_injector/dependency_list.rb +55 -0
  89. data/lib/flows/plugin/dependency_injector/errors.rb +58 -0
  90. data/lib/flows/plugin/implicit_init.rb +45 -0
  91. data/lib/flows/plugin/interface.rb +84 -0
  92. data/lib/flows/plugin/output_contract.rb +85 -0
  93. data/lib/flows/plugin/output_contract/dsl.rb +48 -0
  94. data/lib/flows/plugin/output_contract/errors.rb +74 -0
  95. data/lib/flows/plugin/output_contract/wrapper.rb +55 -0
  96. data/lib/flows/plugin/profiler.rb +114 -0
  97. data/lib/flows/plugin/profiler/injector.rb +35 -0
  98. data/lib/flows/plugin/profiler/report.rb +48 -0
  99. data/lib/flows/plugin/profiler/report/events.rb +43 -0
  100. data/lib/flows/plugin/profiler/report/flat.rb +41 -0
  101. data/lib/flows/plugin/profiler/report/flat/method_report.rb +80 -0
  102. data/lib/flows/plugin/profiler/report/raw.rb +15 -0
  103. data/lib/flows/plugin/profiler/report/tree.rb +98 -0
  104. data/lib/flows/plugin/profiler/report/tree/calculated_node.rb +116 -0
  105. data/lib/flows/plugin/profiler/report/tree/node.rb +34 -0
  106. data/lib/flows/plugin/profiler/wrapper.rb +53 -0
  107. data/lib/flows/railway.rb +140 -34
  108. data/lib/flows/railway/dsl.rb +8 -18
  109. data/lib/flows/railway/errors.rb +8 -12
  110. data/lib/flows/railway/step.rb +24 -0
  111. data/lib/flows/railway/step_list.rb +38 -0
  112. data/lib/flows/result.rb +188 -2
  113. data/lib/flows/result/do.rb +158 -16
  114. data/lib/flows/result/err.rb +12 -6
  115. data/lib/flows/result/errors.rb +29 -17
  116. data/lib/flows/result/helpers.rb +25 -3
  117. data/lib/flows/result/ok.rb +12 -6
  118. data/lib/flows/shared_context_pipeline.rb +342 -0
  119. data/lib/flows/shared_context_pipeline/dsl.rb +12 -0
  120. data/lib/flows/shared_context_pipeline/dsl/callbacks.rb +35 -0
  121. data/lib/flows/shared_context_pipeline/dsl/tracks.rb +52 -0
  122. data/lib/flows/shared_context_pipeline/errors.rb +17 -0
  123. data/lib/flows/shared_context_pipeline/mutation_step.rb +30 -0
  124. data/lib/flows/shared_context_pipeline/router_definition.rb +21 -0
  125. data/lib/flows/shared_context_pipeline/step.rb +55 -0
  126. data/lib/flows/shared_context_pipeline/track.rb +54 -0
  127. data/lib/flows/shared_context_pipeline/track_list.rb +51 -0
  128. data/lib/flows/shared_context_pipeline/wrap.rb +73 -0
  129. data/lib/flows/util.rb +17 -0
  130. data/lib/flows/util/inheritable_singleton_vars.rb +86 -0
  131. data/lib/flows/util/inheritable_singleton_vars/dup_strategy.rb +100 -0
  132. data/lib/flows/util/inheritable_singleton_vars/isolation_strategy.rb +91 -0
  133. data/lib/flows/util/prepend_to_class.rb +191 -0
  134. data/lib/flows/version.rb +1 -1
  135. metadata +253 -38
  136. data/Gemfile.lock +0 -174
  137. data/bin/demo +0 -66
  138. data/bin/examples.rb +0 -195
  139. data/bin/profile_10steps +0 -106
  140. data/bin/ruby_benchmarks +0 -26
  141. data/docs/CNAME +0 -1
  142. data/docs/contributing/benchmarks_profiling.md +0 -3
  143. data/docs/contributing/local_development.md +0 -3
  144. data/docs/flow/direct_usage.md +0 -3
  145. data/docs/flow/general_idea.md +0 -3
  146. data/docs/operation/basic_usage.md +0 -1
  147. data/docs/operation/inject_steps.md +0 -3
  148. data/docs/operation/lambda_steps.md +0 -3
  149. data/docs/operation/result_shapes.md +0 -3
  150. data/docs/operation/routing_tracks.md +0 -3
  151. data/docs/operation/wrapping_steps.md +0 -3
  152. data/docs/overview/performance.md +0 -336
  153. data/docs/railway/basic_usage.md +0 -232
  154. data/docs/result_objects/basic_usage.md +0 -196
  155. data/docs/result_objects/do_notation.md +0 -139
  156. data/lib/flows/node.rb +0 -27
  157. data/lib/flows/operation.rb +0 -52
  158. data/lib/flows/operation/builder.rb +0 -130
  159. data/lib/flows/operation/builder/build_router.rb +0 -37
  160. data/lib/flows/operation/dsl.rb +0 -93
  161. data/lib/flows/operation/errors.rb +0 -75
  162. data/lib/flows/operation/executor.rb +0 -78
  163. data/lib/flows/railway/builder.rb +0 -68
  164. data/lib/flows/railway/executor.rb +0 -23
  165. data/lib/flows/result_router.rb +0 -14
  166. data/lib/flows/router.rb +0 -22
@@ -1,26 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # rubocop:disable all
3
-
4
- require 'bundler/setup'
5
- require 'benchmark/ips'
6
-
7
- puts '-' * 50
8
- puts '- method execution'
9
- puts '-' * 50
10
-
11
- class OneMethod
12
- def meth
13
- :ok
14
- end
15
- end
16
-
17
- one_method = OneMethod.new
18
- method_obj = one_method.method(:meth)
19
-
20
- Benchmark.ips do |b|
21
- b.report('native call') { one_method.meth }
22
- b.report('send(...)') { one_method.send(:meth) }
23
- b.report('Method#call') { method_obj.call }
24
-
25
- b.compare!
26
- end
data/docs/CNAME DELETED
@@ -1 +0,0 @@
1
- flows.ffloyd.tech
@@ -1,3 +0,0 @@
1
- # Benchmarks & Profiling
2
-
3
- TODO
@@ -1,3 +0,0 @@
1
- # Local Development
2
-
3
- TODO
@@ -1,3 +0,0 @@
1
- # Direct Usage
2
-
3
- TODO
@@ -1,3 +0,0 @@
1
- # General Idea
2
-
3
- TODO
@@ -1 +0,0 @@
1
- # Basic Usage
@@ -1,3 +0,0 @@
1
- # Inject Steps
2
-
3
- TODO
@@ -1,3 +0,0 @@
1
- # Lambda Steps
2
-
3
- TODO
@@ -1,3 +0,0 @@
1
- # Result Shapes
2
-
3
- TODO
@@ -1,3 +0,0 @@
1
- # Routing & Tracks
2
-
3
- TODO
@@ -1,3 +0,0 @@
1
- # Wrapping Steps
2
-
3
- TODO
@@ -1,336 +0,0 @@
1
- # Performance
2
-
3
- Host:
4
-
5
- * MacBook Pro (13-inch, 2017, Four Thunderbolt 3 Ports)
6
- * 3.1 GHz Intel Core i5
7
- * 8 GB 2133 MHz LPDDR3
8
-
9
- ## Comparison with Trailblazer
10
-
11
- `Flows::Railway` does not support tracks and routes, so it's reasonable to compare with `Flows::Operation` only.
12
-
13
- `WITH_OP=1 WITH_TB=1 bin/benchmark` results:
14
-
15
- ```
16
- --------------------------------------------------
17
- - task: A + B, one step implementation
18
- --------------------------------------------------
19
- Warming up --------------------------------------
20
- Flows::Operation (build once)
21
- 25.356k i/100ms
22
- Flows::Operation (build each time)
23
- 9.168k i/100ms
24
- Trailblazer::Operation
25
- 5.016k i/100ms
26
- Calculating -------------------------------------
27
- Flows::Operation (build once)
28
- 277.460k (± 1.2%) i/s - 1.395M in 5.027011s
29
- Flows::Operation (build each time)
30
- 95.740k (± 2.7%) i/s - 485.904k in 5.079226s
31
- Trailblazer::Operation
32
- 52.975k (± 1.8%) i/s - 265.848k in 5.020109s
33
-
34
- Comparison:
35
- Flows::Operation (build once): 277459.5 i/s
36
- Flows::Operation (build each time): 95739.6 i/s - 2.90x slower
37
- Trailblazer::Operation: 52974.6 i/s - 5.24x slower
38
-
39
-
40
- --------------------------------------------------
41
- - task: ten steps returns successful result
42
- --------------------------------------------------
43
- Warming up --------------------------------------
44
- Flows::Operation (build once)
45
- 3.767k i/100ms
46
- Flows::Operation (build each time)
47
- 1.507k i/100ms
48
- Trailblazer::Operation
49
- 1.078k i/100ms
50
- Calculating -------------------------------------
51
- Flows::Operation (build once)
52
- 37.983k (± 2.9%) i/s - 192.117k in 5.062658s
53
- Flows::Operation (build each time)
54
- 14.991k (± 4.2%) i/s - 75.350k in 5.035443s
55
- Trailblazer::Operation
56
- 10.897k (± 2.8%) i/s - 54.978k in 5.049665s
57
-
58
- Comparison:
59
- Flows::Operation (build once): 37982.8 i/s
60
- Flows::Operation (build each time): 14990.6 i/s - 2.53x slower
61
- Trailblazer::Operation: 10896.9 i/s - 3.49x slower
62
- ```
63
-
64
- ## Comparison with Dry::Transaction
65
-
66
- `Dry::Transaction` does not support tracks and branching so it's reasonable to compare with `Flows::Railway` only.
67
-
68
- `WITH_RW=1 WITH_DRY=1 bin/benchmark` results:
69
-
70
- ```
71
- --------------------------------------------------
72
- - task: A + B, one step implementation
73
- --------------------------------------------------
74
- Warming up --------------------------------------
75
- Flows::Railway (build once)
76
- 29.324k i/100ms
77
- Flows::Railway (build each time)
78
- 11.159k i/100ms
79
- Dry::Transaction (build once)
80
- 21.480k i/100ms
81
- Dry::Transaction (build each time)
82
- 2.268k i/100ms
83
- Calculating -------------------------------------
84
- Flows::Railway (build once)
85
- 321.837k (± 1.3%) i/s - 1.613M in 5.012156s
86
- Flows::Railway (build each time)
87
- 115.743k (± 2.6%) i/s - 580.268k in 5.016961s
88
- Dry::Transaction (build once)
89
- 231.712k (± 1.7%) i/s - 1.160M in 5.007401s
90
- Dry::Transaction (build each time)
91
- 23.093k (± 2.5%) i/s - 115.668k in 5.012311s
92
-
93
- Comparison:
94
- Flows::Railway (build once): 321837.4 i/s
95
- Dry::Transaction (build once): 231712.5 i/s - 1.39x slower
96
- Flows::Railway (build each time): 115743.1 i/s - 2.78x slower
97
- Dry::Transaction (build each time): 23093.2 i/s - 13.94x slower
98
-
99
-
100
- --------------------------------------------------
101
- - task: ten steps returns successful result
102
- --------------------------------------------------
103
- Warming up --------------------------------------
104
- Flows::Railway (build once)
105
- 5.607k i/100ms
106
- Flows::Railway (build each time)
107
- 2.014k i/100ms
108
- Dry::Transaction (build once)
109
- 2.918k i/100ms
110
- Dry::Transaction (build each time)
111
- 275.000 i/100ms
112
- Calculating -------------------------------------
113
- Flows::Railway (build once)
114
- 57.765k (± 1.4%) i/s - 291.564k in 5.048484s
115
- Flows::Railway (build each time)
116
- 20.413k (± 1.2%) i/s - 102.714k in 5.032467s
117
- Dry::Transaction (build once)
118
- 29.597k (± 1.5%) i/s - 148.818k in 5.029422s
119
- Dry::Transaction (build each time)
120
- 2.753k (± 2.0%) i/s - 14.025k in 5.096279s
121
-
122
- Comparison:
123
- Flows::Railway (build once): 57765.2 i/s
124
- Dry::Transaction (build once): 29596.6 i/s - 1.95x slower
125
- Flows::Railway (build each time): 20413.0 i/s - 2.83x slower
126
- Dry::Transaction (build each time): 2753.2 i/s - 20.98x slower
127
- ```
128
-
129
- ## Railway vs Operation
130
-
131
- `Flows::Railway` is created to improve performance in situations when you don't need tracks, branching and shape control (`Flows::Operation` has this features). So, it should be faster than `Flows::Operation`.
132
-
133
- `WITH_OP=1 WITH_RW=1 bin/benchmark` results:
134
-
135
- ```
136
- --------------------------------------------------
137
- - task: A + B, one step implementation
138
- --------------------------------------------------
139
- Warming up --------------------------------------
140
- Flows::Railway (build once)
141
- 29.440k i/100ms
142
- Flows::Railway (build each time)
143
- 11.236k i/100ms
144
- Flows::Operation (build once)
145
- 25.584k i/100ms
146
- Flows::Operation (build each time)
147
- 9.161k i/100ms
148
- Calculating -------------------------------------
149
- Flows::Railway (build once)
150
- 315.648k (± 8.1%) i/s - 1.590M in 5.078736s
151
- Flows::Railway (build each time)
152
- 117.747k (± 3.5%) i/s - 595.508k in 5.064191s
153
- Flows::Operation (build once)
154
- 266.888k (±12.3%) i/s - 1.279M in 5.090531s
155
- Flows::Operation (build each time)
156
- 91.424k (±11.0%) i/s - 458.050k in 5.097449s
157
-
158
- Comparison:
159
- Flows::Railway (build once): 315647.6 i/s
160
- Flows::Operation (build once): 266888.4 i/s - same-ish: difference falls within error
161
- Flows::Railway (build each time): 117747.2 i/s - 2.68x slower
162
- Flows::Operation (build each time): 91423.7 i/s - 3.45x slower
163
-
164
-
165
- --------------------------------------------------
166
- - task: ten steps returns successful result
167
- --------------------------------------------------
168
- Warming up --------------------------------------
169
- Flows::Railway (build once)
170
- 5.619k i/100ms
171
- Flows::Railway (build each time)
172
- 2.009k i/100ms
173
- Flows::Operation (build once)
174
- 3.650k i/100ms
175
- Flows::Operation (build each time)
176
- 1.472k i/100ms
177
- Calculating -------------------------------------
178
- Flows::Railway (build once)
179
- 58.454k (± 2.8%) i/s - 292.188k in 5.002833s
180
- Flows::Railway (build each time)
181
- 20.310k (± 2.4%) i/s - 102.459k in 5.047579s
182
- Flows::Operation (build once)
183
- 38.556k (± 2.5%) i/s - 193.450k in 5.020871s
184
- Flows::Operation (build each time)
185
- 15.222k (± 2.8%) i/s - 76.544k in 5.032272s
186
-
187
- Comparison:
188
- Flows::Railway (build once): 58453.8 i/s
189
- Flows::Operation (build once): 38556.5 i/s - 1.52x slower
190
- Flows::Railway (build each time): 20310.3 i/s - 2.88x slower
191
- Flows::Operation (build each time): 15221.9 i/s - 3.84x slower
192
- ```
193
-
194
- ## Comparison with Plan Old Ruby Object
195
-
196
- Of course, `flows` cannot be faster than naive implementation without any library usage. But it's nice to know how big infrastructure cost you pay.
197
-
198
- `WITH_RW=1 WITH_PORO=1 bin/benchmark` results:
199
-
200
- ```
201
- --------------------------------------------------
202
- - task: A + B, one step implementation
203
- --------------------------------------------------
204
- Warming up --------------------------------------
205
- Flows::Railway (build once)
206
- 29.276k i/100ms
207
- Flows::Railway (build each time)
208
- 11.115k i/100ms
209
- PORO 309.108k i/100ms
210
- Calculating -------------------------------------
211
- Flows::Railway (build once)
212
- 320.587k (± 3.5%) i/s - 1.610M in 5.029314s
213
- Flows::Railway (build each time)
214
- 118.108k (± 3.0%) i/s - 600.210k in 5.086844s
215
- PORO 9.998M (± 2.1%) i/s - 50.075M in 5.010848s
216
-
217
- Comparison:
218
- PORO: 9998276.0 i/s
219
- Flows::Railway (build once): 320586.8 i/s - 31.19x slower
220
- Flows::Railway (build each time): 118108.5 i/s - 84.65x slower
221
-
222
-
223
- --------------------------------------------------
224
- - task: ten steps returns successful result
225
- --------------------------------------------------
226
- Warming up --------------------------------------
227
- Flows::Railway (build once)
228
- 5.671k i/100ms
229
- Flows::Railway (build each time)
230
- 2.024k i/100ms
231
- PORO 233.375k i/100ms
232
- Calculating -------------------------------------
233
- Flows::Railway (build once)
234
- 58.428k (± 1.6%) i/s - 294.892k in 5.048387s
235
- Flows::Railway (build each time)
236
- 20.388k (± 3.9%) i/s - 103.224k in 5.070844s
237
- PORO 4.937M (± 0.6%) i/s - 24.738M in 5.010488s
238
-
239
- Comparison:
240
- PORO: 4937372.3 i/s
241
- Flows::Railway (build once): 58428.4 i/s - 84.50x slower
242
- Flows::Railway (build each time): 20387.7 i/s - 242.17x slower
243
- ```
244
-
245
- ## All without PORO
246
-
247
- `WITH_ALL=1 bin/benchmark` results:
248
-
249
- ```
250
- --------------------------------------------------
251
- - task: A + B, one step implementation
252
- --------------------------------------------------
253
- Warming up --------------------------------------
254
- Flows::Railway (build once)
255
- 29.351k i/100ms
256
- Flows::Railway (build each time)
257
- 11.044k i/100ms
258
- Flows::Operation (build once)
259
- 25.475k i/100ms
260
- Flows::Operation (build each time)
261
- 8.989k i/100ms
262
- Dry::Transaction (build once)
263
- 21.082k i/100ms
264
- Dry::Transaction (build each time)
265
- 2.272k i/100ms
266
- Trailblazer::Operation
267
- 4.962k i/100ms
268
- Calculating -------------------------------------
269
- Flows::Railway (build once)
270
- 299.326k (±15.6%) i/s - 1.409M in 5.012398s
271
- Flows::Railway (build each time)
272
- 116.186k (± 3.1%) i/s - 585.332k in 5.042902s
273
- Flows::Operation (build once)
274
- 276.980k (± 3.1%) i/s - 1.401M in 5.064018s
275
- Flows::Operation (build each time)
276
- 94.536k (± 2.6%) i/s - 476.417k in 5.042967s
277
- Dry::Transaction (build once)
278
- 229.750k (± 1.6%) i/s - 1.160M in 5.048211s
279
- Dry::Transaction (build each time)
280
- 23.381k (± 1.9%) i/s - 118.144k in 5.054920s
281
- Trailblazer::Operation
282
- 50.936k (± 4.4%) i/s - 258.024k in 5.075897s
283
-
284
- Comparison:
285
- Flows::Railway (build once): 299325.9 i/s
286
- Flows::Operation (build once): 276979.8 i/s - same-ish: difference falls within error
287
- Dry::Transaction (build once): 229749.5 i/s - 1.30x slower
288
- Flows::Railway (build each time): 116185.6 i/s - 2.58x slower
289
- Flows::Operation (build each time): 94536.3 i/s - 3.17x slower
290
- Trailblazer::Operation: 50936.0 i/s - 5.88x slower
291
- Dry::Transaction (build each time): 23380.8 i/s - 12.80x slower
292
-
293
-
294
- --------------------------------------------------
295
- - task: ten steps returns successful result
296
- --------------------------------------------------
297
- Warming up --------------------------------------
298
- Flows::Railway (build once)
299
- 5.734k i/100ms
300
- Flows::Railway (build each time)
301
- 2.064k i/100ms
302
- Flows::Operation (build once)
303
- 3.801k i/100ms
304
- Flows::Operation (build each time)
305
- 1.502k i/100ms
306
- Dry::Transaction (build once)
307
- 2.837k i/100ms
308
- Dry::Transaction (build each time)
309
- 274.000 i/100ms
310
- Trailblazer::Operation
311
- 1.079k i/100ms
312
- Calculating -------------------------------------
313
- Flows::Railway (build once)
314
- 58.541k (± 1.6%) i/s - 298.168k in 5.094712s
315
- Flows::Railway (build each time)
316
- 20.626k (± 3.0%) i/s - 103.200k in 5.008021s
317
- Flows::Operation (build once)
318
- 38.906k (± 2.7%) i/s - 197.652k in 5.084184s
319
- Flows::Operation (build each time)
320
- 14.351k (±12.2%) i/s - 70.594k in 5.011606s
321
- Dry::Transaction (build once)
322
- 29.588k (± 1.8%) i/s - 150.361k in 5.083603s
323
- Dry::Transaction (build each time)
324
- 2.765k (± 1.8%) i/s - 13.974k in 5.054977s
325
- Trailblazer::Operation
326
- 10.861k (± 2.1%) i/s - 55.029k in 5.069204s
327
-
328
- Comparison:
329
- Flows::Railway (build once): 58541.4 i/s
330
- Flows::Operation (build once): 38906.4 i/s - 1.50x slower
331
- Dry::Transaction (build once): 29587.8 i/s - 1.98x slower
332
- Flows::Railway (build each time): 20626.0 i/s - 2.84x slower
333
- Flows::Operation (build each time): 14351.1 i/s - 4.08x slower
334
- Trailblazer::Operation: 10860.9 i/s - 5.39x slower
335
- Dry::Transaction (build each time): 2765.3 i/s - 21.17x slower
336
- ```
@@ -1,232 +0,0 @@
1
- # Railway :: Basic Usage
2
-
3
- `Flows::Railway` is an implementation of a Railway Programming pattern. You may read about this pattern in the following articles:
4
-
5
- * [Programming on rails: Railway Oriented Programming](http://sandordargo.com/blog/2017/09/27/railway_oriented_programming) // it's not about Ruby on Rails
6
- * [Railway Oriented Programming: A powerful Functional Programming pattern](https://medium.com/@naveenkumarmuguda/railway-oriented-programming-a-powerful-functional-programming-pattern-ab454e467f31)
7
- * [Railway Oriented Programming in Elixir with Pattern Matching on Function Level and Pipelining](https://medium.com/elixirlabs/railway-oriented-programming-in-elixir-with-pattern-matching-on-function-level-and-pipelining-e53972cede98)
8
-
9
- Let's review a simple task and solve it using `Flows::Railway`: you have to get a user by ID, get all user's blog posts and convert it to an array of HTML-strings. In such situation, we have to implement three parts of our task and compose it into something we can call, for example, from a Rails controller. Also, the first and third steps may fail (user not found, conversion to HTML failed). And if a step failed - we have to return failure info immediately. Let's draw this using a UML activity diagram:
10
-
11
- ```plantuml
12
- @startuml
13
- |Success Path|
14
- start
15
- -> id: Integer;
16
- :fetch_user;
17
- if (success?) then (yes)
18
- -> user: User;
19
- :get_blog_posts;
20
- -> posts: Array<Post>;
21
- :convert_to_html;
22
- if (success?) then (yes)
23
- -> posts_html: Array<String>;
24
- stop
25
- else (no)
26
- |Failure|
27
- -> message: String;
28
- end
29
- endif
30
- else (no)
31
- |Failure|
32
- -> message: String;
33
- end
34
- endif
35
- @enduml
36
- ```
37
-
38
- And implement using `Flows::Railway`:
39
-
40
- ```ruby
41
- class RenderUserBlogPosts
42
- include Flows::Railway
43
-
44
- step :fetch_user
45
- step :get_blog_posts
46
- step :convert_to_html
47
-
48
- def fetch_user(id:)
49
- user = User.find_by_id(id)
50
- user ? ok(user: user) : err(message: "User #{id} not found")
51
- end
52
-
53
- def get_blog_posts(user:)
54
- ok(posts: User.posts)
55
- end
56
-
57
- def convert_to_html(posts:)
58
- posts_html = post.map(&:text).map do |text|
59
- html = convert(text)
60
- return err(message: "cannot convert to html: #{text}")
61
- end
62
-
63
- ok(posts_html: posts_html)
64
- end
65
-
66
- private
67
-
68
- # returns String or nil
69
- def convert(text)
70
- # some implementation here
71
- end
72
- end
73
- ```
74
-
75
- And execute it:
76
-
77
- ```ruby
78
- # User with id = 1 exists and with id = 2 - doesn't
79
-
80
- RenderUserBlogPosts.new.call(id: 1)
81
- # => Flows::Result::Ok.new(posts_html: [...])
82
-
83
- RenderUserBlogPosts.new.call(id: 2)
84
- # => Flows::Result::Err.new(message: 'User 2 not found')
85
- ```
86
-
87
- ## Flows::Railway rules
88
-
89
- * steps execution happens from the first to the last step
90
- * input arguments (`Railway#call(...)`) becomes the input of the first step
91
- * each step should return Result Object (`Flows::Result::Helpers` already included)
92
- * if step returns failed result - execution stops and failed Result Object returned from Railway
93
- * if step returns successful result - result data becomes arguments of the following step
94
- * if the last step returns successful result - it becomes a result of a Railway execution
95
-
96
- ## Defining Steps
97
-
98
- Two ways of step definition exist. First is by using an instance method:
99
-
100
- ```ruby
101
- step :do_something
102
-
103
- def do_something(**arguments)
104
- # some implementation
105
- # Result Object as return value
106
- end
107
- ```
108
-
109
- Second is by using lambda:
110
-
111
- ```ruby
112
- step :do_something, ->(**arguments) { ok(some: 'data') }
113
- ```
114
-
115
- Definition with lambda exists primarily for debugging/testing purposes. I recommend you to use method-based implementations for all your business logic. Also, this is good for consistency, readability, and maintenance. __Think about Railway as about small book: you have a "table of contents" in a form of step definitions and actual "chapters" in the same order in a form of public methods. And your private methods becomes something like "appendix".__
116
-
117
- ## Dependency Injection
118
-
119
- By default, we search for step implementation methods in a class instance. But you may override method source and inject your own:
120
-
121
- ```ruby
122
- class SayOk
123
- include Flows::Railway
124
-
125
- step :do_job
126
- end
127
-
128
- module Loud
129
- extend Flows::Result::Helpers
130
-
131
- def self.do_job
132
- ok(text: 'OOOOKKKK!!!!')
133
- end
134
- end
135
-
136
- module Normal
137
- extend Flows::Result::Helpers
138
-
139
- def self.do_job
140
- ok(text: 'ok')
141
- end
142
- end
143
-
144
- SayOk.new(method_source: Loud).call.unwrap
145
- # => { text: 'OOOOKKKK!!!!' }
146
-
147
- SayOk.new(method_source: Normal).call.unwrap
148
- # => { text: 'ok' }
149
- ```
150
-
151
- When you change your method source original class is no longer used for methods lookup. But what if we want to just override one of the steps? We can:
152
-
153
- ```ruby
154
- class SayOk
155
- include Flows::Railway
156
-
157
- step :do_job
158
-
159
- def do_job
160
- ok(text: 'ok')
161
- end
162
- end
163
-
164
- say_loud = -> { ok(text: 'OOOOKKKK!!!!') } # or anything with implemented #call method
165
-
166
- SayOk.new.call.unwrap
167
- # => { text: 'OOOOKKKK!!!!' }
168
-
169
- SayOk.new(deps: { do_job: say_loud }).call.unwrap
170
- # => { text: 'ok' }
171
- ```
172
-
173
- Moreover, you can mix both approaches. Injecting using `deps:` has higher priority.
174
-
175
- ## Pre-building and Performance
176
-
177
- As mentioned before, railway execution consists of two phases: build (`.new`) and run (`#call`). And the build phase is expensive. You may compare overheads when you build a railway each time:
178
-
179
- ```
180
- $ WITH_RW=1 bin/benchmark
181
-
182
- --------------------------------------------------
183
- - task: A + B, one step implementation
184
- --------------------------------------------------
185
- Warming up --------------------------------------
186
- Flows::Railway (build once)
187
- 30.995k i/100ms
188
- Flows::Railway (build each time)
189
- 11.553k i/100ms
190
- Calculating -------------------------------------
191
- Flows::Railway (build once)
192
- 347.682k (± 2.1%) i/s - 1.767M in 5.083828s
193
- Flows::Railway (build each time)
194
- 122.908k (± 4.2%) i/s - 623.862k in 5.085459s
195
-
196
- Comparison:
197
- Flows::Railway (build once): 347681.6 i/s
198
- Flows::Railway (build each time): 122908.0 i/s - 2.83x slower
199
-
200
-
201
- --------------------------------------------------
202
- - task: ten steps returns successful result
203
- --------------------------------------------------
204
- Warming up --------------------------------------
205
- Flows::Railway (build once)
206
- 6.130k i/100ms
207
- Flows::Railway (build each time)
208
- 2.168k i/100ms
209
- Calculating -------------------------------------
210
- Flows::Railway (build once)
211
- 63.202k (± 1.6%) i/s - 318.760k in 5.044862s
212
- Flows::Railway (build each time)
213
- 21.645k (± 3.6%) i/s - 108.400k in 5.014725s
214
-
215
- Comparison:
216
- Flows::Railway (build once): 63202.5 i/s
217
- Flows::Railway (build each time): 21645.2 i/s - 2.92x slower
218
- ```
219
-
220
- As the benchmark shows your infrastructure code overhead from Flows will be almost three times lower when you build your railways at 'compile' time. I mean something like that:
221
-
222
- ```ruby
223
- class MyClass
224
- MY_RAILWAY = MyRailway.new # this string will be executed on a class loading stage
225
-
226
- def my_method
227
- MY_RAILWAY.call
228
- end
229
- end
230
- ```
231
-
232
- But if you don't care much about performance - build each time will be fast enough. Check out [Performance](overview/performance.md) page to see a bigger picture.