taski 0.5.0 → 0.7.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/CHANGELOG.md +50 -0
- data/README.md +168 -21
- data/docs/GUIDE.md +394 -0
- data/examples/README.md +65 -17
- data/examples/{context_demo.rb → args_demo.rb} +27 -27
- data/examples/clean_demo.rb +204 -0
- data/examples/data_pipeline_demo.rb +1 -1
- data/examples/group_demo.rb +113 -0
- data/examples/large_tree_demo.rb +519 -0
- data/examples/reexecution_demo.rb +93 -80
- data/examples/simple_progress_demo.rb +80 -0
- data/examples/system_call_demo.rb +56 -0
- data/lib/taski/{context.rb → args.rb} +3 -3
- data/lib/taski/execution/base_progress_display.rb +348 -0
- data/lib/taski/execution/execution_context.rb +383 -0
- data/lib/taski/execution/executor.rb +405 -134
- data/lib/taski/execution/plain_progress_display.rb +76 -0
- data/lib/taski/execution/registry.rb +17 -1
- data/lib/taski/execution/scheduler.rb +308 -0
- data/lib/taski/execution/simple_progress_display.rb +173 -0
- data/lib/taski/execution/task_output_pipe.rb +42 -0
- data/lib/taski/execution/task_output_router.rb +287 -0
- data/lib/taski/execution/task_wrapper.rb +215 -52
- data/lib/taski/execution/tree_progress_display.rb +349 -212
- data/lib/taski/execution/worker_pool.rb +104 -0
- data/lib/taski/section.rb +16 -3
- data/lib/taski/static_analysis/visitor.rb +3 -0
- data/lib/taski/task.rb +218 -37
- data/lib/taski/test_helper/errors.rb +13 -0
- data/lib/taski/test_helper/minitest.rb +38 -0
- data/lib/taski/test_helper/mock_registry.rb +51 -0
- data/lib/taski/test_helper/mock_wrapper.rb +46 -0
- data/lib/taski/test_helper/rspec.rb +38 -0
- data/lib/taski/test_helper.rb +214 -0
- data/lib/taski/version.rb +1 -1
- data/lib/taski.rb +211 -23
- data/sig/taski.rbs +207 -27
- metadata +25 -8
- data/docs/advanced-features.md +0 -625
- data/docs/api-guide.md +0 -509
- data/docs/error-handling.md +0 -684
- data/examples/section_progress_demo.rb +0 -78
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
|
-
# Taski
|
|
4
|
+
# Taski Scope-Based Execution Demo
|
|
5
5
|
#
|
|
6
|
-
# This example demonstrates
|
|
7
|
-
# -
|
|
8
|
-
# - Task.new
|
|
9
|
-
# -
|
|
6
|
+
# This example demonstrates the execution model:
|
|
7
|
+
# - Task.run / Task.value: Fresh execution every time
|
|
8
|
+
# - Task.new.run: Instance-level caching
|
|
9
|
+
# - Dependencies within same execution scope share results
|
|
10
10
|
#
|
|
11
|
-
# Run: ruby examples/reexecution_demo.rb
|
|
11
|
+
# Run: TASKI_PROGRESS_DISABLE=1 ruby examples/reexecution_demo.rb
|
|
12
12
|
|
|
13
13
|
require_relative "../lib/taski"
|
|
14
14
|
|
|
15
|
-
puts "Taski
|
|
16
|
-
puts "=" *
|
|
15
|
+
puts "Taski Scope-Based Execution Demo"
|
|
16
|
+
puts "=" * 50
|
|
17
17
|
|
|
18
|
-
# Task that generates random values (to demonstrate
|
|
18
|
+
# Task that generates random values (to demonstrate execution behavior)
|
|
19
19
|
class RandomGenerator < Taski::Task
|
|
20
20
|
exports :value, :timestamp
|
|
21
21
|
|
|
@@ -23,6 +23,7 @@ class RandomGenerator < Taski::Task
|
|
|
23
23
|
@value = rand(1000)
|
|
24
24
|
@timestamp = Time.now.strftime("%H:%M:%S.%L")
|
|
25
25
|
puts " RandomGenerator.run called: value=#{@value}, time=#{@timestamp}"
|
|
26
|
+
@value
|
|
26
27
|
end
|
|
27
28
|
end
|
|
28
29
|
|
|
@@ -32,96 +33,108 @@ class Consumer < Taski::Task
|
|
|
32
33
|
|
|
33
34
|
def run
|
|
34
35
|
random_value = RandomGenerator.value
|
|
35
|
-
@result = "Consumed
|
|
36
|
-
puts " Consumer.run called
|
|
36
|
+
@result = "Consumed: #{random_value}"
|
|
37
|
+
puts " Consumer.run called with RandomGenerator.value=#{random_value}"
|
|
38
|
+
@result
|
|
37
39
|
end
|
|
38
40
|
end
|
|
39
41
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
42
|
+
# Task that accesses RandomGenerator twice
|
|
43
|
+
class DoubleConsumer < Taski::Task
|
|
44
|
+
exports :first_value, :second_value
|
|
45
|
+
|
|
46
|
+
def run
|
|
47
|
+
@first_value = RandomGenerator.value
|
|
48
|
+
puts " DoubleConsumer: first access = #{@first_value}"
|
|
49
|
+
@second_value = RandomGenerator.value
|
|
50
|
+
puts " DoubleConsumer: second access = #{@second_value}"
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
puts "\n1. Class Method Calls: Fresh Execution Every Time"
|
|
55
|
+
puts "-" * 50
|
|
56
|
+
puts "Each Task.value call creates a NEW execution:"
|
|
57
|
+
puts "\nFirst call:"
|
|
43
58
|
value1 = RandomGenerator.value
|
|
44
59
|
puts " => #{value1}"
|
|
45
60
|
|
|
46
|
-
puts "\nSecond call
|
|
61
|
+
puts "\nSecond call (NEW execution, different value):"
|
|
47
62
|
value2 = RandomGenerator.value
|
|
48
63
|
puts " => #{value2}"
|
|
49
64
|
|
|
50
|
-
puts "\nValues are
|
|
65
|
+
puts "\nValues are different: #{value1 != value2}"
|
|
66
|
+
|
|
67
|
+
puts "\n" + "=" * 50
|
|
68
|
+
puts "\n2. Task.new: Instance-Level Caching"
|
|
69
|
+
puts "-" * 50
|
|
70
|
+
puts "Same instance caches the result:"
|
|
71
|
+
|
|
72
|
+
instance = RandomGenerator.new
|
|
73
|
+
puts "\nFirst run on instance:"
|
|
74
|
+
result1 = instance.run
|
|
75
|
+
puts " instance.value = #{instance.value}"
|
|
76
|
+
|
|
77
|
+
puts "\nSecond run on same instance (returns cached):"
|
|
78
|
+
result2 = instance.run
|
|
79
|
+
puts " instance.value = #{instance.value}"
|
|
80
|
+
|
|
81
|
+
puts "\nSame result: #{result1 == result2}"
|
|
51
82
|
|
|
52
|
-
puts "\n" + "=" *
|
|
53
|
-
puts "\
|
|
54
|
-
puts "-" *
|
|
55
|
-
puts "Creating new instance with RandomGenerator.new:"
|
|
83
|
+
puts "\n" + "=" * 50
|
|
84
|
+
puts "\n3. Different Instances: Independent Executions"
|
|
85
|
+
puts "-" * 50
|
|
56
86
|
|
|
57
87
|
instance1 = RandomGenerator.new
|
|
58
88
|
instance1.run
|
|
59
|
-
puts "
|
|
89
|
+
puts "instance1.value = #{instance1.value}"
|
|
60
90
|
|
|
61
91
|
instance2 = RandomGenerator.new
|
|
62
92
|
instance2.run
|
|
63
|
-
puts "
|
|
64
|
-
|
|
65
|
-
puts "\
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
puts "\
|
|
69
|
-
puts "
|
|
70
|
-
puts "
|
|
71
|
-
|
|
72
|
-
puts "
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
puts "
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
puts "
|
|
83
|
-
puts "
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
RandomGenerator
|
|
87
|
-
Consumer.
|
|
88
|
-
|
|
89
|
-
puts "
|
|
90
|
-
|
|
91
|
-
puts "
|
|
92
|
-
|
|
93
|
-
puts "\nSecond Consumer execution (cached):"
|
|
94
|
-
result2 = Consumer.result
|
|
95
|
-
puts " => #{result2}"
|
|
96
|
-
|
|
97
|
-
puts "\nReset Consumer and re-execute:"
|
|
98
|
-
Consumer.reset!
|
|
99
|
-
result3 = Consumer.result
|
|
100
|
-
puts " => #{result3}"
|
|
101
|
-
puts " (Dependencies are re-resolved when task is reset)"
|
|
102
|
-
|
|
103
|
-
puts "\nReset both tasks:"
|
|
104
|
-
RandomGenerator.reset!
|
|
105
|
-
Consumer.reset!
|
|
106
|
-
result4 = Consumer.result
|
|
107
|
-
puts " => #{result4}"
|
|
108
|
-
puts " (New random value because both were reset)"
|
|
109
|
-
|
|
110
|
-
puts "\n" + "=" * 40
|
|
111
|
-
puts "\n5. Use Cases Summary"
|
|
112
|
-
puts "-" * 40
|
|
93
|
+
puts "instance2.value = #{instance2.value}"
|
|
94
|
+
|
|
95
|
+
puts "\nDifferent values: #{instance1.value != instance2.value}"
|
|
96
|
+
|
|
97
|
+
puts "\n" + "=" * 50
|
|
98
|
+
puts "\n4. Scope-Based Caching for Dependencies"
|
|
99
|
+
puts "-" * 50
|
|
100
|
+
puts "Within ONE execution, dependencies are cached:"
|
|
101
|
+
|
|
102
|
+
puts "\nDoubleConsumer accesses RandomGenerator.value twice:"
|
|
103
|
+
DoubleConsumer.run
|
|
104
|
+
|
|
105
|
+
puts "\nNote: Both accesses return the SAME value!"
|
|
106
|
+
puts "(Because they're in the same execution scope)"
|
|
107
|
+
|
|
108
|
+
puts "\n" + "=" * 50
|
|
109
|
+
puts "\n5. Dependency Chain with Fresh Execution"
|
|
110
|
+
puts "-" * 50
|
|
111
|
+
|
|
112
|
+
puts "Each Consumer.run creates fresh RandomGenerator:"
|
|
113
|
+
puts "\nFirst Consumer.run:"
|
|
114
|
+
Consumer.run
|
|
115
|
+
|
|
116
|
+
puts "\nSecond Consumer.run (different RandomGenerator value):"
|
|
117
|
+
Consumer.run
|
|
118
|
+
|
|
119
|
+
puts "\n" + "=" * 50
|
|
120
|
+
puts "\n6. Use Cases Summary"
|
|
121
|
+
puts "-" * 50
|
|
113
122
|
puts <<~SUMMARY
|
|
114
123
|
TaskClass.run / TaskClass.value
|
|
115
|
-
=>
|
|
124
|
+
=> Fresh execution every time
|
|
125
|
+
=> Dependencies within same execution are cached
|
|
126
|
+
=> Use for: Independent executions, scripts
|
|
116
127
|
|
|
117
128
|
TaskClass.new.run
|
|
118
|
-
=>
|
|
119
|
-
=>
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
=>
|
|
129
|
+
=> Instance caches results
|
|
130
|
+
=> Multiple .run calls return cached value
|
|
131
|
+
=> Use for: Re-execution control, testing
|
|
132
|
+
|
|
133
|
+
instance.reset!
|
|
134
|
+
=> Clears instance cache
|
|
135
|
+
=> Next .run will execute fresh
|
|
136
|
+
=> Use for: Re-running same instance
|
|
124
137
|
SUMMARY
|
|
125
138
|
|
|
126
|
-
puts "\n" + "=" *
|
|
127
|
-
puts "
|
|
139
|
+
puts "\n" + "=" * 50
|
|
140
|
+
puts "Scope-Based Execution demonstration complete!"
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative "../lib/taski"
|
|
5
|
+
|
|
6
|
+
# Demo of simple one-line progress display mode
|
|
7
|
+
# Run with: TASKI_PROGRESS_MODE=simple ruby examples/simple_progress_demo.rb
|
|
8
|
+
# Or set via API: Taski.progress_mode = :simple
|
|
9
|
+
|
|
10
|
+
# Set simple progress mode via API
|
|
11
|
+
Taski.progress_mode = :simple
|
|
12
|
+
|
|
13
|
+
class DownloadLayer1 < Taski::Task
|
|
14
|
+
exports :layer1_data
|
|
15
|
+
|
|
16
|
+
def run
|
|
17
|
+
puts "Downloading base image..."
|
|
18
|
+
sleep(0.8)
|
|
19
|
+
puts "Base image complete"
|
|
20
|
+
@layer1_data = "Layer 1 data (base image)"
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
class DownloadLayer2 < Taski::Task
|
|
25
|
+
exports :layer2_data
|
|
26
|
+
|
|
27
|
+
def run
|
|
28
|
+
puts "Downloading dependencies..."
|
|
29
|
+
sleep(1.2)
|
|
30
|
+
puts "Dependencies complete"
|
|
31
|
+
@layer2_data = "Layer 2 data (dependencies)"
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
class DownloadLayer3 < Taski::Task
|
|
36
|
+
exports :layer3_data
|
|
37
|
+
|
|
38
|
+
def run
|
|
39
|
+
puts "Downloading application..."
|
|
40
|
+
sleep(0.3)
|
|
41
|
+
puts "Application complete"
|
|
42
|
+
@layer3_data = "Layer 3 data (application)"
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
class ExtractLayers < Taski::Task
|
|
47
|
+
exports :extracted_data
|
|
48
|
+
|
|
49
|
+
def run
|
|
50
|
+
layer1 = DownloadLayer1.layer1_data
|
|
51
|
+
layer2 = DownloadLayer2.layer2_data
|
|
52
|
+
layer3 = DownloadLayer3.layer3_data
|
|
53
|
+
|
|
54
|
+
puts "Extracting layers..."
|
|
55
|
+
sleep(0.3)
|
|
56
|
+
@extracted_data = "Extracted: #{layer1}, #{layer2}, #{layer3}"
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
class VerifyImage < Taski::Task
|
|
61
|
+
exports :verification_result
|
|
62
|
+
|
|
63
|
+
def run
|
|
64
|
+
data = ExtractLayers.extracted_data
|
|
65
|
+
|
|
66
|
+
puts "Verifying image..."
|
|
67
|
+
sleep(0.2)
|
|
68
|
+
@verification_result = "Verified: #{data}"
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
puts "=== Simple Progress Display Demo ==="
|
|
73
|
+
puts "Progress mode: #{Taski.progress_mode}"
|
|
74
|
+
puts ""
|
|
75
|
+
|
|
76
|
+
# Execute the final task (all dependencies will be resolved automatically)
|
|
77
|
+
result = VerifyImage.verification_result
|
|
78
|
+
|
|
79
|
+
puts "\n\nFinal result:"
|
|
80
|
+
puts result
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative "../lib/taski"
|
|
5
|
+
|
|
6
|
+
# Demo: Subprocess output capture with system()
|
|
7
|
+
#
|
|
8
|
+
# This example demonstrates how Taski captures output from system() calls
|
|
9
|
+
# and displays them in the progress spinner.
|
|
10
|
+
# Run with: ruby examples/system_call_demo.rb
|
|
11
|
+
|
|
12
|
+
class SlowOutputTask < Taski::Task
|
|
13
|
+
exports :success
|
|
14
|
+
|
|
15
|
+
def run
|
|
16
|
+
puts "Running command with streaming output..."
|
|
17
|
+
# Use a command that produces output over time
|
|
18
|
+
@success = system("for i in 1 2 3 4 5; do echo \"Processing step $i...\"; sleep 0.3; done")
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
class AnotherSlowTask < Taski::Task
|
|
23
|
+
exports :result
|
|
24
|
+
|
|
25
|
+
def run
|
|
26
|
+
puts "Running another slow command..."
|
|
27
|
+
@result = system("for i in A B C; do echo \"Stage $i complete\"; sleep 0.4; done")
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
class MainTask < Taski::Task
|
|
32
|
+
exports :summary
|
|
33
|
+
|
|
34
|
+
def run
|
|
35
|
+
puts "Starting main task..."
|
|
36
|
+
slow1 = SlowOutputTask.success
|
|
37
|
+
slow2 = AnotherSlowTask.result
|
|
38
|
+
@summary = {slow1: slow1, slow2: slow2}
|
|
39
|
+
puts "All done!"
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
puts "=" * 60
|
|
44
|
+
puts "Subprocess Output Capture Demo"
|
|
45
|
+
puts "Watch the spinner show system() output in real-time!"
|
|
46
|
+
puts "=" * 60
|
|
47
|
+
puts
|
|
48
|
+
|
|
49
|
+
Taski.progress_display&.start
|
|
50
|
+
result = MainTask.summary
|
|
51
|
+
Taski.progress_display&.stop
|
|
52
|
+
|
|
53
|
+
puts
|
|
54
|
+
puts "=" * 60
|
|
55
|
+
puts "Result: #{result.inspect}"
|
|
56
|
+
puts "=" * 60
|
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
require "monitor"
|
|
4
4
|
|
|
5
5
|
module Taski
|
|
6
|
-
# Runtime
|
|
6
|
+
# Runtime arguments accessible from any task.
|
|
7
7
|
# Holds user-defined options and execution metadata.
|
|
8
|
-
#
|
|
9
|
-
class
|
|
8
|
+
# Args is immutable after creation - options cannot be modified during task execution.
|
|
9
|
+
class Args
|
|
10
10
|
attr_reader :started_at, :working_directory, :root_task
|
|
11
11
|
|
|
12
12
|
# @param options [Hash] User-defined options (immutable after creation)
|