Class: CLI::UI::Spinner::SpinGroup
- Inherits:
-
Object
- Object
- CLI::UI::Spinner::SpinGroup
- Extended by:
- T::Sig
- Defined in:
- lib/cli/ui/spinner/spin_group.rb
Defined Under Namespace
Classes: Task
Constant Summary collapse
Class Attribute Summary collapse
-
.pause_mutex ⇒ Object
readonly
Returns the value of attribute pause_mutex.
Class Method Summary collapse
Instance Method Summary collapse
- #add(title, final_glyph: DEFAULT_FINAL_GLYPH, merged_output: false, duplicate_output_to: File.new(File::NULL, 'w'), &block) ⇒ Object
- #all_succeeded? ⇒ Boolean
- #debrief(to: $stdout) ⇒ Object
- #failure_debrief(&block) ⇒ Object
-
#initialize(auto_debrief: true, interrupt_debrief: false, max_concurrent: 0, work_queue: nil, to: $stdout) ⇒ SpinGroup
constructor
A new instance of SpinGroup.
- #puts_above(message) ⇒ Object
- #stop ⇒ Object
- #stopped? ⇒ Boolean
- #success_debrief(&block) ⇒ Object
- #wait(to: $stdout) ⇒ Object
Methods included from T::Sig
Constructor Details
#initialize(auto_debrief: true, interrupt_debrief: false, max_concurrent: 0, work_queue: nil, to: $stdout) ⇒ SpinGroup
Returns a new instance of SpinGroup.
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
# File 'lib/cli/ui/spinner/spin_group.rb', line 79 def initialize(auto_debrief: true, interrupt_debrief: false, max_concurrent: 0, work_queue: nil, to: $stdout) @m = Mutex.new @tasks = [] @puts_above = [] @auto_debrief = auto_debrief @interrupt_debrief = interrupt_debrief @start = Time.new @stopped = false @internal_work_queue = work_queue.nil? @work_queue = T.let( work_queue || WorkQueue.new(max_concurrent.zero? ? 1024 : max_concurrent), WorkQueue, ) if block_given? yield self wait(to: to) end end |
Class Attribute Details
.pause_mutex ⇒ Object (readonly)
Returns the value of attribute pause_mutex.
16 17 18 |
# File 'lib/cli/ui/spinner/spin_group.rb', line 16 def pause_mutex @pause_mutex end |
Class Method Details
.pause_spinners(&block) ⇒ Object
28 29 30 31 32 33 34 35 36 37 38 39 |
# File 'lib/cli/ui/spinner/spin_group.rb', line 28 def pause_spinners(&block) previous_paused = T.let(nil, T.nilable(T::Boolean)) @pause_mutex.synchronize do previous_paused = @paused @paused = true end block.call ensure @pause_mutex.synchronize do @paused = previous_paused end end |
.paused? ⇒ Boolean
19 20 21 |
# File 'lib/cli/ui/spinner/spin_group.rb', line 19 def paused? @paused end |
Instance Method Details
#add(title, final_glyph: DEFAULT_FINAL_GLYPH, merged_output: false, duplicate_output_to: File.new(File::NULL, 'w'), &block) ⇒ Object
307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 |
# File 'lib/cli/ui/spinner/spin_group.rb', line 307 def add( title, final_glyph: DEFAULT_FINAL_GLYPH, merged_output: false, duplicate_output_to: File.new(File::NULL, 'w'), &block ) @m.synchronize do @tasks << Task.new( title, final_glyph: final_glyph, merged_output: merged_output, duplicate_output_to: duplicate_output_to, work_queue: @work_queue, &block ) end end |
#all_succeeded? ⇒ Boolean
480 481 482 483 484 |
# File 'lib/cli/ui/spinner/spin_group.rb', line 480 def all_succeeded? @m.synchronize do @tasks.all?(&:success) end end |
#debrief(to: $stdout) ⇒ Object
494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 |
# File 'lib/cli/ui/spinner/spin_group.rb', line 494 def debrief(to: $stdout) @m.synchronize do @tasks.each do |task| next unless task.done title = task.title out = task.stdout err = task.stderr if task.success next @success_debrief&.call(title, out, err) end # exception will not be set if the wait loop is stopped before the task is checked e = task.exception next @failure_debrief.call(title, e, out, err) if @failure_debrief CLI::UI::Frame.open('Task Failed: ' + title, color: :red, timing: Time.new - @start) do if e to.puts("#{e.class}: #{e.}") to.puts("\tfrom #{e.backtrace.join("\n\tfrom ")}") end CLI::UI::Frame.divider('STDOUT') out = '(empty)' if out.nil? || out.strip.empty? to.puts(out) CLI::UI::Frame.divider('STDERR') err = '(empty)' if err.nil? || err.strip.empty? to.puts(err) end end @tasks.all?(&:success) end end |
#failure_debrief(&block) ⇒ Object
465 466 467 |
# File 'lib/cli/ui/spinner/spin_group.rb', line 465 def failure_debrief(&block) @failure_debrief = block end |
#puts_above(message) ⇒ Object
453 454 455 456 457 |
# File 'lib/cli/ui/spinner/spin_group.rb', line 453 def puts_above() @m.synchronize do @puts_above << end end |
#stop ⇒ Object
327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 |
# File 'lib/cli/ui/spinner/spin_group.rb', line 327 def stop # If we already own the mutex (called from within another synchronized block), # set stopped directly to avoid deadlock if @m.owned? return if @stopped @stopped = true else @m.synchronize do return if @stopped @stopped = true end end # Interrupt is thread-safe on its own, so we can call it outside the mutex @work_queue.interrupt end |
#stopped? ⇒ Boolean
346 347 348 349 350 351 352 |
# File 'lib/cli/ui/spinner/spin_group.rb', line 346 def stopped? if @m.owned? @stopped else @m.synchronize { @stopped } end end |
#success_debrief(&block) ⇒ Object
475 476 477 |
# File 'lib/cli/ui/spinner/spin_group.rb', line 475 def success_debrief(&block) @success_debrief = block end |
#wait(to: $stdout) ⇒ Object
367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 |
# File 'lib/cli/ui/spinner/spin_group.rb', line 367 def wait(to: $stdout) idx = 0 consumed_lines = 0 @work_queue.close if @internal_work_queue tasks_seen = @tasks.map { false } tasks_seen_done = @tasks.map { false } loop do break if stopped? done_count = 0 width = CLI::UI::Terminal.width self.class.pause_mutex.synchronize do next if self.class.paused? @m.synchronize do CLI::UI.raw do force_full_render = false unless @puts_above.empty? to.print(CLI::UI::ANSI.cursor_up(consumed_lines)) if CLI::UI.enable_cursor? while ( = @puts_above.shift) to.print(CLI::UI::ANSI.insert_lines(.lines.count)) if CLI::UI.enable_cursor? .lines.each do |line| to.print(CLI::UI::Frame.prefix + CLI::UI.fmt(line)) end to.print("\n") end # we descend with newlines rather than ANSI.cursor_down as the inserted lines may've # pushed the spinner off the front of the buffer, so we can't move back down below it to.print("\n" * consumed_lines) if CLI::UI.enable_cursor? force_full_render = true end @tasks.each.with_index do |task, int_index| nat_index = int_index + 1 task_done = task.check done_count += 1 if task_done if CLI::UI.enable_cursor? if nat_index > consumed_lines to.print(task.render(idx, true, width: width) + "\n") consumed_lines += 1 else offset = consumed_lines - int_index move_to = CLI::UI::ANSI.cursor_up(offset) + "\r" move_from = "\r" + CLI::UI::ANSI.cursor_down(offset) to.print(move_to + task.render(idx, idx.zero? || force_full_render, width: width) + move_from) end elsif !tasks_seen[int_index] || (task_done && !tasks_seen_done[int_index]) to.print(task.render(idx, true, width: width) + "\n") end tasks_seen[int_index] = true tasks_seen_done[int_index] ||= task_done end end end end break if done_count == @tasks.size idx = (idx + 1) % GLYPHS.size Spinner.index = idx sleep(PERIOD) end if @auto_debrief debrief(to: to) else all_succeeded? end rescue Interrupt @work_queue.interrupt debrief(to: to) if @interrupt_debrief stopped? ? false : raise end |