Cool CLIs in Elixir with IO.write/2

Done!") end def count(current) do IO.

write("
#{current} ") Process.

sleep(250) count(current – 1) endendCount.

count(10)Great ProgressCool progress bar written in ElixirThat’s most of what I wanted to show you, but I want to end by showing you one of the coolest applications for this.

This example is a little bit more difficult to follow, but it will allow you to add a simple progress bar to a CLI tool.

My friend Trevor Fenn implemented it in Ruby for some stuff we’re doing at Podium, so I’ve ported it over to Elixir for this example.

To start off we’re going to make a module called Progress containing a function called bar.

Let’s have bar receive two parameters: count and total.

I’m going to add a private percent_complete function that takes in the same count and total and we’ll calculate the actual percentage here.

So let’s start by dividing the count by the total which gives us the decimal percentage, and then we’ll multiply that by 100 to give us a more human readable percentage.

Then finally let’s round it to the nearest hundredth so that it doesn’t get too big.

I’m going to use a module attribute constant for the rounding precision though so that it’s easily changeable if we want to do that later.

defmodule Progress do @rounding_precision 2 def bar(count, total) do end defp percent_complete(count, total) do Float.

round(100.

0 * count / total, @rounding_precision) endendOK now that we have the percentage complete, we need to actually create the progress bar from that.

So how this progress bar will work is we’re going to have two different unicode characters that we’re using.

The first is a halftone box (░) and the other is a filled box (█).

We’ll want to show the filled box the number of times that it takes to show the current progress and then the halftone box for the rest of it.

So I’m going to create another module attribute constant called @progress_bar_size to say how many characters long the progress bar should be.

I think I’m going to say 50 for now to simplify the math.

So to figure out how many percentage point each character is worth I will divide 100 by the progress bar size, so in this case, each character is worth 2% of the total.

Then we’ll calculate how many completed characters we should write to the line and then subtract that from the total progress bar size to know how many incomplete characters to write.

def bar(count, total) do percent = percent_complete(count, total) divisor = 100 / @progress_bar_size complete_count = round(percent / divisor) incomplete_count = @progress_bar_size – complete_countendIn the video, I created a new function called repeat that returns a string with the specified number of that character.

When I showed my code to Trevor though, he showed me that there is a String.

duplicate/2 that will do exactly what I was going for, so we’ll use that to generate the full progress bar.

We’ll start by writing the repeated completed characters and then we’ll append the incomplete characters onto that.

And finally let’s write the percent complete at the end to make it a little more usable.

defmodule Progress do .

@complete_character "█" @incomplete_character "░" def bar(count, total) do .

complete = String.

duplicate(@complete_character, complete_count) incomplete = String.

duplicate(@incomplete_character, incomplete_count) "#{complete}#{incomplete} (#{percent}%)" endendSo now let’s actually run it.

Outside of the module let’s say we have a total of 50 tasks to complete.

So we’ll call Enum.

each over a range from 1 to 50 and with each number we can generate a progress bar with the current task number and the total.

Let’s go ahead and write this to the console with a carriage return at the front to tell it to overwrite the previous progress bar.

Then to make it a little more visible, let’s add a Process.

sleep/1 to each iteration.

And then at the end let’s write a newline so that when the execution finishes it moves our cursor down to the next line.

defmodule Progress do .

endtotal = 50Enum.

each(1.

total, fn task -> IO.

write("
#{Progress.

bar(task, total)}") Process.

sleep(50)end)IO.

write(".")So overall, you’re looking at something like this.

defmodule Progress do @rounding_precision 2 @progress_bar_size 50 @complete_character "█" @incomplete_character "░" def bar(count, total) do percent = percent_complete(count, total) divisor = 100 / @progress_bar_size complete_count = round(percent / divisor) incomplete_count = @progress_bar_size – complete_count complete = String.

duplicate(@complete_character, complete_count) incomplete = String.

duplicate(@incomplete_character, incomplete_count) "#{complete}#{incomplete} (#{percent}%)" end defp percent_complete(count, total) do Float.

round(100.

0 * count / total, @rounding_precision) endendtotal = 50Enum.

each(1.

total, fn task -> IO.

write("
#{Progress.

bar(task, total)}") Process.

sleep(50)end)IO.

write(".")So there you have it: a pretty simple, but pretty cool progress bar for CLI applications.

If you end up using this somewhere please let me know.

You can find me on Twitter at dnsbty or on the Elixir Slack group with the same name.

Or you can just let me know in the comments of the video above.

I’m hoping to put out more videos like this, so if you’re interested, please like the video and subscribe to my channel so that YouTube will let you know when they come out.

Thanks for reading!Originally published at dennisbeatty.

com on January 9, 2019.

.

. More details

Leave a Reply