Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixing issue #19 #30

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,26 @@ Enum.each 1..100, fn (i) ->
end
```

But you can also simplify working with `Enum`:

```elixir
1..100
|> ProgressBar.from_enum(fn (i) ->
:timer.sleep 25
end)
```

Or even can work with `Stream` (be careful, it will use a `Stream.count/1` to determine total number of elements):

```elixir
1..100
|> ProgressBar.from_stream()
|> Enum.map(fn (i) ->
:timer.sleep 25
end)
|> Enum.into([])
```

#### Width

The bar will automatically set its width to fit the terminal. If the terminal width can't be determined automatically, an 80 column width will be assumed.
Expand Down
28 changes: 28 additions & 0 deletions lib/progress_bar.ex
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,32 @@ defmodule ProgressBar do
def render_spinner(custom_format, fun) do
ProgressBar.Spinner.render(custom_format, fun)
end

def from_enum(list, fun, opts \\ []) do
total = Enum.count(list)

ProgressBar.render(0, total, opts)

list
|> Enum.with_index(1)
|> Enum.each(fn {el, i} ->
fun.(el)
ProgressBar.render(i, total, opts)
end)

list
end

def from_stream(list, opts \\ []) do
total = Enum.count(list)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My Elixir is rusty (haven't written much in the past few years) and I never were super familiar with streams.

Is it reasonable to execute the stream here by counting the length? It was suggested in #19 that the length could be passed in, but I'm not sure in what situations you might know the length and also have a stream that you don't want to execute here. The user will need to execute it again after the map/each below to get any output.

cc @kelvinst if you remember or still have any specific use case around this where this detail would matter.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well thought. I can't think of a case where one would have the length of a stream before actually going through it. Maybe one would already go through the stream of another operation and already have its length, but then it does not make sense to attach a progress bar after that again, IDK.

Anyway, I think it's ok to get the length this way, we might just want to warn people that the Stream is going to be traversed to get its final length and print the %

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will add this message to readme


ProgressBar.render(0, total, opts)

list
|> Stream.with_index(1)
|> Stream.map(fn {el, i} ->
henrik marked this conversation as resolved.
Show resolved Hide resolved
ProgressBar.render(i, total, opts)
el
end)
end
end
5 changes: 3 additions & 2 deletions lib/progress_bar/determinate.ex
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ defmodule ProgressBar.Determinate do
suffix: false,
bar_color: [],
blank_color: [],
width: :auto,
width: :auto
]

def render(current, total, custom_format \\ @default_format) when current <= total do
Expand All @@ -21,7 +21,7 @@ defmodule ProgressBar.Determinate do
suffix = [
formatted_percent(format[:percent], percent),
formatted_suffix(format[:suffix], current, total),
newline_if_complete(current, total),
newline_if_complete(current, total)
]

ProgressBar.BarFormatter.write(
Expand All @@ -35,6 +35,7 @@ defmodule ProgressBar.Determinate do
# Private

defp formatted_percent(false, _), do: ""

defp formatted_percent(true, number) do
number
|> Integer.to_string()
Expand Down
35 changes: 29 additions & 6 deletions test/determinate_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -118,16 +118,39 @@ defmodule DeterminateTest do
mb = 1_000_000
format = [suffix: :bytes, width: @width]

assert_bar ProgressBar.render(0, mb, format) == "| | 0% (0.00/1.00 MB)"
assert_bar ProgressBar.render(mb / 2, mb, format) == "|================================================== | 50% (0.50/1.00 MB)"
assert_bar ProgressBar.render(mb, mb, format) == "|====================================================================================================| 100% (1.00 MB)"
assert_bar(
ProgressBar.render(0, mb, format) ==
"| | 0% (0.00/1.00 MB)"
)

assert_bar(
ProgressBar.render(mb / 2, mb, format) ==
"|================================================== | 50% (0.50/1.00 MB)"
)

assert_bar(
ProgressBar.render(mb, mb, format) ==
"|====================================================================================================| 100% (1.00 MB)"
)
end

test "suffix: :count" do
mb = 100
format = [suffix: :count, width: @width]
assert_bar ProgressBar.render(0, mb, format) == "| | 0% (0/100)"
assert_bar ProgressBar.render(50, mb, format) == "|================================================== | 50% (50/100)"
assert_bar ProgressBar.render(mb, mb, format) == "|====================================================================================================| 100% (100)"

assert_bar(
ProgressBar.render(0, mb, format) ==
"| | 0% (0/100)"
)

assert_bar(
ProgressBar.render(50, mb, format) ==
"|================================================== | 50% (50/100)"
)

assert_bar(
ProgressBar.render(mb, mb, format) ==
"|====================================================================================================| 100% (100)"
)
end
end
76 changes: 76 additions & 0 deletions test/enum_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
defmodule EnumTest do
use ExUnit.Case

alias ProgressBar.Utils
import ExUnit.CaptureIO

test "it works with enums" do
test_pid = self()

io =
capture_io(fn ->
list = ProgressBar.from_enum([1, 2, 3, 4, 5], fn _ -> send(test_pid, :fn_run) end)

assert list == [1, 2, 3, 4, 5]
end)

assert split_bars(io) == [
"|",
"|",
"0%",
"|===============",
"|",
"20%",
"|=============================",
"|",
"40%",
"|============================================",
"|",
"60%",
"|==========================================================",
"|",
"80%",
"|=========================================================================|",
"100%"
]

for _ <- 1..5, do: assert_received(:fn_run)
refute_received :fn_run
end

test "it works with streams" do
io =
capture_io(fn ->
list =
[1, 2, 3, 4, 5]
|> ProgressBar.from_stream()
|> Enum.into([])

assert list == [1, 2, 3, 4, 5]
end)

assert split_bars(io) == [
"|",
"|",
"0%",
"|===============",
"|",
"20%",
"|=============================",
"|",
"40%",
"|============================================",
"|",
"60%",
"|==========================================================",
"|",
"80%",
"|=========================================================================|",
"100%"
]
end

defp split_bars(string) do
string |> String.replace(Utils.ansi_prefix(), "\n") |> String.split()
end
end