View Source Owl.LiveScreen (Owl v0.12.0)

A server that handles live updates in terminal.

It partially implements The Erlang I/O Protocol, so it is possible to use Owl.LiveScreen as an I/O-device in Logger and functions like Owl.IO.puts/2, IO.puts/2. When used as I/O-device, then output is printed above dynamic blocks.

Example

require Logger
:ok = :logger.remove_handler(:default)

:ok =
  :logger.add_handler(:default, :logger_std_h, %{
    config: %{type: {:device, Owl.LiveScreen}},
    formatter: Logger.Formatter.new()
  })

Owl.LiveScreen.add_block(:dependency,
  state: :init,
  render: fn
    :init -> "init..."
    dependency -> ["dependency: ", Owl.Data.tag(dependency, :yellow)]
  end
)

Owl.LiveScreen.add_block(:compiling,
  render: fn
    :init -> "init..."
    filename -> ["compiling: ", Owl.Data.tag(to_string(filename), :cyan)]
  end
)

["ecto", "phoenix", "ex_doc", "broadway"]
|> Enum.each(fn dependency ->
  Owl.LiveScreen.update(:dependency, dependency)

  1..5
  |> Enum.map(&"filename#{&1}.ex")
  |> Enum.each(fn filename ->
    Owl.LiveScreen.update(:compiling, filename)
    Process.sleep(1000)
    Logger.debug("#{filename} compiled for dependency #{dependency}")
  end)
end)

Summary

Functions

Adds a sticky block to the bottom of the screen that can be updated using update/3.

Awaits for next rendering.

Renders data in buffer and detaches blocks.

Returns the current device used by the LiveScreen.

Allows updating the device of the LiveScreen.

Starts a server.

Renders data in buffer and terminates a server.

Updates a state of the block for using it in the next render iteration.

Types

@type add_block_option() ::
  {:state, any()} | {:render, (block_state :: any() -> Owl.Data.t())}
@type block_id() :: any()
@type start_option() ::
  {:name, GenServer.name()}
  | {:refresh_every, pos_integer()}
  | {:terminal_width, pos_integer() | :auto}
  | {:device, IO.device()}

Functions

Link to this function

add_block(server \\ __MODULE__, block_id, opts)

View Source
@spec add_block(GenServer.server(), block_id(), [add_block_option()]) :: :ok

Adds a sticky block to the bottom of the screen that can be updated using update/3.

Options

  • :render - a function that accepts state and returns a view of the block. Defaults to Function.identity/1, which means that state has to have type Owl.Data.t/0.
  • :state - initial state of the block. Defaults to nil.

Example

Owl.LiveScreen.add_block(:footer, state: "starting...")
# which is equivalent to
Owl.LiveScreen.add_block(:footer, render: fn
  nil -> "starting..."
  data -> data
end)
Link to this function

await_render(server \\ __MODULE__)

View Source
@spec await_render(GenServer.server()) :: :ok

Awaits for next rendering.

This is useful when you want to ensure that last published state is rendered on the screen.

Example

Owl.LiveScreen.add_block(:status, state: "starting...")
Owl.LiveScreen.update(:status, "done!")
Owl.LiveScreen.await_render()
Link to this function

capture_stdio(server \\ __MODULE__, callback)

View Source
@spec capture_stdio(GenServer.server(), (-> result)) :: result when result: any()

Redirects output from :stdio to Owl.LiveScreen.

Example

Owl.ProgressBar.start(id: :users, label: "Creating users", total: 100)

Owl.LiveScreen.capture_stdio(fn ->
  Enum.each(1..100, fn i ->
    Process.sleep(10)
    Owl.ProgressBar.inc(id: :users)
    # Output from next call will be printed above progress bar
    IO.inspect([:test, i])
  end)
end)

Owl.LiveScreen.await_render()
Link to this function

flush(server \\ __MODULE__)

View Source
@spec flush(GenServer.server()) :: :ok

Renders data in buffer and detaches blocks.

Link to this function

get_device(server \\ __MODULE__)

View Source
@spec get_device(GenServer.server()) :: IO.device()

Returns the current device used by the LiveScreen.

Link to this function

set_device(server \\ __MODULE__, new_device)

View Source
@spec set_device(GenServer.server(), IO.device()) :: :ok

Allows updating the device of the LiveScreen.

This is useful for example when using the Erlang SSH console.

Example

group_leader = Process.group_leader()

Owl.LiveScreen.set_device(group_leader)
@spec start_link([start_option()]) :: GenServer.on_start()

Starts a server.

Server is started automatically by :owl application as a named process.

Options

  • :name - used for name registration as described in the "Name registration" section in the documentation for GenServer. Defaults to Owl.LiveScreen
  • :refresh_every - a period of refreshing a screen in milliseconds. Defaults to 60.
  • :terminal_width - a width of terminal in symbols. Defaults to :auto, which gets value from Owl.IO.columns/1.
  • :device - an I/O device. Defaults to :stdio. If terminal is not available, then the server won't be started.
Link to this function

stop(server \\ __MODULE__)

View Source
@spec stop(GenServer.server()) :: :ok

Renders data in buffer and terminates a server.

Link to this function

update(server \\ __MODULE__, block_id, block_state)

View Source
@spec update(GenServer.server(), block_id(), block_state :: any()) :: :ok

Updates a state of the block for using it in the next render iteration.

Example

Owl.LiveScreen.add_block(:footer, state: "starting...")
Process.sleep(1000)
Owl.LiveScreen.update(:footer, "...almost done...")
Process.sleep(1000)
Owl.LiveScreen.update(:footer, "done!!!")