Class: CLI::UI::Spinner::SpinGroup
- Inherits:
-
Object
- Object
- CLI::UI::Spinner::SpinGroup
- 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
: Mutex.
Class Method Summary collapse
-
.pause_spinners(&block) ⇒ Object
: [T] { -> T } -> T.
-
.paused? ⇒ Boolean
: -> bool.
Instance Method Summary collapse
-
#add(title, final_glyph: DEFAULT_FINAL_GLYPH, merged_output: false, duplicate_output_to: File.new(File::NULL, 'w'), &block) ⇒ Object
Add a new task.
-
#all_succeeded? ⇒ Boolean
: -> bool.
-
#failure_debrief(&block) ⇒ Object
Provide an alternative debriefing for failed tasks : { (String title, Exception? exception, String out, String err) -> void } -> void.
-
#initialize(auto_debrief: true, interrupt_debrief: false, max_concurrent: 0, work_queue: nil, to: $stdout) ⇒ SpinGroup
constructor
Initializes a new spin group This lets you add
Taskobjects to the group to multi-thread work. -
#puts_above(message) ⇒ Object
: (String message) -> void.
-
#stop ⇒ Object
: -> void.
-
#stopped? ⇒ Boolean
: -> bool.
-
#success_debrief(&block) ⇒ Object
Provide a debriefing for successful tasks : { (String title, String out, String err) -> void } -> void.
-
#wait(to: $stdout) ⇒ Object
Tells the group you’re done adding tasks and to wait for all of them to finish.
Constructor Details
#initialize(auto_debrief: true, interrupt_debrief: false, max_concurrent: 0, work_queue: nil, to: $stdout) ⇒ SpinGroup
Initializes a new spin group This lets you add Task objects to the group to multi-thread work
Options
-
:auto_debrief- Automatically debrief exceptions or through success_debrief? Default to true -
:interrupt_debrief- Automatically debrief on interrupt. Default to false -
:max_concurrent- Maximum number of concurrent tasks. Default is 0 (effectively unlimited) -
:work_queue- Custom WorkQueue instance. If not provided, a new one will be created -
:to- Target stream, like $stdout or $stderr. Can be anything with print and puts methods, or under Sorbet, IO or StringIO. Defaults to $stdout
Example Usage
CLI::UI::SpinGroup.new do |spin_group|
spin_group.add('Title') { |spinner| sleep 3.0 }
spin_group.add('Title 2') { |spinner| sleep 3.0; spinner.update_title('New Title'); sleep 3.0 }
end
Output:

: (?auto_debrief: bool, ?interrupt_debrief: bool, ?max_concurrent: Integer, ?work_queue: WorkQueue?, ?to: io_like) -> void
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
# File 'lib/cli/ui/spinner/spin_group.rb', line 63 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 = 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)
: Mutex
14 15 16 |
# File 'lib/cli/ui/spinner/spin_group.rb', line 14 def pause_mutex @pause_mutex end |
Class Method Details
.pause_spinners(&block) ⇒ Object
: [T] { -> T } -> T
22 23 24 25 26 27 28 29 30 31 32 33 |
# File 'lib/cli/ui/spinner/spin_group.rb', line 22 def pause_spinners(&block) previous_paused = nil #: bool? @pause_mutex.synchronize do previous_paused = @paused @paused = true end block.call ensure @pause_mutex.synchronize do @paused = previous_paused end end |
.paused? ⇒ Boolean
: -> bool
17 18 19 |
# File 'lib/cli/ui/spinner/spin_group.rb', line 17 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
Add a new task
Attributes
-
title- Title of the task -
block- Block for the task, will be provided with an instance of the spinner
Example Usage:
spin_group = CLI::UI::SpinGroup.new
spin_group.add('Title') { |spinner| sleep 1.0 }
spin_group.wait
: (String title, ?final_glyph: ^(bool success) -> (Glyph | String), ?merged_output: bool, ?duplicate_output_to: IO) { (Task task) -> void } -> void
304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 |
# File 'lib/cli/ui/spinner/spin_group.rb', line 304 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
: -> bool
464 465 466 467 468 |
# File 'lib/cli/ui/spinner/spin_group.rb', line 464 def all_succeeded? @m.synchronize do @tasks.all?(&:success) end end |
#failure_debrief(&block) ⇒ Object
Provide an alternative debriefing for failed tasks : { (String title, Exception? exception, String out, String err) -> void } -> void
453 454 455 |
# File 'lib/cli/ui/spinner/spin_group.rb', line 453 def failure_debrief(&block) @failure_debrief = block end |
#puts_above(message) ⇒ Object
: (String message) -> void
445 446 447 448 449 |
# File 'lib/cli/ui/spinner/spin_group.rb', line 445 def puts_above() @m.synchronize do @puts_above << end end |
#stop ⇒ Object
: -> void
324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 |
# File 'lib/cli/ui/spinner/spin_group.rb', line 324 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
: -> bool
343 344 345 346 347 348 349 |
# File 'lib/cli/ui/spinner/spin_group.rb', line 343 def stopped? if @m.owned? @stopped else @m.synchronize { @stopped } end end |
#success_debrief(&block) ⇒ Object
Provide a debriefing for successful tasks : { (String title, String out, String err) -> void } -> void
459 460 461 |
# File 'lib/cli/ui/spinner/spin_group.rb', line 459 def success_debrief(&block) @success_debrief = block end |
#wait(to: $stdout) ⇒ Object
Tells the group you’re done adding tasks and to wait for all of them to finish
Options
-
:to- Target stream, like $stdout or $stderr. Can be anything with print and puts methods, or under Sorbet, IO or StringIO. Defaults to $stdout
Example Usage:
spin_group = CLI::UI::SpinGroup.new
spin_group.add('Title') { |spinner| sleep 1.0 }
spin_group.wait
: (?to: io_like) -> bool
364 365 366 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 |
# File 'lib/cli/ui/spinner/spin_group.rb', line 364 def wait(to: $stdout) result = false #: bool CLI::UI::ProgressReporter.with_progress(mode: :indeterminate, to: to, delay_start: true) do |reporter| idx = 0 consumed_lines = 0 @work_queue.close if @internal_work_queue tasks_seen = @tasks.map { false } tasks_seen_done = @tasks.map { false } current_mode = :indeterminate #: Symbol first_render = true #: bool loop do break if stopped? done_count = 0 width = CLI::UI::Terminal.width # Update progress mode based on task states current_mode = update_progress_mode(reporter, current_mode, first_render) self.class.pause_mutex.synchronize do next if self.class.paused? @m.synchronize do CLI::UI.raw do # Render any messages above the spinner force_full_render = render_puts_above(to, consumed_lines) # Render all tasks done_count, consumed_lines = render_tasks( to: to, tasks_seen: tasks_seen, tasks_seen_done: tasks_seen_done, consumed_lines: consumed_lines, idx: idx, force_full_render: force_full_render, width: width, ) end end end break if done_count == @tasks.size # After first render, start the progress reporter in indeterminate mode if first_render reporter.force_set_indeterminate first_render = false end idx = (idx + 1) % GLYPHS.size Spinner.index = idx sleep(PERIOD) end # Show error state briefly if tasks failed success = all_succeeded? unless success reporter.set_error sleep(0.5) end result = if @auto_debrief debrief(to: to) else all_succeeded? end end result rescue Interrupt @work_queue.interrupt debrief(to: to) if @interrupt_debrief stopped? ? false : raise end |