Make Port docs examples more formatter-compatible (#7959)

defmodule Port do
  @moduledoc ~S"""
  Functions for interacting with the external world through ports.

  Ports provide a mechanism to start operating system processes external
  to the Erlang VM and communicate with them via message passing.

  ## Example

      iex> port = Port.open({:spawn, "cat"}, [:binary])
      iex> send(port, {self(), {:command, "hello"}})
      iex> send(port, {self(), {:command, "world"}})
      iex> flush()
      {#Port<0.1444>, {:data, "hello"}}
      {#Port<0.1444>, {:data, "world"}}
      iex> send(port, {self(), :close})
      :ok
      iex> flush()
      {#Port<0.1464>, :closed}
      :ok

  In the example above, we have created a new port that executes the
  program `cat`. `cat` is a program available on UNIX systems that
  receives data from multiple inputs and concatenates them in the output.

  After the port was created, we sent it two commands in the form of
  messages using `Kernel.send/2`. The first command has the binary payload
  of "hello" and the second has "world".

  After sending those two messages, we invoked the IEx helper `flush()`,
  which printed all messages received from the port, in this case we got
  "hello" and "world" back. Notice the messages are in binary because we
  passed the `:binary` option when opening the port in `Port.open/2`. Without
  such option, it would have yielded a list of bytes.

  Once everything was done, we closed the port.

  Elixir provides many conveniences for working with ports and some drawbacks.
  We will explore those below.

  ## Message and function APIs

  There are two APIs for working with ports. It can be either asynchronous via
  message passing, as in the example above, or by calling the functions on this
  module.

  The messages supported by ports and their counterpart function APIs are
  listed below:

    * `{pid, {:command, binary}}` - sends the given data to the port.
      See `command/3`.

    * `{pid, :close}` - closes the port. Unless the port is already closed,
      the port will reply with `{port, :closed}` message once it has flushed
      its buffers and effectively closed. See `close/1`.

    * `{pid, {:connect, new_pid}}` - sets the `new_pid` as the new owner of
      the port. Once a port is opened, the port is linked and connected to the
      caller process and communication to the port only happens through the
      connected process. This message makes `new_pid` the new connected processes.
      Unless the port is dead, the port will reply to the old owner with
      `{port, :connected}`. See `connect/2`.

  On its turn, the port will send the connected process the following messages:

    * `{port, {:data, data}}` - data sent by the port
    * `{port, :closed}` - reply to the `{pid, :close}` message
    * `{port, :connected}` - reply to the `{pid, {:connect, new_pid}}` message
    * `{:EXIT, port, reason}` - exit signals in case the port crashes. If reason
      is not `:normal`, this message will only be received if the owner process
      is trapping exits

  ## Open mechanisms

  The port can be opened through four main mechanisms.

  As a short summary, prefer to using the `:spawn` and `:spawn_executable`
  options mentioned below. The other two options, `:spawn_driver` and `:fd`
  are for advanced usage within the VM. Also consider using `System.cmd/3`
  if all you want is to execute a program and retrieve its return value.

  ### spawn

  The `:spawn` tuple receives a binary that is going to be executed as a
  full invocation. For example, we can use it to invoke "echo hello" directly:

      iex> port = Port.open({:spawn, "echo hello"}, [:binary])
      iex> flush()
      {#Port<0.1444>, {:data, "hello\n"}}

  `:spawn` will retrieve the program name from the argument and traverse your
  OS `$PATH` environment variable looking for a matching program.

  Although the above is handy, it means it is impossible to invoke an executable
  that has whitespaces on its name or in any of its arguments. For those reasons,
  most times it is preferable to execute `:spawn_executable`.

  ### spawn_executable

  Spawn executable is a more restricted and explicit version of spawn. It expects
  full file paths to the executable you want to execute. If they are in your `$PATH`,
  they can be retrieved by calling `System.find_executable/1`:

      iex> path = System.find_executable("echo")
      iex> port = Port.open({:spawn_executable, path}, [:binary, args: ["hello world"]])
      iex> flush()
      {#Port<0.1380>, {:data, "hello world\n"}}

  When using `:spawn_executable`, the list of arguments can be passed via
  the `:args` option as done above. For the full list of options, see the
  documentation for the Erlang function `:erlang.open_port/2`.

  ### fd

  The `:fd` name option allows developers to access `in` and `out` file
  descriptors used by the Erlang VM. You would use those only if you are
  reimplementing core part of the Runtime System, such as the `:user` and
  `:shell` processes.

  ## Zombie OS processes

  A port can be closed via the `close/1` function or by sending a `{pid, :close}`
  message. However, if the VM crashes, a long-running program started by the port
  will have its stdin and stdout channels closed but **it won't be automatically
  terminated**.

  While most UNIX command line tools will exit once its communication channels
  are closed, not all command line applications will do so. While we encourage
  graceful termination by detecting if stdin/stdout has been closed, we do not
  always have control over how 3rd party software terminates. In those cases,
  you can wrap the application in a script that checks for stdin. Here is such
  script in bash:

      #!/bin/sh
      "$@" &
      pid=$!
      while read line ; do
        :
      done
      kill -KILL $pid

  Now instead of:

      Port.open(
        {:spawn_executable, "/path/to/program"},
        args: ["a", "b", "c"]
      )

  You may invoke:

      Port.open(
        {:spawn_executable, "/path/to/wrapper"},
        args: ["/path/to/program", "a", "b", "c"]
      )

  """

  @type name ::
          {:spawn, charlist | binary}
          | {:spawn_driver, charlist | binary}
          | {:spawn_executable, charlist | atom}
          | {:fd, non_neg_integer, non_neg_integer}

  @doc """
  Opens a port given a tuple `name` and a list of `options`.

  The module documentation above contains documentation and examples
  for the supported `name` values, summarized below:

    * `{:spawn, command}` - runs an external program. `command` must contain
      the program name and optionally a list of arguments separated by space.
      If passing programs or arguments with space in their name, use the next option.
    * `{:spawn_executable, filename}` - runs the executable given by the absolute
      file name `filename`. Arguments can be passed via the `:args` option.
    * `{:spawn_driver, command}` - spawns so-called port drivers.
    * `{:fd, fd_in, fd_out}` - accesses file descriptors, `fd_in` and `fd_out`
      opened by the VM.

  For more information and the list of options, see `:erlang.open_port/2`.

  Inlined by the compiler.
  """
  @spec open(name, list) :: port
  def open(name, options) do
    :erlang.open_port(name, options)
  end

  @doc """
  Closes the `port`.

  For more information, see `:erlang.port_close/1`.

  Inlined by the compiler.
  """
  @spec close(port) :: true
  def close(port) do
    :erlang.port_close(port)
  end

  @doc """
  Sends `data` to the port driver `port`.

  For more information, see `:erlang.port_command/2`.

  Inlined by the compiler.
  """
  @spec command(port, iodata, [:force | :nosuspend]) :: boolean
  def command(port, data, options \\ []) do
    :erlang.port_command(port, data, options)
  end

  @doc """
  Associates the `port` identifier with a `pid`.

  For more information, see `:erlang.port_connect/2`.

  Inlined by the compiler.
  """
  @spec connect(port, pid) :: true
  def connect(port, pid) do
    :erlang.port_connect(port, pid)
  end

  @doc """
  Returns information about the `port` or `nil` if the port is closed.

  For more information, see `:erlang.port_info/1`.
  """
  def info(port) do
    nillify(:erlang.port_info(port))
  end

  @doc """
  Returns information about the `port` or `nil` if the port is closed.

  For more information, see `:erlang.port_info/2`.
  """
  @spec info(port, atom) :: {atom, term} | nil
  def info(port, spec)

  def info(port, :registered_name) do
    case :erlang.port_info(port, :registered_name) do
      :undefined -> nil
      [] -> {:registered_name, []}
      other -> other
    end
  end

  def info(port, item) do
    nillify(:erlang.port_info(port, item))
  end

  @doc """
  Starts monitoring the given `port` from the calling process.

  Once the monitored port process dies, a message is delivered to the
  monitoring process in the shape of:

      {:DOWN, ref, :port, object, reason}

  where:

    * `ref` is a monitor reference returned by this function;
    * `object` is either the `port` being monitored (when monitoring by port id)
    or `{name, node}` (when monitoring by a port name);
    * `reason` is the exit reason.

  See `:erlang.monitor/2` for more info.

  Inlined by the compiler.
  """
  @doc since: "1.6.0"
  @spec monitor(port | {name :: atom, node :: atom} | name :: atom) :: reference
  def monitor(port) do
    :erlang.monitor(:port, port)
  end

  @doc """
  Demonitors the monitor identified by the given `reference`.

  If `monitor_ref` is a reference which the calling process
  obtained by calling `monitor/1`, that monitoring is turned off.
  If the monitoring is already turned off, nothing happens.

  See `:erlang.demonitor/2` for more info.

  Inlined by the compiler.
  """
  @doc since: "1.6.0"
  @spec demonitor(reference, options :: [:flush | :info]) :: boolean
  defdelegate demonitor(monitor_ref, options \\ []), to: :erlang

  @doc """
  Returns a list of all ports in the current node.

  Inlined by the compiler.
  """
  @spec list :: [port]
  def list do
    :erlang.ports()
  end

  @compile {:inline, nillify: 1}
  defp nillify(:undefined), do: nil
  defp nillify(other), do: other
end

defmodule Port do
  @moduledoc ~S"""
  Functions for interacting with the external world through ports.

  Ports provide a mechanism to start operating system processes external
  to the Erlang VM and communicate with them via message passing.

  ## Example

      iex> port = Port.open({:spawn, "cat"}, [:binary])
      iex> send(port, {self(), {:command, "hello"}})
      iex> send(port, {self(), {:command, "world"}})
      iex> flush()
      {#Port<0.1444>, {:data, "hello"}}
      {#Port<0.1444>, {:data, "world"}}
      iex> send(port, {self(), :close})
      :ok
      iex> flush()
      {#Port<0.1464>, :closed}
      :ok

  In the example above, we have created a new port that executes the
  program `cat`. `cat` is a program available on UNIX systems that
  receives data from multiple inputs and concatenates them in the output.

  After the port was created, we sent it two commands in the form of
  messages using `Kernel.send/2`. The first command has the binary payload
  of "hello" and the second has "world".

  After sending those two messages, we invoked the IEx helper `flush()`,
  which printed all messages received from the port, in this case we got
  "hello" and "world" back. Notice the messages are in binary because we
  passed the `:binary` option when opening the port in `Port.open/2`. Without
  such option, it would have yielded a list of bytes.

  Once everything was done, we closed the port.

  Elixir provides many conveniences for working with ports and some drawbacks.
  We will explore those below.

  ## Message and function APIs

  There are two APIs for working with ports. It can be either asynchronous via
  message passing, as in the example above, or by calling the functions on this
  module.

  The messages supported by ports and their counterpart function APIs are
  listed below:

    * `{pid, {:command, binary}}` - sends the given data to the port.
      See `command/3`.

    * `{pid, :close}` - closes the port. Unless the port is already closed,
      the port will reply with `{port, :closed}` message once it has flushed
      its buffers and effectively closed. See `close/1`.

    * `{pid, {:connect, new_pid}}` - sets the `new_pid` as the new owner of
      the port. Once a port is opened, the port is linked and connected to the
      caller process and communication to the port only happens through the
      connected process. This message makes `new_pid` the new connected processes.
      Unless the port is dead, the port will reply to the old owner with
      `{port, :connected}`. See `connect/2`.

  On its turn, the port will send the connected process the following messages:

    * `{port, {:data, data}}` - data sent by the port
    * `{port, :closed}` - reply to the `{pid, :close}` message
    * `{port, :connected}` - reply to the `{pid, {:connect, new_pid}}` message
    * `{:EXIT, port, reason}` - exit signals in case the port crashes. If reason
      is not `:normal`, this message will only be received if the owner process
      is trapping exits

  ## Open mechanisms

  The port can be opened through four main mechanisms.

  As a short summary, prefer to using the `:spawn` and `:spawn_executable`
  options mentioned below. The other two options, `:spawn_driver` and `:fd`
  are for advanced usage within the VM. Also consider using `System.cmd/3`
  if all you want is to execute a program and retrieve its return value.

  ### spawn

  The `:spawn` tuple receives a binary that is going to be executed as a
  full invocation. For example, we can use it to invoke "echo hello" directly:

      iex> port = Port.open({:spawn, "echo hello"}, [:binary])
      iex> flush()
      {#Port<0.1444>, {:data, "hello\n"}}

  `:spawn` will retrieve the program name from the argument and traverse your
  OS `$PATH` environment variable looking for a matching program.

  Although the above is handy, it means it is impossible to invoke an executable
  that has whitespaces on its name or in any of its arguments. For those reasons,
  most times it is preferable to execute `:spawn_executable`.

  ### spawn_executable

  Spawn executable is a more restricted and explicit version of spawn. It expects
  full file paths to the executable you want to execute. If they are in your `$PATH`,
  they can be retrieved by calling `System.find_executable/1`:

      iex> path = System.find_executable("echo")
      iex> port = Port.open({:spawn_executable, path}, [:binary, args: ["hello world"]])
      iex> flush()
      {#Port<0.1380>, {:data, "hello world\n"}}

  When using `:spawn_executable`, the list of arguments can be passed via
  the `:args` option as done above. For the full list of options, see the
  documentation for the Erlang function `:erlang.open_port/2`.

  ### fd

  The `:fd` name option allows developers to access `in` and `out` file
  descriptors used by the Erlang VM. You would use those only if you are
  reimplementing core part of the Runtime System, such as the `:user` and
  `:shell` processes.

  ## Zombie OS processes

  A port can be closed via the `close/1` function or by sending a `{pid, :close}`
  message. However, if the VM crashes, a long-running program started by the port
  will have its stdin and stdout channels closed but **it won't be automatically
  terminated**.

  While most UNIX command line tools will exit once its communication channels
  are closed, not all command line applications will do so. While we encourage
  graceful termination by detecting if stdin/stdout has been closed, we do not
  always have control over how 3rd party software terminates. In those cases,
  you can wrap the application in a script that checks for stdin. Here is such
  script in bash:

      #!/bin/sh
      "$@" &
      pid=$!
      while read line ; do
        :
      done
      kill -KILL $pid

  Now instead of:

      Port.open({:spawn_executable, "/path/to/program"},
                [args: ["a", "b", "c"]])

  You may invoke:

      Port.open({:spawn_executable, "/path/to/wrapper"},
                [args: ["/path/to/program", "a", "b", "c"]])

  """

  @type name ::
          {:spawn, charlist | binary}
          | {:spawn_driver, charlist | binary}
          | {:spawn_executable, charlist | atom}
          | {:fd, non_neg_integer, non_neg_integer}

  @doc """
  Opens a port given a tuple `name` and a list of `options`.

  The module documentation above contains documentation and examples
  for the supported `name` values, summarized below:

    * `{:spawn, command}` - runs an external program. `command` must contain
      the program name and optionally a list of arguments separated by space.
      If passing programs or arguments with space in their name, use the next option.
    * `{:spawn_executable, filename}` - runs the executable given by the absolute
      file name `filename`. Arguments can be passed via the `:args` option.
    * `{:spawn_driver, command}` - spawns so-called port drivers.
    * `{:fd, fd_in, fd_out}` - accesses file descriptors, `fd_in` and `fd_out`
      opened by the VM.

  For more information and the list of options, see `:erlang.open_port/2`.

  Inlined by the compiler.
  """
  @spec open(name, list) :: port
  def open(name, options) do
    :erlang.open_port(name, options)
  end

  @doc """
  Closes the `port`.

  For more information, see `:erlang.port_close/1`.

  Inlined by the compiler.
  """
  @spec close(port) :: true
  def close(port) do
    :erlang.port_close(port)
  end

  @doc """
  Sends `data` to the port driver `port`.

  For more information, see `:erlang.port_command/2`.

  Inlined by the compiler.
  """
  @spec command(port, iodata, [:force | :nosuspend]) :: boolean
  def command(port, data, options \\ []) do
    :erlang.port_command(port, data, options)
  end

  @doc """
  Associates the `port` identifier with a `pid`.

  For more information, see `:erlang.port_connect/2`.

  Inlined by the compiler.
  """
  @spec connect(port, pid) :: true
  def connect(port, pid) do
    :erlang.port_connect(port, pid)
  end

  @doc """
  Returns information about the `port` or `nil` if the port is closed.

  For more information, see `:erlang.port_info/1`.
  """
  def info(port) do
    nillify(:erlang.port_info(port))
  end

  @doc """
  Returns information about the `port` or `nil` if the port is closed.

  For more information, see `:erlang.port_info/2`.
  """
  @spec info(port, atom) :: {atom, term} | nil
  def info(port, spec)

  def info(port, :registered_name) do
    case :erlang.port_info(port, :registered_name) do
      :undefined -> nil
      [] -> {:registered_name, []}
      other -> other
    end
  end

  def info(port, item) do
    nillify(:erlang.port_info(port, item))
  end

  @doc """
  Starts monitoring the given `port` from the calling process.

  Once the monitored port process dies, a message is delivered to the
  monitoring process in the shape of:

      {:DOWN, ref, :port, object, reason}

  where:

    * `ref` is a monitor reference returned by this function;
    * `object` is either the `port` being monitored (when monitoring by port id)
    or `{name, node}` (when monitoring by a port name);
    * `reason` is the exit reason.

  See `:erlang.monitor/2` for more info.

  Inlined by the compiler.
  """
  @doc since: "1.6.0"
  @spec monitor(port | {name :: atom, node :: atom} | name :: atom) :: reference
  def monitor(port) do
    :erlang.monitor(:port, port)
  end

  @doc """
  Demonitors the monitor identified by the given `reference`.

  If `monitor_ref` is a reference which the calling process
  obtained by calling `monitor/1`, that monitoring is turned off.
  If the monitoring is already turned off, nothing happens.

  See `:erlang.demonitor/2` for more info.

  Inlined by the compiler.
  """
  @doc since: "1.6.0"
  @spec demonitor(reference, options :: [:flush | :info]) :: boolean
  defdelegate demonitor(monitor_ref, options \\ []), to: :erlang

  @doc """
  Returns a list of all ports in the current node.

  Inlined by the compiler.
  """
  @spec list :: [port]
  def list do
    :erlang.ports()
  end

  @compile {:inline, nillify: 1}
  defp nillify(:undefined), do: nil
  defp nillify(other), do: other
end


Fix Port.open/2 signature (#7360)

Fix Port.open/2 signature (#7360)

Use options instead of settings to adhere Elixir naming conventions.

defmodule Port do
  @moduledoc ~S"""
  Functions for interacting with the external world through ports.

  Ports provide a mechanism to start operating system processes external
  to the Erlang VM and communicate with them via message passing.

  ## Example

      iex> port = Port.open({:spawn, "cat"}, [:binary])
      iex> send(port, {self(), {:command, "hello"}})
      iex> send(port, {self(), {:command, "world"}})
      iex> flush()
      {#Port<0.1444>, {:data, "hello"}}
      {#Port<0.1444>, {:data, "world"}}
      iex> send(port, {self(), :close})
      :ok
      iex> flush()
      {#Port<0.1464>, :closed}
      :ok

  In the example above, we have created a new port that executes the
  program `cat`. `cat` is a program available on UNIX systems that
  receives data from multiple inputs and concatenates them in the output.

  After the port was created, we sent it two commands in the form of
  messages using `Kernel.send/2`. The first command has the binary payload
  of "hello" and the second has "world".

  After sending those two messages, we invoked the IEx helper `flush()`,
  which printed all messages received from the port, in this case we got
  "hello" and "world" back. Notice the messages are in binary because we
  passed the `:binary` option when opening the port in `Port.open/2`. Without
  such option, it would have yielded a list of bytes.

  Once everything was done, we closed the port.

  Elixir provides many conveniences for working with ports and some drawbacks.
  We will explore those below.

  ## Message and function APIs

  There are two APIs for working with ports. It can be either asynchronous via
  message passing, as in the example above, or by calling the functions on this
  module.

  The messages supported by ports and their counterpart function APIs are
  listed below:

    * `{pid, {:command, binary}}` - sends the given data to the port.
      See `command/3`.

    * `{pid, :close}` - closes the port. Unless the port is already closed,
      the port will reply with `{port, :closed}` message once it has flushed
      its buffers and effectively closed. See `close/1`.

    * `{pid, {:connect, new_pid}}` - sets the `new_pid` as the new owner of
      the port. Once a port is opened, the port is linked and connected to the
      caller process and communication to the port only happens through the
      connected process. This message makes `new_pid` the new connected processes.
      Unless the port is dead, the port will reply to the old owner with
      `{port, :connected}`. See `connect/2`.

  On its turn, the port will send the connected process the following messages:

    * `{port, {:data, data}}` - data sent by the port
    * `{port, :closed}` - reply to the `{pid, :close}` message
    * `{port, :connected}` - reply to the `{pid, {:connect, new_pid}}` message
    * `{:EXIT, port, reason}` - exit signals in case the port crashes. If reason
      is not `:normal`, this message will only be received if the owner process
      is trapping exits

  ## Open mechanisms

  The port can be opened through four main mechanisms.

  As a short summary, prefer to using the `:spawn` and `:spawn_executable`
  options mentioned below. The other two options, `:spawn_driver` and `:fd`
  are for advanced usage within the VM. Also consider using `System.cmd/3`
  if all you want is to execute a program and retrieve its return value.

  ### spawn

  The `:spawn` tuple receives a binary that is going to be executed as a
  full invocation. For example, we can use it to invoke "echo hello" directly:

      iex> port = Port.open({:spawn, "echo hello"}, [:binary])
      iex> flush()
      {#Port<0.1444>, {:data, "hello\n"}}

  `:spawn` will retrieve the program name from the argument and traverse your
  OS `$PATH` environment variable looking for a matching program.

  Although the above is handy, it means it is impossible to invoke an executable
  that has whitespaces on its name or in any of its arguments. For those reasons,
  most times it is preferable to execute `:spawn_executable`.

  ### spawn_executable

  Spawn executable is a more restricted and explicit version of spawn. It expects
  full file paths to the executable you want to execute. If they are in your `$PATH`,
  they can be retrieved by calling `System.find_executable/1`:

      iex> path = System.find_executable("echo")
      iex> port = Port.open({:spawn_executable, path}, [:binary, args: ["hello world"]])
      iex> flush()
      {#Port<0.1380>, {:data, "hello world\n"}}

  When using `:spawn_executable`, the list of arguments can be passed via
  the `:args` option as done above. For the full list of options, see the
  documentation for the Erlang function `:erlang.open_port/2`.

  ### fd

  The `:fd` name option allows developers to access `in` and `out` file
  descriptors used by the Erlang VM. You would use those only if you are
  reimplementing core part of the Runtime System, such as the `:user` and
  `:shell` processes.

  ## Zombie processes

  A port can be closed via the `close/1` function or by sending a `{pid, :close}`
  message. However, if the VM crashes, a long-running program started by the port
  will have its stdin and stdout channels closed but **it won't be automatically
  terminated**.

  While most UNIX command line tools will exit once its communication channels
  are closed, not all command line applications will do so. While we encourage
  graceful termination by detecting if stdin/stdout has been closed, we do not
  always have control over how 3rd party software terminates. In those cases,
  you can wrap the application in a script that checks for stdin. Here is such
  script in bash:

      #!/bin/sh
      "$@" &
      pid=$!
      while read line ; do
        :
      done
      kill -KILL $pid

  Now instead of:

      Port.open({:spawn_executable, "/path/to/program"},
                [args: ["a", "b", "c"]])

  You may invoke:

      Port.open({:spawn_executable, "/path/to/wrapper"},
                [args: ["/path/to/program", "a", "b", "c"]])

  """

  @type name ::
          {:spawn, charlist | binary}
          | {:spawn_driver, charlist | binary}
          | {:spawn_executable, charlist | atom}
          | {:fd, non_neg_integer, non_neg_integer}

  @doc """
  Opens a port given a tuple `name` and a list of `options`.

  The module documentation above contains documentation and examples
  for the supported `name` values, summarized below:

    * `{:spawn, command}` - runs an external program. `command` must contain
      the program name and optionally a list of arguments separated by space.
      If passing programs or arguments with space in their name, use the next option.
    * `{:spawn_executable, filename}` - runs the executable given by the absolute
      file name `filename`. Arguments can be passed via the `:args` option.
    * `{:spawn_driver, command}` - spawns so-called port drivers.
    * `{:fd, fd_in, fd_out}` - accesses file descriptors, `fd_in` and `fd_out`
      opened by the VM.

  For more information and the list of options, see `:erlang.open_port/2`.

  Inlined by the compiler.
  """
  @spec open(name, list) :: port
  def open(name, options) do
    :erlang.open_port(name, options)
  end

  @doc """
  Closes the `port`.

  For more information, see `:erlang.port_close/1`.

  Inlined by the compiler.
  """
  @spec close(port) :: true
  def close(port) do
    :erlang.port_close(port)
  end

  @doc """
  Sends `data` to the port driver `port`.

  For more information, see `:erlang.port_command/2`.

  Inlined by the compiler.
  """
  @spec command(port, iodata, [:force | :nosuspend]) :: boolean
  def command(port, data, options \\ []) do
    :erlang.port_command(port, data, options)
  end

  @doc """
  Associates the `port` identifier with a `pid`.

  For more information, see `:erlang.port_connect/2`.

  Inlined by the compiler.
  """
  @spec connect(port, pid) :: true
  def connect(port, pid) do
    :erlang.port_connect(port, pid)
  end

  @doc """
  Returns information about the `port` or `nil` if the port is closed.

  For more information, see `:erlang.port_info/1`.
  """
  def info(port) do
    nillify(:erlang.port_info(port))
  end

  @doc """
  Returns information about the `port` or `nil` if the port is closed.

  For more information, see `:erlang.port_info/2`.
  """
  @spec info(port, atom) :: {atom, term} | nil
  def info(port, spec)

  def info(port, :registered_name) do
    case :erlang.port_info(port, :registered_name) do
      [] -> {:registered_name, []}
      other -> nillify(other)
    end
  end

  def info(port, item) do
    nillify(:erlang.port_info(port, item))
  end

  @doc """
  Starts monitoring the given `port` from the calling process.

  Once the monitored port process dies, a message is delivered to the
  monitoring process in the shape of:

      {:DOWN, ref, :port, object, reason}

  where:

    * `ref` is a monitor reference returned by this function;
    * `object` is either the `port` being monitored (when monitoring by port id)
    or `{name, node}` (when monitoring by a port name);
    * `reason` is the exit reason.

  See `:erlang.monitor/2` for more info.

  Inlined by the compiler.
  """
  @since "1.6.0"
  @spec monitor(port | {name :: atom, node :: atom} | name :: atom) :: reference
  def monitor(port) do
    :erlang.monitor(:port, port)
  end

  @doc """
  Demonitors the monitor identified by the given `reference`.

  If `monitor_ref` is a reference which the calling process
  obtained by calling `monitor/1`, that monitoring is turned off.
  If the monitoring is already turned off, nothing happens.

  See `:erlang.demonitor/2` for more info.

  Inlined by the compiler.
  """
  @since "1.6.0"
  @spec demonitor(reference, options :: [:flush | :info]) :: boolean
  defdelegate demonitor(monitor_ref, options \\ []), to: :erlang

  @doc """
  Returns a list of all ports in the current node.

  Inlined by the compiler.
  """
  @spec list :: [port]
  def list do
    :erlang.ports()
  end

  @compile {:inline, nillify: 1}
  defp nillify(:undefined), do: nil
  defp nillify(other), do: other
end

defmodule Port do
  @moduledoc ~S"""
  Functions for interacting with the external world through ports.

  Ports provide a mechanism to start operating system processes external
  to the Erlang VM and communicate with them via message passing.

  ## Example

      iex> port = Port.open({:spawn, "cat"}, [:binary])
      iex> send(port, {self(), {:command, "hello"}})
      iex> send(port, {self(), {:command, "world"}})
      iex> flush()
      {#Port<0.1444>, {:data, "hello"}}
      {#Port<0.1444>, {:data, "world"}}
      iex> send(port, {self(), :close})
      :ok
      iex> flush()
      {#Port<0.1464>, :closed}
      :ok

  In the example above, we have created a new port that executes the
  program `cat`. `cat` is a program available on UNIX systems that
  receives data from multiple inputs and concatenates them in the output.

  After the port was created, we sent it two commands in the form of
  messages using `Kernel.send/2`. The first command has the binary payload
  of "hello" and the second has "world".

  After sending those two messages, we invoked the IEx helper `flush()`,
  which printed all messages received from the port, in this case we got
  "hello" and "world" back. Notice the messages are in binary because we
  passed the `:binary` option when opening the port in `Port.open/2`. Without
  such option, it would have yielded a list of bytes.

  Once everything was done, we closed the port.

  Elixir provides many conveniences for working with ports and some drawbacks.
  We will explore those below.

  ## Message and function APIs

  There are two APIs for working with ports. It can be either asynchronous via
  message passing, as in the example above, or by calling the functions on this
  module.

  The messages supported by ports and their counterpart function APIs are
  listed below:

    * `{pid, {:command, binary}}` - sends the given data to the port.
      See `command/3`.

    * `{pid, :close}` - closes the port. Unless the port is already closed,
      the port will reply with `{port, :closed}` message once it has flushed
      its buffers and effectively closed. See `close/1`.

    * `{pid, {:connect, new_pid}}` - sets the `new_pid` as the new owner of
      the port. Once a port is opened, the port is linked and connected to the
      caller process and communication to the port only happens through the
      connected process. This message makes `new_pid` the new connected processes.
      Unless the port is dead, the port will reply to the old owner with
      `{port, :connected}`. See `connect/2`.

  On its turn, the port will send the connected process the following messages:

    * `{port, {:data, data}}` - data sent by the port
    * `{port, :closed}` - reply to the `{pid, :close}` message
    * `{port, :connected}` - reply to the `{pid, {:connect, new_pid}}` message
    * `{:EXIT, port, reason}` - exit signals in case the port crashes. If reason
      is not `:normal`, this message will only be received if the owner process
      is trapping exits

  ## Open mechanisms

  The port can be opened through four main mechanisms.

  As a short summary, prefer to using the `:spawn` and `:spawn_executable`
  options mentioned below. The other two options, `:spawn_driver` and `:fd`
  are for advanced usage within the VM. Also consider using `System.cmd/3`
  if all you want is to execute a program and retrieve its return value.

  ### spawn

  The `:spawn` tuple receives a binary that is going to be executed as a
  full invocation. For example, we can use it to invoke "echo hello" directly:

      iex> port = Port.open({:spawn, "echo hello"}, [:binary])
      iex> flush()
      {#Port<0.1444>, {:data, "hello\n"}}

  `:spawn` will retrieve the program name from the argument and traverse your
  OS `$PATH` environment variable looking for a matching program.

  Although the above is handy, it means it is impossible to invoke an executable
  that has whitespaces on its name or in any of its arguments. For those reasons,
  most times it is preferable to execute `:spawn_executable`.

  ### spawn_executable

  Spawn executable is a more restricted and explicit version of spawn. It expects
  full file paths to the executable you want to execute. If they are in your `$PATH`,
  they can be retrieved by calling `System.find_executable/1`:

      iex> path = System.find_executable("echo")
      iex> port = Port.open({:spawn_executable, path}, [:binary, args: ["hello world"]])
      iex> flush()
      {#Port<0.1380>, {:data, "hello world\n"}}

  When using `:spawn_executable`, the list of arguments can be passed via
  the `:args` option as done above. For the full list of options, see the
  documentation for the Erlang function `:erlang.open_port/2`.

  ### fd

  The `:fd` name option allows developers to access `in` and `out` file
  descriptors used by the Erlang VM. You would use those only if you are
  reimplementing core part of the Runtime System, such as the `:user` and
  `:shell` processes.

  ## Zombie processes

  A port can be closed via the `close/1` function or by sending a `{pid, :close}`
  message. However, if the VM crashes, a long-running program started by the port
  will have its stdin and stdout channels closed but **it won't be automatically
  terminated**.

  While most UNIX command line tools will exit once its communication channels
  are closed, not all command line applications will do so. While we encourage
  graceful termination by detecting if stdin/stdout has been closed, we do not
  always have control over how 3rd party software terminates. In those cases,
  you can wrap the application in a script that checks for stdin. Here is such
  script in bash:

      #!/bin/sh
      "$@" &
      pid=$!
      while read line ; do
        :
      done
      kill -KILL $pid

  Now instead of:

      Port.open({:spawn_executable, "/path/to/program"},
                [args: ["a", "b", "c"]])

  You may invoke:

      Port.open({:spawn_executable, "/path/to/wrapper"},
                [args: ["/path/to/program", "a", "b", "c"]])

  """

  @type name ::
          {:spawn, charlist | binary}
          | {:spawn_driver, charlist | binary}
          | {:spawn_executable, charlist | atom}
          | {:fd, non_neg_integer, non_neg_integer}

  @doc """
  Opens a port given a tuple `name` and a list of `options`.

  The module documentation above contains documentation and examples
  for the supported `name` values, summarized below:

    * `{:spawn, command}` - runs an external program. `command` must contain
      the program name and optionally a list of arguments separated by space.
      If passing programs or arguments with space in their name, use the next option.
    * `{:spawn_executable, filename}` - runs the executable given by the absolute
      file name `filename`. Arguments can be passed via the `:args` option.
    * `{:spawn_driver, command}` - spawns so-called port drivers.
    * `{:fd, fd_in, fd_out}` - accesses file descriptors, `fd_in` and `fd_out`
      opened by the VM.

  For more information and the list of options, see `:erlang.open_port/2`.

  Inlined by the compiler.
  """
  @spec open(name, list) :: port
  def open(name, settings) do
    :erlang.open_port(name, settings)
  end

  @doc """
  Closes the `port`.

  For more information, see `:erlang.port_close/1`.

  Inlined by the compiler.
  """
  @spec close(port) :: true
  def close(port) do
    :erlang.port_close(port)
  end

  @doc """
  Sends `data` to the port driver `port`.

  For more information, see `:erlang.port_command/2`.

  Inlined by the compiler.
  """
  @spec command(port, iodata, [:force | :nosuspend]) :: boolean
  def command(port, data, options \\ []) do
    :erlang.port_command(port, data, options)
  end

  @doc """
  Associates the `port` identifier with a `pid`.

  For more information, see `:erlang.port_connect/2`.

  Inlined by the compiler.
  """
  @spec connect(port, pid) :: true
  def connect(port, pid) do
    :erlang.port_connect(port, pid)
  end

  @doc """
  Returns information about the `port` or `nil` if the port is closed.

  For more information, see `:erlang.port_info/1`.
  """
  def info(port) do
    nillify(:erlang.port_info(port))
  end

  @doc """
  Returns information about the `port` or `nil` if the port is closed.

  For more information, see `:erlang.port_info/2`.
  """
  @spec info(port, atom) :: {atom, term} | nil
  def info(port, spec)

  def info(port, :registered_name) do
    case :erlang.port_info(port, :registered_name) do
      [] -> {:registered_name, []}
      other -> nillify(other)
    end
  end

  def info(port, item) do
    nillify(:erlang.port_info(port, item))
  end

  @doc """
  Starts monitoring the given `port` from the calling process.

  Once the monitored port process dies, a message is delivered to the
  monitoring process in the shape of:

      {:DOWN, ref, :port, object, reason}

  where:

    * `ref` is a monitor reference returned by this function;
    * `object` is either the `port` being monitored (when monitoring by port id)
    or `{name, node}` (when monitoring by a port name);
    * `reason` is the exit reason.

  See `:erlang.monitor/2` for more info.

  Inlined by the compiler.
  """
  @since "1.6.0"
  @spec monitor(port | {name :: atom, node :: atom} | name :: atom) :: reference
  def monitor(port) do
    :erlang.monitor(:port, port)
  end

  @doc """
  Demonitors the monitor identified by the given `reference`.

  If `monitor_ref` is a reference which the calling process
  obtained by calling `monitor/1`, that monitoring is turned off.
  If the monitoring is already turned off, nothing happens.

  See `:erlang.demonitor/2` for more info.

  Inlined by the compiler.
  """
  @since "1.6.0"
  @spec demonitor(reference, options :: [:flush | :info]) :: boolean
  defdelegate demonitor(monitor_ref, options \\ []), to: :erlang

  @doc """
  Returns a list of all ports in the current node.

  Inlined by the compiler.
  """
  @spec list :: [port]
  def list do
    :erlang.ports()
  end

  @compile {:inline, nillify: 1}
  defp nillify(:undefined), do: nil
  defp nillify(other), do: other
end


Allow configuring owner from mix.exs

Allow configuring owner from mix.exs

defmodule ExrmDeb.Config do
  defstruct name: nil, version: nil, licenses: nil, maintainers: nil,
            external_dependencies: nil, maintainer_scripts: [],
            homepage: nil, description: nil, vendor: nil,
            arch: nil, owner: [user: "root", group: "root"]

  use Vex.Struct
  alias  ReleaseManager.Utils.Logger
  import Logger, only: [debug: 1, error: 1]

  # This version number format should be able to handle regular version
  # numbers as well as alpha/beta versions
  @version_number_format ~r/([0-9]+)\.([0-9]+)\.([0-9]+)\-?([a-z[0-9]*)/

  validates :name, presence: true
  validates :version, presence: true, format: @version_number_format
  validates :arch, presence: true
  validates :vendor, presence: true
  validates :description, presence: true
  validates :homepage, presence: true
  validates [:owner, :user], presence: true
  validates [:owner, :group], presence: true
  validates :licenses, presence: true
  validates :maintainers, presence: true

  def build_config(old_config = %{} \\ %{}) do
    base_config =
      [
        {:name, Atom.to_string(Mix.Project.config[:app])},
        {:version,Mix.Project.config[:version]},
        {:description, Mix.Project.config[:description]},
        {:arch, ExrmDeb.Utils.Config.detect_arch}
      ] ++ config_from_package(Mix.Project.config[:package])
      |> Enum.dedup
      |> Enum.reject(&(is_nil(&1)))
      |> Enum.into(%{})

    ExrmDeb.Config
    |> struct(Map.merge(base_config, old_config))
    |> ExrmDeb.Utils.Config.sanitize_config
    |> check_valid
  end

  defp config_from_package(nil) do
    """
    Error: You haven't defined any 'package' data in mix.exs.
    Check the configuration section of the github repository to
    see how to add this in.
    """
    |> String.replace("\n", " ")
    |> throw
  end
  defp config_from_package(value) when is_list(value) do
    Enum.map(value, fn({key, value}) -> handle_config(key, value) end)
    |> Enum.dedup
    |> Enum.reject(&(is_nil(&1)))
  end

  defp handle_config(:licenses, value) when is_list(value) do
    case Enum.count(value) do
      0 -> nil
      _ -> {:licenses, Enum.join(value, ", ")}
    end
  end
  defp handle_config(:maintainers, value) when is_list(value) do
    case Enum.count(value) do
      0 -> nil
      _ -> {:maintainers, Enum.join(value, ", ")}
    end
  end
  defp handle_config(:external_dependencies, value) when is_list(value) do
    case Enum.count(value) do
      0 -> nil
      _ -> {:external_dependenices, Enum.join(value, ", ")}
    end
  end
  defp handle_config(:maintainer_scripts, value) when is_list(value) do
    case Enum.count(value) do
      0 -> nil
      _ -> {:maintainer_scripts, value}
    end
  end
  defp handle_config(:links, value) when is_map(value) do
    case Map.fetch!(value, "Homepage") do
      nil -> nil
      homepage -> {:homepage, homepage}
    end
  end
  defp handle_config(:vendor, value) when is_binary(value) do
    case String.length(value) do
      0 -> nil
      _ -> {:vendor, value}
    end
  end
  defp handle_config(:owner, value) when is_list(value) do
    case {value[:user], value[:group]} do
      {user, group} when user != nil and group != nil ->
        {:owner, value}
      _ ->
        nil
    end
  end
  defp handle_config(_, _), do: nil

  defp check_valid(config = %ExrmDeb.Config{}) do
    # Use Vex to validate whether the config is valid. If not,
    # then raise an error with a list of config errors
    if Vex.valid?(config) == false do
      error "The configuration is invalid!"
      for err = {:error, _field, _type, _msg} <- Vex.errors(config) do
        print_validation_error(err)
      end
      {:error, Vex.errors(config)}
    else
      {:ok, config}
    end
  end

  defp print_validation_error({:error, field, _type, msg}) when is_atom(field) do
    error(" - '#{Atom.to_string(field)}' #{msg}")
  end
  defp print_validation_error({:error, field, _type, msg}) when is_list(field) do
    field = Enum.map_join(field, " -> ", &("'#{&1}'"))
    error(" - #{field} #{msg}")
  end

end

defmodule ExrmDeb.Config do
  defstruct name: nil, version: nil, licenses: nil, maintainers: nil,
            external_dependencies: nil, maintainer_scripts: [],
            homepage: nil, description: nil, vendor: nil,
            arch: nil, owner: [user: "root", group: "root"]

  use Vex.Struct
  alias  ReleaseManager.Utils.Logger
  import Logger, only: [debug: 1, error: 1]

  # This version number format should be able to handle regular version
  # numbers as well as alpha/beta versions
  @version_number_format ~r/([0-9]+)\.([0-9]+)\.([0-9]+)\-?([a-z[0-9]*)/

  validates :name, presence: true
  validates :version, presence: true, format: @version_number_format
  validates :arch, presence: true
  validates :vendor, presence: true
  validates :description, presence: true
  validates :homepage, presence: true
  validates [:owner, :user], presence: true
  validates [:owner, :group], presence: true
  validates :licenses, presence: true
  validates :maintainers, presence: true

  def build_config(old_config = %{} \\ %{}) do
    base_config =
      [
        {:name, Atom.to_string(Mix.Project.config[:app])},
        {:version,Mix.Project.config[:version]},
        {:description, Mix.Project.config[:description]},
        {:arch, ExrmDeb.Utils.Config.detect_arch}
      ] ++ config_from_package(Mix.Project.config[:package])
      |> Enum.dedup
      |> Enum.reject(&(is_nil(&1)))
      |> Enum.into(%{})

    ExrmDeb.Config
    |> struct(Map.merge(base_config, old_config))
    |> ExrmDeb.Utils.Config.sanitize_config
    |> check_valid
  end

  defp config_from_package(nil) do
    """
    Error: You haven't defined any 'package' data in mix.exs.
    Check the configuration section of the github repository to
    see how to add this in.
    """
    |> String.replace("\n", " ")
    |> throw
  end
  defp config_from_package(value) when is_list(value) do
    Enum.map(value, fn({key, value}) -> handle_config(key, value) end)
    |> Enum.dedup
    |> Enum.reject(&(is_nil(&1)))
  end

  defp handle_config(:licenses, value) when is_list(value) do
    case Enum.count(value) do
      0 -> nil
      _ -> {:licenses, Enum.join(value, ", ")}
    end
  end
  defp handle_config(:maintainers, value) when is_list(value) do
    case Enum.count(value) do
      0 -> nil
      _ -> {:maintainers, Enum.join(value, ", ")}
    end
  end
  defp handle_config(:external_dependencies, value) when is_list(value) do
    case Enum.count(value) do
      0 -> nil
      _ -> {:external_dependenices, Enum.join(value, ", ")}
    end
  end
  defp handle_config(:maintainer_scripts, value) when is_list(value) do
    case Enum.count(value) do
      0 -> nil
      _ -> {:maintainer_scripts, value}
    end
  end
  defp handle_config(:links, value) when is_map(value) do
    case Map.fetch!(value, "Homepage") do
      nil -> nil
      homepage -> {:homepage, homepage}
    end
  end
  defp handle_config(:vendor, value) when is_binary(value) do
    case String.length(value) do
      0 -> nil
      _ -> {:vendor, value}
    end
  end
  defp handle_config(_, _), do: nil

  defp check_valid(config = %ExrmDeb.Config{}) do
    # Use Vex to validate whether the config is valid. If not,
    # then raise an error with a list of config errors
    if Vex.valid?(config) == false do
      error "The configuration is invalid!"
      for err = {:error, _field, _type, _msg} <- Vex.errors(config) do
        print_validation_error(err)
      end
      {:error, Vex.errors(config)}
    else
      {:ok, config}
    end
  end

  defp print_validation_error({:error, field, _type, msg}) when is_atom(field) do
    error(" - '#{Atom.to_string(field)}' #{msg}")
  end
  defp print_validation_error({:error, field, _type, msg}) when is_list(field) do
    field = Enum.map_join(field, " -> ", &("'#{&1}'"))
    error(" - #{field} #{msg}")
  end

end


:hammer: transliterate words to remove tildes and other chars

defmodule Dictionary do
  def random_word do
    word_list()
    |> Enum.random
    |> String.normalize(:nfd)
    |> String.replace(~r/[^A-z\s]/u, "")
  end

  def word_list do
    "../assets/lemario-general-del-espanol.txt"
    |> Path.expand(__DIR__)
    |> File.read!
    |> String.split(~r/\n/)
  end
end

defmodule Dictionary do
  def random_word do
    word_list()
    |> Enum.random
  end

  def word_list do
    "../assets/lemario-general-del-espanol.txt"
    |> Path.expand(__DIR__)
    |> File.read!
    |> String.split(~r/\n/)
  end
end


Fix formatting and proof-read GenEvent docs

defmodule GenEvent do
  @moduledoc """
  A behaviour module for implementing event handling functionality.

  The event handling model consists of a generic event manager
  process with an arbitrary number of event handlers which are
  added and deleted dynamically.

  An event manager implemented using this module will have a standard
  set of interface functions and include functionality for tracing and
  error reporting. It will also fit into an supervision tree.

  ## Example

  There are many use cases for event handlers. For example, a logging
  system can be built using event handlers where which log message is
  an event and different event handlers can be plugged to handle the
  log messages. One handler may print error messages on the terminal,
  another can write it to a file, while a third one can keep the
  messages in memory (like a buffer) until they are read.

  As an example, let's have a GenEvent that accumulates messages until
  they are collected by an explicit call.

      defmodule LoggerHandler do
        use GenEvent

        # Callbacks

        def handle_event({:log, x}, messages) do
          {:ok, [x|messages]}
        end

        def handle_call(:messages, messages) do
          {:ok, Enum.reverse(messages), []}
        end
      end

      {:ok, pid} = GenEvent.start_link()

      GenEvent.add_handler(pid, LoggerHandler, [])
      #=> :ok

      GenEvent.notify(pid, {:log, 1})
      #=> :ok

      GenEvent.notify(pid, {:log, 2})
      #=> :ok

      GenEvent.call(pid, LoggerHandler, :messages)
      #=> [1, 2]

      GenEvent.call(pid, LoggerHandler, :messages)
      #=> []

  We start a new event manager by calling `GenEvent.start_link/0`.
  Notifications can be sent to the event manager which will then
  invoke `handle_event/0` for each registered handler.

  We can add new handlers with `add_handler/4`. Calls can also
  be made to specific handlers by using `call/3`.

  ## Callbacks

  There are 6 callbacks required to be implemented in a `GenEvent`. By
  adding `use GenEvent` to your module, Elixir will automatically define
  all 6 callbacks for you, leaving it up to you to implement the ones
  you want to customize. The callbacks are:

  * `init(args)` - invoked when the event handler is added

    It must return:

    *  `{:ok, state}`
    *  `{:ok, state, :hibernate}`
    *  `{:error, reason}`

  * `handle_event(msg, state)` - invoked whenever an event is sent via
    `notify/2` or `sync_notify/2`.

    It must return:

    *  `{:ok, new_state}`
    *  `{:ok, new_state, :hibernate}`
    *  `{:swap_handler, args1, new_state, handler2, args2}`
    *  `:remove_handler`

  * `handle_call(msg, state)` - invoked when a `call/3` is done to a specific handler.

    It must return:

    *  `{:ok, reply, new_state}`
    *  `{:ok, reply, new_state, :hibernate}`
    *  `{:swap_handler, reply, args1, new_state, handler2, args2}`
    *  `{:remove_handler, reply}`

  * `handle_info(msg, state)` - invoked to handle all other messages which
    are received by the process. Must return the same values as `handle_event/2`;

    It must return:

    *  `{:noreply, state}`
    *  `{:noreply, state, timeout}`
    *  `{:stop, reason, state}`

  * `terminate(reason, state)` - called when the event handler is removed or the
    event manager is terminating. It can return any term.

  * `code_change(old_vsn, state, extra)` - called when the application
    code is being upgraded live (hot code swapping).

    It must return:

    *  `{:ok, new_state}`

  ## Name registration

  A GenEvent is bound to the same name registration rules as a `GenServer`.
  Read more about it in the `GenServer` docs.

  ## Streaming

  `GenEvent`s can be streamed from and streamed with the help of `stream/2`.
  Here are some examples:

      stream = GenEvent.stream(pid)

      # Take the next 10 events
      Enum.take(stream, 10)

      # Print all remaining events
      for event <- stream do
        IO.inspect event
      end

  A stream may also be given an id, which allows all streams with the given
  id to be cancelled at any moment via `cancel_streams/1`.

  ## Learn more

  If you wish to find out more about gen events, Elixir getting started
  guides provide a tutorial-like introduction. The documentation and links
  in Erlang can also provide extra insight.

  * http://elixir-lang.org/getting_started/mix/1.html
  * http://www.erlang.org/doc/man/gen_event.html
  * http://learnyousomeerlang.com/event-handlers
  """

  @typedoc "Return values of `start*` functions"
  @type on_start :: {:ok, pid} | {:error, {:already_started, pid}}

  @typedoc "The GenEvent manager name"
  @type name :: atom | {:global, term} | {:via, module, term}

  @typedoc "Options used by the `start*` functions"
  @type options :: [name: name]

  @typedoc "The event manager reference"
  @type manager :: pid | name | {atom, node}

  @typedoc "Supported values for new handlers"
  @type handler :: module | {module, term}

  @doc """
  Defines a `GenEvent` stream.

  This is a struct returned by `stream/2`. The struct is public and
  contains the following fields:

  * `:manager` - the manager reference given to `GenEvent.stream/2`
  * `:id` - the event stream id for cancellation
  * `:timeout` - the timeout in between events, defaults to `:infinity`
  * `:duration` - the duration of the subscription, defaults to `:infinity`
  """
  defstruct manager: nil, id: nil, timeout: :infinity, duration: :infinity

  @doc false
  defmacro __using__(_) do
    quote location: :keep do
      @behaviour :gen_event

      @doc false
      def init(args) do
        {:ok, args}
      end

      @doc false
      def handle_event(_event, state) do
        {:ok, state}
      end

      @doc false
      def handle_call(_request, state) do
        {:ok, {:error, :bad_call}, state}
      end

      @doc false
      def handle_info(_msg, state) do
        {:ok, state}
      end

      @doc false
      def terminate(reason, state) do
        :ok
      end

      @doc false
      def code_change(_old, state, _extra) do
        {:ok, state}
      end

      defoverridable [init: 1, handle_event: 2, handle_call: 2,
                      handle_info: 2, terminate: 2, code_change: 3]
    end
  end

  @doc """
  Starts an event manager linked to the current process.

  This is often used to start the `GenEvent` as part of a supervision tree.

  It accepts the `:name` option which is described under the `Name Registration`
  section in the `GenServer` module docs.

  If the event manager is successfully created and initialized, the function
  returns `{:ok, pid}`, where pid is the pid of the server. If there already
  exists a process with the specified server name, the function returns
  `{:error, {:already_started, pid}}` with the pid of that process.
  """
  @spec start_link(options) :: on_start
  def start_link(options \\ []) when is_list(options) do
    do_start(:link, options)
  end

  @doc """
  Starts an event manager process without links (outside of a supervision tree).

  See `start_link/1` for more information.
  """
  @spec start(options) :: on_start
  def start(options \\ []) when is_list(options) do
    do_start(:nolink, options)
  end

  defp do_start(mode, options) do
    case Keyword.get(options, :name) do
      nil ->
        :gen.start(:gen_event, mode, :"no callback module", [], [])
      atom when is_atom(atom) ->
        :gen.start(:gen_event, mode, {:local, atom}, :"no callback module", [], [])
      other when is_tuple(other) ->
        :gen.start(:gen_event, mode, other, :"no callback module", [], [])
    end
  end

  @doc """
  Returns a stream that consumes and notifies events to the `manager`.

  The stream is a `GenEvent` struct that implements the `Enumerable`
  protocol. The supported options are:

  * `:id` - an id to identify all live stream instances. When an `:id` is given,
    existing streams can be called with via `cancel_streams`;
  * `:timeout` (Enumerable) - raises if no event arrives in X milliseconds;
  * `:duration` (Enumerable) - only consume events during the X milliseconds
    from the streaming start;
  """
  def stream(manager, options \\ []) do
    %GenEvent{manager: manager,
              id: Keyword.get(options, :id),
              timeout: Keyword.get(options, :timeout, :infinity),
              duration: Keyword.get(options, :duration, :infinity)}
  end

  @doc """
  Adds a new event handler to the event `manager`.

  The event manager will call the `init/1` callback with `args` to
  initiate the event handler and its internal state.

  If `init/1` returns a correct value indicating successful completion,
  the event manager adds the event handler and this function returns
  `:ok`. If the callback fails with `reason` or returns `{:error, reason}`,
  the event handler is ignored and this function returns `{:EXIT, reason}`
  or `{:error, reason}`, respectively.

  ## Linked handlers

  When adding a handler, a `:link` option with value `true` can be given.
  This means the event handler and the calling process are now linked.

  If the calling process later terminates with `reason`, the event manager
  will delete the event handler by calling the `terminate/2` callback with
  `{:stop, reason}` as argument. If the event handler later is deleted,
  the event manager sends a message `{:gen_event_EXIT, handler, reason}`
  to the calling process. Reason is one of the following:

  * `:normal` - if the event handler has been removed due to a call to
    `remove_handler/3`, or `:remove_handler` has been returned by a callback
    function;

  * `:shutdown` - if the event handler has been removed because the event
    manager is terminating;

  * `{:swapped, new_handler, pid}` - if the process pid has replaced the
    event handler by another;

  * a term - if the event handler is removed due to an error. Which term
    depends on the error;

  """
  @spec add_handler(manager, handler, term, [link: boolean]) :: :ok | {:EXIT, term} | {:error, term}
  def add_handler(manager, handler, args, options \\ []) do
    case Keyword.get(options, :link, false) do
      true  -> :gen_event.add_sup_handler(manager, handler, args)
      false -> :gen_event.add_handler(manager, handler, args)
    end
  end

  @doc """
  Sends an event notification to the event `manager`.

  The event manager will call `handle_event/2` for each installed event handler.

  `notify` is asynchronous and will return immediately after the notification is
  sent. `notify` will not fail even if the specified event manager does not exist,
  unless it is specified as `name` (atom).
  """
  @spec notify(manager, term) :: :ok
  defdelegate notify(manager, event), to: :gen_event

  @doc """
  Sends a sync event notification to the event `manager`.

  In other words, this function only returns `:ok` after the event manager
  invokes the `handle_event/2` on each installed event handler.

  See `notify/2` for more info.
  """
  @spec sync_notify(manager, term) :: :ok
  defdelegate sync_notify(manager, event), to: :gen_event

  @doc """
  Makes a synchronous call to the event `handler` installed in `manager`.

  The given `request` is sent and the caller waits until a reply arrives or
  a timeout occurs. The event manager will call `handle_call/2` to handle
  the request.

  The return value `reply` is defined in the return value of `handle_call/2`.
  If the specified event handler is not installed, the function returns
  `{:error, :bad_module}`.
  """
  @spec call(manager, handler, term, timeout) ::  term | {:error, term}
  def call(manager, handler, request, timeout \\ 5000) do
    :gen_event.call(manager, handler, request, timeout)
  end

  @doc """
  Cancels all streams currently running with the given `:id`.

  In order for a stream to be cancelled, an `:id` must be passed
  when the stream is created via `stream/2`. Passing a stream without
  an id leads to an argument error.
  """
  @spec cancel_streams(t) :: :ok
  def cancel_streams(%GenEvent{id: nil}) do
    raise ArgumentError, "cannot cancel streams without an id"
  end

  def cancel_streams(%GenEvent{manager: manager, id: id}) do
    handlers = :gen_event.which_handlers(manager)

    for {Enumerable.GenEvent, {handler_id, _}} = ref <- handlers,
        handler_id === id do
      :gen_event.delete_handler(manager, ref, :remove_handler)
    end

    :ok
  end

  @doc """
  Removes an event handler from the event `manager`.

  The event manager will call `terminate/2` to terminate the event handler
  and return the callback value. If the specified event handler is not
  installed, the function returns `{:error, :module_not_found}`.
  """
  @spec remove_handler(manager, handler, term) :: term | {:error, term}
  def remove_handler(manager, handler, args) do
    :gen_event.delete_handler(manager, handler, args)
  end

  @doc """
  Replaces an old event handler with a new one in the event `manager`.

  First, the old event handler is deleted by calling `terminate/2` with
  the given `args1` and collects the return value. Then the new event handler
  is added and initiated by calling `init({args2, term}), where term is the
  return value of calling `terminate/2` in the old handler. This makes it
  possible to transfer information from one handler to another.

  The new handler will be added even if the specified old event handler
  is not installed in which case `term = :error` or if the handler fails to
  terminate with a given reason.

  If there was a linked connection between handler1 and a process pid, there
  will be a link connection between handler2 and pid instead. A new link in
  between the caller process and the new handler can also be set with by
  giving `link: true` as option. See `add_handler/4` for more information.

  If `init/1` in the second handler returns a correct value, this function
  returns `:ok`.
  """
  @spec swap_handler(manager, handler, term, handler, term, [link: boolean]) :: :ok | {:error, term}
  def swap_handler(manager, handler1, args1, handler2, args2, options \\ []) do
    case Keyword.get(options, :link, false) do
      true  -> :gen_event.swap_sup_handler(manager, {handler1, args1}, {handler2, args2})
      false -> :gen_event.swap_handler(manager, {handler1, args1}, {handler2, args2})
    end
  end

  @doc """
  Returns a list of all event handlers installed in the `manager`.
  """
  @spec which_handlers(manager) :: [handler]
  defdelegate which_handlers(manager), to: :gen_event

  @doc """
  Terminates the event `manager`.

  Before terminating, the event manager will call `terminate(:stop, ...)`
  for each installed event handler.
  """
  @spec stop(manager) :: :ok
  defdelegate stop(manager), to: :gen_event
end

defimpl Enumerable, for: GenEvent do
  use GenEvent

  @doc false
  def init({mon_pid, pid, ref}) do
    # Tell the mon_pid we are good to go, and send self() so that this handler
    # can be removed later without using the managers name.
    send(mon_pid, {:UP, ref, self()})
    {:ok, {pid, ref}}
  end

  @doc false
  def handle_event(event, {pid, ref} = state) do
    send pid, {ref, event}
    {:ok, state}
  end

  def reduce(stream, acc, fun) do
    start_fun = fn() -> start(stream) end
    next_fun = &next(stream, &1)
    stop_fun = &stop(stream, &1)
    Stream.resource(start_fun, next_fun, stop_fun).(acc, fun)
  end

  def count(_stream) do
    {:error, __MODULE__}
  end

  def member?(_stream, _item) do
    {:error, __MODULE__}
  end

  defp start(%{manager: manager, id: id, duration: duration} = stream) do
    {mon_pid, mon_ref} = add_handler(manager, id, duration)
    send mon_pid, {:UP, mon_ref, self()}

    receive do
      # The subscription process gave us a go.
      {:UP, ^mon_ref, manager_pid} ->
        {mon_ref, manager_pid}
        # The subscription process died due to an abnormal reason.
      {:DOWN, ^mon_ref, _, _, reason} ->
        exit({reason, {__MODULE__, :start, [stream]}})
    end
  end

  defp next(%{timeout: timeout} = stream, {mon_ref, _manager_pid} = acc) do
    receive do
      {^mon_ref, event} -> {event, acc}
      {:DOWN, ^mon_ref, _, _, :normal} -> nil
      {:DOWN, ^mon_ref, _, _, reason} ->
        exit({reason, {__MODULE__, :next, [stream, acc]}})
    after
      timeout ->
        exit({:timeout, {__MODULE__, :next, [stream, acc]}})
    end
  end

  defp stop(%{id: id}, {mon_ref, manager_pid}) do
    remove_handler(mon_ref, manager_pid, id)
    flush_events(mon_ref)
  end

  defp add_handler(manager, id, duration) do
    parent = self()

    # The subscription is managed by another process, that dies if
    # the handler dies, and is killed when there is a need to remove
    # the subscription.
    spawn_monitor(fn ->
      # It is possible that the handler could be removed, and then the GenEvent
      # could exit before this process has exited normally. Because the removal
      # does not cause an unlinking this process would exit with the same
      # reason. Trapping exits ensures that no errors is raised in this case.
      Process.flag(:trap_exit, true)
      parent_ref = Process.monitor(parent)

      # Receive the notification from the parent, unless it died.
      mon_ref = receive do
        {:UP, ref, ^parent} -> ref
        {:DOWN, ^parent_ref, _, _, _} -> exit(:normal)
      end

      cancel = cancel_ref(id, mon_ref)
      :ok = :gen_event.add_sup_handler(manager, {__MODULE__, cancel},
                                       {self(), parent, mon_ref})

      receive do
        # This message is already in the mailbox if we got this far.
        {:UP, ^mon_ref, manager_pid} ->
          send(parent, {:UP, mon_ref, manager_pid})
          receive do
            # If the parent died, we can exit normally.
            {:DOWN, ^parent_ref, _, _, _} ->
              exit(:normal)

            # reason should be normal unless the handler is swapped.
            {:gen_event_EXIT, {__MODULE__, ^cancel}, reason} ->
              exit(reason)

            # Exit if the manager dies, so the streamer is notified.
            {:EXIT, ^manager_pid, :noconnection} ->
              exit({:nodedown, node(manager_pid)})

            {:EXIT, ^manager_pid, reason} ->
              exit(reason)
          after
            # Our time is over, notify the parent.
            duration -> exit(:normal)
          end
      end
    end)
  end

  defp cancel_ref(nil, mon_ref), do: mon_ref
  defp cancel_ref(id, mon_ref),  do: {id, mon_ref}

  defp remove_handler(mon_ref, manager_pid, id) do
    Process.demonitor(mon_ref, [:flush])
    handler = {__MODULE__, cancel_ref(id, mon_ref)}
    # handler may nolonger be there, if it is the removal will cause the monitor
    # process to exit. If this returns successfuly then no more events will be
    # forwarded.
    _ = :gen_event.delete_handler(manager_pid, handler, :remove_handler)
  catch
    # Do not want to overide the exit reason of the mon_pid so catch errors.
    # However if the exit is due to a disconnection, exit because messages could
    # leak if the nodes are reconnected before the manager on the other node
    # removes the handler. In this case it is very likely that the mon_pid
    # exited with the same reason.
    :exit, reason when reason !== {:nodedown, node(manager_pid)} ->
      :ok
  end

  defp flush_events(mon_ref) do
    receive do
      {^mon_ref, _} -> flush_events(mon_ref)
    after
      0 -> :ok
    end
  end
end

defmodule GenEvent do
  @moduledoc """
  A behaviour module for implementing event handling functionality.

  The event handling model consists of a generic event manager
  process with an arbitrary number of event handlers which are
  added and deleted dynamically.

  An event manager implemented using this module will have a standard
  set of interface functions and include functionality for tracing and
  error reporting. It will also fit into an supervision tree.

  ## Example

  There are many use cases for event handlers. For example, a logging
  system can be built using event handlers where which log message is
  an event and different event handlers can be plugged to handle the
  log messages. One handler may print error messages on the terminal,
  another can write it to a file, while a third one can keep the
  messages in memory (like a buffer) until they are read.

  As an example, let's have a GenEvent that accumulates messages until
  they are collected by an explicit call.

      defmodule LoggerHandler do
        use GenEvent

        # Callbacks

        def handle_event({:log, x}, messages) do
          {:ok, [x|messages]}
        end

        def handle_call(:messages, messages) do
          {:ok, Enum.reverse(messages), []}
        end
      end

      {:ok, pid} = GenEvent.start_link()

      GenEvent.add_handler(pid, LoggerHandler, [])
      #=> :ok

      GenEvent.notify(pid, {:log, 1})
      #=> :ok

      GenEvent.notify(pid, {:log, 2})
      #=> :ok

      GenEvent.call(pid, LoggerHandler, :messages)
      #=> [1, 2]

      GenEvent.call(pid, LoggerHandler, :messages)
      #=> []

  We start a new event manager by calling `GenEvent.start_link/0`.
  Notifications can be sent to the event manager which will then
  invoke `handle_event/0` for each registered handler.

  We can add new handlers with `add_handler/4`. Calls can also
  be made to specific handlers by using `call/3`.

  ## Callbacks

  There are 6 callbacks required to be implemented in a `GenEvent`. By
  adding `use GenEvent` to your module, Elixir will automatically define
  all 6 callbacks for you, leaving it up to you to implement the ones
  you want to customize. The callbacks are:

  * `init(args)` - invoked when the event handler is added

    It must return:

        {:ok, state}
        {:ok, state, :hibernate}
        {:error, reason}

  * `handle_event(msg, state)` - invoked whenever an event is sent via
    `notify/2` or `sync_notify/2`.

    It must return:

        {:ok, new_state}
        {:ok, new_state, :hibernate}
        {:swap_handler, args1, new_state, handler2, args2}
        :remove_handler

  * `handle_call(msg, state)` - invoked when a `call/3` is done to a specific handler.

    It must return:

        {:ok, reply, new_state}
        {:ok, reply, new_state, :hibernate}
        {:swap_handler, reply, args1, new_state, handler2, args2}
        {:remove_handler, reply}

  * `handle_info(msg, state)` - invoked to handle all other messages which
     are received by the process. Must return the same values as `handle_event/2`;

     It must return:

        {:noreply, state}
        {:noreply, state, timeout}
        {:stop, reason, state}

  * `terminate(reason, state)` - called when the event handler is removed or the
    event manager is terminating. It can return any term.

  * `code_change(old_vsn, state, extra)` - called when the application
    code is being upgraded live (hot code swap).

    It must return:

        {:ok, new_state}

  ## Name registering

  A GenEvent is bound to the same name registering rules as a `GenServer`.
  Read more about it in the `GenServer` docs.

  ## Streaming

  `GenEvent`'s can be streamed from and streamed with the help of `stream/2`.
  Here are some examples:

      stream = GenEvent.stream(pid)

      # Take the next 10 events
      Enum.take(stream, 10)

      # Print all other events
      for event <- stream do
        IO.inspect event
      end

  A stream may also be given an id, which allows all streams with the given
  id to be cancelled at any moment via `cancel_streams/1`.

  ## Learn more

  In case you desire to learn more about gen events, Elixir getting started
  guides provide a tutorial-like introduction. The documentation and links
  in Erlang can also provide extra insight.

  * http://elixir-lang.org/getting_started/mix/1.html
  * http://www.erlang.org/doc/man/gen_event.html
  * http://learnyousomeerlang.com/event-handlers
  """

  @typedoc "Return values of `start*` functions"
  @type on_start :: {:ok, pid} | {:error, {:already_started, pid}}

  @typedoc "The GenEvent manager name"
  @type name :: atom | {:global, term} | {:via, module, term}

  @typedoc "Options used by the `start*` functions"
  @type options :: [name: name]

  @typedoc "The event manager reference"
  @type manager :: pid | name | {atom, node}

  @typedoc "Supported values for new handlers"
  @type handler :: module | {module, term}

  @doc """
  Defines a `GenEvent` stream.

  This is a struct returned by `stream/2`. The struct is public and
  contains the following fields:

  * `:manager` - the manager reference given to `GenEvent.stream/2`
  * `:id` - the event stream id for cancellation
  * `:timeout` - the timeout in between events, defaults to `:infinity`
  * `:duration` - the duration of the subscription, defaults to `:infinity`
  """
  defstruct manager: nil, id: nil, timeout: :infinity, duration: :infinity

  @doc false
  defmacro __using__(_) do
    quote location: :keep do
      @behaviour :gen_event

      @doc false
      def init(args) do
        {:ok, args}
      end

      @doc false
      def handle_event(_event, state) do
        {:ok, state}
      end

      @doc false
      def handle_call(_request, state) do
        {:ok, {:error, :bad_call}, state}
      end

      @doc false
      def handle_info(_msg, state) do
        {:ok, state}
      end

      @doc false
      def terminate(reason, state) do
        :ok
      end

      @doc false
      def code_change(_old, state, _extra) do
        {:ok, state}
      end

      defoverridable [init: 1, handle_event: 2, handle_call: 2,
                      handle_info: 2, terminate: 2, code_change: 3]
    end
  end

  @doc """
  Starts an event manager linked to the current process.

  This is often used to start the `GenServer` as part of a supervision tree.

  It accepts a the `:name` which is described under the `Name Registering`
  section in the `GenServer` module docs.

  If the event manager is successfully created and initialized the function
  returns `{:ok, pid}`, where pid is the pid of the server. If there already
  exists a process with the specified server name the function returns
  `{:error, {:already_started, pid}}` with the pid of that process.
  """
  @spec start_link(options) :: on_start
  def start_link(options \\ []) when is_list(options) do
    do_start(:link, options)
  end

  @doc """
  Starts an event manager process without links (outside of a supervision tree).

  See `spawn_link/1` for more information.
  """
  @spec start(options) :: on_start
  def start(options \\ []) when is_list(options) do
    do_start(:nolink, options)
  end

  defp do_start(mode, options) do
    case Keyword.get(options, :name) do
      nil ->
        :gen.start(:gen_event, mode, :"no callback module", [], [])
      atom when is_atom(atom) ->
        :gen.start(:gen_event, mode, {:local, atom}, :"no callback module", [], [])
      other when is_tuple(other) ->
        :gen.start(:gen_event, mode, other, :"no callback module", [], [])
    end
  end

  @doc """
  Returns a stream that consumes and notifies events to the `manager`.

  The stream is a `GenEvent` struct that implements the `Enumerable`
  protocol. The supported options are:

  * `:id` - an id to identify all live stream instances. When an `:id` is given,
    existing streams can be called with via `cancel_streams`;
  * `:timeout` (Enumerable) - raises if no event arrives in X milliseconds;
  * `:duration` (Enumerable) - only consume events during the X milliseconds
    from the streaming start;
  """
  def stream(manager, options \\ []) do
    %GenEvent{manager: manager,
              id: Keyword.get(options, :id),
              timeout: Keyword.get(options, :timeout, :infinity),
              duration: Keyword.get(options, :duration, :infinity)}
  end

  @doc """
  Adds a new event handler to the event `manager`.

  The event manager will call the `init/1` callback with `args` to
  initiate the event handler and its internal state.

  If `init/1` returns a correct value indicating successful completion,
  the event manager adds the event handler and this function returns
  `:ok`. If the callback fails with `reason` or returns `{:error, reason}`,
  the event handler is ignored and this function returns `{:EXIT, reason}`
  or `{:error, reason}`, respectively.

  ## Linked handlers

  When adding a handler, a `:link` option may be given as true.
  This means the event handler and the calling process are now linked.

  If the calling process later terminates with `reason`, the event manager
  will delete the event handler by calling the `terminate/2` callback with
  `{:stop, reason}` as argument. If the event handler later is deleted,
  the event manager sends a message `{:gen_event_EXIT, handler, reason}`
  to the calling process. Reason is one of the following:

  * `:normal` - if the event handler has been removed due to a call to
    `remove_handler/3`, or `:remove_handler` has been returned by a callback
    function;

  * `:shutdown` - if the event handler has been removed because the event
    manager is terminating;

  * `{:swapped, new_handler, pid}` - if the process pid has replaced the
    event handler by another;

  * a term - if the event handler is removed due to an error. Which term
    depends on the error;

  """
  @spec add_handler(manager, handler, term, [link: boolean]) :: :ok | {:EXIT, term} | {:error, term}
  def add_handler(manager, handler, args, options \\ []) do
    case Keyword.get(options, :link, false) do
      true  -> :gen_event.add_sup_handler(manager, handler, args)
      false -> :gen_event.add_handler(manager, handler, args)
    end
  end

  @doc """
  Sends an event notification to the event `manager`.

  The event manager will call `handle_event/2` for each installed event handler.

  `notify` is asynchronous and will return immediately after the notification is
  sent. `notify` will not fail even if the specified event manager does not exist,
  unless it is specified as `name` (atom).
  """
  @spec notify(manager, term) :: :ok
  defdelegate notify(manager, event), to: :gen_event

  @doc """
  Sends a sync event notification to the event `manager`.

  In other words, this function only returns `:ok` after the event manager
  invokes the `handle_event/2` on each installed event handler.

  See `notify/2` for more info.
  """
  @spec sync_notify(manager, term) :: :ok
  defdelegate sync_notify(manager, event), to: :gen_event

  @doc """
  Makes a synchronous call to the event `handler` installed in `manager`.

  The given `request` is sent and the caller waits until a reply arrives or
  a timeout occurs. The event manager will call `handle_call/2` to handle
  the request.

  The return value `reply` is defined in the return value of `handle_call/2`.
  If the specified event handler is not installed, the function returns
  `{:error, :bad_module}`.
  """
  @spec call(manager, handler, term, timeout) ::  term | {:error, term}
  def call(manager, handler, request, timeout \\ 5000) do
    :gen_event.call(manager, handler, request, timeout)
  end

  @doc """
  Cancels all streams currently running with the given `:id`.

  In order for a stream to be cancelled, an `:id` must be passed
  when the stream is created via `stream/2`. Passing a stream without
  an id leads to an argument error.
  """
  @spec cancel_streams(t) :: :ok
  def cancel_streams(%GenEvent{id: nil}) do
    raise ArgumentError, "cannot cancel streams without an id"
  end

  def cancel_streams(%GenEvent{manager: manager, id: id}) do
    handlers = :gen_event.which_handlers(manager)

    for {Enumerable.GenEvent, {handler_id, _}} = ref <- handlers,
        handler_id === id do
      :gen_event.delete_handler(manager, ref, :remove_handler)
    end

    :ok
  end

  @doc """
  Removes an event handler from the event `manager`.

  The event manager will call `terminate/2` to terminate the event handler
  and return the callback value. If the specified event handler is not
  installed, the function returns `{:error, :module_not_found}`.
  """
  @spec remove_handler(manager, handler, term) :: term | {:error, term}
  def remove_handler(manager, handler, args) do
    :gen_event.delete_handler(manager, handler, args)
  end

  @doc """
  Replaces an old event handler with a new one in the event `manager`.

  First, the old event handler is deleted by calling `terminate/2` with
  the given `args1` and collects the return value. Then the new event handler
  is added and initiated by calling `init({args2, term}), where term is the
  return value of calling `terminate/2` in the old handler. This makes it
  possible to transfer information from one handler to another.

  The new handler will be added even if the specified old event handler
  is not installed in which case `term = :error` or if the handler fails to
  terminate with a given reason.

  If there was a linked connection between handler1 and a process pid, there
  will be a link connection between handle2 and pid instead. A new link in
  between the caller process and the new handler can also be set with by
  giving `link: true` as option. See `add_handler/4` for more information.

  If `init/1` in the second handler returns a correct value, this function
  returns `:ok`.
  """
  @spec swap_handler(manager, handler, term, handler, term, [link: boolean]) :: :ok | {:error, term}
  def swap_handler(manager, handler1, args1, handler2, args2, options \\ []) do
    case Keyword.get(options, :link, false) do
      true  -> :gen_event.swap_sup_handler(manager, {handler1, args1}, {handler2, args2})
      false -> :gen_event.swap_handler(manager, {handler1, args1}, {handler2, args2})
    end
  end

  @doc """
  Returns a list of all event handlers installed in the `manager`.
  """
  @spec which_handlers(manager) :: [handler]
  defdelegate which_handlers(manager), to: :gen_event

  @doc """
  Terminates the event `manager`.

  Before terminating, the event manager will call `terminate(:stop, ...)`
  for each installed event handler.
  """
  @spec stop(manager) :: :ok
  defdelegate stop(manager), to: :gen_event
end

defimpl Enumerable, for: GenEvent do
  use GenEvent

  @doc false
  def init({mon_pid, pid, ref}) do
    # Tell the mon_pid we are good to go, and send self() so that this handler
    # can be removed later without using the managers name.
    send(mon_pid, {:UP, ref, self()})
    {:ok, {pid, ref}}
  end

  @doc false
  def handle_event(event, {pid, ref} = state) do
    send pid, {ref, event}
    {:ok, state}
  end

  def reduce(stream, acc, fun) do
    start_fun = fn() -> start(stream) end
    next_fun = &next(stream, &1)
    stop_fun = &stop(stream, &1)
    Stream.resource(start_fun, next_fun, stop_fun).(acc, fun)
  end

  def count(_stream) do
    {:error, __MODULE__}
  end

  def member?(_stream, _item) do
    {:error, __MODULE__}
  end

  defp start(%{manager: manager, id: id, duration: duration} = stream) do
    {mon_pid, mon_ref} = add_handler(manager, id, duration)
    send mon_pid, {:UP, mon_ref, self()}

    receive do
      # The subscription process gave us a go.
      {:UP, ^mon_ref, manager_pid} ->
        {mon_ref, manager_pid}
        # The subscription process died due to an abnormal reason.
      {:DOWN, ^mon_ref, _, _, reason} ->
        exit({reason, {__MODULE__, :start, [stream]}})
    end
  end

  defp next(%{timeout: timeout} = stream, {mon_ref, _manager_pid} = acc) do
    receive do
      {^mon_ref, event} -> {event, acc}
      {:DOWN, ^mon_ref, _, _, :normal} -> nil
      {:DOWN, ^mon_ref, _, _, reason} ->
        exit({reason, {__MODULE__, :next, [stream, acc]}})
    after
      timeout ->
        exit({:timeout, {__MODULE__, :next, [stream, acc]}})
    end
  end

  defp stop(%{id: id}, {mon_ref, manager_pid}) do
    remove_handler(mon_ref, manager_pid, id)
    flush_events(mon_ref)
  end

  defp add_handler(manager, id, duration) do
    parent = self()

    # The subscription is managed by another process, that dies if
    # the handler dies, and is killed when there is a need to remove
    # the subscription.
    spawn_monitor(fn ->
      # It is possible that the handler could be removed, and then the GenEvent
      # could exit before this process has exited normally. Because the removal
      # does not cause an unlinking this process would exit with the same
      # reason. Trapping exits ensures that no errors is raised in this case.
      Process.flag(:trap_exit, true)
      parent_ref = Process.monitor(parent)

      # Receive the notification from the parent, unless it died.
      mon_ref = receive do
        {:UP, ref, ^parent} -> ref
        {:DOWN, ^parent_ref, _, _, _} -> exit(:normal)
      end

      cancel = cancel_ref(id, mon_ref)
      :ok = :gen_event.add_sup_handler(manager, {__MODULE__, cancel},
                                       {self(), parent, mon_ref})

      receive do
        # This message is already in the mailbox if we got this far.
        {:UP, ^mon_ref, manager_pid} ->
          send(parent, {:UP, mon_ref, manager_pid})
          receive do
            # If the parent died, we can exit normally.
            {:DOWN, ^parent_ref, _, _, _} ->
              exit(:normal)

            # reason should be normal unless the handler is swapped.
            {:gen_event_EXIT, {__MODULE__, ^cancel}, reason} ->
              exit(reason)

            # Exit if the manager dies, so the streamer is notified.
            {:EXIT, ^manager_pid, :noconnection} ->
              exit({:nodedown, node(manager_pid)})

            {:EXIT, ^manager_pid, reason} ->
              exit(reason)
          after
            # Our time is over, notify the parent.
            duration -> exit(:normal)
          end
      end
    end)
  end

  defp cancel_ref(nil, mon_ref), do: mon_ref
  defp cancel_ref(id, mon_ref),  do: {id, mon_ref}

  defp remove_handler(mon_ref, manager_pid, id) do
    Process.demonitor(mon_ref, [:flush])
    handler = {__MODULE__, cancel_ref(id, mon_ref)}
    # handler may nolonger be there, if it is the removal will cause the monitor
    # process to exit. If this returns successfuly then no more events will be
    # forwarded.
    _ = :gen_event.delete_handler(manager_pid, handler, :remove_handler)
  catch
    # Do not want to overide the exit reason of the mon_pid so catch errors.
    # However if the exit is due to a disconnection, exit because messages could
    # leak if the nodes are reconnected before the manager on the other node
    # removes the handler. In this case it is very likely that the mon_pid
    # exited with the same reason.
    :exit, reason when reason !== {:nodedown, node(manager_pid)} ->
      :ok
  end

  defp flush_events(mon_ref) do
    receive do
      {^mon_ref, _} -> flush_events(mon_ref)
    after
      0 -> :ok
    end
  end
end


fix downloader

fix downloader

defmodule Downloader do
  @moduledoc """
    Blatently ripped off
    from: https://github.com/nerves-project/nerves/blob/master/lib/nerves/utils/http_client.ex
  """
  use GenServer

  @progress_steps 50
  @redirect_status_codes [301, 302, 303, 307, 308]

  def start_link do
    start_httpc()
    GenServer.start_link(__MODULE__, [], name: __MODULE__)
  end

  def stop(client \\ __MODULE__) do
    GenServer.stop(client)
  end

  def get(client \\ __MODULE__, url) do
    GenServer.call(client, {:get, url}, :infinity)
  end

  def run(url, path) do
    {:ok, file} = get(__MODULE__, url)
    :ok = File.write!(path, file)
    if File.exists?(path) do
      path
    else
      throw :NO_FILE
    end
  end

  def init([]) do
    {:ok, %{
      url: nil,
      content_length: 0,
      buffer: "",
      buffer_size: 0,
      filename: "",
      caller: nil,
      number_of_redirects: 0,
    }}
  end

  def handle_call({:get, _url}, _, %{number_of_redirects: n}=s) when n > 5 do
    GenServer.reply(s.caller, {:error, :too_many_redirects})
    {:noreply, %{s | url: nil, number_of_redirects: 0, caller: nil}}
  end

  def handle_call({:get, url}, from, s) do
    headers = [
      {'Content-Type', 'application/octet-stream'}
    ]

    http_opts = [timeout: :infinity, autoredirect: false]
    opts = [stream: :self, receiver: self(), sync: false]
    :httpc.request(:get,
      {String.to_char_list(url), headers}, http_opts, opts, :nerves)
    {:noreply, %{s | url: url, caller: from}}
  end

  @lint false
  def handle_info({:http, {_, :stream_start, headers}}, s) do
    {_, content_length} =
      headers
      |> Enum.find(fn({key, _}) -> key == 'content-length' end)

    {content_length, _} =
      content_length
      |> to_string()
      |> Integer.parse()

    {_, filename} =
      headers
      |> Enum.find(fn({key, _}) -> key == 'content-disposition' end)
    filename =
      filename
      |> to_string
      |> String.split(";")
      |> List.last
      |> String.strip
      |> String.trim("filename=")
    {:noreply, %{s | content_length: content_length, filename: filename}}
  end

  def handle_info({:http, {_, :stream, data}}, s) do
    size = byte_size(data) + s.buffer_size
    buffer = s.buffer <> data
    put_progress(size, s.content_length)
    {:noreply, %{s | buffer_size: size, buffer: buffer}}
  end

  def handle_info({:http, {_, :stream_end, _headers}}, s) do
    IO.write(:stderr, "\n")
    GenServer.reply(s.caller, {:ok, s.buffer})
    {:noreply, %{s | filename: "", content_length: 0, buffer: "", buffer_size: 0, url: nil}}
  end

  def handle_info({:http, {_ref, {{_, status_code, _}, headers, _body}}}, s) when status_code in @redirect_status_codes do
    case Enum.find(headers, fn({key,_}) -> key == 'location' end) do
      {'location', next_url} ->
        handle_call({:get, List.to_string(next_url)}, s.caller, %{s | buffer: "", buffer_size: 0, number_of_redirects: s.number_of_redirects + 1})
      _ ->
        GenServer.reply(s.caller, {:error, status_code})
    end
  end

  def handle_info({:http, {error, _headers}}, s) do
    GenServer.reply(s.caller, {:error, error})
    {:noreply, s}
  end

  def terminate(reason, state) do
    GenServer.reply(state.caller, {:error, reason})
  end

  def put_progress(size, max) do
    fraction = (size / max)
    completed = trunc(fraction * @progress_steps)
    percent = trunc(fraction * 100)
    unfilled = @progress_steps - completed
    IO.write(:stderr, "\r|#{String.duplicate("=", completed)}#{String.duplicate(" ", unfilled)}| #{percent}% (#{bytes_to_mb(size)} / #{bytes_to_mb(max)}) MB")
  end

  defp start_httpc do
    :inets.start(:httpc, profile: :nerves)
    opts = [
      max_sessions: 8,
      max_keep_alive_length: 4,
      max_pipeline_length: 4,
      keep_alive_timeout: 120_000,
      pipeline_timeout: 60_000
    ]
    :httpc.set_options(opts, :nerves)
  end

  defp bytes_to_mb(bytes) do
    trunc(bytes / 1024 / 1024)
  end
end

defmodule Downloader do
  @moduledoc """
    Blatently ripped off
    from: https://github.com/nerves-project/nerves/blob/master/lib/nerves/utils/http_client.ex
  """
  use GenServer

  @progress_steps 50
  @redirect_status_codes [301, 302, 303, 307, 308]

  def start_link do
    start_httpc()
    GenServer.start_link(__MODULE__, [], name: __MODULE__)
  end

  def stop(client \\ __MODULE__) do
    GenServer.stop(client)
  end

  def get(client \\ __MODULE__, url) do
    GenServer.call(client, {:get, url}, :infinity)
  end

  def run(url, path) do
    {:ok, file} = get(__MODULE__, url)
    :ok = File.write!(path, file)
    if File.exists?(path) do
      path
    else
      throw :NO_FILE
    end
  end

  def init([]) do
    {:ok, %{
      url: nil,
      content_length: 0,
      buffer: "",
      buffer_size: 0,
      filename: "",
      caller: nil,
      number_of_redirects: 0,
    }}
  end

  def handle_call({:get, _url}, _, %{number_of_redirects: n}=s) when n > 5 do
    GenServer.reply(s.caller, {:error, :too_many_redirects})
    {:noreply, %{s | url: nil, number_of_redirects: 0, caller: nil}}
  end

  def handle_call({:get, url}, from, s) do
    headers = [
      {'Content-Type', 'application/octet-stream'}
    ]

    http_opts = [timeout: :infinity, autoredirect: false]
    opts = [stream: :self, receiver: self(), sync: false]
    :httpc.request(:get,
      {String.to_char_list(url), headers}, http_opts, opts, :nerves)
    {:noreply, %{s | url: url, caller: from}}
  end

  @lint false
  def handle_info({:http, {_, :stream_start, headers}}, s) do
    {_, content_length} =
      headers
      |> Enum.find(fn({key, _}) -> key == 'content-length' end)

    {content_length, _} =
      content_length
      |> to_string()
      |> Integer.parse()

    {_, filename} =
      headers
      |> Enum.find(fn({key, _}) -> key == 'content-disposition' end)
    filename =
      filename
      |> to_string
      |> String.split(";")
      |> List.last
      |> String.strip
      |> String.trim("filename=")
    {:noreply, %{s | content_length: content_length, filename: filename}}
  end

  def handle_info({:http, {_, :stream, data}}, s) do
    size = byte_size(data) + s.buffer_size
    buffer = s.buffer <> data
    put_progress(size, s.content_length)
    {:noreply, %{s | buffer_size: size, buffer: buffer}}
  end

  def handle_info({:http, {_, :stream_end, _headers}}, s) do
    IO.write(:stderr, "\n")
    GenServer.reply(s.caller, {:ok, s.buffer})
    {:noreply, %{s | filename: "", content_length: 0, buffer: "", buffer_size: 0, url: nil}}
  end

  def handle_info({:http, {_ref, {{_, status_code, _}, headers, _body}}}, s) when status_code in @redirect_status_codes do
    case Enum.find(headers, fn({key,_}) -> key == 'location' end) do
      {'location', next_url} ->
        handle_call({:get, List.to_string(next_url)}, s.caller, %{s | buffer: "", buffer_size: 0, number_of_redirects: s.number_of_redirects + 1})
      _ ->
        GenServer.reply(s.caller, {:error, status_code})
    end
  end
  def handle_info({:http, {error, _headers}}, s) do
    GenServer.reply(s.caller, {:error, error})
    {:noreply, s}
  end

  def put_progress(size, max) do
    fraction = (size / max)
    completed = trunc(fraction * @progress_steps)
    percent = trunc(fraction * 100)
    unfilled = @progress_steps - completed
    IO.write(:stderr, "\r|#{String.duplicate("=", completed)}#{String.duplicate(" ", unfilled)}| #{percent}% (#{bytes_to_mb(size)} / #{bytes_to_mb(max)}) MB")
  end

  defp start_httpc do
    :inets.start(:httpc, profile: :nerves)
    opts = [
      max_sessions: 8,
      max_keep_alive_length: 4,
      max_pipeline_length: 4,
      keep_alive_timeout: 120_000,
      pipeline_timeout: 60_000
    ]
    :httpc.set_options(opts, :nerves)
  end

  defp bytes_to_mb(bytes) do
    trunc(bytes / 1024 / 1024)
  end
end