simple_flow 0.1.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 (80) hide show
  1. checksums.yaml +7 -0
  2. data/.envrc +1 -0
  3. data/.github/workflows/deploy-github-pages.yml +52 -0
  4. data/.rubocop.yml +57 -0
  5. data/CHANGELOG.md +4 -0
  6. data/COMMITS.md +196 -0
  7. data/LICENSE +21 -0
  8. data/README.md +481 -0
  9. data/Rakefile +15 -0
  10. data/benchmarks/parallel_vs_sequential.rb +98 -0
  11. data/benchmarks/pipeline_overhead.rb +130 -0
  12. data/docs/api/middleware.md +468 -0
  13. data/docs/api/parallel-step.md +363 -0
  14. data/docs/api/pipeline.md +382 -0
  15. data/docs/api/result.md +375 -0
  16. data/docs/concurrent/best-practices.md +687 -0
  17. data/docs/concurrent/introduction.md +246 -0
  18. data/docs/concurrent/parallel-steps.md +418 -0
  19. data/docs/concurrent/performance.md +481 -0
  20. data/docs/core-concepts/flow-control.md +452 -0
  21. data/docs/core-concepts/middleware.md +389 -0
  22. data/docs/core-concepts/overview.md +219 -0
  23. data/docs/core-concepts/pipeline.md +315 -0
  24. data/docs/core-concepts/result.md +168 -0
  25. data/docs/core-concepts/steps.md +391 -0
  26. data/docs/development/benchmarking.md +443 -0
  27. data/docs/development/contributing.md +380 -0
  28. data/docs/development/dagwood-concepts.md +435 -0
  29. data/docs/development/testing.md +514 -0
  30. data/docs/getting-started/examples.md +197 -0
  31. data/docs/getting-started/installation.md +62 -0
  32. data/docs/getting-started/quick-start.md +218 -0
  33. data/docs/guides/choosing-concurrency-model.md +441 -0
  34. data/docs/guides/complex-workflows.md +440 -0
  35. data/docs/guides/data-fetching.md +478 -0
  36. data/docs/guides/error-handling.md +635 -0
  37. data/docs/guides/file-processing.md +505 -0
  38. data/docs/guides/validation-patterns.md +496 -0
  39. data/docs/index.md +169 -0
  40. data/examples/.gitignore +3 -0
  41. data/examples/01_basic_pipeline.rb +112 -0
  42. data/examples/02_error_handling.rb +178 -0
  43. data/examples/03_middleware.rb +186 -0
  44. data/examples/04_parallel_automatic.rb +221 -0
  45. data/examples/05_parallel_explicit.rb +279 -0
  46. data/examples/06_real_world_ecommerce.rb +288 -0
  47. data/examples/07_real_world_etl.rb +277 -0
  48. data/examples/08_graph_visualization.rb +246 -0
  49. data/examples/09_pipeline_visualization.rb +266 -0
  50. data/examples/10_concurrency_control.rb +235 -0
  51. data/examples/11_sequential_dependencies.rb +243 -0
  52. data/examples/12_none_constant.rb +161 -0
  53. data/examples/README.md +374 -0
  54. data/examples/regression_test/01_basic_pipeline.txt +38 -0
  55. data/examples/regression_test/02_error_handling.txt +92 -0
  56. data/examples/regression_test/03_middleware.txt +61 -0
  57. data/examples/regression_test/04_parallel_automatic.txt +86 -0
  58. data/examples/regression_test/05_parallel_explicit.txt +80 -0
  59. data/examples/regression_test/06_real_world_ecommerce.txt +53 -0
  60. data/examples/regression_test/07_real_world_etl.txt +58 -0
  61. data/examples/regression_test/08_graph_visualization.txt +429 -0
  62. data/examples/regression_test/09_pipeline_visualization.txt +305 -0
  63. data/examples/regression_test/10_concurrency_control.txt +96 -0
  64. data/examples/regression_test/11_sequential_dependencies.txt +86 -0
  65. data/examples/regression_test/12_none_constant.txt +64 -0
  66. data/examples/regression_test.rb +105 -0
  67. data/lib/simple_flow/dependency_graph.rb +120 -0
  68. data/lib/simple_flow/dependency_graph_visualizer.rb +326 -0
  69. data/lib/simple_flow/middleware.rb +36 -0
  70. data/lib/simple_flow/parallel_executor.rb +80 -0
  71. data/lib/simple_flow/pipeline.rb +405 -0
  72. data/lib/simple_flow/result.rb +88 -0
  73. data/lib/simple_flow/step_tracker.rb +58 -0
  74. data/lib/simple_flow/version.rb +5 -0
  75. data/lib/simple_flow.rb +41 -0
  76. data/mkdocs.yml +146 -0
  77. data/pipeline_graph.dot +51 -0
  78. data/pipeline_graph.html +60 -0
  79. data/pipeline_graph.mmd +19 -0
  80. metadata +127 -0
