taski 0.5.0 → 0.7.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 +4 -4
- data/CHANGELOG.md +50 -0
- data/README.md +40 -21
- data/docs/GUIDE.md +340 -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/group_demo.rb +113 -0
- data/examples/reexecution_demo.rb +93 -80
- data/examples/system_call_demo.rb +56 -0
- data/lib/taski/{context.rb → args.rb} +3 -3
- data/lib/taski/execution/execution_context.rb +379 -0
- data/lib/taski/execution/executor.rb +408 -117
- data/lib/taski/execution/registry.rb +17 -1
- data/lib/taski/execution/scheduler.rb +308 -0
- data/lib/taski/execution/task_output_pipe.rb +42 -0
- data/lib/taski/execution/task_output_router.rb +216 -0
- data/lib/taski/execution/task_wrapper.rb +210 -40
- data/lib/taski/execution/tree_progress_display.rb +385 -98
- data/lib/taski/execution/worker_pool.rb +104 -0
- data/lib/taski/section.rb +16 -3
- data/lib/taski/task.rb +222 -36
- data/lib/taski/version.rb +1 -1
- data/lib/taski.rb +138 -23
- data/sig/taski.rbs +207 -27
- metadata +13 -7
- 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,36 +1,36 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
|
-
# Taski
|
|
4
|
+
# Taski Args API Example
|
|
5
5
|
#
|
|
6
|
-
# This example demonstrates the
|
|
6
|
+
# This example demonstrates the Args API for accessing runtime information:
|
|
7
7
|
# - working_directory: Where execution started
|
|
8
8
|
# - started_at: When execution began
|
|
9
9
|
# - root_task: The first task class that was called
|
|
10
|
-
# - User-defined options: Custom values passed via run(
|
|
10
|
+
# - User-defined options: Custom values passed via run(args: {...})
|
|
11
11
|
#
|
|
12
|
-
# Run: ruby examples/
|
|
12
|
+
# Run: ruby examples/args_demo.rb
|
|
13
13
|
|
|
14
14
|
require_relative "../lib/taski"
|
|
15
15
|
|
|
16
|
-
puts "Taski
|
|
16
|
+
puts "Taski Args API Example"
|
|
17
17
|
puts "=" * 40
|
|
18
18
|
|
|
19
|
-
# Task that uses
|
|
19
|
+
# Task that uses args information for logging
|
|
20
20
|
class SetupTask < Taski::Task
|
|
21
21
|
exports :setup_info
|
|
22
22
|
|
|
23
23
|
def run
|
|
24
24
|
puts "Setup running..."
|
|
25
|
-
puts " Working directory: #{Taski.
|
|
26
|
-
puts " Started at: #{Taski.
|
|
27
|
-
puts " Root task: #{Taski.
|
|
28
|
-
puts " Environment: #{Taski.
|
|
25
|
+
puts " Working directory: #{Taski.args.working_directory}"
|
|
26
|
+
puts " Started at: #{Taski.args.started_at}"
|
|
27
|
+
puts " Root task: #{Taski.args.root_task}"
|
|
28
|
+
puts " Environment: #{Taski.args[:env]}"
|
|
29
29
|
|
|
30
30
|
@setup_info = {
|
|
31
|
-
directory: Taski.
|
|
32
|
-
timestamp: Taski.
|
|
33
|
-
env: Taski.
|
|
31
|
+
directory: Taski.args.working_directory,
|
|
32
|
+
timestamp: Taski.args.started_at,
|
|
33
|
+
env: Taski.args[:env]
|
|
34
34
|
}
|
|
35
35
|
end
|
|
36
36
|
end
|
|
@@ -40,9 +40,9 @@ class FileProcessor < Taski::Task
|
|
|
40
40
|
exports :output_path
|
|
41
41
|
|
|
42
42
|
def run
|
|
43
|
-
# Use
|
|
44
|
-
base_dir = Taski.
|
|
45
|
-
env = Taski.
|
|
43
|
+
# Use args to determine output location
|
|
44
|
+
base_dir = Taski.args.working_directory
|
|
45
|
+
env = Taski.args.fetch(:env, "development")
|
|
46
46
|
@output_path = File.join(base_dir, "tmp", env, "output.txt")
|
|
47
47
|
|
|
48
48
|
puts "FileProcessor: Would write to #{@output_path}"
|
|
@@ -55,12 +55,12 @@ class TimingTask < Taski::Task
|
|
|
55
55
|
exports :duration_info
|
|
56
56
|
|
|
57
57
|
def run
|
|
58
|
-
start_time = Taski.
|
|
58
|
+
start_time = Taski.args.started_at
|
|
59
59
|
current_time = Time.now
|
|
60
60
|
elapsed = current_time - start_time
|
|
61
61
|
|
|
62
62
|
puts "TimingTask: #{elapsed.round(3)}s since execution started"
|
|
63
|
-
puts " Debug mode: #{Taski.
|
|
63
|
+
puts " Debug mode: #{Taski.args.fetch(:debug, false)}"
|
|
64
64
|
|
|
65
65
|
@duration_info = {
|
|
66
66
|
started: start_time,
|
|
@@ -76,8 +76,8 @@ class MainTask < Taski::Task
|
|
|
76
76
|
|
|
77
77
|
def run
|
|
78
78
|
puts "\nMainTask executing..."
|
|
79
|
-
puts " Root task is: #{Taski.
|
|
80
|
-
puts " Environment: #{Taski.
|
|
79
|
+
puts " Root task is: #{Taski.args.root_task}"
|
|
80
|
+
puts " Environment: #{Taski.args[:env]}"
|
|
81
81
|
|
|
82
82
|
# Access dependencies
|
|
83
83
|
setup = SetupTask.setup_info
|
|
@@ -88,7 +88,7 @@ class MainTask < Taski::Task
|
|
|
88
88
|
setup: setup,
|
|
89
89
|
output_path: output,
|
|
90
90
|
timing: timing,
|
|
91
|
-
root_task: Taski.
|
|
91
|
+
root_task: Taski.args.root_task.to_s
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
puts "\nExecution Summary:"
|
|
@@ -98,15 +98,15 @@ class MainTask < Taski::Task
|
|
|
98
98
|
end
|
|
99
99
|
end
|
|
100
100
|
|
|
101
|
-
puts "\n1. Running MainTask with
|
|
101
|
+
puts "\n1. Running MainTask with args options"
|
|
102
102
|
puts "-" * 40
|
|
103
|
-
MainTask.run(
|
|
103
|
+
MainTask.run(args: {env: "production", debug: true})
|
|
104
104
|
|
|
105
105
|
puts "\n" + "=" * 40
|
|
106
|
-
puts "\n2. Running SetupTask directly with different
|
|
106
|
+
puts "\n2. Running SetupTask directly with different args"
|
|
107
107
|
puts "-" * 40
|
|
108
108
|
SetupTask.reset!
|
|
109
|
-
SetupTask.run(
|
|
109
|
+
SetupTask.run(args: {env: "staging"})
|
|
110
110
|
|
|
111
111
|
puts "\n" + "=" * 40
|
|
112
112
|
puts "\n3. Dependency Tree"
|
|
@@ -114,5 +114,5 @@ puts "-" * 40
|
|
|
114
114
|
puts MainTask.tree
|
|
115
115
|
|
|
116
116
|
puts "\n" + "=" * 40
|
|
117
|
-
puts "
|
|
118
|
-
puts "Note:
|
|
117
|
+
puts "Args API demonstration complete!"
|
|
118
|
+
puts "Note: Args provides runtime information and user options without affecting dependency analysis."
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Taski Clean Demo
|
|
5
|
+
#
|
|
6
|
+
# This example demonstrates the clean functionality:
|
|
7
|
+
# - Defining clean methods for resource cleanup
|
|
8
|
+
# - Reverse dependency order execution (dependents cleaned before dependencies)
|
|
9
|
+
# - Parallel clean execution when possible
|
|
10
|
+
#
|
|
11
|
+
# Run: ruby examples/clean_demo.rb
|
|
12
|
+
|
|
13
|
+
require_relative "../lib/taski"
|
|
14
|
+
|
|
15
|
+
puts "Taski Clean Demo"
|
|
16
|
+
puts "=" * 40
|
|
17
|
+
|
|
18
|
+
# Simulated build artifact paths
|
|
19
|
+
BUILD_DIR = "/tmp/taski_clean_demo"
|
|
20
|
+
|
|
21
|
+
# Base task: creates a build directory
|
|
22
|
+
class SetupBuildDir < Taski::Task
|
|
23
|
+
exports :build_path
|
|
24
|
+
|
|
25
|
+
##
|
|
26
|
+
# Prepares the task's build directory and simulates its creation.
|
|
27
|
+
#
|
|
28
|
+
# Sets the task's exported `build_path` to the "build" subdirectory under `BUILD_DIR`
|
|
29
|
+
# and performs a simulated creation step with informational output.
|
|
30
|
+
def run
|
|
31
|
+
@build_path = "#{BUILD_DIR}/build"
|
|
32
|
+
puts "[SetupBuildDir] Creating build directory: #{@build_path}"
|
|
33
|
+
# Simulated directory creation
|
|
34
|
+
sleep 0.8
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
##
|
|
38
|
+
# Removes the directory referenced by @build_path.
|
|
39
|
+
# Cleans up build artifacts created by this task.
|
|
40
|
+
def clean
|
|
41
|
+
puts "[SetupBuildDir] Removing build directory: #{@build_path}"
|
|
42
|
+
# Simulated directory removal
|
|
43
|
+
sleep 0.8
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Compiles source code (depends on build directory)
|
|
48
|
+
class CompileSource < Taski::Task
|
|
49
|
+
exports :binary_path
|
|
50
|
+
|
|
51
|
+
##
|
|
52
|
+
# Compiles source artifacts and sets the compiled binary path for downstream tasks.
|
|
53
|
+
# This method sets @binary_path to the build directory's "app.bin" file and emits a console message indicating the compilation target.
|
|
54
|
+
def run
|
|
55
|
+
build_dir = SetupBuildDir.build_path
|
|
56
|
+
@binary_path = "#{build_dir}/app.bin"
|
|
57
|
+
puts "[CompileSource] Compiling to: #{@binary_path}"
|
|
58
|
+
sleep 1.5
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
##
|
|
62
|
+
# Removes the compiled binary produced by this task.
|
|
63
|
+
# Performs the task's cleanup step and simulates the deletion process.
|
|
64
|
+
def clean
|
|
65
|
+
puts "[CompileSource] Removing compiled binary: #{@binary_path}"
|
|
66
|
+
sleep 0.6
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Generates documentation (depends on build directory)
|
|
71
|
+
class GenerateDocs < Taski::Task
|
|
72
|
+
exports :docs_path
|
|
73
|
+
|
|
74
|
+
##
|
|
75
|
+
# Generates documentation for the build and records the output path.
|
|
76
|
+
#
|
|
77
|
+
# Sets the task's `@docs_path` to the "docs" subdirectory under `SetupBuildDir.build_path`
|
|
78
|
+
# and prints a progress message to STDOUT.
|
|
79
|
+
def run
|
|
80
|
+
build_dir = SetupBuildDir.build_path
|
|
81
|
+
@docs_path = "#{build_dir}/docs"
|
|
82
|
+
puts "[GenerateDocs] Generating docs to: #{@docs_path}"
|
|
83
|
+
sleep 1.2
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
##
|
|
87
|
+
# Removes the generated documentation at the task's docs_path.
|
|
88
|
+
# Prints a removal message for @docs_path and simulates its deletion.
|
|
89
|
+
def clean
|
|
90
|
+
puts "[GenerateDocs] Removing generated docs: #{@docs_path}"
|
|
91
|
+
sleep 0.5
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Creates release package (depends on both compiled source and docs)
|
|
96
|
+
class CreateRelease < Taski::Task
|
|
97
|
+
exports :release_path
|
|
98
|
+
|
|
99
|
+
##
|
|
100
|
+
# Creates the release package path and announces the included artifacts.
|
|
101
|
+
# Uses CompileSource.binary_path and GenerateDocs.docs_path to determine the package inputs, sets @release_path to "#{BUILD_DIR}/release.zip", and prints the binary, docs, and output paths.
|
|
102
|
+
def run
|
|
103
|
+
binary = CompileSource.binary_path
|
|
104
|
+
docs = GenerateDocs.docs_path
|
|
105
|
+
@release_path = "#{BUILD_DIR}/release.zip"
|
|
106
|
+
puts "[CreateRelease] Creating release package with:"
|
|
107
|
+
puts " - Binary: #{binary}"
|
|
108
|
+
puts " - Docs: #{docs}"
|
|
109
|
+
puts " - Output: #{@release_path}"
|
|
110
|
+
sleep 0.7
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
##
|
|
114
|
+
# Removes the release package produced by this task.
|
|
115
|
+
# Uses the task's `@release_path` as the target for cleanup.
|
|
116
|
+
def clean
|
|
117
|
+
puts "[CreateRelease] Removing release package: #{@release_path}"
|
|
118
|
+
sleep 0.5
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
puts "\n--- Task Tree Structure ---"
|
|
123
|
+
puts CreateRelease.tree
|
|
124
|
+
puts
|
|
125
|
+
|
|
126
|
+
puts "--- Running build process ---"
|
|
127
|
+
CreateRelease.run
|
|
128
|
+
puts
|
|
129
|
+
|
|
130
|
+
puts "Build completed!"
|
|
131
|
+
puts " Release: #{CreateRelease.release_path}"
|
|
132
|
+
puts
|
|
133
|
+
|
|
134
|
+
puts "--- Cleaning up (reverse dependency order) ---"
|
|
135
|
+
puts "Note: CreateRelease cleans first, then CompileSource/GenerateDocs in parallel,"
|
|
136
|
+
puts " and finally SetupBuildDir cleans last."
|
|
137
|
+
puts
|
|
138
|
+
|
|
139
|
+
# Reset to allow clean execution
|
|
140
|
+
Taski::Task.reset!
|
|
141
|
+
|
|
142
|
+
# Re-run to set up state for clean
|
|
143
|
+
CreateRelease.run
|
|
144
|
+
|
|
145
|
+
puts "\nNow cleaning..."
|
|
146
|
+
CreateRelease.clean
|
|
147
|
+
|
|
148
|
+
puts
|
|
149
|
+
puts "Clean completed! All artifacts removed."
|
|
150
|
+
|
|
151
|
+
puts
|
|
152
|
+
puts "=" * 40
|
|
153
|
+
puts "run_and_clean Demo"
|
|
154
|
+
puts "=" * 40
|
|
155
|
+
puts
|
|
156
|
+
puts "The run_and_clean method executes both phases in a single operation."
|
|
157
|
+
puts "Key benefits:"
|
|
158
|
+
puts " - Single progress display session for both phases"
|
|
159
|
+
puts " - Clean always runs, even if run fails (resource release)"
|
|
160
|
+
puts " - Cleaner API for the common use case"
|
|
161
|
+
puts
|
|
162
|
+
|
|
163
|
+
# Reset for new demonstration
|
|
164
|
+
Taski::Task.reset!
|
|
165
|
+
|
|
166
|
+
puts "--- Using run_and_clean ---"
|
|
167
|
+
puts "This is equivalent to calling run followed by clean, but in a single operation."
|
|
168
|
+
puts
|
|
169
|
+
|
|
170
|
+
CreateRelease.run_and_clean
|
|
171
|
+
|
|
172
|
+
puts
|
|
173
|
+
puts "Both run and clean phases completed in a single call!"
|
|
174
|
+
puts
|
|
175
|
+
|
|
176
|
+
# Demonstrate error handling
|
|
177
|
+
Taski::Task.reset!
|
|
178
|
+
|
|
179
|
+
puts "--- Error Handling Demo ---"
|
|
180
|
+
puts "When run fails, clean is still executed for resource release."
|
|
181
|
+
puts
|
|
182
|
+
|
|
183
|
+
# Task that fails during run
|
|
184
|
+
class FailingBuild < Taski::Task
|
|
185
|
+
exports :result
|
|
186
|
+
|
|
187
|
+
def run
|
|
188
|
+
puts "[FailingBuild] Starting build..."
|
|
189
|
+
raise StandardError, "Build failed: missing dependencies"
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def clean
|
|
193
|
+
puts "[FailingBuild] Cleaning up partial build artifacts..."
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
begin
|
|
198
|
+
FailingBuild.run_and_clean
|
|
199
|
+
rescue => e
|
|
200
|
+
puts "[Error caught] #{e.message}"
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
puts
|
|
204
|
+
puts "Notice: clean was still executed despite run failing!"
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require_relative "../lib/taski"
|
|
5
|
+
|
|
6
|
+
# Demo for group blocks in progress display
|
|
7
|
+
# Run with: ruby examples/group_demo.rb
|
|
8
|
+
#
|
|
9
|
+
# Groups organize output messages within a task into logical phases,
|
|
10
|
+
# displayed as children of the task in the progress tree.
|
|
11
|
+
|
|
12
|
+
class ConfigTask < Taski::Task
|
|
13
|
+
exports :config
|
|
14
|
+
|
|
15
|
+
def run
|
|
16
|
+
group("Loading configuration") do
|
|
17
|
+
puts "Reading config file..."
|
|
18
|
+
sleep(0.2)
|
|
19
|
+
puts "Parsing YAML..."
|
|
20
|
+
sleep(0.1)
|
|
21
|
+
puts "Config loaded"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
@config = {
|
|
25
|
+
database_url: "postgresql://localhost/myapp",
|
|
26
|
+
redis_url: "redis://localhost:6379"
|
|
27
|
+
}
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
class BuildTask < Taski::Task
|
|
32
|
+
exports :artifacts
|
|
33
|
+
|
|
34
|
+
def run
|
|
35
|
+
ConfigTask.config # Ensure config is loaded
|
|
36
|
+
|
|
37
|
+
group("Compiling source") do
|
|
38
|
+
puts "Analyzing dependencies..."
|
|
39
|
+
sleep(0.2)
|
|
40
|
+
puts "Compiling 42 files..."
|
|
41
|
+
sleep(0.3)
|
|
42
|
+
puts "Compilation complete"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
group("Running tests") do
|
|
46
|
+
puts "Running unit tests..."
|
|
47
|
+
sleep(0.2)
|
|
48
|
+
puts "Running integration tests..."
|
|
49
|
+
sleep(0.2)
|
|
50
|
+
puts "All 128 tests passed"
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
group("Bundling assets") do
|
|
54
|
+
puts "Minifying JavaScript..."
|
|
55
|
+
sleep(0.1)
|
|
56
|
+
puts "Optimizing images..."
|
|
57
|
+
sleep(0.2)
|
|
58
|
+
puts "Assets bundled"
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
@artifacts = ["app.js", "app.css", "images/"]
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
class DeployTask < Taski::Task
|
|
66
|
+
exports :deploy_url
|
|
67
|
+
|
|
68
|
+
def run
|
|
69
|
+
artifacts = BuildTask.artifacts
|
|
70
|
+
|
|
71
|
+
group("Preparing deployment") do
|
|
72
|
+
puts "Creating deployment package..."
|
|
73
|
+
sleep(0.2)
|
|
74
|
+
puts "Package size: 12.5 MB"
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
group("Uploading to server") do
|
|
78
|
+
puts "Connecting to server..."
|
|
79
|
+
sleep(0.1)
|
|
80
|
+
puts "Uploading #{artifacts.size} artifacts..."
|
|
81
|
+
sleep(0.3)
|
|
82
|
+
puts "Upload complete"
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
group("Starting application") do
|
|
86
|
+
puts "Stopping old instance..."
|
|
87
|
+
sleep(0.1)
|
|
88
|
+
puts "Starting new instance..."
|
|
89
|
+
sleep(0.2)
|
|
90
|
+
puts "Health check passed"
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
@deploy_url = "https://app.example.com"
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Show tree structure before execution
|
|
98
|
+
puts "Task Tree Structure:"
|
|
99
|
+
puts "=" * 60
|
|
100
|
+
puts DeployTask.tree
|
|
101
|
+
puts "=" * 60
|
|
102
|
+
puts
|
|
103
|
+
|
|
104
|
+
# Reset for execution
|
|
105
|
+
DeployTask.reset!
|
|
106
|
+
|
|
107
|
+
# Execute with progress display
|
|
108
|
+
url = DeployTask.deploy_url
|
|
109
|
+
|
|
110
|
+
puts "\n"
|
|
111
|
+
puts "=" * 60
|
|
112
|
+
puts "Deployment completed!"
|
|
113
|
+
puts "Application URL: #{url}"
|
|
@@ -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!"
|