taski 0.9.0 → 0.9.2

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.
@@ -0,0 +1,400 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "prism"
4
+
5
+ module Taski
6
+ module StaticAnalysis
7
+ # Analyzes a task's run method AST to find dependencies that are safe
8
+ # to speculatively pre-start (start_dep). Uses a whitelist approach:
9
+ # only confirmed patterns are collected; unknown patterns cause the
10
+ # analyzer to stop (returning what was collected so far up to that point).
11
+ #
12
+ # Currently handles variable assignment patterns only:
13
+ # a = Dep.value (LocalVariableWriteNode)
14
+ # @a = Dep.value (InstanceVariableWriteNode)
15
+ #
16
+ # This is a performance optimization only — if analysis fails or returns
17
+ # empty, tasks still work correctly via lazy Fiber pull (need_dep).
18
+ class StartDepAnalyzer
19
+ DepInfo = Data.define(:klass, :method_name)
20
+ AnalysisResult = Data.define(:start_deps, :sync_deps)
21
+
22
+ # AST node types that are known safe (not dependencies, won't stop scanning)
23
+ SAFE_TYPES = Set[
24
+ Prism::LocalVariableReadNode, Prism::InstanceVariableReadNode,
25
+ Prism::ConstantReadNode, Prism::ConstantPathNode,
26
+ Prism::IntegerNode, Prism::FloatNode, Prism::StringNode,
27
+ Prism::SymbolNode, Prism::NilNode, Prism::TrueNode, Prism::FalseNode,
28
+ Prism::SelfNode
29
+ ].freeze
30
+
31
+ @cache = {}
32
+ @cache_mutex = Mutex.new
33
+
34
+ class << self
35
+ # Analyze a task class and return deps safe to prestart.
36
+ # Results are cached per task class.
37
+ # @param task_class [Class] The task class to analyze
38
+ # @return [Array<DepInfo>] Deduplicated list of safe dependencies
39
+ def analyze(task_class)
40
+ @cache_mutex.synchronize do
41
+ return @cache[task_class] if @cache.key?(task_class)
42
+ end
43
+
44
+ result = new.analyze(task_class)
45
+
46
+ @cache_mutex.synchronize do
47
+ @cache[task_class] ||= result
48
+ end
49
+ end
50
+
51
+ # Clear cache (for testing)
52
+ def clear_cache!
53
+ @cache_mutex.synchronize { @cache.clear }
54
+ end
55
+ end
56
+
57
+ EMPTY_RESULT = AnalysisResult.new(start_deps: Set.new.freeze, sync_deps: Set.new.freeze).freeze
58
+
59
+ def initialize
60
+ @deps = []
61
+ @seen_classes = Set.new
62
+ end
63
+
64
+ # Analyze a task class's run method and return safe-to-prestart deps
65
+ # and sync_dep_classes (deps whose proxy variables are used unsafely).
66
+ # @param task_class [Class] The task class to analyze
67
+ # @return [AnalysisResult]
68
+ def analyze(task_class)
69
+ @task_class = task_class
70
+ @exported_ivars = Set.new(task_class.exported_methods.map { |m| :"@#{m}" })
71
+ source_location = task_class.instance_method(:run).source_location
72
+ return EMPTY_RESULT unless source_location
73
+
74
+ file_path, _line = source_location
75
+ parse_result = Prism.parse_file(file_path)
76
+
77
+ run_node = find_run_method(parse_result.value, task_class)
78
+ return EMPTY_RESULT unless run_node&.body
79
+
80
+ scan_statements(run_node.body)
81
+ unsafe_classes = detect_unsafe_proxy_usage(run_node.body)
82
+ all_dep_classes = Set.new(@deps.map(&:klass))
83
+ start_deps = all_dep_classes - unsafe_classes
84
+ sync_deps = unsafe_classes
85
+ AnalysisResult.new(start_deps: start_deps, sync_deps: sync_deps)
86
+ rescue NameError
87
+ EMPTY_RESULT
88
+ end
89
+
90
+ private
91
+
92
+ # Find the def run node inside the target class
93
+ def find_run_method(program_node, task_class)
94
+ target_name = task_class.name
95
+ find_run_in_tree(program_node, [], target_name)
96
+ end
97
+
98
+ def find_run_in_tree(node, namespace_path, target_name)
99
+ case node
100
+ when Prism::ProgramNode
101
+ node.statements.body.each do |child|
102
+ result = find_run_in_tree(child, namespace_path, target_name)
103
+ return result if result
104
+ end
105
+ when Prism::ModuleNode
106
+ name = node.constant_path.slice
107
+ new_path = namespace_path + [name]
108
+ node.body&.body&.each do |child|
109
+ result = find_run_in_tree(child, new_path, target_name)
110
+ return result if result
111
+ end
112
+ when Prism::ClassNode
113
+ name = node.constant_path.slice
114
+ new_path = namespace_path + [name]
115
+ full_name = new_path.join("::")
116
+
117
+ node.body&.body&.each do |child|
118
+ if full_name == target_name
119
+ return child if child.is_a?(Prism::DefNode) && child.name == :run
120
+ else
121
+ result = find_run_in_tree(child, new_path, target_name)
122
+ return result if result
123
+ end
124
+ end
125
+ when Prism::StatementsNode
126
+ node.body.each do |child|
127
+ result = find_run_in_tree(child, namespace_path, target_name)
128
+ return result if result
129
+ end
130
+ end
131
+
132
+ nil
133
+ end
134
+
135
+ # Scan statements, collecting deps. Stops at the first unknown pattern.
136
+ def scan_statements(node)
137
+ return unless node.is_a?(Prism::StatementsNode)
138
+ node.body.each { |stmt| break unless try_match(stmt) }
139
+ end
140
+
141
+ # Match a statement against known patterns.
142
+ # Returns true to continue scanning, false to stop.
143
+ def try_match(stmt)
144
+ case stmt
145
+ when Prism::LocalVariableWriteNode, Prism::InstanceVariableWriteNode
146
+ check_dep_call(stmt.value)
147
+ true
148
+ when *SAFE_TYPES
149
+ true
150
+ else
151
+ false
152
+ end
153
+ end
154
+
155
+ # Check if a node is a Task dependency call (Constant.method) and collect it.
156
+ def check_dep_call(node)
157
+ return unless node.is_a?(Prism::CallNode)
158
+ return unless node.receiver
159
+
160
+ case node.receiver
161
+ when Prism::ConstantReadNode, Prism::ConstantPathNode
162
+ constant_name = node.receiver.slice
163
+ resolved = resolve_constant(constant_name)
164
+ if resolved.is_a?(Class) && defined?(Taski::Task) && resolved < Taski::Task
165
+ collect_dep(node)
166
+ end
167
+ end
168
+ end
169
+
170
+ # Collect a dependency, deduplicating by class
171
+ def collect_dep(call_node)
172
+ constant_name = call_node.receiver.slice
173
+ method_name = call_node.name
174
+ klass = resolve_constant(constant_name)
175
+ return unless klass
176
+
177
+ @deps << DepInfo.new(klass: klass, method_name: method_name) if @seen_classes.add?(klass)
178
+ end
179
+
180
+ # Phase 2: Detect proxy variables used in unsafe contexts.
181
+ # Returns a Set of dep classes whose proxy variables are used unsafely.
182
+ # A proxy variable is a local variable assigned from a Taski::Task dep call
183
+ # (e.g., `a = Dep.value`). If such a variable is later used in an unsafe
184
+ # context (as argument, condition, array element, etc.), the dep class is
185
+ # added to sync_dep_classes so it will be resolved synchronously.
186
+ def detect_unsafe_proxy_usage(body_node)
187
+ proxy_vars = build_proxy_var_map(body_node)
188
+
189
+ unsafe_classes = Set.new
190
+ scan_for_unsafe_usage(body_node, proxy_vars, unsafe_classes)
191
+ unsafe_classes
192
+ end
193
+
194
+ # Build mapping of { local_var_name => dep_class } from assignment statements
195
+ def build_proxy_var_map(body_node)
196
+ proxy_vars = {}
197
+ return proxy_vars unless body_node.is_a?(Prism::StatementsNode)
198
+
199
+ body_node.body.each do |stmt|
200
+ next unless stmt.is_a?(Prism::LocalVariableWriteNode)
201
+
202
+ dep_class = extract_dep_class(stmt.value)
203
+ proxy_vars[stmt.name] = dep_class if dep_class
204
+ end
205
+ proxy_vars
206
+ end
207
+
208
+ # Extract the dep class from a call node if it's a Taski::Task dep call
209
+ def extract_dep_class(node)
210
+ return nil unless node.is_a?(Prism::CallNode)
211
+ return nil unless node.receiver
212
+
213
+ case node.receiver
214
+ when Prism::ConstantReadNode, Prism::ConstantPathNode
215
+ constant_name = node.receiver.slice
216
+ resolved = resolve_constant(constant_name)
217
+ if resolved.is_a?(Class) && defined?(Taski::Task) && resolved < Taski::Task
218
+ resolved
219
+ end
220
+ end
221
+ end
222
+
223
+ # Recursively scan AST for unsafe proxy variable usage.
224
+ # Safe contexts: receiver of CallNode, string interpolation,
225
+ # RHS of local/ivar assignment. Everything else is unsafe.
226
+ def scan_for_unsafe_usage(node, proxy_vars, unsafe_classes) # rubocop:disable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
227
+ case node
228
+ when Prism::StatementsNode
229
+ node.body.each { |child| scan_for_unsafe_usage(child, proxy_vars, unsafe_classes) }
230
+
231
+ when Prism::LocalVariableWriteNode
232
+ if (dep_class = proxy_dep_class(node.value, proxy_vars))
233
+ # Reassignment or direct dep call: track the new variable name
234
+ proxy_vars[node.name] = dep_class
235
+ else
236
+ scan_for_unsafe_usage(node.value, proxy_vars, unsafe_classes)
237
+ end
238
+
239
+ when Prism::InstanceVariableWriteNode
240
+ if (dep_class = proxy_dep_class(node.value, proxy_vars))
241
+ if @exported_ivars.include?(node.name)
242
+ # @exported = proxy → safe (resolve_proxy_exports handles it)
243
+ else
244
+ # @non_exported = proxy → track for unsafe usage detection
245
+ proxy_vars[node.name] = dep_class
246
+ end
247
+ else
248
+ scan_for_unsafe_usage(node.value, proxy_vars, unsafe_classes)
249
+ end
250
+
251
+ when Prism::CallNode
252
+ # Receiver: proxy.foo → safe (method_missing fires)
253
+ if proxy_var_read?(node.receiver, proxy_vars)
254
+ # safe — don't flag receiver
255
+ elsif node.receiver
256
+ scan_for_unsafe_usage(node.receiver, proxy_vars, unsafe_classes)
257
+ end
258
+ # Arguments: foo(proxy) → UNSAFE
259
+ node.arguments&.arguments&.each do |arg|
260
+ if proxy_var_read?(arg, proxy_vars)
261
+ unsafe_classes.add(proxy_vars[arg.name])
262
+ else
263
+ scan_for_unsafe_usage(arg, proxy_vars, unsafe_classes)
264
+ end
265
+ end
266
+ scan_for_unsafe_usage(node.block, proxy_vars, unsafe_classes) if node.block
267
+
268
+ when Prism::IfNode
269
+ check_predicate_unsafe(node.predicate, proxy_vars, unsafe_classes)
270
+ scan_for_unsafe_usage(node.statements, proxy_vars, unsafe_classes) if node.statements
271
+ scan_for_unsafe_usage(node.subsequent, proxy_vars, unsafe_classes) if node.subsequent
272
+
273
+ when Prism::UnlessNode
274
+ check_predicate_unsafe(node.predicate, proxy_vars, unsafe_classes)
275
+ scan_for_unsafe_usage(node.statements, proxy_vars, unsafe_classes) if node.statements
276
+ scan_for_unsafe_usage(node.else_clause, proxy_vars, unsafe_classes) if node.else_clause
277
+
278
+ when Prism::WhileNode, Prism::UntilNode
279
+ check_predicate_unsafe(node.predicate, proxy_vars, unsafe_classes)
280
+ scan_for_unsafe_usage(node.statements, proxy_vars, unsafe_classes) if node.statements
281
+
282
+ when Prism::InterpolatedStringNode
283
+ node.parts.each do |part|
284
+ next unless part.is_a?(Prism::EmbeddedStatementsNode)
285
+
286
+ if part.statements&.body&.size == 1 &&
287
+ proxy_var_read?(part.statements.body[0], proxy_vars)
288
+ # safe — string interpolation calls to_s
289
+ else
290
+ scan_for_unsafe_usage(part, proxy_vars, unsafe_classes)
291
+ end
292
+ end
293
+
294
+ when Prism::EmbeddedStatementsNode
295
+ scan_for_unsafe_usage(node.statements, proxy_vars, unsafe_classes) if node.statements
296
+
297
+ when Prism::ArrayNode
298
+ node.elements.each do |elem|
299
+ if proxy_var_read?(elem, proxy_vars)
300
+ unsafe_classes.add(proxy_vars[elem.name])
301
+ else
302
+ scan_for_unsafe_usage(elem, proxy_vars, unsafe_classes)
303
+ end
304
+ end
305
+
306
+ when Prism::ElseNode
307
+ scan_for_unsafe_usage(node.statements, proxy_vars, unsafe_classes) if node.statements
308
+
309
+ when Prism::BeginNode
310
+ scan_for_unsafe_usage(node.statements, proxy_vars, unsafe_classes) if node.statements
311
+ scan_for_unsafe_usage(node.rescue_clause, proxy_vars, unsafe_classes) if node.rescue_clause
312
+ scan_for_unsafe_usage(node.ensure_clause, proxy_vars, unsafe_classes) if node.ensure_clause
313
+
314
+ when Prism::RescueNode
315
+ scan_for_unsafe_usage(node.statements, proxy_vars, unsafe_classes) if node.statements
316
+ scan_for_unsafe_usage(node.subsequent, proxy_vars, unsafe_classes) if node.subsequent
317
+
318
+ when Prism::EnsureNode
319
+ scan_for_unsafe_usage(node.statements, proxy_vars, unsafe_classes) if node.statements
320
+
321
+ when Prism::ParenthesesNode
322
+ scan_for_unsafe_usage(node.body, proxy_vars, unsafe_classes) if node.body
323
+
324
+ when Prism::LocalVariableReadNode, Prism::InstanceVariableReadNode
325
+ # Bare proxy variable read in unknown context → UNSAFE
326
+ unsafe_classes.add(proxy_vars[node.name]) if proxy_vars.key?(node.name)
327
+
328
+ else
329
+ # For any unhandled node type, recurse into children (safety-first)
330
+ if node.respond_to?(:compact_child_nodes)
331
+ node.compact_child_nodes.each do |child|
332
+ scan_for_unsafe_usage(child, proxy_vars, unsafe_classes)
333
+ end
334
+ end
335
+ end
336
+ end
337
+
338
+ # Check if a predicate node is an unsafe proxy variable read
339
+ def check_predicate_unsafe(predicate, proxy_vars, unsafe_classes)
340
+ if proxy_var_read?(predicate, proxy_vars)
341
+ unsafe_classes.add(proxy_vars[predicate_key(predicate)])
342
+ else
343
+ scan_for_unsafe_usage(predicate, proxy_vars, unsafe_classes)
344
+ end
345
+ end
346
+
347
+ # Return the dep class if the node reads a proxy variable (local or ivar)
348
+ # or is a direct dep call. Returns nil otherwise.
349
+ def proxy_dep_class(node, proxy_vars)
350
+ if proxy_var_read?(node, proxy_vars)
351
+ proxy_vars[predicate_key(node)]
352
+ else
353
+ extract_dep_class(node)
354
+ end
355
+ end
356
+
357
+ # Check if node is a proxy variable read (local var or ivar)
358
+ def proxy_var_read?(node, proxy_vars)
359
+ case node
360
+ when Prism::LocalVariableReadNode
361
+ proxy_vars.key?(node.name)
362
+ when Prism::InstanceVariableReadNode
363
+ proxy_vars.key?(node.name)
364
+ else
365
+ false
366
+ end
367
+ end
368
+
369
+ # Extract the proxy_vars key from a variable read node
370
+ def predicate_key(node)
371
+ node.name
372
+ end
373
+
374
+ # Resolve a constant name to the class, with namespace fallback.
375
+ def resolve_constant(constant_name)
376
+ Object.const_get(constant_name)
377
+ rescue NameError
378
+ resolve_with_namespace(constant_name)
379
+ end
380
+
381
+ def resolve_with_namespace(constant_name)
382
+ return nil unless @task_class
383
+
384
+ namespace_parts = @task_class.name.split("::")
385
+ namespace_parts.length.downto(0) do |i|
386
+ prefix = namespace_parts.take(i).join("::")
387
+ full_name = prefix.empty? ? constant_name : "#{prefix}::#{constant_name}"
388
+
389
+ begin
390
+ return Object.const_get(full_name)
391
+ rescue NameError
392
+ next
393
+ end
394
+ end
395
+
396
+ nil
397
+ end
398
+ end
399
+ end
400
+ end
data/lib/taski/task.rb CHANGED
@@ -6,6 +6,7 @@ require_relative "execution/registry"
6
6
  require_relative "execution/task_wrapper"
