Skip to content

Custom Output Classes

Jeff Felchner edited this page Sep 15, 2017 · 9 revisions

WARNING: THIS FEATURE WORKS BUT BREAKING CHANGES WILL NOT BE CONSIDERED CAUSE FOR A MAJOR VERSION SEMVER BUMP. USE AT YOUR OWN RISK!

By default ruby-progressbar will attempt to cover the main use cases for outputting the bar. These include outputting to a screen and outputting to a file-ish sort of thing. The technical term would be TTY and non-TTY. However there are some edge cases whereby you might want something different to happen than either of those.

For example, you might want to log the progressbar to a file with some special logic, or you may want to log to a file in certain cases and to a screen in other cases, or you may want to log to a screen and a file at the same time.

Whatever the case, you now have effectively full control over how the progressbar is sent to a destination.

Full Example

One of the use cases for a custom outputter is a "null" destination. In a case like this, the user doesn't ever want to see the progressbar and instead only ever wants to access it via #to_s. A use case might be a CLI program which, when executed, prints the current status of multiple bars (maybe with extra information before or after it) and then exits. Since by default progressbar prints itself to stdout on every update, there would be undesirable output for the above case.

Side Note: There is a builtin output class for just this use case which is officially supported. See the wiki article for details.

How Do We Fix This?

In order to fix this issue, we can create a custom Output class which is a no-op for all of our base Output class' methods. Here's what that might look like:

require 'ruby-progressbar/output'

class MyNullOutput < ProgressBar::Output
  alias refresh_with_format_change with_refresh

  def clear;        end
  def log(_string); end
  def refresh(*);   end

  def clear_string
    ''
  end

  def bar_update_string
    ''
  end

  def default_format
    ''
  end

  def resolve_format(_format)
    ''
  end

  def eol
    ''
  end
end

IMPORTANT NOTE: The class must inherit from ProgressBar::Output, even if every method is overridden.

Now, in order to use it, all we have to do is pass it into the constructor of the progressbar when we create it.

progressbar = ProgressBar.create(output: MyNullOutput)

ruby-progressbar will take care of the rest!

Overriding stream

If you want to implement an Output class which actually sends the progressbar somewhere, you'll need to override stream and set it to return an IO-like object.

Example

class MyOutput < ProgressBar::Output
  alias refresh_with_format_change with_refresh

  # This is REQUIRED for the custom output class to work properly
  def stream
    $stdout
  end

  def clear
    stream.print clear_string
    stream.print "\r"
  end

  def log(message)
    clear
    stream.puts "This is a log statement \/ \/ \/"
    stream.puts message

    refresh(:force => true) unless bar.stopped?
  end

  def bar_update_string
    bar.to_s + ' ' + Time.now.iso8601
  end

  def default_format
    'This is some custom stuff %w%i'
  end

  def resolve_format(_format)
    default_format
  end

  def eol
    "\n"
  end
end