Make Process docs examples more formatter-compatible [skip-ci] (#7943)

defmodule Process do
  @moduledoc """
  Conveniences for working with processes and the process dictionary.

  Besides the functions available in this module, the `Kernel` module
  exposes and auto-imports some basic functionality related to processes
  available through the following functions:

    * `Kernel.spawn/1` and `Kernel.spawn/3`
    * `Kernel.spawn_link/1` and `Kernel.spawn_link/3`
    * `Kernel.spawn_monitor/1` and `Kernel.spawn_monitor/3`
    * `Kernel.self/0`
    * `Kernel.send/2`

  While this module provides low-level conveniences to work with processes,
  developers typically use abstractions such as `Agent`, `GenServer`,
  `Registry`, `Supervisor` and `Task`  for building their systems and
  resort to this module for gathering information, trapping exits, links
  and monitoring.
  """

  @doc """
  Tells whether the given process is alive on the local node.

  If the process identified by `pid` is alive (that is, it's not exiting and has
  not exited yet) than this function returns `true`. Otherwise, it returns
  `false`.

  `pid` must refer to a process running on the local node or `ArgumentError` is raised.

  Inlined by the compiler.
  """

  @typedoc """
  A process destination.

  A remote or local PID, a local port, a locally registered name, or a tuple in
  the form of `{registered_name, node}` for a registered name at another node.
  """
  @type dest :: pid | port | registered_name :: atom | {registered_name :: atom, node}

  @spec alive?(pid) :: boolean
  defdelegate alive?(pid), to: :erlang, as: :is_process_alive

  @doc """
  Returns all key-value pairs in the process dictionary.

  Inlined by the compiler.
  """
  @spec get() :: [{term, term}]
  defdelegate get(), to: :erlang

  @doc """
  Returns the value for the given `key` in the process dictionary,
  or `default` if `key` is not set.

  ## Examples

      # Assuming :locale was not set
      iex> Process.get(:locale, "pt")
      "pt"
      iex> Process.put(:locale, "fr")
      nil
      iex> Process.get(:locale, "pt")
      "fr"

  """
  @spec get(term, default :: term) :: term
  def get(key, default \\ nil) do
    case :erlang.get(key) do
      :undefined -> default
      value -> value
    end
  end

  @doc """
  Returns all keys in the process dictionary.

  Inlined by the compiler.

  ## Examples

      # Assuming :locale was not set
      iex> :locale in Process.get_keys()
      false
      iex> Process.put(:locale, "pt")
      nil
      iex> :locale in Process.get_keys()
      true

  """
  @spec get_keys() :: [term]
  defdelegate get_keys(), to: :erlang

  @doc """
  Returns all keys in the process dictionary that have the given `value`.

  Inlined by the compiler.
  """
  @spec get_keys(term) :: [term]
  defdelegate get_keys(value), to: :erlang

  @doc """
  Stores the given `key`-`value` pair in the process dictionary.

  The return value of this function is the value that was previously stored
  under `key`, or `nil` in case no value was stored under `key`.

  ## Examples

      # Assuming :locale was not set
      iex> Process.put(:locale, "en")
      nil
      iex> Process.put(:locale, "fr")
      "en"

  """
  @spec put(term, term) :: term | nil
  def put(key, value) do
    nillify(:erlang.put(key, value))
  end

  @doc """
  Deletes the given `key` from the process dictionary.

  Returns the value that was under `key` in the process dictionary,
  or `nil` if `key` was not stored in the process dictionary.

  ## Examples

      iex> Process.put(:comments, ["comment", "other comment"])
      iex> Process.delete(:comments)
      ["comment", "other comment"]
      iex> Process.delete(:comments)
      nil

  """
  @spec delete(term) :: term | nil
  def delete(key) do
    nillify(:erlang.erase(key))
  end

  @doc """
  Sends an exit signal with the given `reason` to `pid`.

  The following behaviour applies if `reason` is any term except `:normal`
  or `:kill`:

    1. If `pid` is not trapping exits, `pid` will exit with the given
       `reason`.

    2. If `pid` is trapping exits, the exit signal is transformed into a
       message `{:EXIT, from, reason}` and delivered to the message queue
       of `pid`.

  If `reason` is the atom `:normal`, `pid` will not exit (unless `pid` is
  the calling process, in which case it will exit with the reason `:normal`).
  If it is trapping exits, the exit signal is transformed into a message
  `{:EXIT, from, :normal}` and delivered to its message queue.

  If `reason` is the atom `:kill`, that is if `Process.exit(pid, :kill)` is called,
  an untrappable exit signal is sent to `pid` which will unconditionally exit
  with reason `:killed`.

  Inlined by the compiler.

  ## Examples

      Process.exit(pid, :kill)
      #=> true

  """
  @spec exit(pid, term) :: true
  defdelegate exit(pid, reason), to: :erlang

  @doc """
  Sleeps the current process for the given `timeout`.

  `timeout` is either the number of milliseconds to sleep as an
  integer or the atom `:infinity`. When `:infinity` is given,
  the current process will sleep forever, and not
  consume or reply to messages.

  **Use this function with extreme care**. For almost all situations
  where you would use `sleep/1` in Elixir, there is likely a
  more correct, faster and precise way of achieving the same with
  message passing.

  For example, if you are waiting for a process to perform some
  action, it is better to communicate the progress of such action
  with messages.

  In other words, **do not**:

      Task.start_link(fn ->
        do_something()
        ...
      end)

      # Wait until work is done
      Process.sleep(2000)

  But **do**:

      parent = self()

      Task.start_link(fn ->
        do_something()
        send(parent, :work_is_done)
        ...
      end)

      receive do
        :work_is_done -> :ok
      after
        # Optional timeout
        30_000 -> :timeout
      end

  For cases like the one above, `Task.async/1` and `Task.await/2` are
  preferred.

  Similarly, if you are waiting for a process to terminate,
  monitor that process instead of sleeping. **Do not**:

      Task.start_link(fn ->
        ...
      end)

      # Wait until task terminates
      Process.sleep(2000)

  Instead **do**:

      {:ok, pid} =
        Task.start_link(fn ->
          ...
        end)

      ref = Process.monitor(pid)

      receive do
        {:DOWN, ^ref, _, _, _} -> :task_is_down
      after
        # Optional timeout
        30_000 -> :timeout
      end

  """
  @spec sleep(timeout) :: :ok
  def sleep(timeout)
      when is_integer(timeout) and timeout >= 0
      when timeout == :infinity do
    receive after: (timeout -> :ok)
  end

  @doc """
  Sends a message to the given `dest`.

  `dest` may be a remote or local PID, a local port, a locally
  registered name, or a tuple in the form of `{registered_name, node}` for a
  registered name at another node.

  Inlined by the compiler.

  ## Options

    * `:noconnect` - when used, if sending the message would require an
      auto-connection to another node the message is not sent and `:noconnect` is
      returned.

    * `:nosuspend` - when used, if sending the message would cause the sender to
      be suspended the message is not sent and `:nosuspend` is returned.

  Otherwise the message is sent and `:ok` is returned.

  ## Examples

      iex> Process.send({:name, :node_that_does_not_exist}, :hi, [:noconnect])
      :noconnect

  """
  @spec send(dest, msg, [option]) :: :ok | :noconnect | :nosuspend
        when dest: dest(),
             msg: any,
             option: :noconnect | :nosuspend
  defdelegate send(dest, msg, options), to: :erlang

  @doc """
  Sends `msg` to `dest` after `time` milliseconds.

  If `dest` is a PID, it must be the PID of a local process, dead or alive.
  If `dest` is an atom, it must be the name of a registered process
  which is looked up at the time of delivery. No error is produced if the name does
  not refer to a process.

  This function returns a timer reference, which can be read with `read_timer/1`
  or canceled with `cancel_timer/1`.

  The timer will be automatically canceled if the given `dest` is a PID
  which is not alive or when the given PID exits. Note that timers will not be
  automatically canceled when `dest` is an atom (as the atom resolution is done
  on delivery).

  Inlined by the compiler.

  ## Options

    * `:abs` - (boolean) when `false`, `time` is treated as relative to the
    current monotonic time. When `true`, `time` is the absolute value of the
    Erlang monotonic time at which `msg` should be delivered to `dest`.
    To read more about Erlang monotonic time and other time-related concepts,
    look at the documentation for the `System` module. Defaults to `false`.

  ## Examples

      timer_ref = Process.send_after(pid, :hi, 1000)

  """
  @spec send_after(pid | atom, term, non_neg_integer, [option]) :: reference
        when option: {:abs, boolean}
  def send_after(dest, msg, time, opts \\ []) do
    :erlang.send_after(time, dest, msg, opts)
  end

  @doc """
  Cancels a timer returned by `send_after/3`.

  When the result is an integer, it represents the time in milliseconds
  left until the timer would have expired.

  When the result is `false`, a timer corresponding to `timer_ref` could not be
  found. This can happen either because the timer expired, because it has
  already been canceled, or because `timer_ref` never corresponded to a timer.

  Even if the timer had expired and the message was sent, this function does not
  tell you if the timeout message has arrived at its destination yet.

  Inlined by the compiler.

  ## Options

    * `:async` - (boolean) when `false`, the request for cancellation is
      synchronous. When `true`, the request for cancellation is asynchronous,
      meaning that the request to cancel the timer is issued and `:ok` is
      returned right away. Defaults to `false`.

    * `:info` - (boolean) whether to return information about the timer being
      cancelled. When the `:async` option is `false` and `:info` is `true`, then
      either an integer or `false` (like described above) is returned. If
      `:async` is `false` and `:info` is `false`, `:ok` is returned. If `:async`
      is `true` and `:info` is `true`, a message in the form `{:cancel_timer,
      timer_ref, result}` (where `result` is an integer or `false` like
      described above) is sent to the caller of this function when the
      cancellation has been performed. If `:async` is `true` and `:info` is
      `false`, no message is sent. Defaults to `true`.

  """
  @spec cancel_timer(reference, options) :: non_neg_integer | false | :ok
        when options: [async: boolean, info: boolean]
  defdelegate cancel_timer(timer_ref, options \\ []), to: :erlang

  @doc """
  Reads a timer created by `send_after/3`.

  When the result is an integer, it represents the time in milliseconds
  left until the timer will expire.

  When the result is `false`, a timer corresponding to `timer_ref` could not be
  found. This can be either because the timer expired, because it has already
  been canceled, or because `timer_ref` never corresponded to a timer.

  Even if the timer had expired and the message was sent, this function does not
  tell you if the timeout message has arrived at its destination yet.

  Inlined by the compiler.
  """
  @spec read_timer(reference) :: non_neg_integer | false
  defdelegate read_timer(timer_ref), to: :erlang

  @type spawn_opt ::
          :link
          | :monitor
          | {:priority, :low | :normal | :high}
          | {:fullsweep_after, non_neg_integer}
          | {:min_heap_size, non_neg_integer}
          | {:min_bin_vheap_size, non_neg_integer}
  @type spawn_opts :: [spawn_opt]

  @doc """
  Spawns the given function according to the given options.

  The result depends on the given options. In particular,
  if `:monitor` is given as an option, it will return a tuple
  containing the PID and the monitoring reference, otherwise
  just the spawned process PID.

  More options are available; for the comprehensive list of available options
  check `:erlang.spawn_opt/4`.

  Inlined by the compiler.

  ## Examples

      Process.spawn(fn -> 1 + 2 end, [:monitor])
      #=> {#PID<0.93.0>, #Reference<0.18808174.1939079169.202418>}
      Process.spawn(fn -> 1 + 2 end, [:link])
      #=> #PID<0.95.0>

  """
  @spec spawn((() -> any), spawn_opts) :: pid | {pid, reference}
  defdelegate spawn(fun, opts), to: :erlang, as: :spawn_opt

  @doc """
  Spawns the given function `fun` from module `mod`, passing the given `args`
  according to the given options.

  The result depends on the given options. In particular,
  if `:monitor` is given as an option, it will return a tuple
  containing the PID and the monitoring reference, otherwise
  just the spawned process PID.

  It also accepts extra options, for the list of available options
  check `:erlang.spawn_opt/4`.

  Inlined by the compiler.
  """
  @spec spawn(module, atom, list, spawn_opts) :: pid | {pid, reference}
  defdelegate spawn(mod, fun, args, opts), to: :erlang, as: :spawn_opt

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

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

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

  where:

    * `ref` is a monitor reference returned by this function;
    * `object` is either a `pid` of the monitored process (if monitoring
      a PID) or `{name, node}` (if monitoring a remote or local name);
    * `reason` is the exit reason.

  If the process is already dead when calling `Process.monitor/1`, a
  `:DOWN` message is delivered immediately.

  See [the need for monitoring](http://elixir-lang.org/getting-started/mix-otp/genserver.html#the-need-for-monitoring)
  for an example. See `:erlang.monitor/2` for more info.

  Inlined by the compiler.

  ## Examples

      pid = spawn(fn -> 1 + 2 end)
      #=> #PID<0.118.0>
      Process.monitor(pid)
      #=> #Reference<0.906660723.3006791681.40191>
      Process.exit(pid, :kill)
      #=> true
      receive do
        msg -> msg
      end
      #=> {:DOWN, #Reference<0.906660723.3006791681.40191>, :process, #PID<0.118.0>, :noproc}

  """
  @spec monitor(pid | {name :: atom, node :: atom} | name :: atom) :: reference
  def monitor(item) do
    :erlang.monitor(:process, item)
  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.

  ## Examples

      pid = spawn(fn -> 1 + 2 end)
      ref = Process.monitor(pid)
      Process.demonitor(ref)
      #=> true

  """
  @spec demonitor(reference, options :: [:flush | :info]) :: boolean
  defdelegate demonitor(monitor_ref, options \\ []), to: :erlang

  @doc """
  Returns a list of PIDs corresponding to all the
  processes currently existing on the local node.

  Note that if a process is exiting, it is considered to exist but not be
  alive. This means that for such process, `alive?/1` will return `false` but
  its PID will be part of the list of PIDs returned by this function.

  See `:erlang.processes/0` for more info.

  Inlined by the compiler.

  ## Examples

      Process.list()
      #=> [#PID<0.0.0>, #PID<0.1.0>, #PID<0.2.0>, #PID<0.3.0>, ...]

  """
  @spec list() :: [pid]
  defdelegate list(), to: :erlang, as: :processes

  @doc """
  Creates a link between the calling process and the given item (process or
  port).

  Links are bidirectional. Linked processes can be unlinked by using `unlink/1`.

  If such a link exists already, this function does nothing since there can only
  be one link between two given processes. If a process tries to create a link
  to itself, nothing will happen.

  When two processes are linked, each one receives exit signals from the other
  (see also `exit/2`). Let's assume `pid1` and `pid2` are linked. If `pid2`
  exits with a reason other than `:normal` (which is also the exit reason used
  when a process finishes its job) and `pid1` is not trapping exits (see
  `flag/2`), then `pid1` will exit with the same reason as `pid2` and in turn
  emit an exit signal to all its other linked processes. The behaviour when
  `pid1` is trapping exits is described in `exit/2`.

  See `:erlang.link/1` for more info.

  Inlined by the compiler.
  """
  @spec link(pid | port) :: true
  defdelegate link(pid_or_port), to: :erlang

  @doc """
  Removes the link between the calling process and the given item (process or
  port).

  If there is no such link, this function does nothing. If `pid_or_port` does
  not exist, this function does not produce any errors and simply does nothing.

  The return value of this function is always `true`.

  See `:erlang.unlink/1` for more info.

  Inlined by the compiler.
  """
  @spec unlink(pid | port) :: true
  defdelegate unlink(pid_or_port), to: :erlang

  @doc """
  Registers the given `pid_or_port` under the given `name`.

  `name` must be an atom and can then be used instead of the
  PID/port identifier when sending messages with `Kernel.send/2`.

  `register/2` will fail with `ArgumentError` in any of the following cases:

    * the PID/Port is not existing locally and alive
    * the name is already registered
    * the `pid_or_port` is already registered under a different `name`

  The following names are reserved and cannot be assigned to
  processes nor ports:

    * `nil`
    * `false`
    * `true`
    * `:undefined`

  ## Examples

      Process.register(self(), :test)
      #=> true
      send(:test, :hello)
      #=> :hello
      send(:wrong_name, :hello)
      #=> ** (ArgumentError) argument error

  """
  @spec register(pid | port, atom) :: true
  def register(pid_or_port, name)
      when is_atom(name) and name not in [nil, false, true, :undefined] do
    :erlang.register(name, pid_or_port)
  catch
    :error, :badarg when node(pid_or_port) != node() ->
      message = "could not register #{inspect(pid_or_port)} because it belongs to another node"
      :erlang.error(ArgumentError.exception(message), [pid_or_port, name])

    :error, :badarg ->
      message =
        "could not register #{inspect(pid_or_port)} with " <>
          "name #{inspect(name)} because it is not alive, the name is already " <>
          "taken, or it has already been given another name"

      :erlang.error(ArgumentError.exception(message), [pid_or_port, name])
  end

  @doc """
  Removes the registered `name`, associated with a PID
  or a port identifier.

  Fails with `ArgumentError` if the name is not registered
  to any PID or port.

  Inlined by the compiler.

  ## Examples

      Process.register(self(), :test)
      #=> true
      Process.unregister(:test)
      #=> true
      Process.unregister(:wrong_name)
      #=> ** (ArgumentError) argument error

  """
  @spec unregister(atom) :: true
  defdelegate unregister(name), to: :erlang

  @doc """
  Returns the PID or port identifier registered under `name` or `nil` if the
  name is not registered.

  See `:erlang.whereis/1` for more info.

  ## Examples

      Process.register(self(), :test)
      Process.whereis(:test)
      #=> #PID<0.84.0>
      Process.whereis(:wrong_name)
      #=> nil

  """
  @spec whereis(atom) :: pid | port | nil
  def whereis(name) do
    nillify(:erlang.whereis(name))
  end

  @doc """
  Returns the PID of the group leader for the calling process.

  Inlined by the compiler.

  ## Examples

      Process.group_leader()
      #=> #PID<0.53.0>

  """
  @spec group_leader() :: pid
  defdelegate group_leader(), to: :erlang

  @doc """
  Sets the group leader of the given `pid` to `leader`.

  Typically, this is used when a process started from a certain shell should
  have a group leader other than `:init`.

  Inlined by the compiler.
  """
  @spec group_leader(pid, leader :: pid) :: true
  def group_leader(pid, leader) do
    :erlang.group_leader(leader, pid)
  end

  @doc """
  Returns a list of names which have been registered using `register/2`.

  Inlined by the compiler.

  ## Examples

      Process.register(self(), :test)
      Process.registered()
      #=> [:test, :elixir_config, :inet_db, ...]

  """
  @spec registered() :: [atom]
  defdelegate registered(), to: :erlang

  @typep heap_size ::
           non_neg_integer
           | %{size: non_neg_integer, kill: boolean, error_logger: boolean}

  @typep priority_level :: :low | :normal | :high | :max

  @doc """
  Sets the given `flag` to `value` for the calling process.

  Returns the old value of `flag`.

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

  Inlined by the compiler.
  """
  @spec flag(:error_handler, module) :: module
  @spec flag(:max_heap_size, heap_size) :: heap_size
  @spec flag(:message_queue_data, :erlang.message_queue_data()) :: :erlang.message_queue_data()
  @spec flag(:min_bin_vheap_size, non_neg_integer) :: non_neg_integer
  @spec flag(:min_heap_size, non_neg_integer) :: non_neg_integer
  @spec flag(:monitor_nodes, term) :: term
  @spec flag({:monitor_nodes, term()}, term) :: term
  @spec flag(:priority, priority_level) :: priority_level
  @spec flag(:save_calls, 0..10000) :: 0..10000
  @spec flag(:sensitive, boolean) :: boolean
  @spec flag(:trap_exit, boolean) :: boolean
  defdelegate flag(flag, value), to: :erlang, as: :process_flag

  @doc """
  Sets the given `flag` to `value` for the given process `pid`.

  Returns the old value of `flag`.

  It raises `ArgumentError` if `pid` is not a local process.

  The allowed values for `flag` are only a subset of those allowed in `flag/2`,
  namely `:save_calls`.

  See `:erlang.process_flag/3` for more info.

  Inlined by the compiler.
  """
  @spec flag(pid, :save_calls, 0..10000) :: 0..10000
  defdelegate flag(pid, flag, value), to: :erlang, as: :process_flag

  @doc """
  Returns information about the process identified by `pid`, or returns `nil` if the process
  is not alive.

  Use this only for debugging information.

  See `:erlang.process_info/1` for more info.
  """
  @spec info(pid) :: keyword
  def info(pid) do
    nillify(:erlang.process_info(pid))
  end

  @doc """
  Returns information about the process identified by `pid`,
  or returns `nil` if the process is not alive.

  See `:erlang.process_info/2` for more info.
  """
  @spec info(pid, atom | [atom]) :: {atom, term} | [{atom, term}] | nil
  def info(pid, spec)

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

  def info(pid, spec) when is_atom(spec) or is_list(spec) do
    nillify(:erlang.process_info(pid, spec))
  end

  @doc """
  Puts the calling process into a "hibernation" state.

  The calling process is put into a waiting state
  where its memory allocation has been reduced as much as possible,
  which is useful if the process does not expect to receive any messages
  in the near future.

  See `:erlang.hibernate/3` for more info.

  Inlined by the compiler.
  """
  @spec hibernate(module, atom, list) :: no_return
  defdelegate hibernate(mod, fun_name, args), to: :erlang

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

defmodule Process do
  @moduledoc """
  Conveniences for working with processes and the process dictionary.

  Besides the functions available in this module, the `Kernel` module
  exposes and auto-imports some basic functionality related to processes
  available through the following functions:

    * `Kernel.spawn/1` and `Kernel.spawn/3`
    * `Kernel.spawn_link/1` and `Kernel.spawn_link/3`
    * `Kernel.spawn_monitor/1` and `Kernel.spawn_monitor/3`
    * `Kernel.self/0`
    * `Kernel.send/2`

  While this module provides low-level conveniences to work with processes,
  developers typically use abstractions such as `Agent`, `GenServer`,
  `Registry`, `Supervisor` and `Task`  for building their systems and
  resort to this module for gathering information, trapping exits, links
  and monitoring.
  """

  @doc """
  Tells whether the given process is alive on the local node.

  If the process identified by `pid` is alive (that is, it's not exiting and has
  not exited yet) than this function returns `true`. Otherwise, it returns
  `false`.

  `pid` must refer to a process running on the local node or `ArgumentError` is raised.

  Inlined by the compiler.
  """

  @typedoc """
  A process destination.

  A remote or local PID, a local port, a locally registered name, or a tuple in
  the form of `{registered_name, node}` for a registered name at another node.
  """
  @type dest :: pid | port | registered_name :: atom | {registered_name :: atom, node}

  @spec alive?(pid) :: boolean
  defdelegate alive?(pid), to: :erlang, as: :is_process_alive

  @doc """
  Returns all key-value pairs in the process dictionary.

  Inlined by the compiler.
  """
  @spec get() :: [{term, term}]
  defdelegate get(), to: :erlang

  @doc """
  Returns the value for the given `key` in the process dictionary,
  or `default` if `key` is not set.

  ## Examples

      # Assuming :locale was not set
      iex> Process.get(:locale, "pt")
      "pt"
      iex> Process.put(:locale, "fr")
      nil
      iex> Process.get(:locale, "pt")
      "fr"

  """
  @spec get(term, default :: term) :: term
  def get(key, default \\ nil) do
    case :erlang.get(key) do
      :undefined -> default
      value -> value
    end
  end

  @doc """
  Returns all keys in the process dictionary.

  Inlined by the compiler.

  ## Examples

      # Assuming :locale was not set
      iex> :locale in Process.get_keys()
      false
      iex> Process.put(:locale, "pt")
      nil
      iex> :locale in Process.get_keys()
      true

  """
  @spec get_keys() :: [term]
  defdelegate get_keys(), to: :erlang

  @doc """
  Returns all keys in the process dictionary that have the given `value`.

  Inlined by the compiler.
  """
  @spec get_keys(term) :: [term]
  defdelegate get_keys(value), to: :erlang

  @doc """
  Stores the given `key`-`value` pair in the process dictionary.

  The return value of this function is the value that was previously stored
  under `key`, or `nil` in case no value was stored under `key`.

  ## Examples

      # Assuming :locale was not set
      iex> Process.put(:locale, "en")
      nil
      iex> Process.put(:locale, "fr")
      "en"

  """
  @spec put(term, term) :: term | nil
  def put(key, value) do
    nillify(:erlang.put(key, value))
  end

  @doc """
  Deletes the given `key` from the process dictionary.

  Returns the value that was under `key` in the process dictionary,
  or `nil` if `key` was not stored in the process dictionary.

  ## Examples

      iex> Process.put(:comments, ["comment", "other comment"])
      iex> Process.delete(:comments)
      ["comment", "other comment"]
      iex> Process.delete(:comments)
      nil

  """
  @spec delete(term) :: term | nil
  def delete(key) do
    nillify(:erlang.erase(key))
  end

  @doc """
  Sends an exit signal with the given `reason` to `pid`.

  The following behaviour applies if `reason` is any term except `:normal`
  or `:kill`:

    1. If `pid` is not trapping exits, `pid` will exit with the given
       `reason`.

    2. If `pid` is trapping exits, the exit signal is transformed into a
       message `{:EXIT, from, reason}` and delivered to the message queue
       of `pid`.

  If `reason` is the atom `:normal`, `pid` will not exit (unless `pid` is
  the calling process, in which case it will exit with the reason `:normal`).
  If it is trapping exits, the exit signal is transformed into a message
  `{:EXIT, from, :normal}` and delivered to its message queue.

  If `reason` is the atom `:kill`, that is if `Process.exit(pid, :kill)` is called,
  an untrappable exit signal is sent to `pid` which will unconditionally exit
  with reason `:killed`.

  Inlined by the compiler.

  ## Examples

      Process.exit(pid, :kill)
      #=> true

  """
  @spec exit(pid, term) :: true
  defdelegate exit(pid, reason), to: :erlang

  @doc """
  Sleeps the current process for the given `timeout`.

  `timeout` is either the number of milliseconds to sleep as an
  integer or the atom `:infinity`. When `:infinity` is given,
  the current process will sleep forever, and not
  consume or reply to messages.

  **Use this function with extreme care**. For almost all situations
  where you would use `sleep/1` in Elixir, there is likely a
  more correct, faster and precise way of achieving the same with
  message passing.

  For example, if you are waiting for a process to perform some
  action, it is better to communicate the progress of such action
  with messages.

  In other words, **do not**:

      Task.start_link fn ->
        do_something()
        ...
      end

      # Wait until work is done
      Process.sleep(2000)

  But **do**:

      parent = self()
      Task.start_link fn ->
        do_something()
        send parent, :work_is_done
        ...
      end

      receive do
        :work_is_done -> :ok
      after
        30_000 -> :timeout # Optional timeout
      end

  For cases like the one above, `Task.async/1` and `Task.await/2` are
  preferred.

  Similarly, if you are waiting for a process to terminate,
  monitor that process instead of sleeping. **Do not**:

      Task.start_link fn ->
        ...
      end

      # Wait until task terminates
      Process.sleep(2000)

  Instead **do**:

      {:ok, pid} =
        Task.start_link fn ->
          ...
        end

      ref = Process.monitor(pid)
      receive do
        {:DOWN, ^ref, _, _, _} -> :task_is_down
      after
        30_000 -> :timeout # Optional timeout
      end

  """
  @spec sleep(timeout) :: :ok
  def sleep(timeout)
      when is_integer(timeout) and timeout >= 0
      when timeout == :infinity do
    receive after: (timeout -> :ok)
  end

  @doc """
  Sends a message to the given `dest`.

  `dest` may be a remote or local PID, a local port, a locally
  registered name, or a tuple in the form of `{registered_name, node}` for a
  registered name at another node.

  Inlined by the compiler.

  ## Options

    * `:noconnect` - when used, if sending the message would require an
      auto-connection to another node the message is not sent and `:noconnect` is
      returned.

    * `:nosuspend` - when used, if sending the message would cause the sender to
      be suspended the message is not sent and `:nosuspend` is returned.

  Otherwise the message is sent and `:ok` is returned.

  ## Examples

      iex> Process.send({:name, :node_that_does_not_exist}, :hi, [:noconnect])
      :noconnect

  """
  @spec send(dest, msg, [option]) :: :ok | :noconnect | :nosuspend
        when dest: dest(),
             msg: any,
             option: :noconnect | :nosuspend
  defdelegate send(dest, msg, options), to: :erlang

  @doc """
  Sends `msg` to `dest` after `time` milliseconds.

  If `dest` is a PID, it must be the PID of a local process, dead or alive.
  If `dest` is an atom, it must be the name of a registered process
  which is looked up at the time of delivery. No error is produced if the name does
  not refer to a process.

  This function returns a timer reference, which can be read with `read_timer/1`
  or canceled with `cancel_timer/1`.

  The timer will be automatically canceled if the given `dest` is a PID
  which is not alive or when the given PID exits. Note that timers will not be
  automatically canceled when `dest` is an atom (as the atom resolution is done
  on delivery).

  Inlined by the compiler.

  ## Options

    * `:abs` - (boolean) when `false`, `time` is treated as relative to the
    current monotonic time. When `true`, `time` is the absolute value of the
    Erlang monotonic time at which `msg` should be delivered to `dest`.
    To read more about Erlang monotonic time and other time-related concepts,
    look at the documentation for the `System` module. Defaults to `false`.

  ## Examples

      timer_ref = Process.send_after(pid, :hi, 1000)

  """
  @spec send_after(pid | atom, term, non_neg_integer, [option]) :: reference
        when option: {:abs, boolean}
  def send_after(dest, msg, time, opts \\ []) do
    :erlang.send_after(time, dest, msg, opts)
  end

  @doc """
  Cancels a timer returned by `send_after/3`.

  When the result is an integer, it represents the time in milliseconds
  left until the timer would have expired.

  When the result is `false`, a timer corresponding to `timer_ref` could not be
  found. This can happen either because the timer expired, because it has
  already been canceled, or because `timer_ref` never corresponded to a timer.

  Even if the timer had expired and the message was sent, this function does not
  tell you if the timeout message has arrived at its destination yet.

  Inlined by the compiler.

  ## Options

    * `:async` - (boolean) when `false`, the request for cancellation is
      synchronous. When `true`, the request for cancellation is asynchronous,
      meaning that the request to cancel the timer is issued and `:ok` is
      returned right away. Defaults to `false`.

    * `:info` - (boolean) whether to return information about the timer being
      cancelled. When the `:async` option is `false` and `:info` is `true`, then
      either an integer or `false` (like described above) is returned. If
      `:async` is `false` and `:info` is `false`, `:ok` is returned. If `:async`
      is `true` and `:info` is `true`, a message in the form `{:cancel_timer,
      timer_ref, result}` (where `result` is an integer or `false` like
      described above) is sent to the caller of this function when the
      cancellation has been performed. If `:async` is `true` and `:info` is
      `false`, no message is sent. Defaults to `true`.

  """
  @spec cancel_timer(reference, options) :: non_neg_integer | false | :ok
        when options: [async: boolean, info: boolean]
  defdelegate cancel_timer(timer_ref, options \\ []), to: :erlang

  @doc """
  Reads a timer created by `send_after/3`.

  When the result is an integer, it represents the time in milliseconds
  left until the timer will expire.

  When the result is `false`, a timer corresponding to `timer_ref` could not be
  found. This can be either because the timer expired, because it has already
  been canceled, or because `timer_ref` never corresponded to a timer.

  Even if the timer had expired and the message was sent, this function does not
  tell you if the timeout message has arrived at its destination yet.

  Inlined by the compiler.
  """
  @spec read_timer(reference) :: non_neg_integer | false
  defdelegate read_timer(timer_ref), to: :erlang

  @type spawn_opt ::
          :link
          | :monitor
          | {:priority, :low | :normal | :high}
          | {:fullsweep_after, non_neg_integer}
          | {:min_heap_size, non_neg_integer}
          | {:min_bin_vheap_size, non_neg_integer}
  @type spawn_opts :: [spawn_opt]

  @doc """
  Spawns the given function according to the given options.

  The result depends on the given options. In particular,
  if `:monitor` is given as an option, it will return a tuple
  containing the PID and the monitoring reference, otherwise
  just the spawned process PID.

  More options are available; for the comprehensive list of available options
  check `:erlang.spawn_opt/4`.

  Inlined by the compiler.

  ## Examples

      Process.spawn(fn -> 1 + 2 end, [:monitor])
      #=> {#PID<0.93.0>, #Reference<0.18808174.1939079169.202418>}
      Process.spawn(fn -> 1 + 2 end, [:link])
      #=> #PID<0.95.0>

  """
  @spec spawn((() -> any), spawn_opts) :: pid | {pid, reference}
  defdelegate spawn(fun, opts), to: :erlang, as: :spawn_opt

  @doc """
  Spawns the given function `fun` from module `mod`, passing the given `args`
  according to the given options.

  The result depends on the given options. In particular,
  if `:monitor` is given as an option, it will return a tuple
  containing the PID and the monitoring reference, otherwise
  just the spawned process PID.

  It also accepts extra options, for the list of available options
  check `:erlang.spawn_opt/4`.

  Inlined by the compiler.
  """
  @spec spawn(module, atom, list, spawn_opts) :: pid | {pid, reference}
  defdelegate spawn(mod, fun, args, opts), to: :erlang, as: :spawn_opt

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

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

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

  where:

    * `ref` is a monitor reference returned by this function;
    * `object` is either a `pid` of the monitored process (if monitoring
      a PID) or `{name, node}` (if monitoring a remote or local name);
    * `reason` is the exit reason.

  If the process is already dead when calling `Process.monitor/1`, a
  `:DOWN` message is delivered immediately.

  See [the need for monitoring](http://elixir-lang.org/getting-started/mix-otp/genserver.html#the-need-for-monitoring)
  for an example. See `:erlang.monitor/2` for more info.

  Inlined by the compiler.

  ## Examples

      pid = spawn fn -> 1 + 2 end
      #=> #PID<0.118.0>
      Process.monitor(pid)
      #=> #Reference<0.906660723.3006791681.40191>
      Process.exit(pid, :kill)
      #=> true
      receive do
        msg -> msg
      end
      #=> {:DOWN, #Reference<0.906660723.3006791681.40191>, :process, #PID<0.118.0>, :noproc}

  """
  @spec monitor(pid | {name :: atom, node :: atom} | name :: atom) :: reference
  def monitor(item) do
    :erlang.monitor(:process, item)
  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.

  ## Examples

      pid = spawn fn -> 1 + 2 end
      ref = Process.monitor(pid)
      Process.demonitor(ref)
      #=> true

  """
  @spec demonitor(reference, options :: [:flush | :info]) :: boolean
  defdelegate demonitor(monitor_ref, options \\ []), to: :erlang

  @doc """
  Returns a list of PIDs corresponding to all the
  processes currently existing on the local node.

  Note that if a process is exiting, it is considered to exist but not be
  alive. This means that for such process, `alive?/1` will return `false` but
  its PID will be part of the list of PIDs returned by this function.

  See `:erlang.processes/0` for more info.

  Inlined by the compiler.

  ## Examples

      Process.list()
      #=> [#PID<0.0.0>, #PID<0.1.0>, #PID<0.2.0>, #PID<0.3.0>, ...]

  """
  @spec list() :: [pid]
  defdelegate list(), to: :erlang, as: :processes

  @doc """
  Creates a link between the calling process and the given item (process or
  port).

  Links are bidirectional. Linked processes can be unlinked by using `unlink/1`.

  If such a link exists already, this function does nothing since there can only
  be one link between two given processes. If a process tries to create a link
  to itself, nothing will happen.

  When two processes are linked, each one receives exit signals from the other
  (see also `exit/2`). Let's assume `pid1` and `pid2` are linked. If `pid2`
  exits with a reason other than `:normal` (which is also the exit reason used
  when a process finishes its job) and `pid1` is not trapping exits (see
  `flag/2`), then `pid1` will exit with the same reason as `pid2` and in turn
  emit an exit signal to all its other linked processes. The behaviour when
  `pid1` is trapping exits is described in `exit/2`.

  See `:erlang.link/1` for more info.

  Inlined by the compiler.
  """
  @spec link(pid | port) :: true
  defdelegate link(pid_or_port), to: :erlang

  @doc """
  Removes the link between the calling process and the given item (process or
  port).

  If there is no such link, this function does nothing. If `pid_or_port` does
  not exist, this function does not produce any errors and simply does nothing.

  The return value of this function is always `true`.

  See `:erlang.unlink/1` for more info.

  Inlined by the compiler.
  """
  @spec unlink(pid | port) :: true
  defdelegate unlink(pid_or_port), to: :erlang

  @doc """
  Registers the given `pid_or_port` under the given `name`.

  `name` must be an atom and can then be used instead of the
  PID/port identifier when sending messages with `Kernel.send/2`.

  `register/2` will fail with `ArgumentError` in any of the following cases:

    * the PID/Port is not existing locally and alive
    * the name is already registered
    * the `pid_or_port` is already registered under a different `name`

  The following names are reserved and cannot be assigned to
  processes nor ports:

    * `nil`
    * `false`
    * `true`
    * `:undefined`

  ## Examples

      Process.register(self(), :test)
      #=> true
      send(:test, :hello)
      #=> :hello
      send(:wrong_name, :hello)
      #=> ** (ArgumentError) argument error

  """
  @spec register(pid | port, atom) :: true
  def register(pid_or_port, name)
      when is_atom(name) and name not in [nil, false, true, :undefined] do
    :erlang.register(name, pid_or_port)
  catch
    :error, :badarg when node(pid_or_port) != node() ->
      message = "could not register #{inspect(pid_or_port)} because it belongs to another node"
      :erlang.error(ArgumentError.exception(message), [pid_or_port, name])

    :error, :badarg ->
      message =
        "could not register #{inspect(pid_or_port)} with " <>
          "name #{inspect(name)} because it is not alive, the name is already " <>
          "taken, or it has already been given another name"

      :erlang.error(ArgumentError.exception(message), [pid_or_port, name])
  end

  @doc """
  Removes the registered `name`, associated with a PID
  or a port identifier.

  Fails with `ArgumentError` if the name is not registered
  to any PID or port.

  Inlined by the compiler.

  ## Examples

      Process.register(self(), :test)
      #=> true
      Process.unregister(:test)
      #=> true
      Process.unregister(:wrong_name)
      #=> ** (ArgumentError) argument error

  """
  @spec unregister(atom) :: true
  defdelegate unregister(name), to: :erlang

  @doc """
  Returns the PID or port identifier registered under `name` or `nil` if the
  name is not registered.

  See `:erlang.whereis/1` for more info.

  ## Examples

      Process.register(self(), :test)
      Process.whereis(:test)
      #=> #PID<0.84.0>
      Process.whereis(:wrong_name)
      #=> nil

  """
  @spec whereis(atom) :: pid | port | nil
  def whereis(name) do
    nillify(:erlang.whereis(name))
  end

  @doc """
  Returns the PID of the group leader for the calling process.

  Inlined by the compiler.

  ## Examples

      Process.group_leader()
      #=> #PID<0.53.0>

  """
  @spec group_leader() :: pid
  defdelegate group_leader(), to: :erlang

  @doc """
  Sets the group leader of the given `pid` to `leader`.

  Typically, this is used when a process started from a certain shell should
  have a group leader other than `:init`.

  Inlined by the compiler.
  """
  @spec group_leader(pid, leader :: pid) :: true
  def group_leader(pid, leader) do
    :erlang.group_leader(leader, pid)
  end

  @doc """
  Returns a list of names which have been registered using `register/2`.

  Inlined by the compiler.

  ## Examples

      Process.register(self(), :test)
      Process.registered()
      #=> [:test, :elixir_config, :inet_db, ...]

  """
  @spec registered() :: [atom]
  defdelegate registered(), to: :erlang

  @typep heap_size ::
           non_neg_integer
           | %{size: non_neg_integer, kill: boolean, error_logger: boolean}

  @typep priority_level :: :low | :normal | :high | :max

  @doc """
  Sets the given `flag` to `value` for the calling process.

  Returns the old value of `flag`.

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

  Inlined by the compiler.
  """
  @spec flag(:error_handler, module) :: module
  @spec flag(:max_heap_size, heap_size) :: heap_size
  @spec flag(:message_queue_data, :erlang.message_queue_data()) :: :erlang.message_queue_data()
  @spec flag(:min_bin_vheap_size, non_neg_integer) :: non_neg_integer
  @spec flag(:min_heap_size, non_neg_integer) :: non_neg_integer
  @spec flag(:monitor_nodes, term) :: term
  @spec flag({:monitor_nodes, term()}, term) :: term
  @spec flag(:priority, priority_level) :: priority_level
  @spec flag(:save_calls, 0..10000) :: 0..10000
  @spec flag(:sensitive, boolean) :: boolean
  @spec flag(:trap_exit, boolean) :: boolean
  defdelegate flag(flag, value), to: :erlang, as: :process_flag

  @doc """
  Sets the given `flag` to `value` for the given process `pid`.

  Returns the old value of `flag`.

  It raises `ArgumentError` if `pid` is not a local process.

  The allowed values for `flag` are only a subset of those allowed in `flag/2`,
  namely `:save_calls`.

  See `:erlang.process_flag/3` for more info.

  Inlined by the compiler.
  """
  @spec flag(pid, :save_calls, 0..10000) :: 0..10000
  defdelegate flag(pid, flag, value), to: :erlang, as: :process_flag

  @doc """
  Returns information about the process identified by `pid`, or returns `nil` if the process
  is not alive.

  Use this only for debugging information.

  See `:erlang.process_info/1` for more info.
  """
  @spec info(pid) :: keyword
  def info(pid) do
    nillify(:erlang.process_info(pid))
  end

  @doc """
  Returns information about the process identified by `pid`,
  or returns `nil` if the process is not alive.

  See `:erlang.process_info/2` for more info.
  """
  @spec info(pid, atom | [atom]) :: {atom, term} | [{atom, term}] | nil
  def info(pid, spec)

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

  def info(pid, spec) when is_atom(spec) or is_list(spec) do
    nillify(:erlang.process_info(pid, spec))
  end

  @doc """
  Puts the calling process into a "hibernation" state.

  The calling process is put into a waiting state
  where its memory allocation has been reduced as much as possible,
  which is useful if the process does not expect to receive any messages
  in the near future.

  See `:erlang.hibernate/3` for more info.

  Inlined by the compiler.
  """
  @spec hibernate(module, atom, list) :: no_return
  defdelegate hibernate(mod, fun_name, args), to: :erlang

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


Add note to compile application for generators

defmodule Mix.Tasks.Kitto.New do
  use Mix.Task
  import Mix.Generator

  @version Mix.Project.config[:version]
  @shortdoc "Creates a new Kitto v#{@version} application"
  @repo "https://github.com/kittoframework/kitto"

  # File mappings
  @lint {Credo.Check.Readability.MaxLineLength, false}
  @new [
    {:eex,  "new/config/config.exs",                    "config/config.exs"},
    {:text, "new/config/dev.exs",                       "config/dev.exs"},
    {:text, "new/config/prod.exs",                      "config/prod.exs"},
    {:eex,  "new/rel/config.exs",                       "rel/config.exs"},
    {:text, "new/rel/plugins/compile_assets_task.exs",  "rel/plugins/compile_assets_task.exs"},
    {:eex,  "new/mix.exs",                              "mix.exs"},
    {:eex,  "new/README.md",                            "README.md"},
    {:text, "new/.gitignore",                           ".gitignore"},
    {:text, "new/Dockerfile",                           "Dockerfile"},
    {:text, "new/.dockerignore",                        ".dockerignore"},
    {:text, "new/Procfile",                             "Procfile"},
    {:text, "new/elixir_buildpack.config",              "elixir_buildpack.config"},
    {:eex,  "new/lib/application_name.ex",              "lib/application_name.ex"},
    {:text, "new/dashboards/error.html.eex",            "dashboards/error.html.eex"},
    {:text, "new/dashboards/layout.html.eex",           "dashboards/layout.html.eex"},
    {:text, "new/dashboards/sample.html.eex",           "dashboards/sample.html.eex"},
    {:text, "new/dashboards/rotator.html.eex",          "dashboards/rotator.html.eex"},
    {:text, "new/dashboards/jobs.html.eex",             "dashboards/jobs.html.eex"},
    {:text, "new/widgets/clock/clock.js",               "widgets/clock/clock.js"},
    {:text, "new/widgets/clock/clock.scss",             "widgets/clock/clock.scss"},
    {:text, "new/widgets/graph/graph.js",               "widgets/graph/graph.js"},
    {:text, "new/widgets/graph/graph.scss",             "widgets/graph/graph.scss"},
    {:text, "new/widgets/image/image.js",               "widgets/image/image.js"},
    {:text, "new/widgets/image/image.scss",             "widgets/image/image.scss"},
    {:text, "new/widgets/list/list.js",                 "widgets/list/list.js"},
    {:text, "new/widgets/list/list.scss",               "widgets/list/list.scss"},
    {:text, "new/widgets/number/number.js",             "widgets/number/number.js"},
    {:text, "new/widgets/number/number.scss",           "widgets/number/number.scss"},
    {:text, "new/widgets/meter/meter.js",               "widgets/meter/meter.js"},
    {:text, "new/widgets/meter/meter.scss",             "widgets/meter/meter.scss"},
    {:text, "new/widgets/text/text.js",                 "widgets/text/text.js"},
    {:text, "new/widgets/text/text.scss",               "widgets/text/text.scss"},
    {:text, "new/widgets/time_took/time_took.js",       "widgets/time_took/time_took.js"},
    {:text, "new/widgets/time_took/time_took.scss",     "widgets/time_took/time_took.scss"},
    {:text, "new/jobs/phrases.exs",                     "jobs/phrases.exs"},
    {:text, "new/jobs/convergence.exs",                 "jobs/convergence.exs"},
    {:text, "new/jobs/buzzwords.exs",                   "jobs/buzzwords.exs"},
    {:text, "new/jobs/random.exs",                      "jobs/random.exs"},
    {:text, "new/jobs/stats.exs",                       "jobs/stats.exs"},
    {:keep, "new/assets/images",                        "assets/images/"},
    {:keep, "new/assets/fonts",                         "assets/fonts/"},
    {:text, "new/assets/javascripts/application.js",    "assets/javascripts/application.js"},
    {:text, "new/assets/stylesheets/application.scss",  "assets/stylesheets/application.scss"},
    {:keep, "new/public/assets",                        "public/assets"},
    {:text, "new/public/assets/favicon.ico",            "public/assets/favicon.ico"},
    {:text, "new/public/assets/images/placeholder.png", "public/assets/images/placeholder.png"},
    {:text, "new/webpack.config.js",                    "webpack.config.js"},
    {:text, "new/.babelrc",                             ".babelrc"},
    {:eex,  "new/package.json",                         "package.json"}
  ]

  # Embed all defined templates
  root = Path.expand("../templates", __DIR__)

  for {format, source, _} <- @new do
    unless format == :keep do
      @external_resource Path.join(root, source)
      def render(unquote(source)), do: unquote(File.read!(Path.join(root, source)))
    end
  end

  @moduledoc """
  Creates a new Kitto dashboard.

  It expects the path of the project as argument.

      mix kitto.new PATH [--edge] [--dev KITTO_PATH] [--app APP_NAME]

  A project at the given PATH will be created. The application name and module
  name will be retrieved from the path, unless otherwise provided.

  ## Options

  * `--edge` - use the `master` branch of Kitto as your dashboard's dependency
  * `--dev` - use a local copy of Kitto as your dashboard's dependency
  * `--app` - name of the OTP application and base module

  ## Examples

      # Create a new Kitto dashboard
      mix kitto.new hello_world

      # Create a new Kitto dashboard named `Foo` in `./hello_world`
      mix kitto.new hello_world --app foo

      # Create a new Kitto dashboard using the master branch to get the latest
      # Kitto features
      mix kitto.new hello_world --edge

      # Create a new Kitto dashboard using a local copy at ./kitto to test
      # development code in Kitto core
      mix kitto.new hello_world --dev ./kitto

  See: https://github.com/kittoframework/demo
  """

  def run([version]) when version in ~w(-v --version) do
    Mix.shell.info "Kitto v#{@version}"
  end

  def run(argv) do
    {opts, argv} =
      case OptionParser.parse(argv, switches: [edge: :boolean, dev: :string, app: :string]) do
        {opts, argv, []} ->
          {opts, argv}
        {_opts, _argv, [switch | _]} ->
          Mix.raise "Invalid option: " <> switch_to_string(switch)
      end

    case argv do
      [] -> Mix.Task.run "help", ["kitto.new"]
      [path|_] ->
        app = String.downcase(opts[:app] || Path.basename(path))
        check_application_name!(app)
        mod = Macro.camelize(app)

        run(app, mod, path, opts)
    end
  end

  def run(app, mod, path, opts) do
    binding = [application_name: app,
               application_module: mod,
               kitto_dep: kitto_dep(opts),
               npm_kitto_dep: npm_kitto_dep(opts[:dev])]

    copy_from path, binding, @new

    ## Optional contents

    ## Parallel installs
    install? = Mix.shell.yes?("\nFetch and install dependencies?")

    File.cd!(path, fn ->
      mix?    = install_mix(install?)
      webpack? = install_webpack(install?)
      extra   = if mix?, do: [], else: ["$ mix deps.get"]

      print_mix_info(path, extra)
      if !webpack?, do: print_webpack_info()
    end)
  end

  defp switch_to_string({name, nil}), do: name
  defp switch_to_string({name, val}), do: name <> "=" <> val

  defp install_webpack(install?) do
    maybe_cmd "npm install",
              File.exists?("webpack.config.js"),
              install? && System.find_executable("npm")
  end

  defp install_mix(install?) do
    maybe_cmd "mix deps.get", true, install? && Code.ensure_loaded?(Hex)
  end

  defp print_mix_info(path, extra) do
    steps = ["$ cd #{path}"] ++ extra ++ ["$ mix kitto.server"]

    Mix.shell.info """

    We are all set! Run your Dashboard application:

        #{Enum.join(steps, "\n    ")}

    To access generators compile your application first with:

        $ mix compile

    You can also run your app inside IEx (Interactive Elixir) as:

        $ iex -S mix
    """
  end

  defp print_webpack_info do
    Mix.shell.info """

    Kitto uses an assets build tool called webpack
    which requires node.js and npm. Installation instructions for
    node.js, which includes npm, can be found at http://nodejs.org.

    After npm is installed, install your webpack dependencies by
    running inside your app:

        $ npm install
    """
    nil
  end

  defp check_application_name!(app_name) do
    unless app_name =~ ~r/^[a-z][\w_]*$/ do
      Mix.raise "Application name must start with a letter and have only " <>
                "lowercase letters, numbers and underscore, " <>
                "received: #{inspect app_name}"
    end
  end

  ### Helpers

  defp maybe_cmd(cmd, should_run?, can_run?) do
    cond do
      should_run? && can_run? ->
        cmd(cmd)
        true
      should_run? ->
        false
      true ->
        true
    end
  end

  defp cmd(cmd) do
    Mix.shell.info [:green, "* running ", :reset, cmd]

    # We use :os.cmd/1 because there is a bug in OTP
    # where we cannot execute .cmd files on Windows.
    # We could use Mix.shell.cmd/1 but that automatically
    # outputs to the terminal and we don't want that.
    :os.cmd(String.to_char_list(cmd))
  end

  defp kitto_dep(opts) do
    cond do
      opts[:edge] -> ~s[{:kitto, github: "kittoframework/kitto", branch: "master"}]
      opts[:dev] -> ~s[{:kitto, path: "#{kitto_path(opts[:dev])}"}]
      true -> ~s[{:kitto, "~> #{@version}"}]
    end
  end

  defp npm_kitto_dep(path) when is_bitstring(path), do: kitto_path(path)
  defp npm_kitto_dep(_), do: "deps/kitto"

  defp kitto_path(path) do
    {:ok, cwd} = File.cwd()
    path = Path.join([cwd, path])

    if File.exists?(path) do
      path
    else
      install? = Mix.shell.yes?("\nKitto not found. Do you want to clone it?")
      maybe_cmd("git clone #{@repo}", true, install?)
      path
    end
  end

  ### Template helpers

  defp copy_from(target_dir, binding, mapping) when is_list(mapping) do
    application_name = Keyword.fetch!(binding, :application_name)

    for {format, source, target_path} <- mapping do
      target = Path.join(target_dir,
                         String.replace(target_path,
                                        "application_name",
                                        application_name))

      case format do
        :keep ->
          File.mkdir_p!(target)
        :text ->
          create_file(target, render(source))
        :append ->
          append_to(Path.dirname(target), Path.basename(target), render(source))
        :eex  ->
          contents = EEx.eval_string(render(source), binding, file: source)
          create_file(target, contents)
      end
    end
  end

  defp append_to(path, file, contents) do
    file = Path.join(path, file)
    File.write!(file, File.read!(file) <> contents)
  end
end

defmodule Mix.Tasks.Kitto.New do
  use Mix.Task
  import Mix.Generator

  @version Mix.Project.config[:version]
  @shortdoc "Creates a new Kitto v#{@version} application"
  @repo "https://github.com/kittoframework/kitto"

  # File mappings
  @lint {Credo.Check.Readability.MaxLineLength, false}
  @new [
    {:eex,  "new/config/config.exs",                    "config/config.exs"},
    {:text, "new/config/dev.exs",                       "config/dev.exs"},
    {:text, "new/config/prod.exs",                      "config/prod.exs"},
    {:eex,  "new/rel/config.exs",                       "rel/config.exs"},
    {:text, "new/rel/plugins/compile_assets_task.exs",  "rel/plugins/compile_assets_task.exs"},
    {:eex,  "new/mix.exs",                              "mix.exs"},
    {:eex,  "new/README.md",                            "README.md"},
    {:text, "new/.gitignore",                           ".gitignore"},
    {:text, "new/Dockerfile",                           "Dockerfile"},
    {:text, "new/.dockerignore",                        ".dockerignore"},
    {:text, "new/Procfile",                             "Procfile"},
    {:text, "new/elixir_buildpack.config",              "elixir_buildpack.config"},
    {:eex,  "new/lib/application_name.ex",              "lib/application_name.ex"},
    {:text, "new/dashboards/error.html.eex",            "dashboards/error.html.eex"},
    {:text, "new/dashboards/layout.html.eex",           "dashboards/layout.html.eex"},
    {:text, "new/dashboards/sample.html.eex",           "dashboards/sample.html.eex"},
    {:text, "new/dashboards/rotator.html.eex",          "dashboards/rotator.html.eex"},
    {:text, "new/dashboards/jobs.html.eex",             "dashboards/jobs.html.eex"},
    {:text, "new/widgets/clock/clock.js",               "widgets/clock/clock.js"},
    {:text, "new/widgets/clock/clock.scss",             "widgets/clock/clock.scss"},
    {:text, "new/widgets/graph/graph.js",               "widgets/graph/graph.js"},
    {:text, "new/widgets/graph/graph.scss",             "widgets/graph/graph.scss"},
    {:text, "new/widgets/image/image.js",               "widgets/image/image.js"},
    {:text, "new/widgets/image/image.scss",             "widgets/image/image.scss"},
    {:text, "new/widgets/list/list.js",                 "widgets/list/list.js"},
    {:text, "new/widgets/list/list.scss",               "widgets/list/list.scss"},
    {:text, "new/widgets/number/number.js",             "widgets/number/number.js"},
    {:text, "new/widgets/number/number.scss",           "widgets/number/number.scss"},
    {:text, "new/widgets/meter/meter.js",               "widgets/meter/meter.js"},
    {:text, "new/widgets/meter/meter.scss",             "widgets/meter/meter.scss"},
    {:text, "new/widgets/text/text.js",                 "widgets/text/text.js"},
    {:text, "new/widgets/text/text.scss",               "widgets/text/text.scss"},
    {:text, "new/widgets/time_took/time_took.js",       "widgets/time_took/time_took.js"},
    {:text, "new/widgets/time_took/time_took.scss",     "widgets/time_took/time_took.scss"},
    {:text, "new/jobs/phrases.exs",                     "jobs/phrases.exs"},
    {:text, "new/jobs/convergence.exs",                 "jobs/convergence.exs"},
    {:text, "new/jobs/buzzwords.exs",                   "jobs/buzzwords.exs"},
    {:text, "new/jobs/random.exs",                      "jobs/random.exs"},
    {:text, "new/jobs/stats.exs",                       "jobs/stats.exs"},
    {:keep, "new/assets/images",                        "assets/images/"},
    {:keep, "new/assets/fonts",                         "assets/fonts/"},
    {:text, "new/assets/javascripts/application.js",    "assets/javascripts/application.js"},
    {:text, "new/assets/stylesheets/application.scss",  "assets/stylesheets/application.scss"},
    {:keep, "new/public/assets",                        "public/assets"},
    {:text, "new/public/assets/favicon.ico",            "public/assets/favicon.ico"},
    {:text, "new/public/assets/images/placeholder.png", "public/assets/images/placeholder.png"},
    {:text, "new/webpack.config.js",                    "webpack.config.js"},
    {:text, "new/.babelrc",                             ".babelrc"},
    {:eex,  "new/package.json",                         "package.json"}
  ]

  # Embed all defined templates
  root = Path.expand("../templates", __DIR__)

  for {format, source, _} <- @new do
    unless format == :keep do
      @external_resource Path.join(root, source)
      def render(unquote(source)), do: unquote(File.read!(Path.join(root, source)))
    end
  end

  @moduledoc """
  Creates a new Kitto dashboard.

  It expects the path of the project as argument.

      mix kitto.new PATH [--edge] [--dev KITTO_PATH] [--app APP_NAME]

  A project at the given PATH will be created. The application name and module
  name will be retrieved from the path, unless otherwise provided.

  ## Options

  * `--edge` - use the `master` branch of Kitto as your dashboard's dependency
  * `--dev` - use a local copy of Kitto as your dashboard's dependency
  * `--app` - name of the OTP application and base module

  ## Examples

      # Create a new Kitto dashboard
      mix kitto.new hello_world

      # Create a new Kitto dashboard named `Foo` in `./hello_world`
      mix kitto.new hello_world --app foo

      # Create a new Kitto dashboard using the master branch to get the latest
      # Kitto features
      mix kitto.new hello_world --edge

      # Create a new Kitto dashboard using a local copy at ./kitto to test
      # development code in Kitto core
      mix kitto.new hello_world --dev ./kitto

  See: https://github.com/kittoframework/demo
  """

  def run([version]) when version in ~w(-v --version) do
    Mix.shell.info "Kitto v#{@version}"
  end

  def run(argv) do
    {opts, argv} =
      case OptionParser.parse(argv, switches: [edge: :boolean, dev: :string, app: :string]) do
        {opts, argv, []} ->
          {opts, argv}
        {_opts, _argv, [switch | _]} ->
          Mix.raise "Invalid option: " <> switch_to_string(switch)
      end

    case argv do
      [] -> Mix.Task.run "help", ["kitto.new"]
      [path|_] ->
        app = String.downcase(opts[:app] || Path.basename(path))
        check_application_name!(app)
        mod = Macro.camelize(app)

        run(app, mod, path, opts)
    end
  end

  def run(app, mod, path, opts) do
    binding = [application_name: app,
               application_module: mod,
               kitto_dep: kitto_dep(opts),
               npm_kitto_dep: npm_kitto_dep(opts[:dev])]

    copy_from path, binding, @new

    ## Optional contents

    ## Parallel installs
    install? = Mix.shell.yes?("\nFetch and install dependencies?")

    File.cd!(path, fn ->
      mix?    = install_mix(install?)
      webpack? = install_webpack(install?)
      extra   = if mix?, do: [], else: ["$ mix deps.get"]

      print_mix_info(path, extra)
      if !webpack?, do: print_webpack_info()
    end)
  end

  defp switch_to_string({name, nil}), do: name
  defp switch_to_string({name, val}), do: name <> "=" <> val

  defp install_webpack(install?) do
    maybe_cmd "npm install",
              File.exists?("webpack.config.js"),
              install? && System.find_executable("npm")
  end

  defp install_mix(install?) do
    maybe_cmd "mix deps.get", true, install? && Code.ensure_loaded?(Hex)
  end

  defp print_mix_info(path, extra) do
    steps = ["$ cd #{path}"] ++ extra ++ ["$ mix kitto.server"]

    Mix.shell.info """

    We are all set! Run your Dashboard application:

        #{Enum.join(steps, "\n    ")}

    You can also run your app inside IEx (Interactive Elixir) as:

        $ iex -S mix
    """
  end

  defp print_webpack_info do
    Mix.shell.info """

    Kitto uses an assets build tool called webpack
    which requires node.js and npm. Installation instructions for
    node.js, which includes npm, can be found at http://nodejs.org.

    After npm is installed, install your webpack dependencies by
    running inside your app:

        $ npm install
    """
    nil
  end

  defp check_application_name!(app_name) do
    unless app_name =~ ~r/^[a-z][\w_]*$/ do
      Mix.raise "Application name must start with a letter and have only " <>
                "lowercase letters, numbers and underscore, " <>
                "received: #{inspect app_name}"
    end
  end

  ### Helpers

  defp maybe_cmd(cmd, should_run?, can_run?) do
    cond do
      should_run? && can_run? ->
        cmd(cmd)
        true
      should_run? ->
        false
      true ->
        true
    end
  end

  defp cmd(cmd) do
    Mix.shell.info [:green, "* running ", :reset, cmd]

    # We use :os.cmd/1 because there is a bug in OTP
    # where we cannot execute .cmd files on Windows.
    # We could use Mix.shell.cmd/1 but that automatically
    # outputs to the terminal and we don't want that.
    :os.cmd(String.to_char_list(cmd))
  end

  defp kitto_dep(opts) do
    cond do
      opts[:edge] -> ~s[{:kitto, github: "kittoframework/kitto", branch: "master"}]
      opts[:dev] -> ~s[{:kitto, path: "#{kitto_path(opts[:dev])}"}]
      true -> ~s[{:kitto, "~> #{@version}"}]
    end
  end

  defp npm_kitto_dep(path) when is_bitstring(path), do: kitto_path(path)
  defp npm_kitto_dep(_), do: "deps/kitto"

  defp kitto_path(path) do
    {:ok, cwd} = File.cwd()
    path = Path.join([cwd, path])

    if File.exists?(path) do
      path
    else
      install? = Mix.shell.yes?("\nKitto not found. Do you want to clone it?")
      maybe_cmd("git clone #{@repo}", true, install?)
      path
    end
  end

  ### Template helpers

  defp copy_from(target_dir, binding, mapping) when is_list(mapping) do
    application_name = Keyword.fetch!(binding, :application_name)

    for {format, source, target_path} <- mapping do
      target = Path.join(target_dir,
                         String.replace(target_path,
                                        "application_name",
                                        application_name))

      case format do
        :keep ->
          File.mkdir_p!(target)
        :text ->
          create_file(target, render(source))
        :append ->
          append_to(Path.dirname(target), Path.basename(target), render(source))
        :eex  ->
          contents = EEx.eval_string(render(source), binding, file: source)
          create_file(target, contents)
      end
    end
  end

  defp append_to(path, file, contents) do
    file = Path.join(path, file)
    File.write!(file, File.read!(file) <> contents)
  end
end


Add Chromic opts

Add Chromic opts

defmodule Siwapp.Application do
  # See https://hexdocs.pm/elixir/Application.html
  # for more information on OTP Applications
  @moduledoc false

  use Application

  defp chromic_pdf_opts do
    [no_sandbox: true]
  end

  @impl Application
  def start(_type, _args) do
    Logger.add_backend(Sentry.LoggerBackend)

    children = [
      # Start the Ecto repository
      Siwapp.Repo,
      # Start the Telemetry supervisor
      SiwappWeb.Telemetry,
      # Start the PubSub system
      {Phoenix.PubSub, name: Siwapp.PubSub},
      # Start the Endpoint (http/https)
      SiwappWeb.Endpoint,
      {ChromicPDF, chromic_pdf_opts()}
      # Start a worker by calling: Siwapp.Worker.start_link(arg)
      # {Siwapp.Worker, arg}
      {Cachex, name: :siwapp_cache},
      Siwapp.TimerEvents
    ]

    # See https://hexdocs.pm/elixir/Supervisor.html
    # for other strategies and supported options
    opts = [strategy: :one_for_one, name: Siwapp.Supervisor]
    Supervisor.start_link(children, opts)
  end

  # Tell Phoenix to update the endpoint configuration
  # whenever the application is updated.
  @impl Application
  def config_change(changed, _new, removed) do
    SiwappWeb.Endpoint.config_change(changed, removed)
    :ok
  end
end

defmodule Siwapp.Application do
  # See https://hexdocs.pm/elixir/Application.html
  # for more information on OTP Applications
  @moduledoc false

  use Application

  @impl Application
  def start(_type, _args) do
    Logger.add_backend(Sentry.LoggerBackend)

    children = [
      # Start the Ecto repository
      Siwapp.Repo,
      # Start the Telemetry supervisor
      SiwappWeb.Telemetry,
      # Start the PubSub system
      {Phoenix.PubSub, name: Siwapp.PubSub},
      # Start the Endpoint (http/https)
      SiwappWeb.Endpoint,
      ChromicPDF,
      # Start a worker by calling: Siwapp.Worker.start_link(arg)
      # {Siwapp.Worker, arg}
      {Cachex, name: :siwapp_cache},
      Siwapp.TimerEvents
    ]

    # See https://hexdocs.pm/elixir/Supervisor.html
    # for other strategies and supported options
    opts = [strategy: :one_for_one, name: Siwapp.Supervisor]
    Supervisor.start_link(children, opts)
  end

  # Tell Phoenix to update the endpoint configuration
  # whenever the application is updated.
  @impl Application
  def config_change(changed, _new, removed) do
    SiwappWeb.Endpoint.config_change(changed, removed)
    :ok
  end
end


Ensure cowboy and plug are started when using preview plug

defmodule Swoosh.Application do
  use Application

  require Logger

  def start(_type, _args) do
    import Supervisor.Spec, warn: false

    children = [
      worker(Swoosh.InMemoryMailbox, []),
    ]

    children =
      if Application.get_env(:swoosh, :serve_mailbox) do
        Application.ensure_all_started(:cowboy)
        Application.ensure_all_started(:plug)

        Logger.info("Running Swoosh mailbox preview server with Cowboy using http on port 4000")
        port = Application.get_env(:swoosh, :preview_port, 4000)
        [Plug.Adapters.Cowboy.child_spec(:http, Plug.Swoosh.MailboxPreview, [], port: port) | children]
      else
        children
      end

    opts = [strategy: :one_for_one, name: Swoosh.Supervisor]
    Supervisor.start_link(children, opts)
  end
end

defmodule Swoosh.Application do
  use Application

  require Logger

  def start(_type, _args) do
    import Supervisor.Spec, warn: false

    children = [
      worker(Swoosh.InMemoryMailbox, []),
    ]

    children =
      if Application.get_env(:swoosh, :serve_mailbox) do
        Logger.info("Running Swoosh mailbox preview server with Cowboy using http on port 4000")
        port = Application.get_env(:swoosh, :preview_port, 4000)
        [Plug.Adapters.Cowboy.child_spec(:http, Plug.Swoosh.MailboxPreview, [], port: port) | children]
      else
        children
      end

    opts = [strategy: :one_for_one, name: Swoosh.Supervisor]
    Supervisor.start_link(children, opts)
  end
end


removed channel test case

removed channel test case

defmodule Democracy.ChannelCase do
  @moduledoc """
  This module defines the test case to be used by
  channel tests.

  Such tests rely on `Phoenix.ChannelTest` and also
  imports other functionality to make it easier
  to build and query models.

  Finally, if the test case interacts with the database,
  it cannot be async. For this reason, every test runs
  inside a transaction which is reset at the beginning
  of the test unless the test case is marked as async.
  """

  use ExUnit.CaseTemplate

  using do
    quote do
      # Import conveniences for testing with channels
      use Phoenix.ChannelTest

      alias Democracy.Repo
      import Ecto
      import Ecto.Changeset
      import Ecto.Query, only: [from: 1, from: 2]

      # The default endpoint for testing
      @endpoint Democracy.Endpoint
    end
  end

  setup tags do
    unless tags[:async] do
      Ecto.Adapters.SQL.restart_test_transaction(Democracy.Repo, [])
    end

    :ok
  end
end


Rewrite Tracks module to use Agent

Rewrite Tracks module to use Agent

defmodule GnServer.Rqtl.Control do
  defstruct crosstype: "", geno: "", pheno: "", phenocovar: "",
    covar: "", gmap: "", alleles: [], genotypes: {}, sex: {},
    cross_info: {}, x_chr: "", na_strings: []

  @doc """
  Parses a JSON-formatted R/QTL2 control file.
  """
  def parse_control(path) do
    {:ok, file} = File.read(path)
    # {:ok, obj} = Poison.decode(file, as: %GnServer.Rqtl.Control{})
    {:ok, obj} = Poison.decode(file)
    obj
  end

  @doc """
  Adds an URI root to a parsed control object
  """
  def add_uri_root(ctrl, root) do
    %GnServer.Rqtl.Control{ ctrl | geno: root <> ctrl.geno,
                            pheno: root <> ctrl.pheno, phenocovar: root <> ctrl.phenocovar,
                            covar: root <> ctrl.covar, gmap: root <> ctrl.gmap }
  end
end

defmodule GnServer.Rqtl.Tracks do
  @moduledoc """
  Keeps track of all R/QTL2 tracks, and manages fetching of data
  and serving it to the router

  Should be able to serve data both from files as well as databases
  """

  use GenServer

  def start_link do
    Agent.start_link(fn -> %{} end, name: :rqtl_tracks)
  end

  def add_track(track_name, file) do
    ctrl = GnServer.Rqtl.Control.parse_control(file)
    Agent.update(:rqtl_tracks, fn tracks -> Map.put(tracks, track_name, ctrl) end)
  end

  def get_control(track_name) do
    Agent.get(:rqtl_tracks, fn tracks -> tracks[track_name] end)
  end

  def get_file(track_name, file) do
    filename = Agent.get(:rqtl_tracks, fn tracks ->
      tracks[track_name][file]
    end)
    File.read(filename)
  end

end

defmodule GnServer.Rqtl.Control do
  defstruct crosstype: "", geno: "", pheno: "", phenocovar: "",
    covar: "", gmap: "", alleles: [], genotypes: {}, sex: {},
    cross_info: {}, x_chr: "", na_strings: []

  @doc """
  Parses a JSON-formatted R/QTL2 control file.
  """
  def parse_control(path) do
    {:ok, file} = File.read(path)
    # {:ok, obj} = Poison.decode(file, as: %GnServer.Rqtl.Control{})
    {:ok, obj} = Poison.decode(file)
    obj
  end

  @doc """
  Adds an URI root to a parsed control object
  """
  def add_uri_root(ctrl, root) do
    %GnServer.Rqtl.Control{ ctrl | geno: root <> ctrl.geno,
                            pheno: root <> ctrl.pheno, phenocovar: root <> ctrl.phenocovar,
                            covar: root <> ctrl.covar, gmap: root <> ctrl.gmap }
  end
end

defmodule GnServer.Rqtl.Tracks do
  @moduledoc """
  Keeps track of all R/QTL2 tracks, and manages fetching of data
  and serving it to the router

  Should be able to serve data both from files as well as databases
  """

  def start_link do
    Task.start_link(fn -> loop(%{}) end)
  end

  defp loop(map) do
    receive do
      # serve genotype, gmap, etc. file
      {:get_file, {track_name, file}, caller} ->
        ctrl = Map.get(map, track_name)
        case Map.get(map, track_name) do
          nil  ->
            send caller, {:error, "Track doesn't exist"}
          ctrl ->
            {:ok, content} = File.read(ctrl[file])
            send caller, content
        end
        loop(map)

      ## Add a track, given a control file path
      {:add_track, {track_name, file}, caller} ->
        ctrl =
          GnServer.Rqtl.Control.parse_control(file)
          # |> GnServer.Rqtl.Control.add_uri_root("./")
        send caller, {:ok, "Track added"}
        loop(Map.put(map, track_name, ctrl))

      # get track control file (mostly for testing and debugging)
      {:get_control, track_name, caller} ->
        case Map.get(map, track_name) do
          nil  ->
            send caller, {:error, "Track doesn't exist"}
          ctrl ->
            send caller, ctrl
        end
        loop(map)
    end
  end
end


dont log

dont log

defmodule Core.PetitionCount do
  use Agent
  alias Osdi.{Repo, Tagging, Tag}
  import Ecto.Query

  @in_last [hours: -1]

  def start_link do
    Task.start_link(&update/0)
    Agent.start_link(fn -> %{} end, name: __MODULE__)
  end

  def stats_for(title) do
    Agent.get __MODULE__, fn state ->
      case Map.get(state, title) do
        nil -> {:error, "not found"}
        vals -> {:ok, vals}
      end
    end
  end

  def update do
    petition_tags = Repo.all(from t in Tag,
      where: ilike(t.name, "%Signed Petition%"),
      join: pt in Tagging,
      on: pt.tag_id == t.id,
      select: {t.name, t.id})

    tasks =
      petition_tags
      |> Enum.filter(fn {name, _} -> (name |> String.split(":") |> length()) < 5 end)
      |> Enum.reduce(%{}, fn ({name, tag_id}, acc) ->
        petition_name = name |> String.split(":") |> List.last() |> String.trim()
        case Map.get(acc, petition_name) do
          nil -> Map.put(acc, petition_name, [tag_id])
          list -> Map.put(acc, petition_name, [tag_id | list])
        end
      end)
      # |> Enum.map(fn {title, tag_ids} -> Task.async(fn -> update_petition({title, tag_ids}) end) end)
      |> Enum.map(&update_petition/1)

    Enum.map tasks, &Task.await/1
  end

  defp update_petition({title, tag_ids}) do
    [total] = Repo.all(from pt in Tagging,
      where: pt.tag_id in ^tag_ids,
      select: count(pt.id))

    since = Timex.shift(Timex.now(), @in_last)
    [in_last] = Repo.all(from pt in Tagging,
      where: pt.tag_id in ^tag_ids and
        pt.inserted_at > ^since,
      select: count(pt.id))

    Agent.update __MODULE__, fn state ->
      Map.put(state, title, %{total: total, in_last: in_last})
    end
  end

  def dump_state do
    state = Agent.get __MODULE__, fn state -> state end

    Enum.map state, fn {title, %{total: total, in_last: in_last}} ->
      "#{title}:\t#{total}"
    end
  end
end

defmodule Core.PetitionCount do
  use Agent
  alias Osdi.{Repo, Tagging, Tag}
  import Ecto.Query

  @in_last [hours: -1]

  def start_link do
    Task.start_link(&update/0)
    Agent.start_link(fn -> %{} end, name: __MODULE__)
  end

  def stats_for(title) do
    Agent.get __MODULE__, fn state ->
      case Map.get(state, title) do
        nil -> {:error, "not found"}
        vals -> {:ok, vals}
      end
    end
  end

  def update do
    petition_tags = Repo.all(from t in Tag,
      where: ilike(t.name, "%Signed Petition%"),
      join: pt in Tagging,
      on: pt.tag_id == t.id,
      select: {t.name, t.id})

    tasks =
      petition_tags
      |> Enum.filter(fn {name, _} -> (name |> String.split(":") |> length()) < 5 end)
      |> Enum.reduce(%{}, fn ({name, tag_id}, acc) ->
        petition_name = name |> String.split(":") |> List.last() |> String.trim()
        case Map.get(acc, petition_name) do
          nil -> Map.put(acc, petition_name, [tag_id])
          list -> Map.put(acc, petition_name, [tag_id | list])
        end
      end)
      # |> Enum.map(fn {title, tag_ids} -> Task.async(fn -> update_petition({title, tag_ids}) end) end)
      |> Enum.map(&update_petition/1)

    Enum.map tasks, &Task.await/1
  end

  defp update_petition({title, tag_ids}) do
    [total] = Repo.all(from pt in Tagging,
      where: pt.tag_id in ^tag_ids,
      select: count(pt.id))

    since = Timex.shift(Timex.now(), @in_last)
    [in_last] = Repo.all(from pt in Tagging,
      where: pt.tag_id in ^tag_ids and
        pt.inserted_at > ^since,
      select: count(pt.id))

    Agent.update __MODULE__, fn state ->
      IO.puts "#{title}: #{total}"
      Map.put(state, title, %{total: total, in_last: in_last})
    end
  end

  def dump_state do
    state = Agent.get __MODULE__, fn state -> state end

    Enum.map state, fn {title, %{total: total, in_last: in_last}} ->
      "#{title}:\t#{total}"
    end
  end
end


Remove unused var

Remove unused var

defmodule CSV.LineAggregator do
  use CSV.Defaults

  alias CSV.LineAggregator.CorruptStreamError

  @moduledoc ~S"""
  The CSV LineAggregator module - aggregates lines in a stream that are part
  of a common escape sequence.
  """

  @doc """
  Aggregates the common escape sequences of a stream with the given separator.

  ## Options

  Options get transferred from the decoder. They are:

    * `:separator` – The field separator
  """

  def aggregate(stream, options \\ []) do
    separator = options |> Keyword.get(:separator, @separator)
    stream |> Stream.transform(fn -> [] end, fn line, collected ->
      case collected do
        [] -> start_aggregate(line, separator)
        _ -> continue_aggregate(collected, line, separator)
      end
    end, fn collected ->
      case collected do
        [] -> :ok
        _ -> raise CorruptStreamError, message: "Stream halted with unterminated escape sequence"
      end
    end)
  end
  defp start_aggregate(line, separator) do
    cond do
      is_open?(line, separator) ->
        { [], [line] }
      true ->
        { [line], [] }
    end
  end
  defp continue_aggregate(collected, line, separator) do
    { is_closing, tail } = is_closing?(line, separator)
    cond do
      is_closing && is_open?(tail, separator) ->
        { [], collected ++ [line] }
      is_closing ->
        { [collected ++ [line] |> Enum.join(@delimiter)], [] }
      true ->
        { [], collected ++ [line] }
    end
  end

  defp is_closing?(line, separator) do
    is_closing?(line, "", true, separator)
  end

  defp is_closing?(<< @double_quote :: utf8 >> <> tail, << @double_quote :: utf8 >>, quoted, separator) do
    is_closing?(tail, @double_quote, !quoted, separator)
  end
  defp is_closing?(<< _ :: utf8 >> <> tail, << @double_quote :: utf8 >>, quoted, _) do
    { quoted, tail }
  end
  defp is_closing?(<< head :: utf8 >> <> tail, _, quoted, separator) do
    is_closing?(tail, << head :: utf8 >>, quoted, separator)
  end
  defp is_closing?("", << @double_quote :: utf8 >>, _, _) do
    { true, "" }
  end
  defp is_closing?("", _, _, _) do
    { false, "" }
  end

  defp is_open?(line, separator) do
    is_open?(line, "", false, separator)
  end

  defp is_open?(<< @double_quote :: utf8 >> <> tail, << @double_quote :: utf8 >>, quoted, separator) do
    is_open?(tail, @double_quote, quoted, separator)
  end
  defp is_open?(<< @double_quote :: utf8 >> <> tail, last_token, false, separator) when last_token == << separator :: utf8 >> do
    is_open?(tail, @double_quote, true, separator)
  end
  defp is_open?(<< @double_quote :: utf8 >> <> tail, "", false, separator) do
    is_open?(tail, @double_quote, true, separator)
  end
  defp is_open?(<< @double_quote :: utf8 >> <> tail, _, quoted, separator) do
    is_open?(tail, @double_quote, !quoted, separator)
  end
  defp is_open?(<< head :: utf8 >> <> tail, _, quoted, separator) do
    is_open?(tail, << head :: utf8 >>, quoted, separator)
  end
  defp is_open?(<< head >> <> tail, _, quoted, separator) do
    is_open?(tail, << head >>, quoted, separator)
  end
  defp is_open?("", << @double_quote :: utf8 >>, quoted, _) do
    !quoted
  end
  defp is_open?("", _, quoted, _) do
    quoted
  end

end

defmodule CSV.LineAggregator do
  use CSV.Defaults

  alias CSV.LineAggregator.CorruptStreamError

  @moduledoc ~S"""
  The CSV LineAggregator module - aggregates lines in a stream that are part
  of a common escape sequence.
  """

  @doc """
  Aggregates the common escape sequences of a stream with the given separator.

  ## Options

  Options get transferred from the decoder. They are:

    * `:separator` – The field separator
  """

  def aggregate(stream, options \\ []) do
    separator = options |> Keyword.get(:separator, @separator)
    stream |> Stream.transform(fn -> [] end, fn line, collected ->
      case collected do
        [] -> start_aggregate(line, separator)
        _ -> continue_aggregate(collected, line, separator)
      end
    end, fn collected ->
      case collected do
        [] -> :ok
        _ -> raise CorruptStreamError, message: "Stream halted with unterminated escape sequence"
      end
    end)
  end
  defp start_aggregate(line, separator) do
    cond do
      is_open?(line, separator) ->
        { [], [line] }
      true ->
        { [line], [] }
    end
  end
  defp continue_aggregate(collected, line, separator) do
    { is_closing, tail } = is_closing?(line, separator)
    cond do
      is_closing && is_open?(tail, separator) ->
        { [], collected ++ [line] }
      is_closing ->
        { [collected ++ [line] |> Enum.join(@delimiter)], [] }
      true ->
        { [], collected ++ [line] }
    end
  end

  defp is_closing?(line, separator) do
    is_closing?(line, "", true, separator)
  end

  defp is_closing?(<< @double_quote :: utf8 >> <> tail, << @double_quote :: utf8 >>, quoted, separator) do
    is_closing?(tail, @double_quote, !quoted, separator)
  end
  defp is_closing?(<< head :: utf8 >> <> tail, << @double_quote :: utf8 >>, quoted, _) do
    { quoted, tail }
  end
  defp is_closing?(<< head :: utf8 >> <> tail, _, quoted, separator) do
    is_closing?(tail, << head :: utf8 >>, quoted, separator)
  end
  defp is_closing?("", << @double_quote :: utf8 >>, _, _) do
    { true, "" }
  end
  defp is_closing?("", _, _, _) do
    { false, "" }
  end

  defp is_open?(line, separator) do
    is_open?(line, "", false, separator)
  end

  defp is_open?(<< @double_quote :: utf8 >> <> tail, << @double_quote :: utf8 >>, quoted, separator) do
    is_open?(tail, @double_quote, quoted, separator)
  end
  defp is_open?(<< @double_quote :: utf8 >> <> tail, last_token, false, separator) when last_token == << separator :: utf8 >> do
    is_open?(tail, @double_quote, true, separator)
  end
  defp is_open?(<< @double_quote :: utf8 >> <> tail, "", false, separator) do
    is_open?(tail, @double_quote, true, separator)
  end
  defp is_open?(<< @double_quote :: utf8 >> <> tail, _, quoted, separator) do
    is_open?(tail, @double_quote, !quoted, separator)
  end
  defp is_open?(<< head :: utf8 >> <> tail, _, quoted, separator) do
    is_open?(tail, << head :: utf8 >>, quoted, separator)
  end
  defp is_open?(<< head >> <> tail, _, quoted, separator) do
    is_open?(tail, << head >>, quoted, separator)
  end
  defp is_open?("", << @double_quote :: utf8 >>, quoted, _) do
    !quoted
  end
  defp is_open?("", _, quoted, _) do
    quoted
  end

end


Replaced usages of the deprecated HashDict module with Map in sequence.ex (#159)

defmodule ExMachina.Sequence do
  def start_link do
    Agent.start_link(fn -> Map.new end, name: __MODULE__)
  end

  @doc """
  Reset all sequences so that the next sequence starts from 0

  ## Example

      ExMachina.Sequence.next("joe") # "joe0"
      ExMachina.Sequence.next("joe") # "joe1"

      Sequence.reset

      ExMachina.Sequence.next("joe") # resets so the return value is "joe0"

  If you want to reset sequences at the beginning of every test, put it in a
  `setup` block in your test.

     setup do
       ExMachina.Sequence.reset
     end
  """
  def reset do
    Agent.update(__MODULE__, fn(_) -> Map.new end)
  end

  @doc false
  def next(sequence_name) when is_binary(sequence_name) do
    next sequence_name, fn(n)->
      sequence_name <> to_string(n)
    end
  end

  @doc false
  def next(sequence_name) do
    raise(
      ArgumentError,
      "Sequence name must be a string, got #{inspect sequence_name} instead"
    )
  end

  @doc false
  def next(sequence_name, formatter) do
    Agent.get_and_update(__MODULE__, fn(sequences) ->
      current_value = Map.get(sequences, sequence_name, 0)
      new_sequences = Map.put(sequences, sequence_name, current_value + 1)
      {formatter.(current_value), new_sequences}
    end)
  end
end

defmodule ExMachina.Sequence do
  def start_link do
    Agent.start_link(fn -> HashDict.new end, name: __MODULE__)
  end

  @doc """
  Reset all sequences so that the next sequence starts from 0

  ## Example

      ExMachina.Sequence.next("joe") # "joe0"
      ExMachina.Sequence.next("joe") # "joe1"

      Sequence.reset

      ExMachina.Sequence.next("joe") # resets so the return value is "joe0"

  If you want to reset sequences at the beginning of every test, put it in a
  `setup` block in your test.

     setup do
       ExMachina.Sequence.reset
     end
  """
  def reset do
    Agent.update(__MODULE__, fn(_) -> HashDict.new end)
  end

  @doc false
  def next(sequence_name) when is_binary(sequence_name) do
    next sequence_name, fn(n)->
      sequence_name <> to_string(n)
    end
  end

  @doc false
  def next(sequence_name) do
    raise(
      ArgumentError,
      "Sequence name must be a string, got #{inspect sequence_name} instead"
    )
  end

  @doc false
  def next(sequence_name, formatter) do
    Agent.get_and_update(__MODULE__, fn(sequences) ->
      current_value = HashDict.get(sequences, sequence_name, 0)
      new_sequences = HashDict.put(sequences, sequence_name, current_value + 1)
      {formatter.(current_value), new_sequences}
    end)
  end
end


Try locating snapshots when no arguments are passed to mix bench.cmp

defmodule Mix.Tasks.Bench.Cmp do
  use Mix.Task

  @shortdoc "Compare benchmark snapshots"

  @moduledoc """
  ## Usage

      mix bench.cmp [options] <snapshot>...

  A snapshot is the output of a single run of `mix bench`.

  If no arguments are given, bench.cmp will try to read one or two latest
  snapshots from the bench/snapshots directory.

  When given one snapshot, `mix bench.cmp` will pretty-print the results.
  Giving `-` instead of a file name will make bench.cmp read from standard
  input.

  When given two or more snapshots, it will pretty-print the comparison between
  the first and the last one.

  ## Options

      -f <fmt>, --format=<fmt>
          Which format to use for the deltas when pretty-printing.

          One of: ratio, percent.
  """

  alias Benchfella.Snapshot
  alias Benchfella.CLI.Util

  def run(args) do
    switches = [format: :string]
    aliases = [f: :format]
    {snapshots, options} =
      case OptionParser.parse(args, strict: switches, aliases: aliases) do
        {opts, [], []} ->
          {locate_snapshots(), opts}
        {opts, snapshots, []} ->
          {snapshots, opts}
        {_, _, [{opt, val}|_]} ->
          valstr = if val do "=#{val}" end
          Mix.raise "Invalid option: #{opt}#{valstr}"
      end
      |> normalize_options()

    case snapshots do
      [] -> Mix.raise "No snapshots found. Pass - to read from stdin"
      [snapshot] -> pretty_print(snapshot)
      [first|rest] ->
        last = List.last(rest)
        compare(first, last, Map.get(options, :format, :ratio))
    end
  end

  defp locate_snapshots() do
    dir = "bench/snapshots"
    case File.ls(dir) do
      {:error, _} -> []
      {:ok, files} ->
        case files |> Enum.sort |> Enum.reverse do
          [a,b|_] ->  [b,a]
          other -> other
        end
    end
    |> Enum.map(&Path.join(dir, &1))
  end

  defp normalize_options({snapshots, options}) do
    options =
      Enum.reduce(options, %{}, fn
        {:format, fmt}, acc -> Map.put(acc, :format, parse_pretty_format(fmt))
      end)
    {snapshots, options}
  end

  defp parse_pretty_format("ratio"), do: :ratio
  defp parse_pretty_format("percent"), do: :percent
  defp parse_pretty_format(other), do: Mix.raise "Undefined pretty format: #{other}"

  defp pretty_print("-") do
    Util.read_all_input() |> Snapshot.parse |> Snapshot.pretty_print
  end

  defp pretty_print(path) do
    IO.puts "#{path}\n"
    path |> File.read! |> Snapshot.parse |> Snapshot.pretty_print
  end

  defp compare(path1, path2, format) do
    IO.puts "#{path1} vs\n#{path2}\n"

    snapshot1 = File.read!(path1) |> Snapshot.parse()
    snapshot2 = File.read!(path2) |> Snapshot.parse()
    {diffs, leftover} = Snapshot.compare(snapshot1, snapshot2, format)

    max_len = Enum.reduce(diffs, 0, fn {name, _}, len -> max(len, String.length(name)) end)

    diffs
    |> Enum.sort(fn {_, diff1}, {_, diff2} -> diff1 < diff2 end)
    |> Enum.each(fn {name, diff} ->
      :io.format('~*.s ', [-max_len-1, name<>":"])
      color = choose_color(diff, format)
      if format == :percent do
        diff = Snapshot.format_percent(diff)
      end
      colordiff = IO.ANSI.format color ++ ["#{diff}"]
      IO.puts colordiff
    end)

    unless leftover == [] do
      # FIXME: when more than 2 snapshots are given, this wording may be imprecise
      IO.puts "\nThese tests appeared only in one of the snapshots:"
      Enum.each(leftover, fn x -> IO.write "  "; IO.puts x end)
    end
  end

  defp choose_color(diff, :ratio) do
    cond do
      diff < 1.0 -> [:green]
      diff > 1.0 -> [:red]
      true -> []
    end
  end

  defp choose_color(diff, :percent) do
    cond do
      diff < 0 -> [:green]
      diff > 0 -> [:red]
      true -> []
    end
  end
end

defmodule Mix.Tasks.Bench.Cmp do
  use Mix.Task

  @shortdoc "Compare benchmark snapshots"

  @moduledoc """
  ## Usage

      mix bench.cmp [options] <snapshot>...

  A snapshot is the output of a single run of `mix bench` without the
  `--pretty` flag.

  When given one snapshot, `mix bench.cmp` will pretty-print the results.

  When given two or more snapshots, it will pretty-print the comparison between
  the first and the last one.

  ## Options

      -f=<fmt>, --format=<fmt>
          Which format to use for the deltas when pretty-printing.

          One of: ratio, percent.
  """

  alias Benchfella.Snapshot
  alias Benchfella.CLI.Util

  def run(args) do
    switches = [format: :string]
    aliases = [f: :format]
    {snapshots, options} =
      case OptionParser.parse(args, strict: switches, aliases: aliases) do
        {opts, [], []} ->
          {["-"], opts}
        {opts, snapshots, []} ->
          {snapshots, opts}
        {_, _, [{opt, val}|_]} ->
          valstr = if val do "=#{val}" end
          Mix.raise "Invalid option: #{opt}#{valstr}"
      end
      |> normalize_options()

    case snapshots do
      [snapshot] -> pretty_print(snapshot)
      [first|rest] ->
        last = List.last(rest)
        compare(first, last, Map.get(options, :format, :ratio))
    end
  end

  defp normalize_options({snapshots, options}) do
    options =
      Enum.reduce(options, %{}, fn
        {:format, fmt}, acc -> Map.put(acc, :format, parse_pretty_format(fmt))
      end)
    {snapshots, options}
  end

  defp parse_pretty_format("ratio"), do: :ratio
  defp parse_pretty_format("percent"), do: :percent
  defp parse_pretty_format(other), do: Mix.raise "Undefined pretty format: #{other}"

  defp pretty_print("-") do
    Util.read_all_input() |> Snapshot.parse |> Snapshot.pretty_print
  end

  defp pretty_print(path) do
    path |> File.read! |> Snapshot.parse |> Snapshot.pretty_print
  end

  defp compare(path1, path2, format) do
    snapshot1 = File.read!(path1) |> Snapshot.parse()
    snapshot2 = File.read!(path2) |> Snapshot.parse()
    {diffs, leftover} = Snapshot.compare(snapshot1, snapshot2, format)

    max_len = Enum.reduce(diffs, 0, fn {name, _}, len -> max(len, String.length(name)) end)

    diffs
    |> Enum.sort(fn {_, diff1}, {_, diff2} -> diff1 < diff2 end)
    |> Enum.each(fn {name, diff} ->
      :io.format('~*.s ', [-max_len-1, name<>":"])
      color = choose_color(diff, format)
      if format == :percent do
        diff = Snapshot.format_percent(diff)
      end
      colordiff = IO.ANSI.format color ++ ["#{diff}"]
      IO.puts colordiff
    end)

    unless leftover == [] do
      # FIXME: when more than 2 snapshots are given, this wording may be imprecise
      IO.puts "\nThese tests appeared only in one of the snapshots:"
      Enum.each(leftover, fn x -> IO.write "  "; IO.puts x end)
    end
  end

  defp choose_color(diff, :ratio) do
    cond do
      diff < 1.0 -> [:green]
      diff > 1.0 -> [:red]
      true -> []
    end
  end

  defp choose_color(diff, :percent) do
    cond do
      diff < 0 -> [:green]
      diff > 0 -> [:red]
      true -> []
    end
  end
end