Overwriting Console Output Using Curses in Ruby

01 April 2013

Καλό μήνα! A while back I wrote about how to overwrite console output in Ruby. Originally the project I used this technique for only required overwriting a single line of output. That project has now grown and I need to be able to maintain a mini-dashboard on the console.

Fortunately, Ruby rocks and has a standard library wrapper for Curses. I would encourage you to read through the documentation. I’ve written a simple script to show-off the most basic use-case. In this example I initialize ten workers. Each worker is responsible for keeping track of the work she has completed, @percent, and then reporting that progress on her own line, @index, of the console.

#!/usr/bin/env ruby

require 'curses'

Curses.noecho
Curses.init_screen

class Worker
  def initialize(index)
    @index = index
    @percent = 0
  end

  def run
    (1..10).each do
      work
      report
      sleep(rand())
    end
  end

  def to_s
    "Worker ##{'%2d' % @index} is #{'%3d' % @percent}% complete"
  end

  private

  def work
    @percent += 10
  end

  def report
    Curses.setpos(@index, 0)
    Curses.addstr(to_s)
    Curses.refresh
  end
end

workers = (1..10).map{ |index| Worker.new(index) }

at_exit do
  workers.each{ |worker| puts worker }
end

workers.map{ |worker| Thread.new{ worker.run } }.each(&:join)

Curses.close_screen

Because Curses clears the screen once it is complete, the at_exit block ensures that the last known state of the workers is echoed to the screen one last time.

Another command I found useful was the Curses.clear command. This function clears the entire screen before rewriting output. This is particularly helpful when you want to overwrite a long line of text with a shorter one.

I hope this was helpful! Have fun making some cool console applications. If you build something worth sharing, please leave a comment that links to your code.