Erlang needs more progress bars

I love Ruby’s progress bar gem. I’ve loved it long before I’d ever heard of or used Rails. It was really my first gem.

I’m a firm believer that the value of an application is directly related to how many progress bars there are in it. I couldn’t find an equivelent for Erlang, so I ported Ruby’s to Erlang. You can download it here.

And it makes sexy progress bars that look like this:

Example      95% oooooooooooooooooooooooooooooooooooooo   ETA: 00:00:03


The erlang implementation is less than half the size of the Ruby version. Likely this is because the Erlang version uses no control structures (like if/while/etc), just pattern matching. At points the Ruby version wins in terms of legibility though, especially when it comes to calendar/datetime manipulation.

For example, here’s the ruby code to display ETA:

def format_time (t)
  t = t.to_i
  sec = t % 60
  min  = (t / 60) % 60
  hour = t / 3600
  sprintf(”%02d:%02d:%02d“, hour, min, sec);
end

def eta
  if @current == 0
    ETA:  –:–:–
  else
    elapsed = Time.now - @start_time
    eta = elapsed * @total / @current - elapsed;
    sprintf(”ETA:  %s“, format_time(eta))
  end
end



And the Erlang code:

current_time() ->
    calendar:datetime_to_gregorian_seconds(calendar:now_to_datetime(now())).

eta( 0, _, _ ) -> –:–:–“;
eta( Current, Total, StartTime ) ->
    Elapsed = current_time() - StartTime,
    {_,{Hours,Mins,Secs}} = calendar:gregorian_seconds_to_datetime( Elapsed * Total div Current - Elapsed ),
    io_lib:format(”~2.10.0B:~2.10.0B:~2.10.0B“, [Hours,Mins,Secs]).

Usage goes something like this:

example(N) ->
    PBar = progress_bar:start(”Example“, N),
    example_work(N, PBar),
    progress_bar:finish(PBar).

example_work(0,_) -> [];
example_work(N, PBar) ->
    PBar1 = progress_bar:increment(PBar),
    timer:sleep(100),
    example_work(N-1, PBar1).

Enjoy! You can download it here.

2 comments ↓

#1 Dan Bravender on 06.14.08 at 8:04 pm

If you change the record name to progress_bar you can have more object-orienty virtual dispatch and make calls like Pbar1 = Pbar:increment().

— progress_bar_orig.erl 2008-06-15 12:02:01.000000000 +0900
+++ progress_bar.erl 2008-06-15 12:00:46.000000000 +0900
@@ -21,29 +21,29 @@
-module(progress_bar).
-export([start/2, finish/1, increment/1, example/1]).

–record(pbar_state, { title, total, current, start_time }).
+-record(progress_bar, { title, total, current, start_time }).
-define(bar, “oooooooooooooooooooooooooooooooooooooooo”).
-define(percent(Current, Total), round((Current/Total) * 100)).

start(Title, Total) ->
- PBar = #pbar_state{ title = Title, total = Total, current = 0, start_time = current_time() },
+ PBar = #progress_bar{ title = Title, total = Total, current = 0, start_time = current_time() },
show(PBar),
PBar.

-increment(PBar = #pbar_state{ current = Total, total = Total }) ->
+increment(PBar = #progress_bar{ current = Total, total = Total }) ->
finish(PBar);
-increment(PBar = #pbar_state{ current = Current, total = Total })
+increment(PBar = #progress_bar{ current = Current, total = Total })
when ?percent(Current, Total) =:= ?percent(Current + 1, Total) ->
- PBar#pbar_state{ current = Current + 1 }; %% Only display if there has been a change in percent
-increment(PBar = #pbar_state{ current = Current }) ->
- show(PBar1 = PBar#pbar_state{ current = Current + 1 }),
+ PBar#progress_bar{ current = Current + 1 }; %% Only display if there has been a change in percent
+increment(PBar = #progress_bar{ current = Current }) ->
+ show(PBar1 = PBar#progress_bar{ current = Current + 1 }),
PBar1.

-finish(PBar = #pbar_state{ total = Total }) ->
- show( PBar#pbar_state{ current = Total } ),
+finish(PBar = #progress_bar{ total = Total }) ->
+ show( PBar#progress_bar{ current = Total } ),
io:format(”\n”).

-show(#pbar_state{ title = Title, total = Total, current = Current, start_time = StartTime }) ->
+show(#progress_bar{ title = Title, total = Total, current = Current, start_time = StartTime }) ->
Percent = ?percent(Current, Total),
io:format(”~-12s ~3B% ~s ETA: ~s\r”, [Title, Percent, bar(Percent), eta(Current, Total, StartTime)]).

@@ -70,7 +70,7 @@

example_work(0,_) -> [];
example_work(N, PBar) ->
- PBar1 = increment(PBar),
+ PBar1 = PBar:increment(),
timer:sleep(100),
example_work(N-1, PBar1).

#2 Luke Galea on 06.15.08 at 5:30 am

That’s great! I’ve also made it so that it can run as a server and receive increment messages from multiple processes.

I’ll update with both improvements. Thanks!

You must log in to post a comment.