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,305 @@
1
+ ============================================================
2
+ Direct Pipeline Visualization
3
+ ============================================================
4
+
5
+ Example 1: Simple Pipeline Visualization
6
+ ------------------------------------------------------------
7
+
8
+ Dependency Graph
9
+ ============================================================
10
+
11
+ Dependencies:
12
+ :validate
13
+ └─ depends on: (none)
14
+ :fetch_data
15
+ └─ depends on: :validate
16
+ :process_data
17
+ └─ depends on: :fetch_data
18
+
19
+ Execution Order (sequential):
20
+ :validate
21
+ ↓ :fetch_data
22
+ ↓ :process_data
23
+
24
+ Parallel Execution Groups:
25
+ Group 1:
26
+ └─ :validate (sequential)
27
+ Group 2:
28
+ └─ :fetch_data (sequential)
29
+ Group 3:
30
+ └─ :process_data (sequential)
31
+
32
+ Execution Tree:
33
+ ├─ :validate
34
+ ├─ :fetch_data
35
+ └─ :process_data
36
+
37
+
38
+ ============================================================
39
+ Example 2: E-commerce Pipeline (Automatic Visualization)
40
+ ============================================================
41
+
42
+ Dependency Graph
43
+ ============================================================
44
+
45
+ Dependencies:
46
+ :validate_order
47
+ └─ depends on: (none)
48
+ :check_inventory
49
+ └─ depends on: :validate_order
50
+ :calculate_shipping
51
+ └─ depends on: :validate_order
52
+ :calculate_totals
53
+ └─ depends on: :calculate_shipping, :check_inventory
54
+ :process_payment
55
+ └─ depends on: :calculate_totals
56
+ :reserve_inventory
57
+ └─ depends on: :process_payment
58
+ :create_shipment
59
+ └─ depends on: :reserve_inventory
60
+ :send_email
61
+ └─ depends on: :create_shipment
62
+ :send_sms
63
+ └─ depends on: :create_shipment
64
+ :finalize_order
65
+ └─ depends on: :send_email, :send_sms
66
+
67
+ Execution Order (sequential):
68
+ :validate_order
69
+ ↓ :check_inventory
70
+ ↓ :calculate_shipping
71
+ ↓ :calculate_totals
72
+ ↓ :process_payment
73
+ ↓ :reserve_inventory
74
+ ↓ :create_shipment
75
+ ↓ :send_email
76
+ ↓ :send_sms
77
+ ↓ :finalize_order
78
+
79
+ Parallel Execution Groups:
80
+ Group 1:
81
+ └─ :validate_order (sequential)
82
+ Group 2:
83
+ ├─ Parallel execution of 2 steps:
84
+ ├─ :calculate_shipping
85
+ └─ :check_inventory
86
+ Group 3:
87
+ └─ :calculate_totals (sequential)
88
+ Group 4:
89
+ └─ :process_payment (sequential)
90
+ Group 5:
91
+ └─ :reserve_inventory (sequential)
92
+ Group 6:
93
+ └─ :create_shipment (sequential)
94
+ Group 7:
95
+ ├─ Parallel execution of 2 steps:
96
+ ├─ :send_email
97
+ └─ :send_sms
98
+ Group 8:
99
+ └─ :finalize_order (sequential)
100
+
101
+ Execution Tree:
102
+ ├─ :validate_order
103
+ ├─ [Parallel]
104
+ │ ├─ :calculate_shipping
105
+ │ └─ :check_inventory
106
+ ├─ :calculate_totals
107
+ ├─ :process_payment
108
+ ├─ :reserve_inventory
109
+ ├─ :create_shipment
110
+ ├─ [Parallel]
111
+ │ ├─ :send_email
112
+ │ └─ :send_sms
113
+ └─ :finalize_order
114
+
115
+
116
+ ============================================================
117
+ Example 3: Execution Plan (Direct from Pipeline)
118
+ ============================================================
119
+
120
+ Execution Plan
121
+ ============================================================
122
+
123
+ Total Steps: 10
124
+ Execution Phases: 8
125
+
126
+ Phase 1:
127
+ → Execute :validate_order
128
+
129
+ Phase 2:
130
+ ⚡ Execute in parallel:
131
+ • :calculate_shipping (after :validate_order)
132
+ • :check_inventory (after :validate_order)
133
+
134
+ Phase 3:
135
+ → Execute :calculate_totals
136
+
137
+ Phase 4:
138
+ → Execute :process_payment
139
+
140
+ Phase 5:
141
+ → Execute :reserve_inventory
142
+
143
+ Phase 6:
144
+ → Execute :create_shipment
145
+
146
+ Phase 7:
147
+ ⚡ Execute in parallel:
148
+ • :send_email (after :create_shipment)
149
+ • :send_sms (after :create_shipment)
150
+
151
+ Phase 8:
152
+ → Execute :finalize_order
153
+
154
+ Performance Estimate:
155
+ Sequential execution: 10 time units
156
+ Parallel execution: 8 time units
157
+ Potential speedup: 1.25x
158
+
159
+
160
+ ============================================================
161
+ Example 4: Export Formats (Direct from Pipeline)
162
+ ============================================================
163
+
164
+ ✓ Exported to Graphviz DOT: pipeline_graph.dot
165
+ Generate image: dot -Tpng pipeline_graph.dot -o pipeline.png
166
+
167
+ ✓ Exported to Mermaid: pipeline_graph.mmd
168
+ View at: https://mermaid.live/
169
+
170
+ ✓ Exported to HTML: pipeline_graph.html
171
+ Open in browser for interactive visualization
172
+
173
+
174
+ ============================================================
175
+ Example 5: ETL Pipeline Visualization
176
+ ============================================================
177
+
178
+ Execution Plan
179
+ ============================================================
180
+
181
+ Total Steps: 10
182
+ Execution Phases: 5
183
+
184
+ Phase 1:
185
+ ⚡ Execute in parallel:
186
+ • :extract_orders (no dependencies)
187
+ • :extract_products (no dependencies)
188
+ • :extract_users (no dependencies)
189
+
190
+ Phase 2:
191
+ ⚡ Execute in parallel:
192
+ • :transform_orders (after :extract_orders)
193
+ • :transform_products (after :extract_products)
194
+ • :transform_users (after :extract_users)
195
+
196
+ Phase 3:
197
+ ⚡ Execute in parallel:
198
+ • :aggregate_category_stats (after :transform_products)
199
+ • :aggregate_user_stats (after :transform_orders, :transform_users)
200
+
201
+ Phase 4:
202
+ → Execute :validate_data
203
+
204
+ Phase 5:
205
+ → Execute :prepare_output
206
+
207
+ Performance Estimate:
208
+ Sequential execution: 10 time units
209
+ Parallel execution: 5 time units
210
+ Potential speedup: 2.0x
211
+
212
+
213
+ ============================================================
214
+ Example 6: Checking Visualization Availability
215
+ ============================================================
216
+
217
+ Named pipeline has dependency graph? true
218
+ Unnamed pipeline has dependency graph? false
219
+
220
+ Note: Only pipelines with named steps and dependencies can be auto-visualized
221
+
222
+
223
+ ============================================================
224
+ Example 7: Advanced - Access Dependency Graph
225
+ ============================================================
226
+
227
+ Pipeline dependency information:
228
+ Total steps: 10
229
+ Execution phases: 8
230
+ Parallel groups:
231
+ Phase 1: validate_order
232
+ Phase 2: calculate_shipping, check_inventory
233
+ Phase 3: calculate_totals
234
+ Phase 4: process_payment
235
+ Phase 5: reserve_inventory
236
+ Phase 6: create_shipment
237
+ Phase 7: send_email, send_sms
238
+ Phase 8: finalize_order
239
+
240
+
241
+ ============================================================
242
+ Example 8: Pipeline Structure Comparison
243
+ ============================================================
244
+
245
+ Linear Pipeline:
246
+ Execution Plan
247
+ ============================================================
248
+
249
+ Total Steps: 4
250
+ Execution Phases: 4
251
+
252
+ Phase 1:
253
+ → Execute :step1
254
+
255
+ Phase 2:
256
+ → Execute :step2
257
+
258
+ Phase 3:
259
+ → Execute :step3
260
+
261
+ Phase 4:
262
+ → Execute :step4
263
+
264
+ Performance Estimate:
265
+ Sequential execution: 4 time units
266
+ Parallel execution: 4 time units
267
+ Potential speedup: 1.0x
268
+
269
+
270
+ Parallel Pipeline:
271
+ Execution Plan
272
+ ============================================================
273
+
274
+ Total Steps: 5
275
+ Execution Phases: 3
276
+
277
+ Phase 1:
278
+ → Execute :start
279
+
280
+ Phase 2:
281
+ ⚡ Execute in parallel:
282
+ • :task1 (after :start)
283
+ • :task2 (after :start)
284
+ • :task3 (after :start)
285
+
286
+ Phase 3:
287
+ → Execute :end
288
+
289
+ Performance Estimate:
290
+ Sequential execution: 5 time units
291
+ Parallel execution: 3 time units
292
+ Potential speedup: 1.67x
293
+
294
+ ============================================================
295
+ Direct pipeline visualization completed!
296
+
297
+ Key Takeaway:
298
+ No need to manually recreate dependency structures!
299
+ Just call pipeline.visualize_ascii, pipeline.visualize_dot, etc.
300
+
301
+ Generated files:
302
+ - pipeline_graph.dot
303
+ - pipeline_graph.mmd
304
+ - pipeline_graph.html
305
+ ============================================================
@@ -0,0 +1,96 @@
1
+ ============================================================
2
+ Per-Pipeline Concurrency Control
3
+ ============================================================
4
+
5
+ Async gem available: ✓ Yes
6
+
7
+ Example 1: Auto-Detect Concurrency (Default)
8
+ ------------------------------------------------------------
9
+
10
+ Pipeline concurrency setting: auto
11
+ Will use: Async (fiber-based)
12
+
13
+
14
+ Example 2: Force Threads
15
+ ------------------------------------------------------------
16
+
17
+ Pipeline concurrency setting: threads
18
+ Will use: Ruby Threads (even if async is available)
19
+ [Thread-based] Task A running...
20
+ [Thread-based] Task B running...
21
+ [Thread-based] Task C running...
22
+ Result context: {task_c: :done}
23
+
24
+
25
+ Example 3: Force Async
26
+ ------------------------------------------------------------
27
+
28
+ Pipeline concurrency setting: async
29
+ Will use: Async (fiber-based concurrency)
30
+ [Async] Validating...
31
+ [Async] Fetching A...
32
+ [Async] Fetching B...
33
+ [Async] Merging results...
34
+ Result: Complete
35
+ Context: {a: :done, b: :done}
36
+
37
+
38
+ Example 4: Mixed Concurrency in Same Application
39
+ ------------------------------------------------------------
40
+
41
+ User pipeline uses: threads
42
+ Batch pipeline uses: async
43
+
44
+ Running user pipeline...
45
+ [User/Threads] Validating user...
46
+ [User/Threads] Fetching profile...
47
+ [User/Threads] Fetching settings...
48
+ User result: {profile: {name: "John"}, settings: {theme: "dark"}}
49
+
50
+ Running batch pipeline...
51
+ [Batch/Async] Loading batch...
52
+ [Batch/Async] Processing batch 1...
53
+ [Batch/Async] Processing batch 2...
54
+ Batch result: {batch_1: :done, batch_2: :done}
55
+
56
+
57
+ Example 5: Error Handling
58
+ ------------------------------------------------------------
59
+
60
+ Valid options: :auto, :threads, :async
61
+
62
+ ✓ Caught expected error for invalid concurrency:
63
+ Invalid concurrency option: :invalid. Valid options: [:auto, :threads, :async]
64
+
65
+
66
+
67
+ Example 6: Inspecting Concurrency Settings
68
+ ------------------------------------------------------------
69
+
70
+ Pipeline 1:
71
+ Concurrency: auto
72
+ Async available: true
73
+
74
+ Pipeline 2:
75
+ Concurrency: auto
76
+ Async available: true
77
+
78
+ Pipeline 3:
79
+ Concurrency: threads
80
+ Async available: true
81
+
82
+ Pipeline 4:
83
+ Concurrency: async
84
+ Async available: true
85
+
86
+ ============================================================
87
+ Concurrency control examples completed!
88
+ ============================================================
89
+
90
+ Key Takeaways:
91
+ • concurrency: :auto (default) - auto-detects best option
92
+ • concurrency: :threads - always uses Ruby threads
93
+ • concurrency: :async - requires async gem, uses fibers
94
+ • Different pipelines can use different concurrency models
95
+ • Choose based on your specific workload requirements
96
+
@@ -0,0 +1,86 @@
1
+ ============================================================
2
+ Sequential Step Dependencies
3
+ ============================================================
4
+
5
+ Example 1: All Steps Succeed
6
+ ------------------------------------------------------------
7
+
8
+ [Step 1] Starting workflow...
9
+ [Step 2] Processing data...
10
+ [Step 3] Finalizing...
11
+
12
+ Final result: 22
13
+ Continue? true
14
+ Expected: ((5 + 1) * 2) + 10 = 22
15
+
16
+
17
+ Example 2: Pipeline Halts on Error
18
+ ------------------------------------------------------------
19
+
20
+ [Step 1] Validating input...
21
+ [Step 2] Checking business rules...
22
+
23
+ Final result: -5
24
+ Continue? false
25
+ Errors: {business_rule: ["Value must be positive"]}
26
+ Note: Step 3 never executed because Step 2 halted
27
+
28
+
29
+ Example 3: Early Validation Pattern
30
+ ------------------------------------------------------------
31
+
32
+ Scenario A: System in maintenance mode
33
+ [Pre-flight] Checking system health...
34
+ Continue? false
35
+ Errors: {system: ["Maintenance mode active"]}
36
+
37
+
38
+ Scenario B: System healthy
39
+ [Pre-flight] Checking system health...
40
+ [Pre-flight] System healthy, proceeding...
41
+ [Step 1] Processing order...
42
+ [Step 2] Charging payment...
43
+ [Step 3] Sending confirmation...
44
+ Continue? true
45
+ All steps executed successfully!
46
+
47
+
48
+ Example 4: Error Accumulation Before Halting
49
+ ------------------------------------------------------------
50
+
51
+ [Step 1] Collecting validation errors...
52
+ [Step 1] Found 2 validation error(s)
53
+
54
+ Continue? false
55
+ Errors found: 2
56
+ All errors: ["Email required", "Must be 18+"]
57
+ Note: Step 2 didn't execute because validation failed
58
+
59
+
60
+ Example 5: Sequential vs Parallel Behavior
61
+ ------------------------------------------------------------
62
+
63
+ Sequential execution order:
64
+ [Sequential 1] Step A
65
+ [Sequential 2] Step B (depends on A)
66
+ [Sequential 3] Step C (depends on B)
67
+
68
+ Parallel execution (for comparison):
69
+ [Parallel] Step A (no dependencies)
70
+ [Parallel] Step B (depends on A)
71
+ [Parallel] Step C (depends on A, runs parallel with B)
72
+
73
+ Note: Sequential steps have implicit dependencies
74
+ Parallel steps require explicit depends_on declarations
75
+
76
+ ============================================================
77
+ Sequential dependencies examples completed!
78
+ ============================================================
79
+
80
+ Key Takeaways:
81
+ • Unnamed steps execute sequentially in definition order
82
+ • Each step implicitly depends on the previous step's success
83
+ • Pipeline halts immediately when any step returns result.halt
84
+ • Subsequent steps after a halt are never executed
85
+ • Use named steps with depends_on for parallel execution
86
+
@@ -0,0 +1,64 @@
1
+ ============================================================
2
+ Reserved Dependency Symbols Usage
3
+ ============================================================
4
+
5
+ Example 1: Using :none Symbol for Better Readability
6
+ ------------------------------------------------------------
7
+
8
+ [Step 1] Validating input...
9
+ [Step 2a] Fetching orders in parallel...
10
+ [Step 2b] Fetching products in parallel...
11
+ [Step 3] Merging results...
12
+
13
+ Result: {orders: [1, 2, 3], products: [:a, :b, :c], total: 6}
14
+ Context: validated=true
15
+
16
+
17
+ Example 2: :none Symbol vs Empty Array
18
+ ------------------------------------------------------------
19
+
20
+ [:none syntax] Root step with no dependencies
21
+ [Array syntax] Root step with no dependencies
22
+
23
+ Both produce the same result: 6 == 6
24
+ The :none symbol is simply more readable!
25
+
26
+
27
+ Example 3: Multiple Independent Root Steps
28
+ ------------------------------------------------------------
29
+
30
+ [Root C] Authenticating API...
31
+ [Root B] Connecting to database...
32
+ [Root A] Loading configuration...
33
+ [Final] Initializing application with all resources...
34
+
35
+ Result: Application ready
36
+ All independent steps ran in parallel!
37
+
38
+
39
+ Example 4: Reserved Dependency Symbols
40
+ ------------------------------------------------------------
41
+
42
+ You can use :none or :nothing to indicate no dependencies:
43
+
44
+ • :step_a dependencies: []
45
+ • :step_b dependencies: []
46
+ • :step_c dependencies: [:step_a, :step_b] (filtered!)
47
+
48
+ Reserved symbols :none and :nothing:
49
+ • Automatically filtered from dependency arrays
50
+ • Functionally equivalent to []
51
+ • More semantically clear than empty array
52
+ • Cannot be used as step names (reserved)
53
+ • A signal to readers: 'this step has no dependencies'
54
+
55
+ ============================================================
56
+ Reserved dependency symbols examples completed!
57
+ ============================================================
58
+
59
+ Key Takeaways:
60
+ • Use depends_on: :none for better readability
61
+ • Equivalent to [] but more semantic
62
+ • Can mix in arrays: [:step_a, :none] becomes [:step_a]
63
+ • Makes dependency graphs easier to understand
64
+
@@ -0,0 +1,105 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Regression Test Runner
5
+ # Runs all example scripts and saves their output to regression_test/ directory
6
+ # for comparison purposes.
7
+
8
+ require 'fileutils'
9
+
10
+ class RegressionTestRunner
11
+ EXAMPLES_DIR = File.dirname(__FILE__)
12
+ OUTPUT_DIR = File.join(EXAMPLES_DIR, 'regression_test')
13
+
14
+ def initialize
15
+ @examples = Dir.glob(File.join(EXAMPLES_DIR, '*.rb'))
16
+ .reject { |f| File.basename(f) == 'regression_test.rb' }
17
+ .sort
18
+ end
19
+
20
+ def run
21
+ puts "Regression Test Runner"
22
+ puts "=" * 60
23
+ puts "Output directory: #{OUTPUT_DIR}"
24
+ puts "Found #{@examples.size} example files"
25
+ puts
26
+
27
+ ensure_output_directory
28
+
29
+ results = @examples.map { |example| run_example(example) }
30
+
31
+ print_summary(results)
32
+ end
33
+
34
+ private
35
+
36
+ def ensure_output_directory
37
+ FileUtils.mkdir_p(OUTPUT_DIR) unless Dir.exist?(OUTPUT_DIR)
38
+ end
39
+
40
+ def run_example(example_path)
41
+ basename = File.basename(example_path, '.rb')
42
+ output_path = File.join(OUTPUT_DIR, "#{basename}.txt")
43
+
44
+ print "Running #{basename}... "
45
+
46
+ start_time = Time.now
47
+ output, status = capture_output(example_path)
48
+ elapsed = Time.now - start_time
49
+
50
+ File.write(output_path, output)
51
+
52
+ if status.success?
53
+ puts "OK (#{format('%.2f', elapsed)}s) -> #{File.basename(output_path)}"
54
+ { name: basename, success: true, elapsed: elapsed, output_path: output_path }
55
+ else
56
+ puts "FAILED (exit code: #{status.exitstatus})"
57
+ { name: basename, success: false, elapsed: elapsed, output_path: output_path, exit_code: status.exitstatus }
58
+ end
59
+ end
60
+
61
+ def capture_output(example_path)
62
+ output = `cd #{EXAMPLES_DIR} && ruby #{File.basename(example_path)} 2>&1`
63
+ [output, $?]
64
+ end
65
+
66
+ def print_summary(results)
67
+ puts
68
+ puts "=" * 60
69
+ puts "SUMMARY"
70
+ puts "=" * 60
71
+
72
+ successful = results.count { |r| r[:success] }
73
+ failed = results.count { |r| !r[:success] }
74
+ total_time = results.sum { |r| r[:elapsed] }
75
+
76
+ puts "Total examples: #{results.size}"
77
+ puts "Successful: #{successful}"
78
+ puts "Failed: #{failed}"
79
+ puts "Total time: #{format('%.2f', total_time)}s"
80
+ puts
81
+
82
+ if failed > 0
83
+ puts "Failed examples:"
84
+ results.reject { |r| r[:success] }.each do |result|
85
+ puts " - #{result[:name]} (exit code: #{result[:exit_code]})"
86
+ end
87
+ puts
88
+ end
89
+
90
+ puts "Output files saved to: #{OUTPUT_DIR}/"
91
+ puts
92
+
93
+ # List generated files
94
+ puts "Generated files:"
95
+ results.each do |result|
96
+ file_size = File.size(result[:output_path])
97
+ puts " #{File.basename(result[:output_path])} (#{file_size} bytes)"
98
+ end
99
+ end
100
+ end
101
+
102
+ if __FILE__ == $PROGRAM_NAME
103
+ runner = RegressionTestRunner.new
104
+ runner.run
105
+ end