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
data/examples/README.md
CHANGED
|
@@ -1,69 +1,125 @@
|
|
|
1
1
|
# Taski Examples
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Practical examples demonstrating Taski's parallel task execution and automatic dependency resolution.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Examples
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
### 1. quick_start.rb - Getting Started
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
- Basic task definition with Exports API
|
|
11
|
-
- Automatic dependency resolution
|
|
12
|
-
- Simple task execution
|
|
9
|
+
Basic Exports API usage and automatic dependency resolution.
|
|
13
10
|
|
|
14
11
|
```bash
|
|
15
12
|
ruby examples/quick_start.rb
|
|
16
13
|
```
|
|
17
14
|
|
|
18
|
-
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
-
|
|
22
|
-
|
|
15
|
+
**Covers:**
|
|
16
|
+
- Task definition with `exports`
|
|
17
|
+
- Automatic dependency detection
|
|
18
|
+
- Accessing exported values
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
### 2. section_demo.rb - Runtime Implementation Selection
|
|
23
|
+
|
|
24
|
+
Switch implementations based on environment using the Section API.
|
|
23
25
|
|
|
24
26
|
```bash
|
|
25
|
-
|
|
26
|
-
|
|
27
|
+
ruby examples/section_demo.rb
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**Covers:**
|
|
31
|
+
- `interfaces` for defining contracts
|
|
32
|
+
- Environment-specific implementations
|
|
33
|
+
- Dependency tree visualization with `.tree`
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
### 3. context_demo.rb - Runtime Context and Options
|
|
38
|
+
|
|
39
|
+
Access execution context and pass custom options to tasks.
|
|
27
40
|
|
|
28
|
-
|
|
29
|
-
ruby examples/
|
|
30
|
-
cat build.log
|
|
41
|
+
```bash
|
|
42
|
+
ruby examples/context_demo.rb
|
|
31
43
|
```
|
|
32
44
|
|
|
33
|
-
|
|
34
|
-
-
|
|
35
|
-
-
|
|
36
|
-
-
|
|
37
|
-
-
|
|
45
|
+
**Covers:**
|
|
46
|
+
- User-defined options via `run(context: {...})`
|
|
47
|
+
- `Taski.context[:key]` for option access
|
|
48
|
+
- `Taski.context.fetch(:key, default)` for defaults
|
|
49
|
+
- `Taski.context.working_directory`
|
|
50
|
+
- `Taski.context.started_at`
|
|
51
|
+
- `Taski.context.root_task`
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
### 4. reexecution_demo.rb - Cache Control
|
|
56
|
+
|
|
57
|
+
Understand caching behavior and re-execution patterns.
|
|
38
58
|
|
|
39
59
|
```bash
|
|
40
|
-
ruby examples/
|
|
60
|
+
ruby examples/reexecution_demo.rb
|
|
41
61
|
```
|
|
42
62
|
|
|
43
|
-
|
|
44
|
-
-
|
|
45
|
-
-
|
|
46
|
-
-
|
|
47
|
-
|
|
63
|
+
**Covers:**
|
|
64
|
+
- Default caching behavior
|
|
65
|
+
- `Task.new` for fresh instances
|
|
66
|
+
- `Task.reset!` for clearing caches
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
### 5. data_pipeline_demo.rb - Real-World Pipeline
|
|
71
|
+
|
|
72
|
+
A realistic ETL pipeline with parallel data fetching.
|
|
48
73
|
|
|
49
74
|
```bash
|
|
50
|
-
ruby examples/
|
|
75
|
+
ruby examples/data_pipeline_demo.rb
|
|
51
76
|
```
|
|
52
77
|
|
|
53
|
-
|
|
78
|
+
**Covers:**
|
|
79
|
+
- Multiple data sources in parallel
|
|
80
|
+
- Data transformation stages
|
|
81
|
+
- Aggregation and reporting
|
|
54
82
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
### 6. parallel_progress_demo.rb - Progress Display
|
|
86
|
+
|
|
87
|
+
Real-time progress visualization during parallel execution.
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
TASKI_FORCE_PROGRESS=1 ruby examples/parallel_progress_demo.rb
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
**Covers:**
|
|
94
|
+
- Parallel task execution
|
|
95
|
+
- Progress display with spinners
|
|
96
|
+
- Execution timing
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## Quick Reference
|
|
101
|
+
|
|
102
|
+
| Example | Feature | Complexity |
|
|
103
|
+
|---------|---------|------------|
|
|
104
|
+
| quick_start | Exports API | Basic |
|
|
105
|
+
| section_demo | Section API | Intermediate |
|
|
106
|
+
| context_demo | Context API | Intermediate |
|
|
107
|
+
| reexecution_demo | Cache Control | Intermediate |
|
|
108
|
+
| data_pipeline_demo | ETL Pipeline | Advanced |
|
|
109
|
+
| parallel_progress_demo | Progress Display | Advanced |
|
|
110
|
+
|
|
111
|
+
## Running All Examples
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
# Run each example
|
|
115
|
+
for f in examples/*.rb; do echo "=== $f ===" && ruby "$f" && echo; done
|
|
116
|
+
|
|
117
|
+
# With progress display (for parallel_progress_demo)
|
|
118
|
+
TASKI_FORCE_PROGRESS=1 ruby examples/parallel_progress_demo.rb
|
|
119
|
+
```
|
|
63
120
|
|
|
64
121
|
## Next Steps
|
|
65
122
|
|
|
66
|
-
|
|
67
|
-
-
|
|
68
|
-
-
|
|
69
|
-
- Check out the source code in `lib/taski/`
|
|
123
|
+
- [Main README](../README.md) - Full documentation
|
|
124
|
+
- [Tests](../test/) - More usage patterns
|
|
125
|
+
- [Source](../lib/taski/) - Implementation details
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Taski Context API Example
|
|
5
|
+
#
|
|
6
|
+
# This example demonstrates the Context API for accessing runtime information:
|
|
7
|
+
# - working_directory: Where execution started
|
|
8
|
+
# - started_at: When execution began
|
|
9
|
+
# - root_task: The first task class that was called
|
|
10
|
+
# - User-defined options: Custom values passed via run(context: {...})
|
|
11
|
+
#
|
|
12
|
+
# Run: ruby examples/context_demo.rb
|
|
13
|
+
|
|
14
|
+
require_relative "../lib/taski"
|
|
15
|
+
|
|
16
|
+
puts "Taski Context API Example"
|
|
17
|
+
puts "=" * 40
|
|
18
|
+
|
|
19
|
+
# Task that uses context information for logging
|
|
20
|
+
class SetupTask < Taski::Task
|
|
21
|
+
exports :setup_info
|
|
22
|
+
|
|
23
|
+
def run
|
|
24
|
+
puts "Setup running..."
|
|
25
|
+
puts " Working directory: #{Taski.context.working_directory}"
|
|
26
|
+
puts " Started at: #{Taski.context.started_at}"
|
|
27
|
+
puts " Root task: #{Taski.context.root_task}"
|
|
28
|
+
puts " Environment: #{Taski.context[:env]}"
|
|
29
|
+
|
|
30
|
+
@setup_info = {
|
|
31
|
+
directory: Taski.context.working_directory,
|
|
32
|
+
timestamp: Taski.context.started_at,
|
|
33
|
+
env: Taski.context[:env]
|
|
34
|
+
}
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Task that creates files relative to working directory
|
|
39
|
+
class FileProcessor < Taski::Task
|
|
40
|
+
exports :output_path
|
|
41
|
+
|
|
42
|
+
def run
|
|
43
|
+
# Use context to determine output location
|
|
44
|
+
base_dir = Taski.context.working_directory
|
|
45
|
+
env = Taski.context.fetch(:env, "development")
|
|
46
|
+
@output_path = File.join(base_dir, "tmp", env, "output.txt")
|
|
47
|
+
|
|
48
|
+
puts "FileProcessor: Would write to #{@output_path}"
|
|
49
|
+
puts " (relative to working directory, env: #{env})"
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Task that logs execution timing
|
|
54
|
+
class TimingTask < Taski::Task
|
|
55
|
+
exports :duration_info
|
|
56
|
+
|
|
57
|
+
def run
|
|
58
|
+
start_time = Taski.context.started_at
|
|
59
|
+
current_time = Time.now
|
|
60
|
+
elapsed = current_time - start_time
|
|
61
|
+
|
|
62
|
+
puts "TimingTask: #{elapsed.round(3)}s since execution started"
|
|
63
|
+
puts " Debug mode: #{Taski.context.fetch(:debug, false)}"
|
|
64
|
+
|
|
65
|
+
@duration_info = {
|
|
66
|
+
started: start_time,
|
|
67
|
+
current: current_time,
|
|
68
|
+
elapsed_seconds: elapsed
|
|
69
|
+
}
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Main task that depends on others
|
|
74
|
+
class MainTask < Taski::Task
|
|
75
|
+
exports :summary
|
|
76
|
+
|
|
77
|
+
def run
|
|
78
|
+
puts "\nMainTask executing..."
|
|
79
|
+
puts " Root task is: #{Taski.context.root_task}"
|
|
80
|
+
puts " Environment: #{Taski.context[:env]}"
|
|
81
|
+
|
|
82
|
+
# Access dependencies
|
|
83
|
+
setup = SetupTask.setup_info
|
|
84
|
+
output = FileProcessor.output_path
|
|
85
|
+
timing = TimingTask.duration_info
|
|
86
|
+
|
|
87
|
+
@summary = {
|
|
88
|
+
setup: setup,
|
|
89
|
+
output_path: output,
|
|
90
|
+
timing: timing,
|
|
91
|
+
root_task: Taski.context.root_task.to_s
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
puts "\nExecution Summary:"
|
|
95
|
+
puts " Setup directory: #{setup[:directory]}"
|
|
96
|
+
puts " Output path: #{output}"
|
|
97
|
+
puts " Total elapsed: #{timing[:elapsed_seconds].round(3)}s"
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
puts "\n1. Running MainTask with context options"
|
|
102
|
+
puts "-" * 40
|
|
103
|
+
MainTask.run(context: {env: "production", debug: true})
|
|
104
|
+
|
|
105
|
+
puts "\n" + "=" * 40
|
|
106
|
+
puts "\n2. Running SetupTask directly with different context"
|
|
107
|
+
puts "-" * 40
|
|
108
|
+
SetupTask.reset!
|
|
109
|
+
SetupTask.run(context: {env: "staging"})
|
|
110
|
+
|
|
111
|
+
puts "\n" + "=" * 40
|
|
112
|
+
puts "\n3. Dependency Tree"
|
|
113
|
+
puts "-" * 40
|
|
114
|
+
puts MainTask.tree
|
|
115
|
+
|
|
116
|
+
puts "\n" + "=" * 40
|
|
117
|
+
puts "Context API demonstration complete!"
|
|
118
|
+
puts "Note: Context provides runtime information and user options without affecting dependency analysis."
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Taski Data Pipeline Example
|
|
5
|
+
#
|
|
6
|
+
# This example demonstrates a realistic data processing pipeline:
|
|
7
|
+
# - Section API for switching data sources (production vs test)
|
|
8
|
+
# - Multiple data sources fetched in parallel
|
|
9
|
+
# - Data transformation and aggregation
|
|
10
|
+
#
|
|
11
|
+
# Run: ruby examples/data_pipeline_demo.rb
|
|
12
|
+
# With progress: TASKI_FORCE_PROGRESS=1 ruby examples/data_pipeline_demo.rb
|
|
13
|
+
|
|
14
|
+
require_relative "../lib/taski"
|
|
15
|
+
|
|
16
|
+
# Section: Data source abstraction
|
|
17
|
+
# Switch between production API and test fixtures
|
|
18
|
+
class DataSourceSection < Taski::Section
|
|
19
|
+
interfaces :users, :sales, :activities
|
|
20
|
+
|
|
21
|
+
def impl
|
|
22
|
+
(ENV["USE_TEST_DATA"] == "true") ? TestData : ProductionData
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Production: Fetch from APIs (simulated with delays)
|
|
26
|
+
class ProductionData < Taski::Task
|
|
27
|
+
def run
|
|
28
|
+
puts " [ProductionData] Fetching from APIs..."
|
|
29
|
+
sleep(0.3)
|
|
30
|
+
|
|
31
|
+
@users = [
|
|
32
|
+
{id: 1, name: "Alice", department: "Engineering"},
|
|
33
|
+
{id: 2, name: "Bob", department: "Sales"},
|
|
34
|
+
{id: 3, name: "Charlie", department: "Engineering"},
|
|
35
|
+
{id: 4, name: "Diana", department: "Marketing"}
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
@sales = [
|
|
39
|
+
{user_id: 2, amount: 1000, date: "2024-01"},
|
|
40
|
+
{user_id: 2, amount: 1500, date: "2024-02"},
|
|
41
|
+
{user_id: 4, amount: 800, date: "2024-01"}
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
@activities = [
|
|
45
|
+
{user_id: 1, action: :commit, count: 45},
|
|
46
|
+
{user_id: 3, action: :commit, count: 32},
|
|
47
|
+
{user_id: 1, action: :review, count: 12},
|
|
48
|
+
{user_id: 3, action: :review, count: 8}
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
puts " [ProductionData] Loaded #{@users.size} users, #{@sales.size} sales, #{@activities.size} activities"
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Test: Minimal fixture data (no delays)
|
|
56
|
+
class TestData < Taski::Task
|
|
57
|
+
def run
|
|
58
|
+
puts " [TestData] Loading test fixtures..."
|
|
59
|
+
|
|
60
|
+
@users = [
|
|
61
|
+
{id: 1, name: "Test User", department: "Test Dept"}
|
|
62
|
+
]
|
|
63
|
+
|
|
64
|
+
@sales = [
|
|
65
|
+
{user_id: 1, amount: 100, date: "2024-01"}
|
|
66
|
+
]
|
|
67
|
+
|
|
68
|
+
@activities = [
|
|
69
|
+
{user_id: 1, action: :commit, count: 10}
|
|
70
|
+
]
|
|
71
|
+
|
|
72
|
+
puts " [TestData] Loaded minimal test data"
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Section: Report format selection
|
|
78
|
+
class ReportFormatSection < Taski::Section
|
|
79
|
+
interfaces :format_report
|
|
80
|
+
|
|
81
|
+
def impl
|
|
82
|
+
(ENV["REPORT_FORMAT"] == "json") ? JsonFormat : TextFormat
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
class TextFormat < Taski::Task
|
|
86
|
+
def run
|
|
87
|
+
@format_report = ->(report) {
|
|
88
|
+
report.map do |dept, stats|
|
|
89
|
+
"#{dept}: #{stats[:user_count]} users, $#{stats[:total_sales]} sales"
|
|
90
|
+
end.join("\n")
|
|
91
|
+
}
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
class JsonFormat < Taski::Task
|
|
96
|
+
def run
|
|
97
|
+
require "json"
|
|
98
|
+
@format_report = ->(report) { JSON.pretty_generate(report) }
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Transform: Enrich users with sales data
|
|
104
|
+
class EnrichWithSales < Taski::Task
|
|
105
|
+
exports :users_with_sales
|
|
106
|
+
|
|
107
|
+
def run
|
|
108
|
+
users = DataSourceSection.users
|
|
109
|
+
sales = DataSourceSection.sales
|
|
110
|
+
|
|
111
|
+
sales_by_user = sales.group_by { |s| s[:user_id] }
|
|
112
|
+
.transform_values { |records| records.sum { |r| r[:amount] } }
|
|
113
|
+
|
|
114
|
+
@users_with_sales = users.map do |user|
|
|
115
|
+
user.merge(total_sales: sales_by_user[user[:id]] || 0)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
puts " [EnrichWithSales] Enriched #{@users_with_sales.size} users"
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Transform: Enrich users with activity data
|
|
123
|
+
class EnrichWithActivities < Taski::Task
|
|
124
|
+
exports :users_with_activities
|
|
125
|
+
|
|
126
|
+
def run
|
|
127
|
+
users = DataSourceSection.users
|
|
128
|
+
activities = DataSourceSection.activities
|
|
129
|
+
|
|
130
|
+
activities_by_user = activities.group_by { |a| a[:user_id] }
|
|
131
|
+
.transform_values do |records|
|
|
132
|
+
records.to_h { |r| [r[:action], r[:count]] }
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
@users_with_activities = users.map do |user|
|
|
136
|
+
user.merge(activities: activities_by_user[user[:id]] || {})
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
puts " [EnrichWithActivities] Enriched #{@users_with_activities.size} users"
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Aggregate: Combine enrichments into profiles
|
|
144
|
+
class BuildProfiles < Taski::Task
|
|
145
|
+
exports :profiles
|
|
146
|
+
|
|
147
|
+
def run
|
|
148
|
+
users_sales = EnrichWithSales.users_with_sales
|
|
149
|
+
users_activities = EnrichWithActivities.users_with_activities
|
|
150
|
+
|
|
151
|
+
activities_map = users_activities.to_h { |u| [u[:id], u[:activities]] }
|
|
152
|
+
|
|
153
|
+
@profiles = users_sales.map do |user|
|
|
154
|
+
user.merge(activities: activities_map[user[:id]] || {})
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
puts " [BuildProfiles] Built #{@profiles.size} profiles"
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# Output: Generate department report
|
|
162
|
+
class GenerateReport < Taski::Task
|
|
163
|
+
exports :report, :formatted_output
|
|
164
|
+
|
|
165
|
+
def run
|
|
166
|
+
profiles = BuildProfiles.profiles
|
|
167
|
+
formatter = ReportFormatSection.format_report
|
|
168
|
+
|
|
169
|
+
by_department = profiles.group_by { |p| p[:department] }
|
|
170
|
+
|
|
171
|
+
@report = by_department.transform_values do |dept_users|
|
|
172
|
+
{
|
|
173
|
+
user_count: dept_users.size,
|
|
174
|
+
total_sales: dept_users.sum { |u| u[:total_sales] },
|
|
175
|
+
total_commits: dept_users.sum { |u| u[:activities][:commit] || 0 },
|
|
176
|
+
total_reviews: dept_users.sum { |u| u[:activities][:review] || 0 }
|
|
177
|
+
}
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
@formatted_output = formatter.call(@report)
|
|
181
|
+
puts " [GenerateReport] Generated report for #{@report.size} departments"
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# Demo execution
|
|
186
|
+
puts "Taski Data Pipeline Demo"
|
|
187
|
+
puts "=" * 50
|
|
188
|
+
|
|
189
|
+
puts "\n1. Dependency Tree"
|
|
190
|
+
puts "-" * 50
|
|
191
|
+
puts GenerateReport.tree
|
|
192
|
+
|
|
193
|
+
puts "\n2. Production Data (default)"
|
|
194
|
+
puts "-" * 50
|
|
195
|
+
ENV["USE_TEST_DATA"] = "false"
|
|
196
|
+
ENV["REPORT_FORMAT"] = "text"
|
|
197
|
+
|
|
198
|
+
start_time = Time.now
|
|
199
|
+
GenerateReport.run
|
|
200
|
+
elapsed = Time.now - start_time
|
|
201
|
+
|
|
202
|
+
puts "\nReport (text format):"
|
|
203
|
+
puts GenerateReport.formatted_output
|
|
204
|
+
puts "\nCompleted in #{elapsed.round(3)}s"
|
|
205
|
+
|
|
206
|
+
puts "\n" + "=" * 50
|
|
207
|
+
puts "\n3. Test Data with JSON Format"
|
|
208
|
+
puts "-" * 50
|
|
209
|
+
ENV["USE_TEST_DATA"] = "true"
|
|
210
|
+
ENV["REPORT_FORMAT"] = "json"
|
|
211
|
+
|
|
212
|
+
# Reset all tasks for fresh execution
|
|
213
|
+
[DataSourceSection, ReportFormatSection, EnrichWithSales,
|
|
214
|
+
EnrichWithActivities, BuildProfiles, GenerateReport].each(&:reset!)
|
|
215
|
+
|
|
216
|
+
start_time = Time.now
|
|
217
|
+
GenerateReport.run
|
|
218
|
+
elapsed = Time.now - start_time
|
|
219
|
+
|
|
220
|
+
puts "\nReport (JSON format):"
|
|
221
|
+
puts GenerateReport.formatted_output
|
|
222
|
+
puts "\nCompleted in #{elapsed.round(3)}s"
|
|
223
|
+
|
|
224
|
+
puts "\n" + "=" * 50
|
|
225
|
+
puts "Pipeline demonstration complete!"
|
|
226
|
+
puts "\nKey concepts demonstrated:"
|
|
227
|
+
puts " - DataSourceSection: Switch between production/test data"
|
|
228
|
+
puts " - ReportFormatSection: Switch output format (text/JSON)"
|
|
229
|
+
puts " - Parallel execution of independent transforms"
|
|
230
|
+
puts "\nTry with progress display:"
|
|
231
|
+
puts " TASKI_FORCE_PROGRESS=1 ruby examples/data_pipeline_demo.rb"
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative "../lib/taski"
|
|
5
|
+
|
|
6
|
+
# Demo tasks that simulate parallel execution with progress display
|
|
7
|
+
# Run with: TASKI_FORCE_PROGRESS=1 ruby examples/parallel_progress_demo.rb
|
|
8
|
+
|
|
9
|
+
class DownloadLayer1 < Taski::Task
|
|
10
|
+
exports :layer1_data
|
|
11
|
+
|
|
12
|
+
def run
|
|
13
|
+
sleep(2.3) # Simulate download
|
|
14
|
+
@layer1_data = "Layer 1 data (base image)"
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
class DownloadLayer2 < Taski::Task
|
|
19
|
+
exports :layer2_data
|
|
20
|
+
|
|
21
|
+
def run
|
|
22
|
+
sleep(5.5) # Simulate slower download
|
|
23
|
+
@layer2_data = "Layer 2 data (dependencies)"
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
class DownloadLayer3 < Taski::Task
|
|
28
|
+
exports :layer3_data
|
|
29
|
+
|
|
30
|
+
def run
|
|
31
|
+
sleep(0.2) # Simulate fast download
|
|
32
|
+
@layer3_data = "Layer 3 data (application)"
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
class ExtractLayers < Taski::Task
|
|
37
|
+
exports :extracted_data
|
|
38
|
+
|
|
39
|
+
def run
|
|
40
|
+
# This task depends on all download tasks (via static analysis)
|
|
41
|
+
layer1 = DownloadLayer1.layer1_data
|
|
42
|
+
layer2 = DownloadLayer2.layer2_data
|
|
43
|
+
layer3 = DownloadLayer3.layer3_data
|
|
44
|
+
|
|
45
|
+
sleep(0.3) # Simulate extraction
|
|
46
|
+
@extracted_data = "Extracted: #{layer1}, #{layer2}, #{layer3}"
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
class VerifyImage < Taski::Task
|
|
51
|
+
exports :verification_result
|
|
52
|
+
|
|
53
|
+
def run
|
|
54
|
+
# Depends on ExtractLayers
|
|
55
|
+
data = ExtractLayers.extracted_data
|
|
56
|
+
|
|
57
|
+
sleep(0.2) # Simulate verification
|
|
58
|
+
@verification_result = "Verified: #{data}"
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Start progress display
|
|
63
|
+
Taski.progress_display&.start
|
|
64
|
+
|
|
65
|
+
# Execute the final task (all dependencies will be resolved automatically)
|
|
66
|
+
result = VerifyImage.verification_result
|
|
67
|
+
|
|
68
|
+
# Stop progress display
|
|
69
|
+
Taski.progress_display&.stop
|
|
70
|
+
|
|
71
|
+
puts "\n\nFinal result:"
|
|
72
|
+
puts result
|
data/examples/quick_start.rb
CHANGED
|
@@ -19,7 +19,7 @@ puts "=" * 30
|
|
|
19
19
|
class DatabaseSetup < Taski::Task
|
|
20
20
|
exports :connection_string
|
|
21
21
|
|
|
22
|
-
def
|
|
22
|
+
def run
|
|
23
23
|
@connection_string = "postgresql://localhost/myapp"
|
|
24
24
|
puts "Database configured"
|
|
25
25
|
end
|
|
@@ -28,14 +28,14 @@ end
|
|
|
28
28
|
class APIServer < Taski::Task
|
|
29
29
|
exports :port
|
|
30
30
|
|
|
31
|
-
def
|
|
32
|
-
# Automatic dependency: DatabaseSetup will be
|
|
31
|
+
def run
|
|
32
|
+
# Automatic dependency: DatabaseSetup will be executed first
|
|
33
33
|
puts "Starting API with #{DatabaseSetup.connection_string}"
|
|
34
34
|
@port = 3000
|
|
35
35
|
end
|
|
36
36
|
end
|
|
37
37
|
|
|
38
38
|
# Execute - dependencies are resolved automatically
|
|
39
|
-
APIServer.
|
|
39
|
+
APIServer.run
|
|
40
40
|
|
|
41
41
|
puts "\n✅ Result: APIServer running on port #{APIServer.port}"
|