7
7
  require_relative "progress/layout/tree"
8
8
  require_relative "progress/theme/plain"
9
+ require_relative "task_proxy"
9
10
 
10
11
  module Taski
11
12
  # Base class for all tasks in the Taski framework.
@@ -114,7 +115,7 @@ module Taski
114
115
  def tree
115
116
  output = StringIO.new
116
117
  theme = Progress::Theme::Plain.new
117
- layout = Progress::Layout::Tree.new(output: output, theme: theme)
118
+ layout = Progress::Layout::Tree.for(output: output, theme: theme)
118
119
  context = Execution::ExecutionFacade.new(root_task_class: self)
119
120
  layout.context = context
120
121
  layout.on_ready
@@ -181,12 +182,16 @@ module Taski
181
182
  registry = Taski.current_registry
182
183
  if registry
183
184
  if Thread.current[:taski_fiber_context]
184
- # Fiber-based lazy resolution - yield to the worker loop
185
- result = Fiber.yield([:need_dep, self, method])
186
- if result.is_a?(Array) && result[0] == :_taski_error
187
- raise result[1]
185
+ start_deps = Thread.current[:taski_start_deps]
186
+ if start_deps&.include?(self)
187
+ # Lazy resolution via proxy - safe dep confirmed by static analysis
188
+ TaskProxy.new(self, method)
189
+ else
190
+ # Synchronous resolution: dep not in allowlist (unknown or unsafe usage)
191
+ result = Fiber.yield(Taski::Execution::FiberProtocol::NeedDep.new(self, method))
192
+ raise result.error if result in Taski::Execution::FiberProtocol::DepError
193
+ result
188
194
  end
