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.
- checksums.yaml +7 -0
- data/.envrc +1 -0
- data/.github/workflows/deploy-github-pages.yml +52 -0
- data/.rubocop.yml +57 -0
- data/CHANGELOG.md +4 -0
- data/COMMITS.md +196 -0
- data/LICENSE +21 -0
- data/README.md +481 -0
- data/Rakefile +15 -0
- data/benchmarks/parallel_vs_sequential.rb +98 -0
- data/benchmarks/pipeline_overhead.rb +130 -0
- data/docs/api/middleware.md +468 -0
- data/docs/api/parallel-step.md +363 -0
- data/docs/api/pipeline.md +382 -0
- data/docs/api/result.md +375 -0
- data/docs/concurrent/best-practices.md +687 -0
- data/docs/concurrent/introduction.md +246 -0
- data/docs/concurrent/parallel-steps.md +418 -0
- data/docs/concurrent/performance.md +481 -0
- data/docs/core-concepts/flow-control.md +452 -0
- data/docs/core-concepts/middleware.md +389 -0
- data/docs/core-concepts/overview.md +219 -0
- data/docs/core-concepts/pipeline.md +315 -0
- data/docs/core-concepts/result.md +168 -0
- data/docs/core-concepts/steps.md +391 -0
- data/docs/development/benchmarking.md +443 -0
- data/docs/development/contributing.md +380 -0
- data/docs/development/dagwood-concepts.md +435 -0
- data/docs/development/testing.md +514 -0
- data/docs/getting-started/examples.md +197 -0
- data/docs/getting-started/installation.md +62 -0
- data/docs/getting-started/quick-start.md +218 -0
- data/docs/guides/choosing-concurrency-model.md +441 -0
- data/docs/guides/complex-workflows.md +440 -0
- data/docs/guides/data-fetching.md +478 -0
- data/docs/guides/error-handling.md +635 -0
- data/docs/guides/file-processing.md +505 -0
- data/docs/guides/validation-patterns.md +496 -0
- data/docs/index.md +169 -0
- data/examples/.gitignore +3 -0
- data/examples/01_basic_pipeline.rb +112 -0
- data/examples/02_error_handling.rb +178 -0
- data/examples/03_middleware.rb +186 -0
- data/examples/04_parallel_automatic.rb +221 -0
- data/examples/05_parallel_explicit.rb +279 -0
- data/examples/06_real_world_ecommerce.rb +288 -0
- data/examples/07_real_world_etl.rb +277 -0
- data/examples/08_graph_visualization.rb +246 -0
- data/examples/09_pipeline_visualization.rb +266 -0
- data/examples/10_concurrency_control.rb +235 -0
- data/examples/11_sequential_dependencies.rb +243 -0
- data/examples/12_none_constant.rb +161 -0
- data/examples/README.md +374 -0
- data/examples/regression_test/01_basic_pipeline.txt +38 -0
- data/examples/regression_test/02_error_handling.txt +92 -0
- data/examples/regression_test/03_middleware.txt +61 -0
- data/examples/regression_test/04_parallel_automatic.txt +86 -0
- data/examples/regression_test/05_parallel_explicit.txt +80 -0
- data/examples/regression_test/06_real_world_ecommerce.txt +53 -0
- data/examples/regression_test/07_real_world_etl.txt +58 -0
- data/examples/regression_test/08_graph_visualization.txt +429 -0
- data/examples/regression_test/09_pipeline_visualization.txt +305 -0
- data/examples/regression_test/10_concurrency_control.txt +96 -0
- data/examples/regression_test/11_sequential_dependencies.txt +86 -0
- data/examples/regression_test/12_none_constant.txt +64 -0
- data/examples/regression_test.rb +105 -0
- data/lib/simple_flow/dependency_graph.rb +120 -0
- data/lib/simple_flow/dependency_graph_visualizer.rb +326 -0
- data/lib/simple_flow/middleware.rb +36 -0
- data/lib/simple_flow/parallel_executor.rb +80 -0
- data/lib/simple_flow/pipeline.rb +405 -0
- data/lib/simple_flow/result.rb +88 -0
- data/lib/simple_flow/step_tracker.rb +58 -0
- data/lib/simple_flow/version.rb +5 -0
- data/lib/simple_flow.rb +41 -0
- data/mkdocs.yml +146 -0
- data/pipeline_graph.dot +51 -0
- data/pipeline_graph.html +60 -0
- data/pipeline_graph.mmd +19 -0
- metadata +127 -0
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative '../lib/simple_flow'
|
|
5
|
+
require 'timecop'
|
|
6
|
+
Timecop.travel(Time.local(2001, 9, 11, 7, 0, 0))
|
|
7
|
+
|
|
8
|
+
# Sequential Step Dependencies Example
|
|
9
|
+
#
|
|
10
|
+
# This example demonstrates how unnamed steps automatically depend on the
|
|
11
|
+
# previous step's success, and how the pipeline short-circuits when a step halts.
|
|
12
|
+
|
|
13
|
+
puts "=" * 60
|
|
14
|
+
puts "Sequential Step Dependencies"
|
|
15
|
+
puts "=" * 60
|
|
16
|
+
puts
|
|
17
|
+
|
|
18
|
+
# Example 1: Successful sequential execution
|
|
19
|
+
puts "Example 1: All Steps Succeed"
|
|
20
|
+
puts "-" * 60
|
|
21
|
+
puts
|
|
22
|
+
|
|
23
|
+
successful_pipeline = SimpleFlow::Pipeline.new do
|
|
24
|
+
step ->(result) {
|
|
25
|
+
puts " [Step 1] Starting workflow..."
|
|
26
|
+
result.continue(result.value + 1)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
step ->(result) {
|
|
30
|
+
puts " [Step 2] Processing data..."
|
|
31
|
+
result.continue(result.value * 2)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
step ->(result) {
|
|
35
|
+
puts " [Step 3] Finalizing..."
|
|
36
|
+
result.continue(result.value + 10)
|
|
37
|
+
}
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
result1 = successful_pipeline.call(SimpleFlow::Result.new(5))
|
|
41
|
+
puts "\nFinal result: #{result1.value}"
|
|
42
|
+
puts "Continue? #{result1.continue?}"
|
|
43
|
+
puts "Expected: ((5 + 1) * 2) + 10 = 22"
|
|
44
|
+
puts
|
|
45
|
+
|
|
46
|
+
# Example 2: Pipeline halts in middle step
|
|
47
|
+
puts "\nExample 2: Pipeline Halts on Error"
|
|
48
|
+
puts "-" * 60
|
|
49
|
+
puts
|
|
50
|
+
|
|
51
|
+
halting_pipeline = SimpleFlow::Pipeline.new do
|
|
52
|
+
step ->(result) {
|
|
53
|
+
puts " [Step 1] Validating input..."
|
|
54
|
+
if result.value.nil?
|
|
55
|
+
return result.halt.with_error(:validation, "Input cannot be nil")
|
|
56
|
+
end
|
|
57
|
+
result.continue(result.value)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
step ->(result) {
|
|
61
|
+
puts " [Step 2] Checking business rules..."
|
|
62
|
+
if result.value < 0
|
|
63
|
+
return result.halt.with_error(:business_rule, "Value must be positive")
|
|
64
|
+
end
|
|
65
|
+
result.continue(result.value)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
step ->(result) {
|
|
69
|
+
puts " [Step 3] This step should NOT execute"
|
|
70
|
+
result.continue(result.value * 100)
|
|
71
|
+
}
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
result2 = halting_pipeline.call(SimpleFlow::Result.new(-5))
|
|
75
|
+
puts "\nFinal result: #{result2.value}"
|
|
76
|
+
puts "Continue? #{result2.continue?}"
|
|
77
|
+
puts "Errors: #{result2.errors}"
|
|
78
|
+
puts "Note: Step 3 never executed because Step 2 halted"
|
|
79
|
+
puts
|
|
80
|
+
|
|
81
|
+
# Example 3: Early validation pattern
|
|
82
|
+
puts "\nExample 3: Early Validation Pattern"
|
|
83
|
+
puts "-" * 60
|
|
84
|
+
puts
|
|
85
|
+
|
|
86
|
+
validation_pipeline = SimpleFlow::Pipeline.new do
|
|
87
|
+
step ->(result) {
|
|
88
|
+
puts " [Pre-flight] Checking system health..."
|
|
89
|
+
unless result.context[:system_healthy]
|
|
90
|
+
return result
|
|
91
|
+
.halt("System maintenance in progress")
|
|
92
|
+
.with_error(:system, "Maintenance mode active")
|
|
93
|
+
end
|
|
94
|
+
puts " [Pre-flight] System healthy, proceeding..."
|
|
95
|
+
result.continue(result.value)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
step ->(result) {
|
|
99
|
+
puts " [Step 1] Processing order..."
|
|
100
|
+
result.continue(result.value)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
step ->(result) {
|
|
104
|
+
puts " [Step 2] Charging payment..."
|
|
105
|
+
result.continue(result.value)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
step ->(result) {
|
|
109
|
+
puts " [Step 3] Sending confirmation..."
|
|
110
|
+
result.continue(result.value)
|
|
111
|
+
}
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Test with unhealthy system
|
|
115
|
+
puts "Scenario A: System in maintenance mode"
|
|
116
|
+
result3a = validation_pipeline.call(
|
|
117
|
+
SimpleFlow::Result.new({ order_id: 123 })
|
|
118
|
+
.with_context(:system_healthy, false)
|
|
119
|
+
)
|
|
120
|
+
puts "Continue? #{result3a.continue?}"
|
|
121
|
+
puts "Errors: #{result3a.errors}"
|
|
122
|
+
puts
|
|
123
|
+
|
|
124
|
+
# Test with healthy system
|
|
125
|
+
puts "\nScenario B: System healthy"
|
|
126
|
+
result3b = validation_pipeline.call(
|
|
127
|
+
SimpleFlow::Result.new({ order_id: 456 })
|
|
128
|
+
.with_context(:system_healthy, true)
|
|
129
|
+
)
|
|
130
|
+
puts "Continue? #{result3b.continue?}"
|
|
131
|
+
puts "All steps executed successfully!"
|
|
132
|
+
puts
|
|
133
|
+
|
|
134
|
+
# Example 4: Multi-step validation with error accumulation
|
|
135
|
+
puts "\nExample 4: Error Accumulation Before Halting"
|
|
136
|
+
puts "-" * 60
|
|
137
|
+
puts
|
|
138
|
+
|
|
139
|
+
multi_validation_pipeline = SimpleFlow::Pipeline.new do
|
|
140
|
+
step ->(result) {
|
|
141
|
+
puts " [Step 1] Collecting validation errors..."
|
|
142
|
+
data = result.value
|
|
143
|
+
result_with_errors = result
|
|
144
|
+
|
|
145
|
+
# Accumulate all validation errors
|
|
146
|
+
if data[:email].nil? || data[:email].empty?
|
|
147
|
+
result_with_errors = result_with_errors.with_error(:validation, "Email required")
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
if data[:name].nil? || data[:name].empty?
|
|
151
|
+
result_with_errors = result_with_errors.with_error(:validation, "Name required")
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
if data[:age] && data[:age] < 18
|
|
155
|
+
result_with_errors = result_with_errors.with_error(:validation, "Must be 18+")
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# Halt only if we found errors
|
|
159
|
+
if result_with_errors.errors.any?
|
|
160
|
+
puts " [Step 1] Found #{result_with_errors.errors[:validation].size} validation error(s)"
|
|
161
|
+
return result_with_errors.halt
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
puts " [Step 1] Validation passed"
|
|
165
|
+
result_with_errors.continue(data)
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
step ->(result) {
|
|
169
|
+
puts " [Step 2] Processing valid data..."
|
|
170
|
+
result.continue("Processed: #{result.value}")
|
|
171
|
+
}
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
invalid_data = { email: "", name: "John", age: 16 }
|
|
175
|
+
result4 = multi_validation_pipeline.call(SimpleFlow::Result.new(invalid_data))
|
|
176
|
+
puts "\nContinue? #{result4.continue?}"
|
|
177
|
+
puts "Errors found: #{result4.errors[:validation].size}"
|
|
178
|
+
puts "All errors: #{result4.errors[:validation]}"
|
|
179
|
+
puts "Note: Step 2 didn't execute because validation failed"
|
|
180
|
+
puts
|
|
181
|
+
|
|
182
|
+
# Example 5: Comparing sequential vs parallel execution
|
|
183
|
+
puts "\nExample 5: Sequential vs Parallel Behavior"
|
|
184
|
+
puts "-" * 60
|
|
185
|
+
puts
|
|
186
|
+
|
|
187
|
+
# Sequential pipeline - each step depends on previous
|
|
188
|
+
sequential = SimpleFlow::Pipeline.new do
|
|
189
|
+
step ->(result) {
|
|
190
|
+
puts " [Sequential 1] Step A"
|
|
191
|
+
result.continue(result.value)
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
step ->(result) {
|
|
195
|
+
puts " [Sequential 2] Step B (depends on A)"
|
|
196
|
+
result.continue(result.value)
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
step ->(result) {
|
|
200
|
+
puts " [Sequential 3] Step C (depends on B)"
|
|
201
|
+
result.continue(result.value)
|
|
202
|
+
}
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
puts "Sequential execution order:"
|
|
206
|
+
sequential.call(SimpleFlow::Result.new(nil))
|
|
207
|
+
|
|
208
|
+
puts "\nParallel execution (for comparison):"
|
|
209
|
+
|
|
210
|
+
# Parallel pipeline - explicit dependencies
|
|
211
|
+
parallel = SimpleFlow::Pipeline.new do
|
|
212
|
+
step :step_a, ->(result) {
|
|
213
|
+
puts " [Parallel] Step A (no dependencies)"
|
|
214
|
+
result.continue(result.value)
|
|
215
|
+
}, depends_on: :none
|
|
216
|
+
|
|
217
|
+
step :step_b, ->(result) {
|
|
218
|
+
puts " [Parallel] Step B (depends on A)"
|
|
219
|
+
result.continue(result.value)
|
|
220
|
+
}, depends_on: [:step_a]
|
|
221
|
+
|
|
222
|
+
step :step_c, ->(result) {
|
|
223
|
+
puts " [Parallel] Step C (depends on A, runs parallel with B)"
|
|
224
|
+
result.continue(result.value)
|
|
225
|
+
}, depends_on: [:step_a]
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
parallel.call_parallel(SimpleFlow::Result.new(nil))
|
|
229
|
+
|
|
230
|
+
puts "\nNote: Sequential steps have implicit dependencies"
|
|
231
|
+
puts " Parallel steps require explicit depends_on declarations"
|
|
232
|
+
|
|
233
|
+
puts "\n" + "=" * 60
|
|
234
|
+
puts "Sequential dependencies examples completed!"
|
|
235
|
+
puts "=" * 60
|
|
236
|
+
puts
|
|
237
|
+
puts "Key Takeaways:"
|
|
238
|
+
puts " • Unnamed steps execute sequentially in definition order"
|
|
239
|
+
puts " • Each step implicitly depends on the previous step's success"
|
|
240
|
+
puts " • Pipeline halts immediately when any step returns result.halt"
|
|
241
|
+
puts " • Subsequent steps after a halt are never executed"
|
|
242
|
+
puts " • Use named steps with depends_on for parallel execution"
|
|
243
|
+
puts
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative '../lib/simple_flow'
|
|
5
|
+
require 'timecop'
|
|
6
|
+
Timecop.travel(Time.local(2001, 9, 11, 7, 0, 0))
|
|
7
|
+
|
|
8
|
+
# Using Reserved Dependency Symbols for Better Readability
|
|
9
|
+
#
|
|
10
|
+
# This example demonstrates the use of :none and :nothing reserved symbols
|
|
11
|
+
# for defining steps with no dependencies, providing better readability
|
|
12
|
+
# compared to using an empty array.
|
|
13
|
+
|
|
14
|
+
puts "=" * 60
|
|
15
|
+
puts "Reserved Dependency Symbols Usage"
|
|
16
|
+
puts "=" * 60
|
|
17
|
+
puts
|
|
18
|
+
|
|
19
|
+
# Example 1: Using :none symbol for clarity
|
|
20
|
+
puts "Example 1: Using :none Symbol for Better Readability"
|
|
21
|
+
puts "-" * 60
|
|
22
|
+
puts
|
|
23
|
+
|
|
24
|
+
pipeline = SimpleFlow::Pipeline.new do
|
|
25
|
+
# More readable: explicitly shows "no dependencies" using :none
|
|
26
|
+
step :validate, ->(result) {
|
|
27
|
+
puts " [Step 1] Validating input..."
|
|
28
|
+
result.with_context(:validated, true).continue(result.value)
|
|
29
|
+
}, depends_on: :none # Much cleaner than []!
|
|
30
|
+
|
|
31
|
+
# These two steps run in parallel (both depend only on :validate)
|
|
32
|
+
step :fetch_orders, ->(result) {
|
|
33
|
+
puts " [Step 2a] Fetching orders in parallel..."
|
|
34
|
+
sleep 0.1
|
|
35
|
+
result.with_context(:orders, [1, 2, 3]).continue(result.value)
|
|
36
|
+
}, depends_on: [:validate]
|
|
37
|
+
|
|
38
|
+
step :fetch_products, ->(result) {
|
|
39
|
+
puts " [Step 2b] Fetching products in parallel..."
|
|
40
|
+
sleep 0.1
|
|
41
|
+
result.with_context(:products, [:a, :b, :c]).continue(result.value)
|
|
42
|
+
}, depends_on: [:validate]
|
|
43
|
+
|
|
44
|
+
# This step waits for both parallel steps to complete
|
|
45
|
+
step :merge_data, ->(result) {
|
|
46
|
+
puts " [Step 3] Merging results..."
|
|
47
|
+
orders = result.context[:orders]
|
|
48
|
+
products = result.context[:products]
|
|
49
|
+
result.continue({
|
|
50
|
+
orders: orders,
|
|
51
|
+
products: products,
|
|
52
|
+
total: orders.size + products.size
|
|
53
|
+
})
|
|
54
|
+
}, depends_on: [:fetch_orders, :fetch_products]
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
result = pipeline.call_parallel(SimpleFlow::Result.new({ user_id: 123 }))
|
|
58
|
+
puts "\nResult: #{result.value}"
|
|
59
|
+
puts "Context: validated=#{result.context[:validated]}"
|
|
60
|
+
puts
|
|
61
|
+
|
|
62
|
+
# Example 2: Comparison with empty array syntax
|
|
63
|
+
puts "\nExample 2: :none Symbol vs Empty Array"
|
|
64
|
+
puts "-" * 60
|
|
65
|
+
puts
|
|
66
|
+
|
|
67
|
+
# Using :none symbol (recommended for readability)
|
|
68
|
+
pipeline_with_none = SimpleFlow::Pipeline.new do
|
|
69
|
+
step :root, ->(result) {
|
|
70
|
+
puts " [:none syntax] Root step with no dependencies"
|
|
71
|
+
result.continue(result.value + 1)
|
|
72
|
+
}, depends_on: :none # Clean and readable!
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Using empty array (functionally equivalent)
|
|
76
|
+
pipeline_with_array = SimpleFlow::Pipeline.new do
|
|
77
|
+
step :root, ->(result) {
|
|
78
|
+
puts " [Array syntax] Root step with no dependencies"
|
|
79
|
+
result.continue(result.value + 1)
|
|
80
|
+
}, depends_on: [] # Works, but less semantic
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
result1 = pipeline_with_none.call_parallel(SimpleFlow::Result.new(5))
|
|
84
|
+
result2 = pipeline_with_array.call_parallel(SimpleFlow::Result.new(5))
|
|
85
|
+
|
|
86
|
+
puts "\nBoth produce the same result: #{result1.value} == #{result2.value}"
|
|
87
|
+
puts "The :none symbol is simply more readable!"
|
|
88
|
+
puts
|
|
89
|
+
|
|
90
|
+
# Example 3: Multiple independent root steps
|
|
91
|
+
puts "\nExample 3: Multiple Independent Root Steps"
|
|
92
|
+
puts "-" * 60
|
|
93
|
+
puts
|
|
94
|
+
|
|
95
|
+
complex_pipeline = SimpleFlow::Pipeline.new do
|
|
96
|
+
# Three independent root steps - all use :none for clarity
|
|
97
|
+
step :load_config, ->(result) {
|
|
98
|
+
puts " [Root A] Loading configuration..."
|
|
99
|
+
result.with_context(:config, { timeout: 30 }).continue(result.value)
|
|
100
|
+
}, depends_on: :none
|
|
101
|
+
|
|
102
|
+
step :connect_database, ->(result) {
|
|
103
|
+
puts " [Root B] Connecting to database..."
|
|
104
|
+
result.with_context(:db, :connected).continue(result.value)
|
|
105
|
+
}, depends_on: :none
|
|
106
|
+
|
|
107
|
+
step :authenticate_api, ->(result) {
|
|
108
|
+
puts " [Root C] Authenticating API..."
|
|
109
|
+
result.with_context(:api_token, "abc123").continue(result.value)
|
|
110
|
+
}, depends_on: :none
|
|
111
|
+
|
|
112
|
+
# This step depends on all three independent root steps
|
|
113
|
+
step :initialize_app, ->(result) {
|
|
114
|
+
puts " [Final] Initializing application with all resources..."
|
|
115
|
+
result.continue("Application ready")
|
|
116
|
+
}, depends_on: [:load_config, :connect_database, :authenticate_api]
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
result3 = complex_pipeline.call_parallel(SimpleFlow::Result.new(nil))
|
|
120
|
+
puts "\nResult: #{result3.value}"
|
|
121
|
+
puts "All independent steps ran in parallel!"
|
|
122
|
+
puts
|
|
123
|
+
|
|
124
|
+
# Example 4: About the :none and :nothing symbols
|
|
125
|
+
puts "\nExample 4: Reserved Dependency Symbols"
|
|
126
|
+
puts "-" * 60
|
|
127
|
+
puts
|
|
128
|
+
|
|
129
|
+
puts "You can use :none or :nothing to indicate no dependencies:"
|
|
130
|
+
puts
|
|
131
|
+
|
|
132
|
+
pipeline_examples = SimpleFlow::Pipeline.new do
|
|
133
|
+
step :step_a, ->(result) { result.continue(1) }, depends_on: :none
|
|
134
|
+
step :step_b, ->(result) { result.continue(2) }, depends_on: :nothing
|
|
135
|
+
step :step_c, ->(result) { result.continue(3) }, depends_on: [:step_a, :none, :step_b]
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
graph = pipeline_examples.dependency_graph
|
|
139
|
+
puts " • :step_a dependencies: #{graph.dependencies[:step_a].inspect}"
|
|
140
|
+
puts " • :step_b dependencies: #{graph.dependencies[:step_b].inspect}"
|
|
141
|
+
puts " • :step_c dependencies: #{graph.dependencies[:step_c].inspect} (filtered!)"
|
|
142
|
+
puts
|
|
143
|
+
|
|
144
|
+
puts "Reserved symbols :none and :nothing:"
|
|
145
|
+
puts " • Automatically filtered from dependency arrays"
|
|
146
|
+
puts " • Functionally equivalent to []"
|
|
147
|
+
puts " • More semantically clear than empty array"
|
|
148
|
+
puts " • Cannot be used as step names (reserved)"
|
|
149
|
+
puts " • A signal to readers: 'this step has no dependencies'"
|
|
150
|
+
puts
|
|
151
|
+
|
|
152
|
+
puts "=" * 60
|
|
153
|
+
puts "Reserved dependency symbols examples completed!"
|
|
154
|
+
puts "=" * 60
|
|
155
|
+
puts
|
|
156
|
+
puts "Key Takeaways:"
|
|
157
|
+
puts " • Use depends_on: :none for better readability"
|
|
158
|
+
puts " • Equivalent to [] but more semantic"
|
|
159
|
+
puts " • Can mix in arrays: [:step_a, :none] becomes [:step_a]"
|
|
160
|
+
puts " • Makes dependency graphs easier to understand"
|
|
161
|
+
puts
|