taski 0.8.0 → 0.8.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 +9 -0
- data/examples/message_demo.rb +57 -0
- data/lib/taski/execution/execution_context.rb +26 -0
- data/lib/taski/execution/executor.rb +8 -0
- data/lib/taski/execution/simple_progress_display.rb +28 -1
- data/lib/taski/version.rb +1 -1
- data/lib/taski.rb +24 -1
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7c841d7a7a8e632045f42b090be2c6b10a9ef1510c9b9298bc50737f89486f28
|
|
4
|
+
data.tar.gz: 540f4595d667f191ac2a36464529e324fd56c5a5466dbcc2580b5227030501b5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c4b2064e55edde2f504fd8c857efff572faf6de8466983c0081c2ca902c63784c75d121c482691cf24e1b8f80a863c97c2268e358271692967adc5612933da28
|
|
7
|
+
data.tar.gz: b56270d6324127635290a906bb6d7361a8c9609ad071ea307b26dcd3b16489c073be909e744680b52a202ab99019c611d9a0112cf2b18fc180f8113fb3fcef5a
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.8.1] - 2026-01-26
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- `Taski.message` API for user-facing output during task execution ([#129](https://github.com/ahogappa/taski/pull/129))
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
- Count unselected section candidates as completed in SimpleProgressDisplay ([#128](https://github.com/ahogappa/taski/pull/128))
|
|
17
|
+
- Prioritize environment variable over code settings for progress_mode ([#127](https://github.com/ahogappa/taski/pull/127))
|
|
18
|
+
|
|
10
19
|
## [0.8.0] - 2026-01-23
|
|
11
20
|
|
|
12
21
|
### Added
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Demonstrates Taski.message API
|
|
5
|
+
#
|
|
6
|
+
# Taski.message outputs text to the user without being captured by TaskOutputRouter.
|
|
7
|
+
# Messages are queued during progress display and shown after task completion.
|
|
8
|
+
#
|
|
9
|
+
# Usage:
|
|
10
|
+
# ruby examples/message_demo.rb
|
|
11
|
+
# TASKI_FORCE_PROGRESS=1 ruby examples/message_demo.rb # Force progress display
|
|
12
|
+
|
|
13
|
+
require_relative "../lib/taski"
|
|
14
|
+
|
|
15
|
+
class ProcessDataTask < Taski::Task
|
|
16
|
+
exports :processed_count
|
|
17
|
+
|
|
18
|
+
def run
|
|
19
|
+
puts "Starting data processing..." # Captured by TaskOutputRouter
|
|
20
|
+
|
|
21
|
+
# Simulate processing
|
|
22
|
+
5.times do |i|
|
|
23
|
+
puts "Processing batch #{i + 1}/5..." # Captured
|
|
24
|
+
sleep 0.3
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
@processed_count = 42
|
|
28
|
+
|
|
29
|
+
# These messages bypass TaskOutputRouter and appear after execution
|
|
30
|
+
Taski.message("Created: /tmp/output.txt")
|
|
31
|
+
Taski.message("Summary: #{@processed_count} items processed successfully")
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
class GenerateReportTask < Taski::Task
|
|
36
|
+
exports :report_path
|
|
37
|
+
|
|
38
|
+
def run
|
|
39
|
+
# Dependency: ProcessDataTask will be executed first
|
|
40
|
+
count = ProcessDataTask.processed_count
|
|
41
|
+
puts "Generating report for #{count} items..." # Captured
|
|
42
|
+
|
|
43
|
+
sleep 0.5
|
|
44
|
+
|
|
45
|
+
@report_path = "/tmp/report.pdf"
|
|
46
|
+
|
|
47
|
+
Taski.message("Report available at: #{@report_path}")
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
puts "=== Taski.message Demo ==="
|
|
52
|
+
puts
|
|
53
|
+
|
|
54
|
+
GenerateReportTask.run
|
|
55
|
+
|
|
56
|
+
puts
|
|
57
|
+
puts "=== Done ==="
|
|
@@ -81,6 +81,7 @@ module Taski
|
|
|
81
81
|
@output_capture = nil
|
|
82
82
|
@original_stdout = nil
|
|
83
83
|
@runtime_dependencies = {}
|
|
84
|
+
@message_queue = []
|
|
84
85
|
end
|
|
85
86
|
|
|
86
87
|
# Check if output capture is already active.
|
|
@@ -89,6 +90,31 @@ module Taski
|
|
|
89
90
|
@monitor.synchronize { !@output_capture.nil? }
|
|
90
91
|
end
|
|
91
92
|
|
|
93
|
+
# Queue a message to be displayed after execution completes.
|
|
94
|
+
# Thread-safe for access from worker threads.
|
|
95
|
+
#
|
|
96
|
+
# @param text [String] The message text to queue
|
|
97
|
+
def queue_message(text)
|
|
98
|
+
@monitor.synchronize { @message_queue << text }
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Flush all queued messages to the given output.
|
|
102
|
+
# Clears the queue after flushing.
|
|
103
|
+
#
|
|
104
|
+
# @param output [IO] The output stream to write messages to
|
|
105
|
+
def flush_messages(output)
|
|
106
|
+
messages = @monitor.synchronize { @message_queue.dup.tap { @message_queue.clear } }
|
|
107
|
+
messages.each { |msg| output.puts(msg) }
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Get the original stdout before output capture was set up.
|
|
111
|
+
# Thread-safe accessor.
|
|
112
|
+
#
|
|
113
|
+
# @return [IO, nil] The original stdout or nil if not captured
|
|
114
|
+
def original_stdout
|
|
115
|
+
@monitor.synchronize { @original_stdout }
|
|
116
|
+
end
|
|
117
|
+
|
|
92
118
|
# Set up output capture for inline progress display.
|
|
93
119
|
# Creates TaskOutputRouter and replaces $stdout.
|
|
94
120
|
# Should only be called when progress display is active and not already set up.
|
|
@@ -418,9 +418,17 @@ module Taski
|
|
|
418
418
|
ensure
|
|
419
419
|
stop_progress_display
|
|
420
420
|
@saved_output_capture = @execution_context.output_capture
|
|
421
|
+
flush_queued_messages if should_teardown_capture
|
|
421
422
|
teardown_output_capture if should_teardown_capture
|
|
422
423
|
end
|
|
423
424
|
|
|
425
|
+
# Flush queued messages from ExecutionContext to original stdout.
|
|
426
|
+
# Called after progress display stops to show user messages.
|
|
427
|
+
def flush_queued_messages
|
|
428
|
+
output = @execution_context.original_stdout || $stdout
|
|
429
|
+
@execution_context.flush_messages(output)
|
|
430
|
+
end
|
|
431
|
+
|
|
424
432
|
def create_default_execution_context
|
|
425
433
|
context = ExecutionContext.new
|
|
426
434
|
progress = Taski.progress_display
|
|
@@ -34,6 +34,7 @@ module Taski
|
|
|
34
34
|
@spinner_index = 0
|
|
35
35
|
@renderer_thread = nil
|
|
36
36
|
@running = false
|
|
37
|
+
@section_candidates = {} # section_class => [candidate_classes]
|
|
37
38
|
end
|
|
38
39
|
|
|
39
40
|
protected
|
|
@@ -44,9 +45,18 @@ module Taski
|
|
|
44
45
|
end
|
|
45
46
|
|
|
46
47
|
# Template method: Called when a section impl is registered
|
|
47
|
-
def on_section_impl_registered(
|
|
48
|
+
def on_section_impl_registered(section_class, impl_class)
|
|
48
49
|
@tasks[impl_class] ||= TaskProgress.new
|
|
49
50
|
@tasks[impl_class].is_impl_candidate = false
|
|
51
|
+
|
|
52
|
+
# Mark unselected candidates as completed (skipped)
|
|
53
|
+
candidates = @section_candidates[section_class] || []
|
|
54
|
+
candidates.each do |candidate|
|
|
55
|
+
next if candidate == impl_class
|
|
56
|
+
progress = @tasks[candidate]
|
|
57
|
+
next unless progress
|
|
58
|
+
progress.run_state = :completed
|
|
59
|
+
end
|
|
50
60
|
end
|
|
51
61
|
|
|
52
62
|
# Template method: Determine if display should activate
|
|
@@ -83,6 +93,23 @@ module Taski
|
|
|
83
93
|
# Use TreeProgressDisplay's static method for tree building
|
|
84
94
|
tree = TreeProgressDisplay.build_tree_node(@root_task_class)
|
|
85
95
|
register_tasks_from_tree(tree)
|
|
96
|
+
collect_section_candidates(tree)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def collect_section_candidates(node)
|
|
100
|
+
return unless node
|
|
101
|
+
|
|
102
|
+
task_class = node[:task_class]
|
|
103
|
+
|
|
104
|
+
# If this is a section, collect its implementation candidates
|
|
105
|
+
if node[:is_section]
|
|
106
|
+
candidates = node[:children]
|
|
107
|
+
.select { |c| c[:is_impl_candidate] }
|
|
108
|
+
.map { |c| c[:task_class] }
|
|
109
|
+
@section_candidates[task_class] = candidates unless candidates.empty?
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
node[:children].each { |child| collect_section_candidates(child) }
|
|
86
113
|
end
|
|
87
114
|
|
|
88
115
|
def render_live
|
data/lib/taski/version.rb
CHANGED
data/lib/taski.rb
CHANGED
|
@@ -140,6 +140,7 @@ module Taski
|
|
|
140
140
|
|
|
141
141
|
@args_monitor = Monitor.new
|
|
142
142
|
@env_monitor = Monitor.new
|
|
143
|
+
@message_monitor = Monitor.new
|
|
143
144
|
|
|
144
145
|
# Get the current runtime arguments
|
|
145
146
|
# @return [Args, nil] The current args or nil if no task is running
|
|
@@ -153,6 +154,23 @@ module Taski
|
|
|
153
154
|
@env_monitor.synchronize { @env }
|
|
154
155
|
end
|
|
155
156
|
|
|
157
|
+
# Output a message to the user without being captured by TaskOutputRouter.
|
|
158
|
+
# During task execution with progress display, messages are queued and
|
|
159
|
+
# displayed after execution completes. Without progress display or outside
|
|
160
|
+
# task execution, messages are output immediately.
|
|
161
|
+
#
|
|
162
|
+
# @param text [String] The message text to display
|
|
163
|
+
def self.message(text)
|
|
164
|
+
@message_monitor.synchronize do
|
|
165
|
+
context = Execution::ExecutionContext.current
|
|
166
|
+
if context&.output_capture_active?
|
|
167
|
+
context.queue_message(text)
|
|
168
|
+
else
|
|
169
|
+
$stdout.puts(text)
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
|
|
156
174
|
# Start new execution environment (internal use only)
|
|
157
175
|
# @api private
|
|
158
176
|
# @return [Boolean] true if this call created the env, false if env already existed
|
|
@@ -229,9 +247,14 @@ module Taski
|
|
|
229
247
|
end
|
|
230
248
|
|
|
231
249
|
# Get the current progress mode (:tree or :simple)
|
|
250
|
+
# Environment variable TASKI_PROGRESS_MODE takes precedence over code settings.
|
|
232
251
|
# @return [Symbol] The current progress mode
|
|
233
252
|
def self.progress_mode
|
|
234
|
-
|
|
253
|
+
if ENV["TASKI_PROGRESS_MODE"]
|
|
254
|
+
progress_mode_from_env
|
|
255
|
+
else
|
|
256
|
+
@progress_mode || :tree
|
|
257
|
+
end
|
|
235
258
|
end
|
|
236
259
|
|
|
237
260
|
# Set the progress mode (:tree or :simple)
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: taski
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.8.
|
|
4
|
+
version: 0.8.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- ahogappa
|
|
@@ -62,6 +62,7 @@ files:
|
|
|
62
62
|
- examples/data_pipeline_demo.rb
|
|
63
63
|
- examples/group_demo.rb
|
|
64
64
|
- examples/large_tree_demo.rb
|
|
65
|
+
- examples/message_demo.rb
|
|
65
66
|
- examples/nested_section_demo.rb
|
|
66
67
|
- examples/parallel_progress_demo.rb
|
|
67
68
|
- examples/quick_start.rb
|
|
@@ -120,7 +121,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
120
121
|
- !ruby/object:Gem::Version
|
|
121
122
|
version: '0'
|
|
122
123
|
requirements: []
|
|
123
|
-
rubygems_version: 4.0.
|
|
124
|
+
rubygems_version: 4.0.4
|
|
124
125
|
specification_version: 4
|
|
125
126
|
summary: A simple yet powerful Ruby task runner with static dependency resolution
|
|
126
127
|
(in development).
|