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,514 @@
1
+ # Testing Guide
2
+
3
+ This guide covers testing strategies, patterns, and best practices for SimpleFlow.
4
+
5
+ ## Test Suite Overview
6
+
7
+ SimpleFlow uses Minitest for its test suite.
8
+
9
+ **Current Coverage:**
10
+ - **121 tests**
11
+ - **296 assertions**
12
+ - **96.61% code coverage**
13
+ - **All tests passing**
14
+
15
+ ## Running Tests
16
+
17
+ ### Run All Tests
18
+
19
+ ```bash
20
+ bundle exec rake test
21
+ ```
22
+
23
+ Expected output:
24
+ ```
25
+ Run options: --seed 12345
26
+
27
+ # Running:
28
+
29
+ .............................................................................
30
+
31
+ Finished in 0.123456s, 987.65 runs/s, 2345.67 assertions/s.
32
+
33
+ 121 tests, 296 assertions, 0 failures, 0 errors, 0 skips
34
+ ```
35
+
36
+ ### Run Specific Test File
37
+
38
+ ```bash
39
+ ruby -Ilib:test test/pipeline_test.rb
40
+ ```
41
+
42
+ ### Run Specific Test
43
+
44
+ ```bash
45
+ ruby -Ilib:test test/pipeline_test.rb -n test_basic_pipeline
46
+ ```
47
+
48
+ ### Run Tests with Verbose Output
49
+
50
+ ```bash
51
+ ruby -Ilib:test test/pipeline_test.rb --verbose
52
+ ```
53
+
54
+ ## Test Organization
55
+
56
+ Tests are organized by component:
57
+
58
+ ```
59
+ test/
60
+ ├── test_helper.rb # Test configuration and helpers
61
+ ├── result_test.rb # Result class tests
62
+ ├── pipeline_test.rb # Pipeline class tests
63
+ ├── middleware_test.rb # Middleware tests
64
+ ├── parallel_execution_test.rb # Parallel execution tests
65
+ ├── dependency_graph_test.rb # Dependency graph tests
66
+ ├── dependency_graph_visualizer_test.rb # Visualization tests
67
+ ├── pipeline_visualization_test.rb # Pipeline visualization tests
68
+ └── step_tracker_test.rb # StepTracker tests
69
+ ```
70
+
71
+ ## Writing Tests
72
+
73
+ ### Basic Test Structure
74
+
75
+ ```ruby
76
+ require 'test_helper'
77
+
78
+ class MyFeatureTest < Minitest::Test
79
+ def setup
80
+ # Runs before each test
81
+ @pipeline = SimpleFlow::Pipeline.new
82
+ end
83
+
84
+ def test_feature_description
85
+ # Arrange
86
+ initial = SimpleFlow::Result.new(42)
87
+
88
+ # Act
89
+ result = @pipeline.call(initial)
90
+
91
+ # Assert
92
+ assert result.continue?
93
+ assert_equal 42, result.value
94
+ end
95
+
96
+ def teardown
97
+ # Runs after each test (if needed)
98
+ end
99
+ end
100
+ ```
101
+
102
+ ### Testing Result Objects
103
+
104
+ ```ruby
105
+ class ResultTest < Minitest::Test
106
+ def test_new_result_has_default_values
107
+ result = SimpleFlow::Result.new(42)
108
+
109
+ assert_equal 42, result.value
110
+ assert_equal({}, result.context)
111
+ assert_equal({}, result.errors)
112
+ assert result.continue?
113
+ end
114
+
115
+ def test_with_context_adds_context
116
+ result = SimpleFlow::Result.new(42)
117
+ .with_context(:user_id, 123)
118
+ .with_context(:timestamp, Time.now)
119
+
120
+ assert_equal 123, result.context[:user_id]
121
+ assert result.context[:timestamp]
122
+ end
123
+
124
+ def test_with_error_accumulates_errors
125
+ result = SimpleFlow::Result.new(nil)
126
+ .with_error(:validation, "Error 1")
127
+ .with_error(:validation, "Error 2")
128
+
129
+ assert_equal 2, result.errors[:validation].size
130
+ assert_includes result.errors[:validation], "Error 1"
131
+ assert_includes result.errors[:validation], "Error 2"
132
+ end
133
+
134
+ def test_halt_stops_continuation
135
+ result = SimpleFlow::Result.new(42).halt
136
+
137
+ refute result.continue?
138
+ end
139
+
140
+ def test_immutability
141
+ original = SimpleFlow::Result.new(42)
142
+ modified = original.with_context(:key, "value")
143
+
144
+ assert_equal({}, original.context)
145
+ assert_equal({ key: "value" }, modified.context)
146
+ refute_equal original.object_id, modified.object_id
147
+ end
148
+ end
149
+ ```
150
+
151
+ ### Testing Pipelines
152
+
153
+ ```ruby
154
+ class PipelineTest < Minitest::Test
155
+ def test_basic_sequential_execution
156
+ pipeline = SimpleFlow::Pipeline.new do
157
+ step ->(result) { result.continue(result.value + 1) }
158
+ step ->(result) { result.continue(result.value * 2) }
159
+ end
160
+
161
+ result = pipeline.call(SimpleFlow::Result.new(5))
162
+
163
+ assert_equal 12, result.value # (5 + 1) * 2
164
+ assert result.continue?
165
+ end
166
+
167
+ def test_pipeline_halts_on_error
168
+ pipeline = SimpleFlow::Pipeline.new do
169
+ step ->(result) { result.continue(result.value + 1) }
170
+ step ->(result) { result.halt.with_error(:error, "Failed") }
171
+ step ->(result) { result.continue(result.value * 2) } # Should not execute
172
+ end
173
+
174
+ result = pipeline.call(SimpleFlow::Result.new(5))
175
+
176
+ assert_equal 6, result.value # Only first step executed
177
+ refute result.continue?
178
+ assert_includes result.errors[:error], "Failed"
179
+ end
180
+
181
+ def test_pipeline_with_middleware
182
+ executed = []
183
+
184
+ logging_middleware = ->(callable) {
185
+ ->(result) {
186
+ executed << :before
187
+ output = callable.call(result)
188
+ executed << :after
189
+ output
190
+ }
191
+ }
192
+
193
+ pipeline = SimpleFlow::Pipeline.new do
194
+ use_middleware logging_middleware
195
+
196
+ step ->(result) {
197
+ executed << :step
198
+ result.continue(result.value)
199
+ }
200
+ end
201
+
202
+ pipeline.call(SimpleFlow::Result.new(nil))
203
+
204
+ assert_equal [:before, :step, :after], executed
205
+ end
206
+ end
207
+ ```
208
+
209
+ ### Testing Parallel Execution
210
+
211
+ ```ruby
212
+ class ParallelExecutionTest < Minitest::Test
213
+ def test_parallel_steps_execute_concurrently
214
+ skip unless SimpleFlow::Pipeline.new.async_available?
215
+
216
+ execution_order = []
217
+ mutex = Mutex.new
218
+
219
+ pipeline = SimpleFlow::Pipeline.new do
220
+ step :step_a, ->(result) {
221
+ mutex.synchronize { execution_order << :a_start }
222
+ sleep 0.1
223
+ mutex.synchronize { execution_order << :a_end }
224
+ result.with_context(:a, true).continue(result.value)
225
+ }, depends_on: []
226
+
227
+ step :step_b, ->(result) {
228
+ mutex.synchronize { execution_order << :b_start }
229
+ sleep 0.1
230
+ mutex.synchronize { execution_order << :b_end }
231
+ result.with_context(:b, true).continue(result.value)
232
+ }, depends_on: []
233
+ end
234
+
235
+ result = pipeline.call_parallel(SimpleFlow::Result.new(nil))
236
+
237
+ assert result.context[:a]
238
+ assert result.context[:b]
239
+
240
+ # Both steps started before either finished
241
+ a_start_index = execution_order.index(:a_start)
242
+ b_start_index = execution_order.index(:b_start)
243
+ a_end_index = execution_order.index(:a_end)
244
+ b_end_index = execution_order.index(:b_end)
245
+
246
+ assert a_start_index < a_end_index
247
+ assert b_start_index < b_end_index
248
+ end
249
+
250
+ def test_parallel_execution_merges_contexts
251
+ pipeline = SimpleFlow::Pipeline.new do
252
+ step :step_a, ->(result) {
253
+ result.with_context(:data_a, "from A").continue(result.value)
254
+ }, depends_on: []
255
+
256
+ step :step_b, ->(result) {
257
+ result.with_context(:data_b, "from B").continue(result.value)
258
+ }, depends_on: []
259
+
260
+ step :combine, ->(result) {
261
+ assert_equal "from A", result.context[:data_a]
262
+ assert_equal "from B", result.context[:data_b]
263
+ result.continue(result.value)
264
+ }, depends_on: [:step_a, :step_b]
265
+ end
266
+
267
+ result = pipeline.call_parallel(SimpleFlow::Result.new(nil))
268
+ assert result.continue?
269
+ end
270
+ end
271
+ ```
272
+
273
+ ### Testing Middleware
274
+
275
+ ```ruby
276
+ class MiddlewareTest < Minitest::Test
277
+ def test_logging_middleware_logs_execution
278
+ output = StringIO.new
279
+ logger = Logger.new(output)
280
+
281
+ pipeline = SimpleFlow::Pipeline.new do
282
+ use_middleware SimpleFlow::MiddleWare::Logging, logger: logger
283
+
284
+ step ->(result) { result.continue(result.value) }
285
+ end
286
+
287
+ pipeline.call(SimpleFlow::Result.new(42))
288
+
289
+ log_output = output.string
290
+ assert_match(/Before call/, log_output)
291
+ assert_match(/After call/, log_output)
292
+ end
293
+
294
+ def test_instrumentation_middleware_measures_time
295
+ output = StringIO.new
296
+ $stdout = output
297
+
298
+ pipeline = SimpleFlow::Pipeline.new do
299
+ use_middleware SimpleFlow::MiddleWare::Instrumentation, api_key: 'test'
300
+
301
+ step ->(result) {
302
+ sleep 0.01
303
+ result.continue(result.value)
304
+ }
305
+ end
306
+
307
+ pipeline.call(SimpleFlow::Result.new(nil))
308
+
309
+ $stdout = STDOUT
310
+ assert_match(/Instrumentation: test took/, output.string)
311
+ end
312
+ end
313
+ ```
314
+
315
+ ## Testing Patterns
316
+
317
+ ### Testing Step Classes
318
+
319
+ ```ruby
320
+ class FetchUserStep
321
+ def call(result)
322
+ user = User.find(result.value)
323
+ result.with_context(:user, user).continue(result.value)
324
+ end
325
+ end
326
+
327
+ class FetchUserStepTest < Minitest::Test
328
+ def test_fetches_user_and_adds_to_context
329
+ # Mock User.find
330
+ user = { id: 123, name: "John" }
331
+ User.stub :find, user do
332
+ step = FetchUserStep.new
333
+ result = step.call(SimpleFlow::Result.new(123))
334
+
335
+ assert_equal user, result.context[:user]
336
+ assert result.continue?
337
+ end
338
+ end
339
+
340
+ def test_handles_user_not_found
341
+ User.stub :find, nil do
342
+ step = FetchUserStep.new
343
+ result = step.call(SimpleFlow::Result.new(999))
344
+
345
+ assert_nil result.context[:user]
346
+ end
347
+ end
348
+ end
349
+ ```
350
+
351
+ ### Testing Error Handling
352
+
353
+ ```ruby
354
+ def test_validation_errors
355
+ pipeline = SimpleFlow::Pipeline.new do
356
+ step ->(result) {
357
+ if result.value[:email].nil?
358
+ result.with_error(:validation, "Email required")
359
+ end
360
+
361
+ if result.value[:password].nil?
362
+ result.with_error(:validation, "Password required")
363
+ end
364
+
365
+ if result.errors.any?
366
+ result.halt(result.value)
367
+ else
368
+ result.continue(result.value)
369
+ end
370
+ }
371
+ end
372
+
373
+ result = pipeline.call(SimpleFlow::Result.new({ email: nil, password: nil }))
374
+
375
+ refute result.continue?
376
+ assert_equal 2, result.errors[:validation].size
377
+ assert_includes result.errors[:validation], "Email required"
378
+ assert_includes result.errors[:validation], "Password required"
379
+ end
380
+ ```
381
+
382
+ ### Testing with Mocks and Stubs
383
+
384
+ ```ruby
385
+ def test_external_api_call
386
+ # Stub HTTP client
387
+ mock_response = { status: "ok", data: [1, 2, 3] }
388
+
389
+ HTTP.stub :get, mock_response do
390
+ pipeline = SimpleFlow::Pipeline.new do
391
+ step ->(result) {
392
+ response = HTTP.get("https://api.example.com")
393
+ result.continue(response[:data])
394
+ }
395
+ end
396
+
397
+ result = pipeline.call(SimpleFlow::Result.new(nil))
398
+
399
+ assert_equal [1, 2, 3], result.value
400
+ end
401
+ end
402
+ ```
403
+
404
+ ## Best Practices
405
+
406
+ ### 1. Test Public Interfaces
407
+
408
+ Focus on testing public methods and behaviors:
409
+
410
+ ```ruby
411
+ # GOOD: Tests public interface
412
+ def test_pipeline_processes_data
413
+ result = pipeline.call(initial_data)
414
+ assert_equal expected_output, result.value
415
+ end
416
+
417
+ # AVOID: Testing internal implementation
418
+ def test_internal_step_processing
419
+ # Don't test private methods directly
420
+ end
421
+ ```
422
+
423
+ ### 2. Use Descriptive Test Names
424
+
425
+ ```ruby
426
+ # GOOD: Clear what is being tested
427
+ def test_pipeline_halts_when_validation_fails
428
+ def test_parallel_steps_merge_contexts
429
+ def test_middleware_wraps_steps_in_correct_order
430
+
431
+ # BAD: Vague test names
432
+ def test_pipeline
433
+ def test_it_works
434
+ ```
435
+
436
+ ### 3. Test Edge Cases
437
+
438
+ ```ruby
439
+ def test_handles_nil_value
440
+ def test_handles_empty_array
441
+ def test_handles_large_dataset
442
+ def test_handles_unicode_characters
443
+ ```
444
+
445
+ ### 4. Keep Tests Focused
446
+
447
+ ```ruby
448
+ # GOOD: Tests one thing
449
+ def test_with_context_adds_context
450
+ result = SimpleFlow::Result.new(42).with_context(:key, "value")
451
+ assert_equal "value", result.context[:key]
452
+ end
453
+
454
+ # BAD: Tests multiple things
455
+ def test_result_functionality
456
+ # Tests context, errors, halt, continue all in one test
457
+ end
458
+ ```
459
+
460
+ ### 5. Use Setup and Teardown
461
+
462
+ ```ruby
463
+ class PipelineTest < Minitest::Test
464
+ def setup
465
+ @pipeline = create_test_pipeline
466
+ @initial_data = SimpleFlow::Result.new(test_data)
467
+ end
468
+
469
+ def teardown
470
+ cleanup_test_data if needed
471
+ end
472
+
473
+ def test_something
474
+ result = @pipeline.call(@initial_data)
475
+ # Test assertions
476
+ end
477
+ end
478
+ ```
479
+
480
+ ## Running Tests in CI
481
+
482
+ SimpleFlow uses GitHub Actions for continuous integration. Tests run automatically on:
483
+
484
+ - Every push to any branch
485
+ - Every pull request
486
+ - Multiple Ruby versions (2.7, 3.0, 3.1, 3.2, 3.3)
487
+
488
+ ## Coverage Reports
489
+
490
+ To generate coverage reports locally:
491
+
492
+ ```ruby
493
+ # Add to test_helper.rb
494
+ require 'simplecov'
495
+ SimpleCov.start do
496
+ add_filter '/test/'
497
+ end
498
+ ```
499
+
500
+ Run tests:
501
+ ```bash
502
+ bundle exec rake test
503
+ ```
504
+
505
+ View coverage:
506
+ ```bash
507
+ open coverage/index.html
508
+ ```
509
+
510
+ ## Related Documentation
511
+
512
+ - [Contributing Guide](contributing.md) - How to contribute
513
+ - [Benchmarking Guide](benchmarking.md) - Performance testing
514
+ - [Examples](/Users/dewayne/sandbox/git_repos/madbomber/simple_flow/examples/) - Working examples to test against
@@ -0,0 +1,197 @@
1
+ # Examples
2
+
3
+ Explore real-world examples demonstrating SimpleFlow's capabilities.
4
+
5
+ ## Running Examples
6
+
7
+ All examples are located in the `examples/` directory of the repository. Run them with:
8
+
9
+ ```bash
10
+ ruby examples/example_name.rb
11
+ ```
12
+
13
+ ## Available Examples
14
+
15
+ ### Parallel Data Fetching
16
+
17
+ **File:** `examples/parallel_data_fetching.rb`
18
+
19
+ Demonstrates fetching data from multiple APIs concurrently for improved performance.
20
+
21
+ **Key Features:**
22
+ - Concurrent API calls
23
+ - 4x performance improvement (0.4s → 0.1s)
24
+ - Result merging and aggregation
25
+
26
+ **Run:**
27
+ ```bash
28
+ ruby examples/parallel_data_fetching.rb
29
+ ```
30
+
31
+ **Learn More:** [Data Fetching Guide](../guides/data-fetching.md)
32
+
33
+ ---
34
+
35
+ ### Parallel Validation
36
+
37
+ **File:** `examples/parallel_validation.rb`
38
+
39
+ Shows how to run multiple validation checks concurrently to quickly identify all errors.
40
+
41
+ **Key Features:**
42
+ - Concurrent validation checks
43
+ - Error accumulation
44
+ - Fast feedback on multiple validation failures
45
+
46
+ **Run:**
47
+ ```bash
48
+ ruby examples/parallel_validation.rb
49
+ ```
50
+
51
+ **Learn More:** [Validation Patterns Guide](../guides/validation-patterns.md)
52
+
53
+ ---
54
+
55
+ ### Error Handling
56
+
57
+ **File:** `examples/error_handling.rb`
58
+
59
+ Demonstrates various error handling patterns including validation, graceful degradation, and retry logic.
60
+
61
+ **Key Features:**
62
+ - Validation with error accumulation
63
+ - Graceful degradation with optional services
64
+ - Retry logic with exponential backoff
65
+
66
+ **Run:**
67
+ ```bash
68
+ ruby examples/error_handling.rb
69
+ ```
70
+
71
+ **Learn More:** [Error Handling Guide](../guides/error-handling.md)
72
+
73
+ ---
74
+
75
+ ### File Processing
76
+
77
+ **File:** `examples/file_processing.rb`
78
+
79
+ Shows how to process multiple files in parallel with validation, conversion, and summarization.
80
+
81
+ **Key Features:**
82
+ - Parallel file processing
83
+ - Format conversion (JSON to CSV)
84
+ - Data validation and summarization
85
+
86
+ **Run:**
87
+ ```bash
88
+ ruby examples/file_processing.rb
89
+ ```
90
+
91
+ **Learn More:** [File Processing Guide](../guides/file-processing.md)
92
+
93
+ ---
94
+
95
+ ### Complex Workflow
96
+
97
+ **File:** `examples/complex_workflow.rb`
98
+
99
+ A realistic e-commerce order processing pipeline with multiple stages and parallel execution blocks.
100
+
101
+ **Key Features:**
102
+ - 6-stage workflow
103
+ - 4 parallel execution blocks
104
+ - 15+ steps
105
+ - User validation, inventory checks, payment processing
106
+ - Context accumulation across stages
107
+
108
+ **Run:**
109
+ ```bash
110
+ ruby examples/complex_workflow.rb
111
+ ```
112
+
113
+ **Learn More:** [Complex Workflows Guide](../guides/complex-workflows.md)
114
+
115
+ ---
116
+
117
+ ## Code Snippets
118
+
119
+ ### Basic Pipeline
120
+
121
+ ```ruby
122
+ require 'simple_flow'
123
+
124
+ pipeline = SimpleFlow::Pipeline.new do
125
+ step ->(result) { result.continue(result.value.strip) }
126
+ step ->(result) { result.continue(result.value.downcase) }
127
+ step ->(result) { result.continue("Hello, #{result.value}!") }
128
+ end
129
+
130
+ result = pipeline.call(SimpleFlow::Result.new(" WORLD "))
131
+ puts result.value # => "Hello, world!"
132
+ ```
133
+
134
+ ### With Middleware
135
+
136
+ ```ruby
137
+ pipeline = SimpleFlow::Pipeline.new do
138
+ use_middleware SimpleFlow::MiddleWare::Logging
139
+ use_middleware SimpleFlow::MiddleWare::Instrumentation, api_key: 'abc123'
140
+
141
+ step ->(result) { result.continue(result.value + 10) }
142
+ step ->(result) { result.continue(result.value * 2) }
143
+ end
144
+ ```
145
+
146
+ ### Concurrent Execution
147
+
148
+ ```ruby
149
+ pipeline = SimpleFlow::Pipeline.new do
150
+ step ->(result) { fetch_user(result) }
151
+
152
+ parallel do
153
+ step ->(result) { fetch_orders(result) }
154
+ step ->(result) { fetch_preferences(result) }
155
+ step ->(result) { fetch_analytics(result) }
156
+ end
157
+
158
+ step ->(result) { aggregate_data(result) }
159
+ end
160
+ ```
161
+
162
+ ### Error Handling
163
+
164
+ ```ruby
165
+ pipeline = SimpleFlow::Pipeline.new do
166
+ step ->(result) {
167
+ if result.value < 0
168
+ result.halt.with_error(:validation, "Value must be positive")
169
+ else
170
+ result.continue(result.value)
171
+ end
172
+ }
173
+
174
+ step ->(result) {
175
+ # Only runs if validation passed
176
+ result.continue(result.value * 2)
177
+ }
178
+ end
179
+ ```
180
+
181
+ ## Performance Benchmarks
182
+
183
+ Run benchmarks to see SimpleFlow's performance:
184
+
185
+ ```bash
186
+ # Compare parallel vs sequential execution
187
+ ruby benchmarks/parallel_vs_sequential.rb
188
+
189
+ # Measure pipeline overhead
190
+ ruby benchmarks/pipeline_overhead.rb
191
+ ```
192
+
193
+ ## Next Steps
194
+
195
+ - [Core Concepts](../core-concepts/overview.md) - Understand the fundamentals
196
+ - [Concurrent Execution](../concurrent/introduction.md) - Deep dive into parallelism
197
+ - [API Reference](../api/pipeline.md) - Complete API documentation