189
- result
190
195
  else
191
196
  # Synchronous resolution (clean phase, outside Fiber)
192
197
  wrapper = registry.get_or_create(self) do
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Taski
4
+ # Lazy proxy that defers dependency resolution until the value is actually used.
5
+ # Inherits from BasicObject to minimize available methods, maximizing method_missing delegation.
6
+ class TaskProxy < BasicObject
7
+ def initialize(task_class, method)
8
+ @task_class = task_class
9
+ @method = method
10
+ @resolved = false
11
+ @value = nil
12
+ @error = nil
13
+ end
14
+
15
+ def __resolve__
16
+ ::Kernel.raise @error if @error
17
+ return @value if @resolved
18
+ @value = ::Fiber.yield(::Taski::Execution::FiberProtocol::NeedDep.new(@task_class, @method))
19
+ if @value in ::Taski::Execution::FiberProtocol::DepError
20
+ @error = @value.error
21
+ ::Kernel.raise @error
22
+ end
23
+ @resolved = true
24
+ @value
25
+ end
26
+
27
+ def __taski_proxy_resolve__
28
+ __resolve__
29
+ end
30
+
31
+ def method_missing(name, *args, **kwargs, &block)
32
+ __resolve__.__send__(name, *args, **kwargs, &block)
33
+ end
34
+
35
+ def respond_to_missing?(name, include_private = false)
36
+ name == :__taski_proxy_resolve__ || __resolve__.respond_to?(name, include_private)
37
+ end
38
+
39
+ def !
40
+ !__resolve__
41
+ end
42
+
43
+ def ==(other)
44
+ __resolve__ == other
45
+ end
46
+
47
+ def !=(other)
48
+ __resolve__ != other
49
+ end
50
+
51
+ def equal?(other)
52
+ __resolve__.equal?(other)
53
+ end
54
+
55
+ def respond_to?(name, include_private = false)
56
+ name == :__taski_proxy_resolve__ || __resolve__.respond_to?(name, include_private)
57
+ end
58
+ end
59
+ end
@@ -69,7 +69,7 @@ module Taski
69
69
 