@@ -0,0 +1,382 @@
1
+ # Pipeline API Reference
2
+
3
+ The `Pipeline` class orchestrates step execution with middleware integration and parallel execution support.
4
+
5
+ ## Class: `SimpleFlow::Pipeline`
6
+
7
+ **Location**: `/Users/dewayne/sandbox/git_repos/madbomber/simple_flow/lib/simple_flow/pipeline.rb`
8
+
9
+ ### Constructor
10
+
11
+ #### `new(&config)`
12
+
13
+ Creates a new Pipeline with optional configuration block.
14
+
15
+ **Parameters:**
16
+ - `config` (Block, optional) - Configuration block for defining steps and middleware
17
+
18
+ **Example:**
19
+ ```ruby
20
+ pipeline = SimpleFlow::Pipeline.new do
21
+ use_middleware SimpleFlow::MiddleWare::Logging
22
+ step ->(result) { result.continue(result.value + 1) }
23
+ end
24
+ ```
25
+
26
+ ### DSL Methods
27
+
28
+ #### `use_middleware(middleware, options = {})`
29
+
30
+ Registers middleware to be applied to each step.
31
+
32
+ **Parameters:**
33
+ - `middleware` (Class/Proc) - Middleware class or proc
34
+ - `options` (Hash) - Options passed to middleware constructor
35
+
36
+ **Example:**
37
+ ```ruby
38
+ pipeline = SimpleFlow::Pipeline.new do
39
+ use_middleware SimpleFlow::MiddleWare::Logging
40
+ use_middleware SimpleFlow::MiddleWare::Instrumentation, api_key: 'xyz'
41
+ use_middleware CustomMiddleware, timeout: 30
42
+ end
43
+ ```
44
+
45
+ #### `step(name_or_callable = nil, callable = nil, depends_on: [], &block)`
46
+
47
+ Adds a step to the pipeline. Supports named and unnamed steps.
48
+
49
+ **Parameters:**
50
+ - `name_or_callable` (Symbol/Proc/Object) - Step name or callable
51
+ - `callable` (Proc/Object) - Callable object (if first param is name)
52
+ - `depends_on` (Array) - Dependencies for named steps
53
+ - `block` (Block) - Block to use as step
54
+
55
+ **Returns:** self (for chaining)
56
+
57
+ **Named Steps:**
58
+ ```ruby
59
+ pipeline = SimpleFlow::Pipeline.new do
60
+ step :fetch_user, ->(result) { ... }, depends_on: []
61
+ step :process_data, ->(result) { ... }, depends_on: [:fetch_user]
62
+ end
63
+ ```
64
+
65
+ **Unnamed Steps:**
66
+ ```ruby
67
+ pipeline = SimpleFlow::Pipeline.new do
68
+ step ->(result) { result.continue(result.value + 1) }
69
+ step { |result| result.continue(result.value * 2) }
70
+ end
71
+ ```
72
+
73
+ **Class-Based Steps:**
74
+ ```ruby
75
+ class FetchUser
76
+ def call(result)
77
+ user = User.find(result.value)
78
+ result.with_context(:user, user).continue(result.value)
79
+ end
80
+ end
81
+
82
+ pipeline = SimpleFlow::Pipeline.new do
83
+ step :fetch_user, FetchUser.new, depends_on: []
84
+ end
85
+ ```
86
+
87
+ #### `parallel(&block)`
88
+
89
+ Defines an explicit parallel execution block.
90
+
91
+ **Parameters:**
92
+ - `block` (Block) - Block containing step definitions
93
+
94
+ **Returns:** self (for chaining)
95
+
96
+ **Example:**
97
+ ```ruby
98
+ pipeline = SimpleFlow::Pipeline.new do
99
+ step ->(result) { result.continue(validate(result.value)) }
100
+
101
+ parallel do
102
+ step ->(result) { result.with_context(:api, fetch_api).continue(result.value) }
103
+ step ->(result) { result.with_context(:db, fetch_db).continue(result.value) }
104
+ step ->(result) { result.with_context(:cache, fetch_cache).continue(result.value) }
105
+ end
106
+
107
+ step ->(result) { result.continue(merge_data(result.context)) }
108
+ end
109
+ ```
110
+
111
+ ### Execution Methods
112
+
113
+ #### `call(result)`
114
+
115
+ Executes the pipeline sequentially with a given initial result.
116
+
117
+ **Parameters:**
118
+ - `result` (Result) - Initial Result object
119
+
120
+ **Returns:** Final Result object
121
+
122
+ **Example:**
123
+ ```ruby
124
+ initial = SimpleFlow::Result.new(5)
125
+ result = pipeline.call(initial)
126
+
127
+ result.value # => Final value
128
+ result.context # => Accumulated context
129
+ result.errors # => Any errors
130
+ result.continue? # => true/false
131
+ ```
132
+
133
+ #### `call_parallel(result, strategy: :auto)`
134
+
135
+ Executes the pipeline with parallel execution where possible.
136
+
137
+ **Parameters:**
138
+ - `result` (Result) - Initial Result object
139
+ - `strategy` (Symbol) - Parallelization strategy (`:auto` or `:explicit`)
140
+
141
+ **Returns:** Final Result object
142
+
143
+ **Strategies:**
144
+ - `:auto` (default) - Uses dependency graph if named steps exist
145
+ - `:explicit` - Only uses explicit parallel blocks
146
+
147
+ **Example:**
148
+ ```ruby
149
+ # Automatic strategy (uses dependency graph)
150
+ result = pipeline.call_parallel(initial_data)
151
+
152
+ # Explicit strategy
153
+ result = pipeline.call_parallel(initial_data, strategy: :explicit)
154
+ ```
155
+
156
+ ### Visualization Methods
157
+
158
+ #### `visualize_ascii(show_groups: true)`
159
+
160
+ Returns ASCII visualization of the dependency graph.
161
+
162
+ **Parameters:**
163
+ - `show_groups` (Boolean) - Whether to show parallel execution groups (default: true)
164
+
165
+ **Returns:** String (ASCII art) or nil if no named steps
166
+
167
+ **Example:**
168
+ ```ruby
169
+ puts pipeline.visualize_ascii
170
+
171
+ # Hide parallel groups
172
+ puts pipeline.visualize_ascii(show_groups: false)
173
+ ```
174
+
175
+ #### `visualize_dot(include_groups: true, orientation: 'TB')`
176
+
177
+ Exports dependency graph to Graphviz DOT format.
178
+
179
+ **Parameters:**
180
+ - `include_groups` (Boolean) - Color-code parallel groups (default: true)
181
+ - `orientation` (String) - Graph orientation: 'TB' (top-bottom) or 'LR' (left-right)
182
+
183
+ **Returns:** String (DOT format) or nil if no named steps
184
+
185
+ **Example:**
186
+ ```ruby
187
+ File.write('pipeline.dot', pipeline.visualize_dot)
188
+ # Generate image: dot -Tpng pipeline.dot -o pipeline.png
189
+
190
+ # Left-to-right layout
191
+ File.write('pipeline.dot', pipeline.visualize_dot(orientation: 'LR'))
192
+ ```
193
+
194
+ #### `visualize_mermaid()`
195
+
196
+ Exports dependency graph to Mermaid diagram format.
197
+
198
+ **Returns:** String (Mermaid format) or nil if no named steps
199
+
200
+ **Example:**
201
+ ```ruby
202
+ File.write('pipeline.mmd', pipeline.visualize_mermaid)
203
+ # View at https://mermaid.live/
204
+ ```
205
+
206
+ #### `execution_plan()`
207
+
208
+ Returns detailed execution plan analysis.
209
+
210
+ **Returns:** String (execution plan) or nil if no named steps
211
+
212
+ **Example:**
213
+ ```ruby
214
+ puts pipeline.execution_plan
215
+ ```
216
+
217
+ Output includes:
218
+ - Total steps and execution phases
219
+ - Which steps run in parallel
220
+ - Potential speedup vs sequential execution
221
+
222
+ ### Utility Methods
223
+
224
+ #### `async_available?`
225
+
226
+ Checks if the async gem is available for true parallel execution.
227
+
228
+ **Returns:** Boolean
229
+
230
+ **Example:**
231
+ ```ruby
232
+ if pipeline.async_available?
233
+ puts "Parallel execution enabled"
234
+ else
235
+ puts "Falling back to sequential execution"
236
+ end
237
+ ```
238
+
239
+ #### `dependency_graph`
240
+
241
+ Returns the dependency graph for this pipeline.
242
+
243
+ **Returns:** DependencyGraph or nil if no named steps
244
+
245
+ **Example:**
246
+ ```ruby
247
+ graph = pipeline.dependency_graph
248
+ if graph
249
+ puts graph.order # => [:step1, :step2, :step3]
250
+ puts graph.parallel_order # => [[:step1], [:step2, :step3]]
251
+ end
252
+ ```
253
+
254
+ #### `visualize`
255
+
256
+ Creates a visualizer for this pipeline's dependency graph.
257
+
258
+ **Returns:** DependencyGraphVisualizer or nil if no named steps
259
+
260
+ **Example:**
261
+ ```ruby
262
+ visualizer = pipeline.visualize
263
+ if visualizer
264
+ puts visualizer.to_ascii
265
+ File.write('graph.dot', visualizer.to_dot)
266
+ end
267
+ ```
268
+
269
+ ### Instance Attributes
270
+
271
+ #### `steps`
272
+
273
+ Array of step definitions (read-only).
274
+
275
+ **Type:** Array
276
+
277
+ #### `middlewares`
278
+
279
+ Array of registered middleware (read-only).
280
+
281
+ **Type:** Array
282
+
283
+ #### `named_steps`
284
+
285
+ Hash of named steps (read-only).
286
+
287
+ **Type:** Hash
288
+
289
+ #### `step_dependencies`
290
+
291
+ Hash of step dependencies (read-only).
292
+
293
+ **Type:** Hash
294
+
295
+ ## Usage Examples
296
+
297
+ ### Basic Sequential Pipeline
298
+
299
+ ```ruby
300
+ pipeline = SimpleFlow::Pipeline.new do
301
+ step ->(result) { result.continue(result.value.strip) }
302
+ step ->(result) { result.continue(result.value.downcase) }
303
+ step ->(result) { result.continue("Hello, #{result.value}!") }
304
+ end
305
+
306
+ result = pipeline.call(SimpleFlow::Result.new(" WORLD "))
307
+ result.value # => "Hello, world!"
308
+ ```
309
+
310
+ ### Parallel Pipeline with Dependencies
311
+
312
+ ```ruby
313
+ pipeline = SimpleFlow::Pipeline.new do
314
+ step :fetch_user, ->(result) {
315
+ user = User.find(result.value)
316
+ result.with_context(:user, user).continue(result.value)
317
+ }, depends_on: []
318
+
319
+ step :fetch_orders, ->(result) {
320
+ orders = Order.where(user_id: result.context[:user].id)
321
+ result.with_context(:orders, orders).continue(result.value)
322
+ }, depends_on: [:fetch_user]
323
+
324
+ step :fetch_preferences, ->(result) {
325
+ prefs = Preference.where(user_id: result.context[:user].id)
326
+ result.with_context(:preferences, prefs).continue(result.value)
327
+ }, depends_on: [:fetch_user]
328
+
329
+ step :build_profile, ->(result) {
330
+ profile = {
331
+ user: result.context[:user],
332
+ orders: result.context[:orders],
333
+ preferences: result.context[:preferences]
334
+ }
335
+ result.continue(profile)
336
+ }, depends_on: [:fetch_orders, :fetch_preferences]
337
+ end
338
+
339
+ # fetch_orders and fetch_preferences run in parallel
340
+ result = pipeline.call_parallel(SimpleFlow::Result.new(user_id))
341
+ ```
342
+
343
+ ### Pipeline with Middleware
344
+
345
+ ```ruby
346
+ pipeline = SimpleFlow::Pipeline.new do
347
+ use_middleware SimpleFlow::MiddleWare::Logging
348
+ use_middleware SimpleFlow::MiddleWare::Instrumentation, api_key: 'demo'
349
+
350
+ step ->(result) { result.continue(process(result.value)) }
351
+ end
352
+ ```
353
+
354
+ ### Mixed Execution Styles
355
+
356
+ ```ruby
357
+ pipeline = SimpleFlow::Pipeline.new do
358
+ # Unnamed sequential step
359
+ step ->(result) { result.continue(sanitize(result.value)) }
360
+
361
+ # Named steps with automatic parallelism
362
+ step :step_a, ->(result) { ... }, depends_on: []
363
+ step :step_b, ->(result) { ... }, depends_on: []
364
+ step :step_c, ->(result) { ... }, depends_on: [:step_a, :step_b]
365
+
366
+ # Explicit parallel block
367
+ parallel do
368
+ step ->(result) { ... }
369
+ step ->(result) { ... }
370
+ end
371
+
372
+ # Another sequential step
373
+ step ->(result) { result.continue(finalize(result.value)) }
374
+ end
375
+ ```
376
+
377
+ ## Related Documentation
378
+
379
+ - [Result API](result.md) - Result class reference
380
+ - [Parallel Steps Guide](../concurrent/parallel-steps.md) - Using named steps
381
+ - [Middleware API](middleware.md) - Middleware reference
382
+ - [Performance Guide](../concurrent/performance.md) - Optimization strategies