taski 0.3.1 → 0.4.1
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 +4 -4
- data/README.md +101 -271
- data/Steepfile +19 -0
- data/docs/advanced-features.md +625 -0
- data/docs/api-guide.md +509 -0
- data/docs/error-handling.md +684 -0
- data/examples/README.md +98 -42
- data/examples/context_demo.rb +118 -0
- data/examples/data_pipeline_demo.rb +231 -0
- data/examples/parallel_progress_demo.rb +72 -0
- data/examples/quick_start.rb +4 -4
- data/examples/reexecution_demo.rb +127 -0
- data/examples/{section_configuration.rb → section_demo.rb} +49 -60
- data/lib/taski/context.rb +50 -0
- data/lib/taski/execution/coordinator.rb +63 -0
- data/lib/taski/execution/parallel_progress_display.rb +201 -0
- data/lib/taski/execution/registry.rb +72 -0
- data/lib/taski/execution/task_wrapper.rb +255 -0
- data/lib/taski/section.rb +26 -254
- data/lib/taski/static_analysis/analyzer.rb +46 -0
- data/lib/taski/static_analysis/dependency_graph.rb +90 -0
- data/lib/taski/static_analysis/visitor.rb +130 -0
- data/lib/taski/task.rb +199 -0
- data/lib/taski/version.rb +1 -1
- data/lib/taski.rb +68 -39
- data/rbs_collection.lock.yaml +116 -0
- data/rbs_collection.yaml +19 -0
- data/sig/taski.rbs +269 -62
- metadata +36 -32
- data/examples/advanced_patterns.rb +0 -119
- data/examples/progress_demo.rb +0 -166
- data/examples/tree_demo.rb +0 -205
- data/lib/taski/dependency_analyzer.rb +0 -232
- data/lib/taski/exceptions.rb +0 -17
- data/lib/taski/logger.rb +0 -158
- data/lib/taski/logging/formatter_factory.rb +0 -34
- data/lib/taski/logging/formatter_interface.rb +0 -19
- data/lib/taski/logging/json_formatter.rb +0 -26
- data/lib/taski/logging/simple_formatter.rb +0 -16
- data/lib/taski/logging/structured_formatter.rb +0 -44
- data/lib/taski/progress/display_colors.rb +0 -17
- data/lib/taski/progress/display_manager.rb +0 -117
- data/lib/taski/progress/output_capture.rb +0 -105
- data/lib/taski/progress/spinner_animation.rb +0 -49
- data/lib/taski/progress/task_formatter.rb +0 -25
- data/lib/taski/progress/task_status.rb +0 -38
- data/lib/taski/progress/terminal_controller.rb +0 -35
- data/lib/taski/progress_display.rb +0 -57
- data/lib/taski/reference.rb +0 -40
- data/lib/taski/task/base.rb +0 -91
- data/lib/taski/task/define_api.rb +0 -156
- data/lib/taski/task/dependency_resolver.rb +0 -73
- data/lib/taski/task/exports_api.rb +0 -29
- data/lib/taski/task/instance_management.rb +0 -201
- data/lib/taski/tree_colors.rb +0 -91
- data/lib/taski/utils/dependency_resolver_helper.rb +0 -85
- data/lib/taski/utils/tree_display_helper.rb +0 -68
- data/lib/taski/utils.rb +0 -107
|
@@ -0,0 +1,625 @@
|
|
|
1
|
+
# Advanced Features
|
|
2
|
+
|
|
3
|
+
This guide covers Taski's advanced features including progress display, signal handling, logging, and lifecycle management.
|
|
4
|
+
|
|
5
|
+
## Progress Display
|
|
6
|
+
|
|
7
|
+
Taski provides rich visual feedback during task execution with animated spinners and real-time output capture.
|
|
8
|
+
|
|
9
|
+
### Basic Progress Display
|
|
10
|
+
|
|
11
|
+
```ruby
|
|
12
|
+
class LongRunningTask < Taski::Task
|
|
13
|
+
def run
|
|
14
|
+
puts "Starting process..."
|
|
15
|
+
sleep(1.0)
|
|
16
|
+
puts "Processing data..."
|
|
17
|
+
puts "Almost done..."
|
|
18
|
+
sleep(0.5)
|
|
19
|
+
puts "Completed!"
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
LongRunningTask.run
|
|
24
|
+
# During execution shows:
|
|
25
|
+
# ⠧ LongRunningTask
|
|
26
|
+
# Starting process...
|
|
27
|
+
# Processing data...
|
|
28
|
+
# Almost done...
|
|
29
|
+
# Completed!
|
|
30
|
+
#
|
|
31
|
+
# After completion shows:
|
|
32
|
+
# ✅ LongRunningTask (1500ms)
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Progress Display Features
|
|
36
|
+
|
|
37
|
+
- **Spinner Animation**: Dots-style spinner during task execution
|
|
38
|
+
- **Output Capture**: Real-time display of task output (last 5 lines)
|
|
39
|
+
- **Status Indicators**: ✅ for success, ❌ for failure
|
|
40
|
+
- **Execution Time**: Shows task duration after completion
|
|
41
|
+
- **TTY Detection**: Clean output when redirected to files
|
|
42
|
+
|
|
43
|
+
### Complex Build Pipeline Example
|
|
44
|
+
|
|
45
|
+
```ruby
|
|
46
|
+
class CompileAssets < Taski::Task
|
|
47
|
+
def run
|
|
48
|
+
puts "Compiling SCSS..."
|
|
49
|
+
sleep(0.8)
|
|
50
|
+
puts "Processing JavaScript..."
|
|
51
|
+
sleep(1.2)
|
|
52
|
+
puts "Optimizing images..."
|
|
53
|
+
sleep(0.5)
|
|
54
|
+
puts "Assets compiled successfully"
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
class RunTests < Taski::Task
|
|
59
|
+
def run
|
|
60
|
+
puts "Running unit tests..."
|
|
61
|
+
sleep(2.0)
|
|
62
|
+
puts "Running integration tests..."
|
|
63
|
+
sleep(1.5)
|
|
64
|
+
puts "All tests passed"
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
class DeployApp < Taski::Task
|
|
69
|
+
def run
|
|
70
|
+
puts "Building Docker image..."
|
|
71
|
+
CompileAssets.run
|
|
72
|
+
puts "Running test suite..."
|
|
73
|
+
RunTests.run
|
|
74
|
+
puts "Deploying to production..."
|
|
75
|
+
sleep(1.0)
|
|
76
|
+
puts "Deployment complete"
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
DeployApp.run
|
|
81
|
+
# Shows nested progress with clear hierarchy and timing
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### File Output Mode
|
|
85
|
+
|
|
86
|
+
When output is redirected to a file, Taski automatically disables the interactive spinner:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
# Interactive mode with spinner
|
|
90
|
+
ruby build.rb
|
|
91
|
+
|
|
92
|
+
# Clean output mode for logging
|
|
93
|
+
ruby build.rb > build.log 2>&1
|
|
94
|
+
cat build.log
|
|
95
|
+
# ✅ CompileAssets (800ms)
|
|
96
|
+
# ✅ RunTests (3500ms)
|
|
97
|
+
# ✅ DeployApp (6200ms)
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Signal Handling
|
|
101
|
+
|
|
102
|
+
Taski supports graceful task interruption with comprehensive signal handling.
|
|
103
|
+
|
|
104
|
+
### Basic Signal Handling
|
|
105
|
+
|
|
106
|
+
```ruby
|
|
107
|
+
class LongRunningTask < Taski::Task
|
|
108
|
+
def run
|
|
109
|
+
puts "Starting long operation..."
|
|
110
|
+
perform_long_operation
|
|
111
|
+
rescue Taski::TaskInterruptedException => e
|
|
112
|
+
puts "Task was interrupted: #{e.message}"
|
|
113
|
+
cleanup_resources
|
|
114
|
+
raise # Re-raise to maintain proper error handling
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
private
|
|
118
|
+
|
|
119
|
+
def perform_long_operation
|
|
120
|
+
# Long running operation that can be interrupted
|
|
121
|
+
100.times do |i|
|
|
122
|
+
puts "Processing item #{i + 1}/100..."
|
|
123
|
+
sleep(0.1) # Simulated work
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def cleanup_resources
|
|
128
|
+
puts "Cleaning up temporary files..."
|
|
129
|
+
puts "Closing database connections..."
|
|
130
|
+
puts "Cleanup complete"
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Task can be interrupted with Ctrl+C (SIGINT) or SIGTERM
|
|
135
|
+
LongRunningTask.run
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Signal Handling Features
|
|
139
|
+
|
|
140
|
+
- **Multiple Signals**: Supports INT, TERM, USR1, USR2
|
|
141
|
+
- **Exception Conversion**: Converts signals to TaskInterruptedException
|
|
142
|
+
- **Test Environment**: Automatically disabled in test environments
|
|
143
|
+
- **Graceful Shutdown**: Allows cleanup before termination
|
|
144
|
+
|
|
145
|
+
### Advanced Signal Handling
|
|
146
|
+
|
|
147
|
+
```ruby
|
|
148
|
+
class DatabaseMigration < Taski::Task
|
|
149
|
+
def run
|
|
150
|
+
puts "Starting database migration..."
|
|
151
|
+
|
|
152
|
+
begin
|
|
153
|
+
migrate_tables
|
|
154
|
+
rescue Taski::TaskInterruptedException => e
|
|
155
|
+
puts "Migration interrupted: #{e.message}"
|
|
156
|
+
rollback_partial_changes
|
|
157
|
+
raise
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
puts "Migration completed successfully"
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
private
|
|
164
|
+
|
|
165
|
+
def migrate_tables
|
|
166
|
+
%w[users orders products].each do |table|
|
|
167
|
+
puts "Migrating #{table} table..."
|
|
168
|
+
sleep(2) # Simulated migration time
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def rollback_partial_changes
|
|
173
|
+
puts "Rolling back partial migration..."
|
|
174
|
+
puts "Database restored to consistent state"
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## Error Recovery
|
|
180
|
+
|
|
181
|
+
Taski provides sophisticated error recovery mechanisms for handling dependency failures.
|
|
182
|
+
|
|
183
|
+
### Basic Error Recovery
|
|
184
|
+
|
|
185
|
+
```ruby
|
|
186
|
+
class DatabaseTask < Taski::Task
|
|
187
|
+
exports :connection
|
|
188
|
+
|
|
189
|
+
def run
|
|
190
|
+
# This might fail in test environments
|
|
191
|
+
@connection = connect_to_database
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
private
|
|
195
|
+
|
|
196
|
+
def connect_to_database
|
|
197
|
+
raise "Database connection failed" if ENV['FAIL_DB'] == 'true'
|
|
198
|
+
"postgresql://localhost/myapp"
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
class AppTask < Taski::Task
|
|
203
|
+
# Rescue dependency errors and provide fallback
|
|
204
|
+
rescue_deps StandardError, -> { "fallback://localhost/sqlite" }
|
|
205
|
+
|
|
206
|
+
def run
|
|
207
|
+
# Uses DatabaseTask.connection, or fallback if DatabaseTask fails
|
|
208
|
+
connection = DatabaseTask.connection
|
|
209
|
+
puts "Using connection: #{connection}"
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
ENV['FAIL_DB'] = 'true'
|
|
214
|
+
AppTask.run
|
|
215
|
+
# => Using connection: fallback://localhost/sqlite
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### Multiple Rescue Strategies
|
|
219
|
+
|
|
220
|
+
```ruby
|
|
221
|
+
class ExternalApiTask < Taski::Task
|
|
222
|
+
exports :data
|
|
223
|
+
|
|
224
|
+
def run
|
|
225
|
+
@data = fetch_from_api
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
private
|
|
229
|
+
|
|
230
|
+
def fetch_from_api
|
|
231
|
+
raise "API unavailable" if ENV['API_DOWN'] == 'true'
|
|
232
|
+
{ users: 100, orders: 50 }
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
class CacheTask < Taski::Task
|
|
237
|
+
exports :cached_data
|
|
238
|
+
|
|
239
|
+
def run
|
|
240
|
+
@cached_data = load_from_cache
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
private
|
|
244
|
+
|
|
245
|
+
def load_from_cache
|
|
246
|
+
raise "Cache miss" if ENV['CACHE_MISS'] == 'true'
|
|
247
|
+
{ users: 95, orders: 48 } # Slightly stale data
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
class DataAggregator < Taski::Task
|
|
252
|
+
# Try API first, then cache, then static fallback
|
|
253
|
+
rescue_deps StandardError, -> { ExternalApiTask.data }
|
|
254
|
+
rescue_deps StandardError, -> { CacheTask.cached_data }
|
|
255
|
+
rescue_deps StandardError, -> { { users: 0, orders: 0 } }
|
|
256
|
+
|
|
257
|
+
def run
|
|
258
|
+
data = ExternalApiTask.data
|
|
259
|
+
puts "Data aggregated: #{data}"
|
|
260
|
+
end
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
# Test different failure scenarios
|
|
264
|
+
ENV['API_DOWN'] = 'true'
|
|
265
|
+
DataAggregator.run
|
|
266
|
+
# => Data aggregated: {users: 95, orders: 48} # Falls back to cache
|
|
267
|
+
|
|
268
|
+
ENV['CACHE_MISS'] = 'true'
|
|
269
|
+
DataAggregator.reset!
|
|
270
|
+
DataAggregator.run
|
|
271
|
+
# => Data aggregated: {users: 0, orders: 0} # Falls back to static data
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### Error Recovery Features
|
|
275
|
+
|
|
276
|
+
- **Dependency Rescue**: Automatic fallback for failed dependencies
|
|
277
|
+
- **Custom Handlers**: Lambda-based error handling
|
|
278
|
+
- **Chain of Responsibility**: Multiple rescue strategies
|
|
279
|
+
- **Transparent Fallback**: Seamless error recovery
|
|
280
|
+
|
|
281
|
+
## Advanced Logging
|
|
282
|
+
|
|
283
|
+
Taski provides comprehensive logging with multiple formats and detailed performance monitoring.
|
|
284
|
+
|
|
285
|
+
### Logging Configuration
|
|
286
|
+
|
|
287
|
+
```ruby
|
|
288
|
+
# Configure logging level
|
|
289
|
+
Taski.logger.level = Logger::DEBUG
|
|
290
|
+
|
|
291
|
+
# Configure logging format
|
|
292
|
+
Taski.logger.formatter = Taski::Logging::StructuredFormatter.new
|
|
293
|
+
|
|
294
|
+
# Available formatters:
|
|
295
|
+
# - SimpleFormatter (default)
|
|
296
|
+
# - StructuredFormatter
|
|
297
|
+
# - JsonFormatter
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
### Structured Logging
|
|
301
|
+
|
|
302
|
+
```ruby
|
|
303
|
+
class MonitoredTask < Taski::Task
|
|
304
|
+
def run
|
|
305
|
+
Taski.logger.info "Task starting",
|
|
306
|
+
task: self.class.name,
|
|
307
|
+
environment: ENV['RAILS_ENV']
|
|
308
|
+
|
|
309
|
+
perform_work
|
|
310
|
+
|
|
311
|
+
Taski.logger.info "Task completed",
|
|
312
|
+
duration: "1.2s",
|
|
313
|
+
success: true
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
private
|
|
317
|
+
|
|
318
|
+
def perform_work
|
|
319
|
+
Taski.logger.debug "Processing data",
|
|
320
|
+
items_count: 100,
|
|
321
|
+
batch_size: 10
|
|
322
|
+
|
|
323
|
+
# Simulated work
|
|
324
|
+
sleep(1.2)
|
|
325
|
+
end
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
MonitoredTask.run
|
|
329
|
+
# Structured output:
|
|
330
|
+
# [INFO] Task starting (task=MonitoredTask, environment=development)
|
|
331
|
+
# [DEBUG] Processing data (items_count=100, batch_size=10)
|
|
332
|
+
# [INFO] Task completed (duration=1.2s, success=true)
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
### JSON Logging
|
|
336
|
+
|
|
337
|
+
```ruby
|
|
338
|
+
# Configure JSON formatter for machine-readable logs
|
|
339
|
+
Taski.logger.formatter = Taski::Logging::JsonFormatter.new
|
|
340
|
+
|
|
341
|
+
class ApiTask < Taski::Task
|
|
342
|
+
exports :response_data
|
|
343
|
+
|
|
344
|
+
def run
|
|
345
|
+
Taski.logger.info "API request starting",
|
|
346
|
+
endpoint: "/api/users",
|
|
347
|
+
method: "GET"
|
|
348
|
+
|
|
349
|
+
@response_data = { users: 42 }
|
|
350
|
+
|
|
351
|
+
Taski.logger.info "API request completed",
|
|
352
|
+
endpoint: "/api/users",
|
|
353
|
+
status: 200,
|
|
354
|
+
response_size: @response_data.to_json.size
|
|
355
|
+
end
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
ApiTask.run
|
|
359
|
+
# JSON output:
|
|
360
|
+
# {"timestamp":"2024-01-01T12:00:00Z","level":"INFO","message":"API request starting","endpoint":"/api/users","method":"GET"}
|
|
361
|
+
# {"timestamp":"2024-01-01T12:00:01Z","level":"INFO","message":"API request completed","endpoint":"/api/users","status":200,"response_size":12}
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
### Performance Monitoring
|
|
365
|
+
|
|
366
|
+
```ruby
|
|
367
|
+
class PerformanceTask < Taski::Task
|
|
368
|
+
def run
|
|
369
|
+
# Automatic performance logging is built-in
|
|
370
|
+
slow_operation
|
|
371
|
+
fast_operation
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
private
|
|
375
|
+
|
|
376
|
+
def slow_operation
|
|
377
|
+
Taski.logger.debug "Starting slow operation"
|
|
378
|
+
sleep(2.0)
|
|
379
|
+
Taski.logger.debug "Slow operation completed"
|
|
380
|
+
end
|
|
381
|
+
|
|
382
|
+
def fast_operation
|
|
383
|
+
Taski.logger.debug "Starting fast operation"
|
|
384
|
+
sleep(0.1)
|
|
385
|
+
Taski.logger.debug "Fast operation completed"
|
|
386
|
+
end
|
|
387
|
+
end
|
|
388
|
+
|
|
389
|
+
PerformanceTask.run
|
|
390
|
+
# Automatic timing logs:
|
|
391
|
+
# [INFO] Task build started (task=PerformanceTask, dependencies=0)
|
|
392
|
+
# [DEBUG] Starting slow operation
|
|
393
|
+
# [DEBUG] Slow operation completed
|
|
394
|
+
# [DEBUG] Starting fast operation
|
|
395
|
+
# [DEBUG] Fast operation completed
|
|
396
|
+
# [INFO] Task build completed (task=PerformanceTask, duration_ms=2100)
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
## Lifecycle Management
|
|
400
|
+
|
|
401
|
+
Taski supports comprehensive lifecycle management with run and clean methods.
|
|
402
|
+
|
|
403
|
+
### Basic Lifecycle
|
|
404
|
+
|
|
405
|
+
```ruby
|
|
406
|
+
class DatabaseSetup < Taski::Task
|
|
407
|
+
exports :connection
|
|
408
|
+
|
|
409
|
+
def run
|
|
410
|
+
@connection = "postgresql://localhost:5432/myapp"
|
|
411
|
+
puts "Database connected"
|
|
412
|
+
end
|
|
413
|
+
|
|
414
|
+
def clean
|
|
415
|
+
puts "Database disconnected"
|
|
416
|
+
# Cleanup logic here
|
|
417
|
+
end
|
|
418
|
+
end
|
|
419
|
+
|
|
420
|
+
class WebServer < Taski::Task
|
|
421
|
+
def run
|
|
422
|
+
puts "Web server started with #{DatabaseSetup.connection}"
|
|
423
|
+
end
|
|
424
|
+
|
|
425
|
+
def clean
|
|
426
|
+
puts "Web server stopped"
|
|
427
|
+
end
|
|
428
|
+
end
|
|
429
|
+
|
|
430
|
+
# Start everything
|
|
431
|
+
WebServer.run
|
|
432
|
+
# => Database connected
|
|
433
|
+
# => Web server started with postgresql://localhost:5432/myapp
|
|
434
|
+
|
|
435
|
+
# Clean everything in reverse order
|
|
436
|
+
WebServer.clean
|
|
437
|
+
# => Web server stopped
|
|
438
|
+
# => Database disconnected
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
### Advanced Lifecycle with Resource Management
|
|
442
|
+
|
|
443
|
+
```ruby
|
|
444
|
+
class FileProcessor < Taski::Task
|
|
445
|
+
exports :output_file, :temp_dir
|
|
446
|
+
|
|
447
|
+
def run
|
|
448
|
+
@temp_dir = "/tmp/processing_#{Time.now.to_i}"
|
|
449
|
+
Dir.mkdir(@temp_dir)
|
|
450
|
+
puts "Created temp directory: #{@temp_dir}"
|
|
451
|
+
|
|
452
|
+
@output_file = "#{@temp_dir}/result.txt"
|
|
453
|
+
File.write(@output_file, "Processing complete")
|
|
454
|
+
puts "Created output file: #{@output_file}"
|
|
455
|
+
end
|
|
456
|
+
|
|
457
|
+
def clean
|
|
458
|
+
if @output_file && File.exist?(@output_file)
|
|
459
|
+
File.delete(@output_file)
|
|
460
|
+
puts "Deleted output file: #{@output_file}"
|
|
461
|
+
end
|
|
462
|
+
|
|
463
|
+
if @temp_dir && Dir.exist?(@temp_dir)
|
|
464
|
+
Dir.rmdir(@temp_dir)
|
|
465
|
+
puts "Deleted temp directory: #{@temp_dir}"
|
|
466
|
+
end
|
|
467
|
+
end
|
|
468
|
+
end
|
|
469
|
+
|
|
470
|
+
class ReportGenerator < Taski::Task
|
|
471
|
+
def run
|
|
472
|
+
data = File.read(FileProcessor.output_file)
|
|
473
|
+
puts "Generated report from: #{data}"
|
|
474
|
+
end
|
|
475
|
+
|
|
476
|
+
def clean
|
|
477
|
+
puts "Report generation cleaned up"
|
|
478
|
+
end
|
|
479
|
+
end
|
|
480
|
+
|
|
481
|
+
# Usage
|
|
482
|
+
ReportGenerator.run
|
|
483
|
+
# => Created temp directory: /tmp/processing_1234567890
|
|
484
|
+
# => Created output file: /tmp/processing_1234567890/result.txt
|
|
485
|
+
# => Generated report from: Processing complete
|
|
486
|
+
|
|
487
|
+
ReportGenerator.clean
|
|
488
|
+
# => Report generation cleaned up
|
|
489
|
+
# => Deleted output file: /tmp/processing_1234567890/result.txt
|
|
490
|
+
# => Deleted temp directory: /tmp/processing_1234567890
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
### Idempotent Clean Methods
|
|
494
|
+
|
|
495
|
+
**Important**: Clean methods must be idempotent - safe to call multiple times:
|
|
496
|
+
|
|
497
|
+
```ruby
|
|
498
|
+
class SafeFileTask < Taski::Task
|
|
499
|
+
exports :data_file
|
|
500
|
+
|
|
501
|
+
def run
|
|
502
|
+
@data_file = '/tmp/safe_data.txt'
|
|
503
|
+
File.write(@data_file, 'Important data')
|
|
504
|
+
end
|
|
505
|
+
|
|
506
|
+
def clean
|
|
507
|
+
# ✅ Good: Check before delete
|
|
508
|
+
if @data_file && File.exist?(@data_file)
|
|
509
|
+
File.delete(@data_file)
|
|
510
|
+
puts "Deleted #{@data_file}"
|
|
511
|
+
else
|
|
512
|
+
puts "File #{@data_file} already deleted or doesn't exist"
|
|
513
|
+
end
|
|
514
|
+
end
|
|
515
|
+
end
|
|
516
|
+
|
|
517
|
+
# Safe to call multiple times
|
|
518
|
+
task = SafeFileTask.new
|
|
519
|
+
task.run
|
|
520
|
+
task.clean # => Deleted /tmp/safe_data.txt
|
|
521
|
+
task.clean # => File /tmp/safe_data.txt already deleted or doesn't exist
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
## Dependency Tree Visualization
|
|
525
|
+
|
|
526
|
+
Visualize complex dependency relationships with the tree method:
|
|
527
|
+
|
|
528
|
+
```ruby
|
|
529
|
+
class Database < Taski::Task
|
|
530
|
+
exports :connection
|
|
531
|
+
def run; @connection = "db-conn"; end
|
|
532
|
+
end
|
|
533
|
+
|
|
534
|
+
class Cache < Taski::Task
|
|
535
|
+
exports :redis_url
|
|
536
|
+
def run; @redis_url = "redis://localhost"; end
|
|
537
|
+
end
|
|
538
|
+
|
|
539
|
+
class Config < Taski::Task
|
|
540
|
+
exports :settings
|
|
541
|
+
def run
|
|
542
|
+
@settings = {
|
|
543
|
+
database: Database.connection,
|
|
544
|
+
cache: Cache.redis_url
|
|
545
|
+
}
|
|
546
|
+
end
|
|
547
|
+
end
|
|
548
|
+
|
|
549
|
+
class WebServer < Taski::Task
|
|
550
|
+
def run
|
|
551
|
+
puts "Starting with #{Config.settings}"
|
|
552
|
+
end
|
|
553
|
+
end
|
|
554
|
+
|
|
555
|
+
puts WebServer.tree
|
|
556
|
+
# => WebServer
|
|
557
|
+
# => └── Config
|
|
558
|
+
# => ├── Database
|
|
559
|
+
# => └── Cache
|
|
560
|
+
|
|
561
|
+
# Complex hierarchies are clearly visualized
|
|
562
|
+
class ApiGateway < Taski::Task
|
|
563
|
+
def run
|
|
564
|
+
puts "Gateway starting with #{Config.settings}"
|
|
565
|
+
puts "Using cache: #{Cache.redis_url}"
|
|
566
|
+
end
|
|
567
|
+
end
|
|
568
|
+
|
|
569
|
+
puts ApiGateway.tree
|
|
570
|
+
# => ApiGateway
|
|
571
|
+
# => ├── Config
|
|
572
|
+
# => │ ├── Database
|
|
573
|
+
# => │ └── Cache
|
|
574
|
+
# => └── Cache
|
|
575
|
+
```
|
|
576
|
+
|
|
577
|
+
## Performance Tips
|
|
578
|
+
|
|
579
|
+
### 1. Use Appropriate Reset Strategies
|
|
580
|
+
|
|
581
|
+
```ruby
|
|
582
|
+
# Reset specific tasks when needed
|
|
583
|
+
DatabaseTask.reset! # Only resets this task
|
|
584
|
+
AppTask.reset! # Resets this task and triggers dependency rebuilds
|
|
585
|
+
|
|
586
|
+
# Reset entire dependency tree
|
|
587
|
+
Taski::Task.reset_all! # Nuclear option - resets everything
|
|
588
|
+
```
|
|
589
|
+
|
|
590
|
+
### 2. Optimize Long-Running Tasks
|
|
591
|
+
|
|
592
|
+
```ruby
|
|
593
|
+
class OptimizedTask < Taski::Task
|
|
594
|
+
def run
|
|
595
|
+
# Break long operations into chunks for better progress display
|
|
596
|
+
(1..100).each_slice(10) do |batch|
|
|
597
|
+
puts "Processing batch: #{batch.first}-#{batch.last}"
|
|
598
|
+
process_batch(batch)
|
|
599
|
+
end
|
|
600
|
+
end
|
|
601
|
+
|
|
602
|
+
private
|
|
603
|
+
|
|
604
|
+
def process_batch(items)
|
|
605
|
+
# Process items in batch
|
|
606
|
+
sleep(0.1) # Simulated work
|
|
607
|
+
end
|
|
608
|
+
end
|
|
609
|
+
```
|
|
610
|
+
|
|
611
|
+
### 3. Use Structured Logging for Debugging
|
|
612
|
+
|
|
613
|
+
```ruby
|
|
614
|
+
class DebuggableTask < Taski::Task
|
|
615
|
+
def run
|
|
616
|
+
Taski.logger.debug "Task configuration",
|
|
617
|
+
config: get_config,
|
|
618
|
+
dependencies: self.class.dependencies.map(&:name)
|
|
619
|
+
|
|
620
|
+
perform_work
|
|
621
|
+
end
|
|
622
|
+
end
|
|
623
|
+
```
|
|
624
|
+
|
|
625
|
+
This comprehensive guide covers all of Taski's advanced features. Each feature is designed to work seamlessly together, providing a robust foundation for complex task orchestration.
|