70
70
  if MockRegistry.mock_for(task_class)
71
71
  wrapper.mark_completed(nil) unless wrapper.completed?
72
- @completion_queue.push({task_class: task_class, wrapper: wrapper})
72
+ @completion_queue.push(Taski::Execution::FiberProtocol::TaskCompleted.new(task_class, wrapper))
73
73
  return
74
74
  end
75
75
 
data/lib/taski/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Taski
4
- VERSION = "0.9.0"
4
+ VERSION = "0.9.2"
5
5
  end
data/lib/taski.rb CHANGED
@@ -4,6 +4,8 @@ require_relative "taski/version"
4
4
  require_relative "taski/static_analysis/analyzer"
5
5
  require_relative "taski/static_analysis/visitor"
6
6
  require_relative "taski/static_analysis/dependency_graph"
7
+ require_relative "taski/static_analysis/start_dep_analyzer"
8
+ require_relative "taski/execution/fiber_protocol"
7
9
  require_relative "taski/execution/registry"
8
10
  require_relative "taski/execution/task_observer"
9
11
  require_relative "taski/execution/execution_facade"
@@ -14,6 +16,7 @@ require_relative "taski/execution/executor"
14
16
  require_relative "taski/progress/layout/log"
15
17
  require_relative "taski/progress/layout/simple"
