flows 0.2.0 → 0.6.0

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