taski 0.4.2 → 0.5.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/README.md +12 -13
- data/Steepfile +1 -0
- data/examples/README.md +3 -3
- data/examples/data_pipeline_demo.rb +3 -3
- data/examples/nested_section_demo.rb +161 -0
- data/examples/parallel_progress_demo.rb +1 -1
- data/examples/section_progress_demo.rb +78 -0
- data/examples/tree_progress_demo.rb +164 -0
- data/lib/taski/execution/executor.rb +247 -0
- data/lib/taski/execution/registry.rb +9 -1
- data/lib/taski/execution/task_wrapper.rb +126 -147
- data/lib/taski/execution/tree_progress_display.rb +506 -0
- data/lib/taski/section.rb +10 -0
- data/lib/taski/static_analysis/analyzer.rb +4 -2
- data/lib/taski/static_analysis/visitor.rb +86 -5
- data/lib/taski/task.rb +11 -94
- data/lib/taski/version.rb +1 -1
- data/lib/taski.rb +10 -6
- data/sig/taski.rbs +127 -64
- metadata +6 -3
- data/lib/taski/execution/coordinator.rb +0 -63
- data/lib/taski/execution/parallel_progress_display.rb +0 -201
data/lib/taski/task.rb
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
require_relative "static_analysis/analyzer"
|
|
4
4
|
require_relative "execution/registry"
|
|
5
|
-
require_relative "execution/coordinator"
|
|
6
5
|
require_relative "execution/task_wrapper"
|
|
7
6
|
|
|
8
7
|
module Taski
|
|
@@ -24,11 +23,16 @@ module Taski
|
|
|
24
23
|
# Each call creates a fresh TaskWrapper instance for re-execution support.
|
|
25
24
|
# Use class methods (e.g., MyTask.result) for cached single execution.
|
|
26
25
|
def new
|
|
27
|
-
Execution::
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
fresh_registry = Execution::Registry.new
|
|
27
|
+
task_instance = allocate
|
|
28
|
+
task_instance.send(:initialize)
|
|
29
|
+
wrapper = Execution::TaskWrapper.new(
|
|
30
|
+
task_instance,
|
|
31
|
+
registry: fresh_registry
|
|
31
32
|
)
|
|
33
|
+
# Pre-register to prevent Executor from creating a duplicate wrapper
|
|
34
|
+
fresh_registry.register(self, wrapper)
|
|
35
|
+
wrapper
|
|
32
36
|
end
|
|
33
37
|
|
|
34
38
|
def cached_dependencies
|
|
@@ -55,105 +59,19 @@ module Taski
|
|
|
55
59
|
Taski.global_registry
|
|
56
60
|
end
|
|
57
61
|
|
|
58
|
-
def coordinator
|
|
59
|
-
@coordinator ||= Execution::Coordinator.new(
|
|
60
|
-
registry: registry,
|
|
61
|
-
analyzer: StaticAnalysis::Analyzer
|
|
62
|
-
)
|
|
63
|
-
end
|
|
64
|
-
|
|
65
62
|
def reset!
|
|
66
63
|
registry.reset!
|
|
67
64
|
Taski.reset_global_registry!
|
|
68
65
|
Taski.reset_context!
|
|
69
|
-
@coordinator = nil
|
|
70
66
|
@circular_dependency_checked = false
|
|
71
67
|
end
|
|
72
68
|
|
|
73
69
|
def tree
|
|
74
|
-
|
|
70
|
+
Execution::TreeProgressDisplay.render_static_tree(self)
|
|
75
71
|
end
|
|
76
72
|
|
|
77
73
|
private
|
|
78
74
|
|
|
79
|
-
# ANSI color codes
|
|
80
|
-
COLORS = {
|
|
81
|
-
reset: "\e[0m",
|
|
82
|
-
task: "\e[32m", # green
|
|
83
|
-
section: "\e[34m", # blue
|
|
84
|
-
impl: "\e[33m", # yellow
|
|
85
|
-
tree: "\e[90m", # gray
|
|
86
|
-
name: "\e[1m" # bold
|
|
87
|
-
}.freeze
|
|
88
|
-
|
|
89
|
-
def build_tree(task_class, prefix, task_index_map, is_impl, ancestors = Set.new)
|
|
90
|
-
type_label = colored_type_label(task_class)
|
|
91
|
-
impl_prefix = is_impl ? "#{COLORS[:impl]}[impl]#{COLORS[:reset]} " : ""
|
|
92
|
-
task_number = get_task_number(task_class, task_index_map)
|
|
93
|
-
name = "#{COLORS[:name]}#{task_class.name}#{COLORS[:reset]}"
|
|
94
|
-
|
|
95
|
-
# Detect circular reference
|
|
96
|
-
if ancestors.include?(task_class)
|
|
97
|
-
circular_marker = "#{COLORS[:impl]}(circular)#{COLORS[:reset]}"
|
|
98
|
-
return "#{impl_prefix}#{task_number} #{name} #{type_label} #{circular_marker}\n"
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
result = "#{impl_prefix}#{task_number} #{name} #{type_label}\n"
|
|
102
|
-
|
|
103
|
-
# Register task number if not already registered
|
|
104
|
-
task_index_map[task_class] = task_index_map.size + 1 unless task_index_map.key?(task_class)
|
|
105
|
-
|
|
106
|
-
# Add to ancestors for circular detection
|
|
107
|
-
new_ancestors = ancestors + [task_class]
|
|
108
|
-
|
|
109
|
-
# Use static analysis to include Section.impl candidates for visualization
|
|
110
|
-
dependencies = StaticAnalysis::Analyzer.analyze(task_class).to_a
|
|
111
|
-
is_section = section_class?(task_class)
|
|
112
|
-
|
|
113
|
-
dependencies.each_with_index do |dep, index|
|
|
114
|
-
is_last = (index == dependencies.size - 1)
|
|
115
|
-
result += format_dependency_branch(dep, prefix, is_last, task_index_map, is_section, new_ancestors)
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
result
|
|
119
|
-
end
|
|
120
|
-
|
|
121
|
-
def format_dependency_branch(dep, prefix, is_last, task_index_map, is_impl, ancestors)
|
|
122
|
-
connector, extension = tree_connector_chars(is_last)
|
|
123
|
-
dep_tree = build_tree(dep, "#{prefix}#{extension}", task_index_map, is_impl, ancestors)
|
|
124
|
-
|
|
125
|
-
result = "#{prefix}#{COLORS[:tree]}#{connector}#{COLORS[:reset]}"
|
|
126
|
-
lines = dep_tree.lines
|
|
127
|
-
result += lines.first
|
|
128
|
-
lines.drop(1).each { |line| result += line }
|
|
129
|
-
result
|
|
130
|
-
end
|
|
131
|
-
|
|
132
|
-
def tree_connector_chars(is_last)
|
|
133
|
-
if is_last
|
|
134
|
-
["└── ", " "]
|
|
135
|
-
else
|
|
136
|
-
["├── ", "│ "]
|
|
137
|
-
end
|
|
138
|
-
end
|
|
139
|
-
|
|
140
|
-
def get_task_number(task_class, task_index_map)
|
|
141
|
-
number = task_index_map[task_class] || (task_index_map.size + 1)
|
|
142
|
-
"#{COLORS[:tree]}[#{number}]#{COLORS[:reset]}"
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
def colored_type_label(klass)
|
|
146
|
-
if section_class?(klass)
|
|
147
|
-
"#{COLORS[:section]}(Section)#{COLORS[:reset]}"
|
|
148
|
-
else
|
|
149
|
-
"#{COLORS[:task]}(Task)#{COLORS[:reset]}"
|
|
150
|
-
end
|
|
151
|
-
end
|
|
152
|
-
|
|
153
|
-
def section_class?(klass)
|
|
154
|
-
defined?(Taski::Section) && klass < Taski::Section
|
|
155
|
-
end
|
|
156
|
-
|
|
157
75
|
# Use allocate + initialize instead of new to avoid infinite loop
|
|
158
76
|
# since new is overridden to return TaskWrapper
|
|
159
77
|
def cached_wrapper
|
|
@@ -162,8 +80,7 @@ module Taski
|
|
|
162
80
|
task_instance.send(:initialize)
|
|
163
81
|
Execution::TaskWrapper.new(
|
|
164
82
|
task_instance,
|
|
165
|
-
registry: registry
|
|
166
|
-
coordinator: coordinator
|
|
83
|
+
registry: registry
|
|
167
84
|
)
|
|
168
85
|
end
|
|
169
86
|
end
|
data/lib/taski/version.rb
CHANGED
data/lib/taski.rb
CHANGED
|
@@ -5,9 +5,9 @@ require_relative "taski/static_analysis/analyzer"
|
|
|
5
5
|
require_relative "taski/static_analysis/visitor"
|
|
6
6
|
require_relative "taski/static_analysis/dependency_graph"
|
|
7
7
|
require_relative "taski/execution/registry"
|
|
8
|
-
require_relative "taski/execution/coordinator"
|
|
9
8
|
require_relative "taski/execution/task_wrapper"
|
|
10
|
-
require_relative "taski/execution/
|
|
9
|
+
require_relative "taski/execution/executor"
|
|
10
|
+
require_relative "taski/execution/tree_progress_display"
|
|
11
11
|
require_relative "taski/context"
|
|
12
12
|
require_relative "taski/task"
|
|
13
13
|
require_relative "taski/section"
|
|
@@ -59,13 +59,17 @@ module Taski
|
|
|
59
59
|
@global_registry = nil
|
|
60
60
|
end
|
|
61
61
|
|
|
62
|
+
# Progress display is enabled by default (tree-style).
|
|
63
|
+
# Environment variables:
|
|
64
|
+
# - TASKI_PROGRESS_DISABLE=1: Disable progress display entirely
|
|
65
|
+
# - TASKI_FORCE_PROGRESS=1: Force enable even without TTY (for testing)
|
|
62
66
|
def self.progress_display
|
|
63
|
-
return nil
|
|
64
|
-
@progress_display ||= Execution::
|
|
67
|
+
return nil if progress_disabled?
|
|
68
|
+
@progress_display ||= Execution::TreeProgressDisplay.new
|
|
65
69
|
end
|
|
66
70
|
|
|
67
|
-
def self.
|
|
68
|
-
ENV["
|
|
71
|
+
def self.progress_disabled?
|
|
72
|
+
ENV["TASKI_PROGRESS_DISABLE"] == "1"
|
|
69
73
|
end
|
|
70
74
|
|
|
71
75
|
def self.reset_progress_display!
|
data/sig/taski.rbs
CHANGED
|
@@ -2,7 +2,8 @@ module Taski
|
|
|
2
2
|
VERSION: String
|
|
3
3
|
|
|
4
4
|
self.@global_registry: Execution::Registry?
|
|
5
|
-
self.@progress_display: Execution::
|
|
5
|
+
self.@progress_display: Execution::TreeProgressDisplay?
|
|
6
|
+
self.@context: Context?
|
|
6
7
|
|
|
7
8
|
# Type alias for Task class (singleton type with class methods)
|
|
8
9
|
# This represents a Class that inherits from Taski::Task
|
|
@@ -11,9 +12,12 @@ module Taski
|
|
|
11
12
|
# Module-level methods
|
|
12
13
|
def self.global_registry: () -> Execution::Registry
|
|
13
14
|
def self.reset_global_registry!: () -> void
|
|
14
|
-
def self.progress_display: () -> Execution::
|
|
15
|
-
def self.
|
|
15
|
+
def self.progress_display: () -> Execution::TreeProgressDisplay?
|
|
16
|
+
def self.progress_disabled?: () -> bool
|
|
16
17
|
def self.reset_progress_display!: () -> void
|
|
18
|
+
def self.context: () -> Hash[Symbol, untyped]
|
|
19
|
+
def self.start_context: (options: Hash[Symbol, untyped], root_task: Class) -> void
|
|
20
|
+
def self.reset_context!: () -> void
|
|
17
21
|
|
|
18
22
|
# Custom exceptions
|
|
19
23
|
class TaskAbortException < StandardError
|
|
@@ -29,23 +33,25 @@ module Taski
|
|
|
29
33
|
|
|
30
34
|
# Context class for execution context management
|
|
31
35
|
class Context
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
def
|
|
36
|
+
@options: Hash[Symbol | String, untyped]
|
|
37
|
+
@root_task: Class
|
|
38
|
+
@started_at: Time
|
|
39
|
+
@working_directory: String
|
|
40
|
+
|
|
41
|
+
attr_reader started_at: Time
|
|
42
|
+
attr_reader working_directory: String
|
|
43
|
+
attr_reader root_task: Class
|
|
44
|
+
|
|
45
|
+
def initialize: (options: Hash[Symbol | String, untyped], root_task: Class) -> void
|
|
46
|
+
def []: (Symbol | String key) -> untyped
|
|
47
|
+
def fetch: (Symbol | String key, ?untyped default) ?{ () -> untyped } -> untyped
|
|
48
|
+
def key?: (Symbol | String key) -> bool
|
|
42
49
|
end
|
|
43
50
|
|
|
44
51
|
# Main Task class
|
|
45
52
|
class Task
|
|
46
53
|
self.@exported_methods: Array[Symbol]
|
|
47
54
|
self.@dependencies_cache: Set[task_class]?
|
|
48
|
-
self.@coordinator: Execution::Coordinator?
|
|
49
55
|
self.@circular_dependency_checked: bool
|
|
50
56
|
|
|
51
57
|
# Class methods
|
|
@@ -54,10 +60,9 @@ module Taski
|
|
|
54
60
|
def self.new: () -> Execution::TaskWrapper
|
|
55
61
|
def self.cached_dependencies: () -> Set[task_class]
|
|
56
62
|
def self.clear_dependency_cache: () -> void
|
|
57
|
-
def self.run: () -> untyped
|
|
58
|
-
def self.clean: () -> void
|
|
63
|
+
def self.run: (?context: Hash[Symbol, untyped]) -> untyped
|
|
64
|
+
def self.clean: (?context: Hash[Symbol, untyped]) -> void
|
|
59
65
|
def self.registry: () -> Execution::Registry
|
|
60
|
-
def self.coordinator: () -> Execution::Coordinator
|
|
61
66
|
def self.reset!: () -> void
|
|
62
67
|
def self.tree: () -> String
|
|
63
68
|
|
|
@@ -69,9 +74,6 @@ module Taski
|
|
|
69
74
|
private
|
|
70
75
|
|
|
71
76
|
# Private class methods
|
|
72
|
-
def self.build_tree: (task_class task_class, String prefix, Set[task_class] visited) -> String
|
|
73
|
-
def self.format_dependency_branch: (task_class dep, String prefix, bool is_last, Set[task_class] visited) -> String
|
|
74
|
-
def self.tree_connector_chars: (bool is_last) -> [String, String]
|
|
75
77
|
def self.cached_wrapper: () -> Execution::TaskWrapper
|
|
76
78
|
def self.define_instance_reader: (Symbol method_name) -> void
|
|
77
79
|
def self.define_class_accessor: (Symbol method_name) -> void
|
|
@@ -119,7 +121,8 @@ module Taski
|
|
|
119
121
|
|
|
120
122
|
def initialize: () -> void
|
|
121
123
|
def get_or_create: (Class task_class) { () -> TaskWrapper } -> TaskWrapper
|
|
122
|
-
def get_task: (Class task_class) -> TaskWrapper
|
|
124
|
+
def get_task: (Class task_class) -> TaskWrapper?
|
|
125
|
+
def register: (Class task_class, TaskWrapper wrapper) -> void
|
|
123
126
|
def register_thread: (Thread thread) -> void
|
|
124
127
|
def wait_all: () -> void
|
|
125
128
|
def reset!: () -> void
|
|
@@ -128,31 +131,52 @@ module Taski
|
|
|
128
131
|
def run: (Class task_class, Array[Symbol] exported_methods) -> untyped
|
|
129
132
|
end
|
|
130
133
|
|
|
131
|
-
#
|
|
132
|
-
class
|
|
133
|
-
|
|
134
|
-
|
|
134
|
+
# Executor class for parallel task execution (Producer-Consumer pattern)
|
|
135
|
+
class Executor
|
|
136
|
+
STATE_PENDING: Symbol
|
|
137
|
+
STATE_ENQUEUED: Symbol
|
|
138
|
+
STATE_COMPLETED: Symbol
|
|
135
139
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
+
@registry: Registry
|
|
141
|
+
@worker_count: Integer
|
|
142
|
+
@execution_queue: Thread::Queue
|
|
143
|
+
@completion_queue: Thread::Queue
|
|
144
|
+
@workers: Array[Thread]
|
|
145
|
+
@dependencies: Hash[Class, Set[Class]]
|
|
146
|
+
@task_states: Hash[Class, Symbol]
|
|
147
|
+
@completed_tasks: Set[Class]
|
|
148
|
+
|
|
149
|
+
def self.execute: (Class root_task_class, registry: Registry) -> void
|
|
150
|
+
def initialize: (registry: Registry, ?worker_count: Integer?) -> void
|
|
151
|
+
def execute: (Class root_task_class) -> void
|
|
140
152
|
|
|
141
153
|
private
|
|
142
154
|
|
|
143
|
-
def
|
|
144
|
-
def
|
|
145
|
-
def
|
|
155
|
+
def default_worker_count: () -> Integer
|
|
156
|
+
def build_dependency_graph: (Class root_task_class) -> void
|
|
157
|
+
def enqueue_ready_tasks: () -> void
|
|
158
|
+
def ready_to_execute?: (Class task_class) -> bool
|
|
159
|
+
def enqueue_task: (Class task_class) -> void
|
|
160
|
+
def get_or_create_wrapper: (Class task_class) -> TaskWrapper
|
|
161
|
+
def start_workers: () -> void
|
|
162
|
+
def worker_loop: () -> void
|
|
163
|
+
def execute_task: (Class task_class, TaskWrapper wrapper) -> void
|
|
164
|
+
def run_main_loop: (Class root_task_class) -> void
|
|
165
|
+
def no_running_tasks?: () -> bool
|
|
166
|
+
def handle_completion: (Hash[Symbol, untyped] event) -> void
|
|
167
|
+
def shutdown_workers: () -> void
|
|
168
|
+
def debug_log: (String message) -> void
|
|
146
169
|
end
|
|
147
170
|
|
|
148
171
|
# TaskWrapper class for task execution
|
|
149
172
|
class TaskWrapper
|
|
150
173
|
attr_reader task: Task
|
|
151
174
|
attr_reader result: untyped
|
|
175
|
+
attr_reader error: StandardError?
|
|
176
|
+
attr_reader timing: TaskTiming?
|
|
152
177
|
|
|
153
178
|
@task: Task
|
|
154
179
|
@registry: Registry
|
|
155
|
-
@coordinator: Coordinator
|
|
156
180
|
@result: untyped
|
|
157
181
|
@clean_result: untyped
|
|
158
182
|
@error: StandardError?
|
|
@@ -167,38 +191,38 @@ module Taski
|
|
|
167
191
|
STATE_RUNNING: Symbol
|
|
168
192
|
STATE_COMPLETED: Symbol
|
|
169
193
|
|
|
170
|
-
def initialize: (Task task, registry: Registry
|
|
194
|
+
def initialize: (Task task, registry: Registry) -> void
|
|
195
|
+
def state: () -> Symbol
|
|
196
|
+
def pending?: () -> bool
|
|
197
|
+
def completed?: () -> bool
|
|
171
198
|
def run: () -> untyped
|
|
172
|
-
def clean: () ->
|
|
199
|
+
def clean: () -> untyped
|
|
173
200
|
def get_exported_value: (Symbol method_name) -> untyped
|
|
174
|
-
def
|
|
175
|
-
def
|
|
176
|
-
def
|
|
177
|
-
def
|
|
178
|
-
def
|
|
179
|
-
def wait_for_dependencies: () -> void
|
|
180
|
-
def execute_clean_if_needed: () -> void
|
|
181
|
-
def start_async_clean: () -> void
|
|
182
|
-
def execute_clean: () -> void
|
|
183
|
-
def wait_for_clean_dependencies: () -> void
|
|
184
|
-
def mark_completed: () -> void
|
|
185
|
-
def mark_clean_completed: () -> void
|
|
186
|
-
def wait_for_completion: () -> untyped
|
|
201
|
+
def mark_running: () -> bool
|
|
202
|
+
def mark_completed: (untyped result) -> void
|
|
203
|
+
def mark_failed: (StandardError error) -> void
|
|
204
|
+
def mark_clean_completed: (untyped result) -> void
|
|
205
|
+
def wait_for_completion: () -> void
|
|
187
206
|
def wait_for_clean_completion: () -> void
|
|
188
|
-
def debug_log: (String message) -> void
|
|
189
|
-
def log_start: () -> void
|
|
190
|
-
def log_completion: () -> void
|
|
191
|
-
def log_clean_start: () -> void
|
|
192
|
-
def log_clean_completion: () -> void
|
|
193
|
-
def register_with_progress_display: () -> void
|
|
194
|
-
def update_progress: (Symbol state, ?duration: Numeric?, ?error: Exception?) -> void
|
|
195
207
|
def method_missing: (Symbol name, *untyped args) ?{ (*untyped) -> untyped } -> untyped
|
|
196
208
|
def respond_to_missing?: (Symbol name, ?bool include_private) -> bool
|
|
209
|
+
|
|
210
|
+
private
|
|
211
|
+
|
|
212
|
+
def trigger_execution_and_wait: () -> void
|
|
213
|
+
def trigger_clean_and_wait: () -> void
|
|
214
|
+
def execute_clean: () -> void
|
|
215
|
+
def wait_for_clean_dependencies: () -> void
|
|
216
|
+
def check_abort!: () -> void
|
|
217
|
+
def update_progress: (Symbol state, ?duration: Numeric?, ?error: Exception?) -> void
|
|
218
|
+
def debug_log: (String message) -> void
|
|
197
219
|
end
|
|
198
220
|
|
|
199
|
-
#
|
|
200
|
-
class
|
|
221
|
+
# TreeProgressDisplay class for tree-based progress visualization
|
|
222
|
+
class TreeProgressDisplay
|
|
201
223
|
SPINNER_FRAMES: Array[String]
|
|
224
|
+
COLORS: Hash[Symbol, String]
|
|
225
|
+
ICONS: Hash[Symbol, String]
|
|
202
226
|
|
|
203
227
|
@output: IO
|
|
204
228
|
@tasks: Hash[Class, TaskProgress]
|
|
@@ -206,6 +230,30 @@ module Taski
|
|
|
206
230
|
@spinner_index: Integer
|
|
207
231
|
@renderer_thread: Thread?
|
|
208
232
|
@running: bool
|
|
233
|
+
@nest_level: Integer
|
|
234
|
+
@root_task_class: Class?
|
|
235
|
+
@tree_structure: Hash[Symbol, untyped]?
|
|
236
|
+
@section_impl_map: Hash[Class, Class]
|
|
237
|
+
@last_line_count: Integer
|
|
238
|
+
|
|
239
|
+
# Shared class methods
|
|
240
|
+
def self.section_class?: (Class klass) -> bool
|
|
241
|
+
def self.nested_class?: (Class child_class, Class parent_class) -> bool
|
|
242
|
+
def self.render_static_tree: (Class root_task_class) -> String
|
|
243
|
+
|
|
244
|
+
# Static tree renderer (internal class)
|
|
245
|
+
class StaticTreeRenderer
|
|
246
|
+
@task_index_map: Hash[Class, Integer]
|
|
247
|
+
|
|
248
|
+
def render: (Class root_task_class) -> String
|
|
249
|
+
|
|
250
|
+
private
|
|
251
|
+
|
|
252
|
+
def build_tree: (Class task_class, String prefix, bool is_impl, Set[Class] ancestors) -> String
|
|
253
|
+
def render_dependency_branch: (Class dep, String prefix, bool is_last, bool is_impl, Set[Class] ancestors) -> String
|
|
254
|
+
def get_task_number: (Class task_class) -> String
|
|
255
|
+
def colored_type_label: (Class klass) -> String
|
|
256
|
+
end
|
|
209
257
|
|
|
210
258
|
class TaskProgress
|
|
211
259
|
attr_accessor state: Symbol
|
|
@@ -213,28 +261,39 @@ module Taski
|
|
|
213
261
|
attr_accessor end_time: Time?
|
|
214
262
|
attr_accessor error: Exception?
|
|
215
263
|
attr_accessor duration: Numeric?
|
|
264
|
+
attr_accessor is_impl_candidate: bool
|
|
216
265
|
|
|
217
266
|
def initialize: () -> void
|
|
218
267
|
end
|
|
219
268
|
|
|
220
269
|
def initialize: (?output: IO) -> void
|
|
270
|
+
def set_root_task: (Class root_task_class) -> void
|
|
271
|
+
def register_section_impl: (Class section_class, Class impl_class) -> void
|
|
221
272
|
def register_task: (Class task_class) -> void
|
|
222
273
|
def task_registered?: (Class task_class) -> bool
|
|
223
274
|
def update_task: (Class task_class, state: Symbol, ?duration: Numeric?, ?error: Exception?) -> void
|
|
224
275
|
def task_state: (Class task_class) -> Symbol?
|
|
225
|
-
def render: () -> void
|
|
226
276
|
def start: () -> void
|
|
227
277
|
def stop: () -> void
|
|
228
278
|
|
|
229
279
|
private
|
|
230
280
|
|
|
231
|
-
def
|
|
281
|
+
def build_tree_structure: () -> void
|
|
282
|
+
def build_tree_node: (Class task_class, Set[Class] ancestors) -> Hash[Symbol, untyped]?
|
|
283
|
+
def register_tasks_from_tree: (Hash[Symbol, untyped] node) -> void
|
|
232
284
|
def render_live: () -> void
|
|
233
285
|
def render_final: () -> void
|
|
234
|
-
def
|
|
235
|
-
def
|
|
286
|
+
def build_tree_display: () -> Array[String]
|
|
287
|
+
def build_root_tree_lines: (Hash[Symbol, untyped] node, String prefix, Array[String] lines) -> void
|
|
288
|
+
def render_children: (Hash[Symbol, untyped] node, String prefix, Array[String] lines, Class parent_task_class, bool ancestor_selected) -> void
|
|
289
|
+
def format_tree_line: (Class task_class, TaskProgress? progress, bool is_impl, bool is_selected) -> String
|
|
290
|
+
def format_unknown_task: (Class task_class, ?bool is_selected) -> String
|
|
291
|
+
def task_status_icon: (Symbol state, bool is_selected) -> String
|
|
236
292
|
def spinner_char: () -> String
|
|
293
|
+
def type_label_for: (Class task_class, ?bool is_selected) -> String
|
|
237
294
|
def task_details: (TaskProgress progress) -> String
|
|
295
|
+
def section_class?: (Class klass) -> bool
|
|
296
|
+
def nested_class?: (Class child_class, Class parent_class) -> bool
|
|
238
297
|
end
|
|
239
298
|
end
|
|
240
299
|
|
|
@@ -246,19 +305,21 @@ module Taski
|
|
|
246
305
|
|
|
247
306
|
private
|
|
248
307
|
|
|
249
|
-
def self.
|
|
308
|
+
def self.target_method_for: (Class task_class) -> Symbol
|
|
309
|
+
def self.extract_method_location: (Class task_class, Symbol method_name) -> [String, Integer]?
|
|
250
310
|
end
|
|
251
311
|
|
|
252
312
|
# Visitor class for AST traversal
|
|
253
313
|
class Visitor < Prism::Visitor
|
|
254
314
|
@target_task_class: Class
|
|
315
|
+
@target_method: Symbol
|
|
255
316
|
@dependencies: Set[task_class]
|
|
256
|
-
@
|
|
317
|
+
@in_target_method: bool
|
|
257
318
|
@current_namespace_path: Array[String?]
|
|
258
319
|
|
|
259
320
|
attr_reader dependencies: Set[task_class]
|
|
260
321
|
|
|
261
|
-
def initialize: (Class task_class) -> void
|
|
322
|
+
def initialize: (Class task_class, Symbol target_method) -> void
|
|
262
323
|
def visit_class_node: (Prism::ClassNode node) -> untyped
|
|
263
324
|
def visit_module_node: (Prism::ModuleNode node) -> untyped
|
|
264
325
|
def visit_def_node: (Prism::DefNode node) -> untyped
|
|
@@ -268,8 +329,10 @@ module Taski
|
|
|
268
329
|
|
|
269
330
|
def within_namespace: (String? name) { () -> void } -> void
|
|
270
331
|
def in_target_class?: () -> bool
|
|
332
|
+
def in_impl_method?: () -> bool
|
|
271
333
|
def extract_constant_name: (untyped node) -> String?
|
|
272
334
|
def detect_task_dependency: (Prism::CallNode node) -> void
|
|
335
|
+
def detect_impl_candidate: (untyped node) -> void
|
|
273
336
|
def extract_receiver_constant: (untyped receiver) -> String?
|
|
274
337
|
def resolve_and_add_dependency: (String constant_name) -> void
|
|
275
338
|
def resolve_constant: (String name) -> task_class?
|
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.
|
|
4
|
+
version: 0.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- ahogappa
|
|
@@ -60,16 +60,19 @@ files:
|
|
|
60
60
|
- examples/README.md
|
|
61
61
|
- examples/context_demo.rb
|
|
62
62
|
- examples/data_pipeline_demo.rb
|
|
63
|
+
- examples/nested_section_demo.rb
|
|
63
64
|
- examples/parallel_progress_demo.rb
|
|
64
65
|
- examples/quick_start.rb
|
|
65
66
|
- examples/reexecution_demo.rb
|
|
66
67
|
- examples/section_demo.rb
|
|
68
|
+
- examples/section_progress_demo.rb
|
|
69
|
+
- examples/tree_progress_demo.rb
|
|
67
70
|
- lib/taski.rb
|
|
68
71
|
- lib/taski/context.rb
|
|
69
|
-
- lib/taski/execution/
|
|
70
|
-
- lib/taski/execution/parallel_progress_display.rb
|
|
72
|
+
- lib/taski/execution/executor.rb
|
|
71
73
|
- lib/taski/execution/registry.rb
|
|
72
74
|
- lib/taski/execution/task_wrapper.rb
|
|
75
|
+
- lib/taski/execution/tree_progress_display.rb
|
|
73
76
|
- lib/taski/section.rb
|
|
74
77
|
- lib/taski/static_analysis/analyzer.rb
|
|
75
78
|
- lib/taski/static_analysis/dependency_graph.rb
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Taski
|
|
4
|
-
module Execution
|
|
5
|
-
class Coordinator
|
|
6
|
-
def initialize(registry:, analyzer:)
|
|
7
|
-
@registry = registry
|
|
8
|
-
@analyzer = analyzer
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
# @param task_class [Class] The task class whose dependencies should be started
|
|
12
|
-
def start_dependencies(task_class)
|
|
13
|
-
dependencies = get_dependencies(task_class)
|
|
14
|
-
return if dependencies.empty?
|
|
15
|
-
|
|
16
|
-
dependencies.each do |dep_class|
|
|
17
|
-
start_dependency_execution(dep_class)
|
|
18
|
-
end
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
# @param task_class [Class] The task class whose dependencies should be cleaned
|
|
22
|
-
def start_clean_dependencies(task_class)
|
|
23
|
-
dependencies = get_dependencies(task_class)
|
|
24
|
-
return if dependencies.empty?
|
|
25
|
-
|
|
26
|
-
dependencies.each do |dep_class|
|
|
27
|
-
start_dependency_clean(dep_class)
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
private
|
|
32
|
-
|
|
33
|
-
def get_dependencies(task_class)
|
|
34
|
-
if task_class.respond_to?(:cached_dependencies)
|
|
35
|
-
task_class.cached_dependencies
|
|
36
|
-
else
|
|
37
|
-
@analyzer.analyze(task_class)
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
def start_thread_with(&block)
|
|
42
|
-
thread = Thread.new(&block)
|
|
43
|
-
@registry.register_thread(thread)
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
def start_dependency_execution(dep_class)
|
|
47
|
-
exported_methods = dep_class.exported_methods
|
|
48
|
-
|
|
49
|
-
exported_methods.each do |method|
|
|
50
|
-
start_thread_with do
|
|
51
|
-
dep_class.public_send(method)
|
|
52
|
-
end
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
def start_dependency_clean(dep_class)
|
|
57
|
-
start_thread_with do
|
|
58
|
-
dep_class.public_send(:clean)
|
|
59
|
-
end
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
|
-
end
|
|
63
|
-
end
|