16
18
  require_relative "taski/progress/layout/tree"
19
+ require_relative "taski/progress/config"
17
20
  require_relative "taski/args"
18
21
  require_relative "taski/env"
19
22
  require_relative "taski/logging"
@@ -265,14 +268,28 @@ module Taski
265
268
  reset_args! if created_args
266
269
  end
267
270
 
268
- NOT_CONFIGURED = Object.new.freeze
269
271
  PROGRESS_MONITOR = Monitor.new
270
- @progress_display = NOT_CONFIGURED
272
+ PROGRESS_NOT_SET = Object.new.freeze
273
+ @progress_display = PROGRESS_NOT_SET
274
+ @progress_config = Progress::Config.new {
275
+ PROGRESS_MONITOR.synchronize do
276
+ unless @progress_display.equal?(PROGRESS_NOT_SET)
277
+ @progress_display.stop if @progress_display.respond_to?(:stop)
278
+ end
279
+ @progress_display = PROGRESS_NOT_SET
280
+ end
281
+ }
282
+
283
+ # Get the progress configuration singleton.
284
+ # @return [Progress::Config]
285
+ def self.progress
286
+ PROGRESS_MONITOR.synchronize { @progress_config }
287
+ end
271
288
 
272
289
  def self.progress_display
273
290
  PROGRESS_MONITOR.synchronize do
274
- if @progress_display.equal?(NOT_CONFIGURED)
275
- @progress_display = Progress::Layout::Simple.new
291
+ if @progress_display.equal?(PROGRESS_NOT_SET)
292
+ @progress_display = @progress_config.build
276
293
  end
277
294
  @progress_display
278
295
  end
@@ -280,7 +297,7 @@ module Taski
280
297
 
281
298
  def self.progress_display=(display)
282
299
  PROGRESS_MONITOR.synchronize do
283
- unless @progress_display.equal?(NOT_CONFIGURED)
300
+ unless @progress_display.equal?(PROGRESS_NOT_SET)
284
301
  @progress_display.stop if @progress_display.respond_to?(:stop)
285
302
  end
286
303
  @progress_display = display
@@ -289,10 +306,11 @@ module Taski
289
306
 
290
307
  def self.reset_progress_display!
291
308
  PROGRESS_MONITOR.synchronize do
292
- unless @progress_display.equal?(NOT_CONFIGURED)
309
+ unless @progress_display.equal?(PROGRESS_NOT_SET)
293
310
  @progress_display.stop if @progress_display.respond_to?(:stop)
294
311
  end
295
- @progress_display = NOT_CONFIGURED
312
+ @progress_display = PROGRESS_NOT_SET
313
+ @progress_config.reset
296
314
  end
297
315
  end
298
316
 
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.9.0
4
+ version: 0.9.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - ahogappa
@@ -9,6 +9,20 @@ bindir: exe
9
9
  cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: base64
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '0'
12
26
  - !ruby/object:Gem::Dependency
13
27
  name: liquid
14
28
  requirement: !ruby/object:Gem::Requirement
@@ -84,6 +98,7 @@ files:
84
98
  - lib/taski/env.rb
85
99
  - lib/taski/execution/execution_facade.rb
86
100
  - lib/taski/execution/executor.rb
101
+ - lib/taski/execution/fiber_protocol.rb
87
102
  - lib/taski/execution/registry.rb
88
103
  - lib/taski/execution/scheduler.rb
89
104
  - lib/taski/execution/task_observer.rb
@@ -92,6 +107,7 @@ files:
92
107
  - lib/taski/execution/task_wrapper.rb
93
108
  - lib/taski/execution/worker_pool.rb
94
109
  - lib/taski/logging.rb
110
+ - lib/taski/progress/config.rb
95
111
  - lib/taski/progress/layout/base.rb
96
112
  - lib/taski/progress/layout/filters.rb
97
113
  - lib/taski/progress/layout/log.rb
@@ -99,6 +115,9 @@ files:
99
115
  - lib/taski/progress/layout/tags.rb
100
116
  - lib/taski/progress/layout/theme_drop.rb
101
117
  - lib/taski/progress/layout/tree.rb
118
+ - lib/taski/progress/layout/tree/event.rb
119
+ - lib/taski/progress/layout/tree/live.rb
120
+ - lib/taski/progress/layout/tree/structure.rb
102
121
  - lib/taski/progress/theme/base.rb
103
122
  - lib/taski/progress/theme/compact.rb
104
123
  - lib/taski/progress/theme/default.rb
@@ -106,8 +125,10 @@ files:
106
125
  - lib/taski/progress/theme/plain.rb
107
126
  - lib/taski/static_analysis/analyzer.rb
108
127
  - lib/taski/static_analysis/dependency_graph.rb
128
+ - lib/taski/static_analysis/start_dep_analyzer.rb
109
129
  - lib/taski/static_analysis/visitor.rb
110
130
  - lib/taski/task.rb
131
+ - lib/taski/task_proxy.rb
111
132
  - lib/taski/test_helper.rb
112
133
  - lib/taski/test_helper/errors.rb
113
134
  - lib/taski/test_helper/minitest.rb