fix uniquness dump on pid change

fix uniquness dump on pid change

defmodule Pan.Persona do
  use Pan.Web, :model
  alias Pan.Like
  alias Pan.Repo
  alias Pan.Follow
  alias Pan.Persona

  schema "personas" do
    field :pid, :string
    field :name, :string
    field :uri, :string
    field :email, :string
    field :description, :string
    field :long_description, :string
    field :image_url, :string
    field :image_title, :string

    belongs_to :redirect, Pan.Persona
    has_many :engagements, Pan.Engagement
    has_many :gigs, Pan.Gig

    many_to_many :delegates, Pan.Persona,
                             join_through: "delegations",
                             join_keys: [persona_id: :id, delegate_id: :id]
    timestamps()
  end

  def changeset(struct, params \\ %{}) do
    struct
    |> cast(params, [:pid, :name, :uri, :email, :description, :image_url, :image_title,
                     :redirect_id, :long_description])
    |> validate_required([:pid, :name, :uri])
    |> unique_constraint(:pid)
  end

  def user_changeset(struct, params \\ %{}) do
    struct
    |> cast(params, [:name, :uri])
    |> validate_required([:pid, :name, :uri])
    |> unique_constraint(:pid)
  end

  def like(persona_id, current_user_id) do
    case Repo.get_by(Like, enjoyer_id: current_user_id,
                           persona_id: persona_id) do
      nil ->
        %Like{enjoyer_id: current_user_id, persona_id: persona_id}
        |> Repo.insert
      like ->
        Repo.delete!(like)
    end
  end

  def follow(persona_id, current_user_id) do
    case Repo.get_by(Follow, follower_id: current_user_id,
                             persona_id: persona_id) do
      nil ->
        %Follow{follower_id: current_user_id, persona_id: persona_id}
        |> Repo.insert
      like ->
        Repo.delete!(like)
    end
  end

  def follower_mailboxes(user_id) do
    Repo.all(from l in Follow, where: l.user_id == ^user_id,
                               select: [:follower_id])
    |> Enum.map(fn(user) ->  "mailboxes:" <> Integer.to_string(user.follower_id) end)
  end

  def likes(id) do
    from(l in Like, where: l.persona_id == ^id)
    |> Repo.aggregate(:count, :id)
    |> Integer.to_string
  end

  def follows(id) do
    from(f in Follow, where: f.persona_id == ^id)
    |> Repo.aggregate(:count, :id)
    |> Integer.to_string
  end

  def update_search_index(id) do
    persona = Repo.get(Persona, id)

    if persona.redirect_id do
      delete("http://localhost:9200/panoptikum_" <> Application.get_env(:pan, :environment) <>
             "/personas/" <> Integer.to_string(id))
      # fallback, in case Tirexs brakes again:
      # url = "http://localhost:9200/panoptikum_" <> Application.get_env(:pan, :environment) <> "/personas/" <> Integer.to_string(id)
      # :httpc.request(:delete, {to_charlist(url), [],'application/json', ""}, [], [])
    else
      put("/panoptikum_" <> Application.get_env(:pan, :environment) <> "/personas/" <> Integer.to_string(id),
          [name:             persona.name,
           pid:              persona.pid,
           uri:              persona.uri,
           description:      persona.description,
           long_description: persona.long_description,
           image_url:        persona.image_url,
           image_title:      persona.image_title,
           url:              persona_frontend_path(Pan.Endpoint, :show, id)])
    end
  end

  def update_search_all() do
    persona_ids = Repo.all(from p in Persona, select: p.id)

    for persona_id <- persona_ids do
      update_search_index(persona_id)
    end
  end

  def delete_search_index_orphans() do
    persona_ids = (from c in Persona, select: c.id)
                  |> Repo.all()

    max_persona_id = Enum.max(persona_ids)
    all_ids = Range.new(1, max_persona_id) |> Enum.to_list()
    deleted_ids = all_ids -- persona_ids

    for deleted_id <- deleted_ids do
      delete("http://127.0.0.1:9200/panoptikum_" <> Application.get_env(:pan, :environment) <>
             "/personas/" <> Integer.to_string(deleted_id))
    end
  end
end

defmodule Pan.Persona do
  use Pan.Web, :model
  alias Pan.Like
  alias Pan.Repo
  alias Pan.Follow
  alias Pan.Persona

  schema "personas" do
    field :pid, :string
    field :name, :string
    field :uri, :string
    field :email, :string
    field :description, :string
    field :long_description, :string
    field :image_url, :string
    field :image_title, :string

    belongs_to :redirect, Pan.Persona
    has_many :engagements, Pan.Engagement
    has_many :gigs, Pan.Gig

    many_to_many :delegates, Pan.Persona,
                             join_through: "delegations",
                             join_keys: [persona_id: :id, delegate_id: :id]
    timestamps()
  end

  def changeset(struct, params \\ %{}) do
    struct
    |> cast(params, [:pid, :name, :uri, :email, :description, :image_url, :image_title,
                     :redirect_id, :long_description])
    |> validate_required([:pid, :name, :uri])
  end

  def user_changeset(struct, params \\ %{}) do
    struct
    |> cast(params, [:name, :uri])
    |> validate_required([:pid, :name, :uri])
  end

  def like(persona_id, current_user_id) do
    case Repo.get_by(Like, enjoyer_id: current_user_id,
                           persona_id: persona_id) do
      nil ->
        %Like{enjoyer_id: current_user_id, persona_id: persona_id}
        |> Repo.insert
      like ->
        Repo.delete!(like)
    end
  end

  def follow(persona_id, current_user_id) do
    case Repo.get_by(Follow, follower_id: current_user_id,
                             persona_id: persona_id) do
      nil ->
        %Follow{follower_id: current_user_id, persona_id: persona_id}
        |> Repo.insert
      like ->
        Repo.delete!(like)
    end
  end

  def follower_mailboxes(user_id) do
    Repo.all(from l in Follow, where: l.user_id == ^user_id,
                               select: [:follower_id])
    |> Enum.map(fn(user) ->  "mailboxes:" <> Integer.to_string(user.follower_id) end)
  end

  def likes(id) do
    from(l in Like, where: l.persona_id == ^id)
    |> Repo.aggregate(:count, :id)
    |> Integer.to_string
  end

  def follows(id) do
    from(f in Follow, where: f.persona_id == ^id)
    |> Repo.aggregate(:count, :id)
    |> Integer.to_string
  end

  def update_search_index(id) do
    persona = Repo.get(Persona, id)

    if persona.redirect_id do
      delete("http://localhost:9200/panoptikum_" <> Application.get_env(:pan, :environment) <>
             "/personas/" <> Integer.to_string(id))
      # fallback, in case Tirexs brakes again:
      # url = "http://localhost:9200/panoptikum_" <> Application.get_env(:pan, :environment) <> "/personas/" <> Integer.to_string(id)
      # :httpc.request(:delete, {to_charlist(url), [],'application/json', ""}, [], [])
    else
      put("/panoptikum_" <> Application.get_env(:pan, :environment) <> "/personas/" <> Integer.to_string(id),
          [name:             persona.name,
           pid:              persona.pid,
           uri:              persona.uri,
           description:      persona.description,
           long_description: persona.long_description,
           image_url:        persona.image_url,
           image_title:      persona.image_title,
           url:              persona_frontend_path(Pan.Endpoint, :show, id)])
    end
  end

  def update_search_all() do
    persona_ids = Repo.all(from p in Persona, select: p.id)

    for persona_id <- persona_ids do
      update_search_index(persona_id)
    end
  end

  def delete_search_index_orphans() do
    persona_ids = (from c in Persona, select: c.id)
                  |> Repo.all()

    max_persona_id = Enum.max(persona_ids)
    all_ids = Range.new(1, max_persona_id) |> Enum.to_list()
    deleted_ids = all_ids -- persona_ids

    for deleted_id <- deleted_ids do
      delete("http://127.0.0.1:9200/panoptikum_" <> Application.get_env(:pan, :environment) <>
             "/personas/" <> Integer.to_string(deleted_id))
    end
  end
end


correct wording

correct wording

defmodule Code do
  @moduledoc """
  Utilities for managing code compilation, code evaluation and code loading.

  This module complements [Erlang's code module](http://www.erlang.org/doc/man/code.html)
  to add behaviour which is specific to Elixir. Almost all of the functions in this module
  have global side effects on the behaviour of Elixir.
  """

  @doc """
  Lists all loaded files.

  ## Examples

      Code.require_file("../eex/test/eex_test.exs")
      List.first(Code.loaded_files) =~ "eex_test.exs" #=> true

  """
  def loaded_files do
    :elixir_code_server.call :loaded
  end

  @doc """
  Removes files from the loaded files list.

  The modules defined in the file are not removed;
  calling this function only removes them from the list,
  allowing them to be required again.

  ## Examples

      # Load EEx test code, unload file, check for functions still available
      Code.load_file("../eex/test/eex_test.exs")
      Code.unload_files(Code.loaded_files)
      function_exported?(EExTest.Compiled, :before_compile, 0) #=> true

  """
  def unload_files(files) do
    :elixir_code_server.cast {:unload_files, files}
  end

  @doc """
  Appends a path to the end of the Erlang VM code path list.

  This is the list of directories the Erlang VM uses for
  finding module code.

  The path is expanded with `Path.expand/1` before being appended.
  If this path does not exist, an error is returned.

  ## Examples

      Code.append_path(".") #=> true

      Code.append_path("/does_not_exist") #=> {:error, :bad_directory}

  """
  def append_path(path) do
    :code.add_pathz(to_char_list(Path.expand path))
  end

  @doc """
  Prepends a path to the begining of the Erlang VM code path list.

  This is the list of directories the Erlang VM uses for finding
  module code.

  The path is expanded with `Path.expand/1` before being prepended.
  If this path does not exist, an error is returned.

  ## Examples

      Code.prepend_path(".") #=> true

      Code.prepend_path("/does_not_exist") #=> {:error, :bad_directory}

  """
  def prepend_path(path) do
    :code.add_patha(to_char_list(Path.expand path))
  end

  @doc """
  Deletes a path from the Erlang VM code path list. This is the list of
  directories the Erlang VM uses for finding module code.

  The path is expanded with `Path.expand/1` before being deleted. If the
  path does not exist it returns `false`.

  ## Examples

      Code.prepend_path(".")
      Code.delete_path(".") #=> true

      Code.delete_path("/does_not_exist") #=> false

  """
  def delete_path(path) do
    :code.del_path(to_char_list(Path.expand path))
  end

  @doc """
  Evaluates the contents given by `string`.

  The `binding` argument is a keyword list of variable bindings.
  The `opts` argument is a keyword list of environment options.

  Those options can be:

    * `:file` - the file to be considered in the evaluation
    * `:line` - the line on which the script starts

  Additionally, the following scope values can be configured:

    * `:aliases` - a list of tuples with the alias and its target

    * `:requires` - a list of modules required

    * `:functions` - a list of tuples where the first element is a module
      and the second a list of imported function names and arity; the list
      of function names and arity must be sorted

    * `:macros` - a list of tuples where the first element is a module
      and the second a list of imported macro names and arity; the list
      of function names and arity must be sorted

  Notice that setting any of the values above overrides Elixir's default
  values. For example, setting `:requires` to `[]`, will no longer
  automatically require the `Kernel` module; in the same way setting
  `:macros` will no longer auto-import `Kernel` macros like `if`, `case`,
  etc.

  Returns a tuple of the form `{value, binding}`,
  where `value` is the value returned from evaluating `string`.
  If an error occurs while evaluating `string` an exception will be raised.

  `binding` is a keyword list with the value of all variable bindings
  after evaluating `string`. The binding key is usually an atom, but it
  may be a tuple for variables defined in a different context.

  ## Examples

      iex> Code.eval_string("a + b", [a: 1, b: 2], file: __ENV__.file, line: __ENV__.line)
      {3, [a: 1, b: 2]}

      iex> Code.eval_string("c = a + b", [a: 1, b: 2], __ENV__)
      {3, [a: 1, b: 2, c: 3]}

      iex> Code.eval_string("a = a + b", [a: 1, b: 2])
      {3, [a: 3, b: 2]}

  For convenience, you can pass `__ENV__` as the `opts` argument and
  all imports, requires and aliases defined in the current environment
  will be automatically carried over:

      iex> Code.eval_string("a + b", [a: 1, b: 2], __ENV__)
      {3, [a: 1, b: 2]}

  """
  def eval_string(string, binding \\ [], opts \\ [])

  def eval_string(string, binding, %Macro.Env{} = env) do
    {value, binding, _env, _scope} = :elixir.eval to_char_list(string), binding, Map.to_list(env)
    {value, binding}
  end

  def eval_string(string, binding, opts) when is_list(opts) do
    validate_eval_opts(opts)
    {value, binding, _env, _scope} = :elixir.eval to_char_list(string), binding, opts
    {value, binding}
  end

  @doc """
  Evaluates the quoted contents.

  See `eval_string/3` for a description of arguments and return values.

  ## Examples

      iex> contents = quote(do: var!(a) + var!(b))
      iex> Code.eval_quoted(contents, [a: 1, b: 2], file: __ENV__.file, line: __ENV__.line)
      {3, [a: 1, b: 2]}

  For convenience, you can pass `__ENV__` as the `opts` argument and
  all options will be automatically extracted from the current environment:

      iex> contents = quote(do: var!(a) + var!(b))
      iex> Code.eval_quoted(contents, [a: 1, b: 2], __ENV__)
      {3, [a: 1, b: 2]}

  """
  def eval_quoted(quoted, binding \\ [], opts \\ [])

  def eval_quoted(quoted, binding, %Macro.Env{} = env) do
    {value, binding, _env, _scope} = :elixir.eval_quoted quoted, binding, Map.to_list(env)
    {value, binding}
  end

  def eval_quoted(quoted, binding, opts) when is_list(opts) do
    validate_eval_opts(opts)
    {value, binding, _env, _scope} = :elixir.eval_quoted quoted, binding, opts
    {value, binding}
  end

  defp validate_eval_opts(opts) do
    if f = opts[:functions], do: validate_imports(:functions, f)
    if m = opts[:macros],    do: validate_imports(:macros, m)
    if a = opts[:aliases],   do: validate_aliases(:aliases, a)
    if r = opts[:requires],  do: validate_requires(:requires, r)
  end

  defp validate_requires(kind, requires) do
    valid = is_list(requires) and Enum.all?(requires, &is_atom(&1))

    unless valid do
      raise ArgumentError, "expected :#{kind} option given to eval in the format: [module]"
    end
  end

  defp validate_aliases(kind, aliases) do
    valid = is_list(aliases) and Enum.all?(aliases, fn {k, v} ->
      is_atom(k) and is_atom(v)
    end)

    unless valid do
      raise ArgumentError, "expected :#{kind} option given to eval in the format: [{module, module}]"
    end
  end

  defp validate_imports(kind, imports) do
    valid = is_list(imports) and Enum.all?(imports, fn {k, v} ->
      is_atom(k) and is_list(v) and Enum.all?(v, fn {name, arity} ->
        is_atom(name) and is_integer(arity)
      end)
    end)

    unless valid do
      raise ArgumentError, "expected :#{kind} option given to eval in the format: [{module, [{name, arity}]}]"
    end
  end

  @doc """
  Converts the given string to its quoted form.

  Returns `{:ok, quoted_form}`
  if it succeeds, `{:error, {line, error, token}}` otherwise.

  ## Options

    * `:file` - the filename to be used in stacktraces
      and the file reported in the `__ENV__` variable

    * `:line` - the line reported in the `__ENV__` variable

    * `:existing_atoms_only` - when `true`, raises an error
      when non-existing atoms are found by the tokenizer

  ## Macro.to_string/2

  The opposite of converting a string to its quoted form is
  `Macro.to_string/2`, which converts a quoted form to a string/binary
  representation.
  """
  def string_to_quoted(string, opts \\ []) when is_list(opts) do
    file = Keyword.get opts, :file, "nofile"
    line = Keyword.get opts, :line, 1
    :elixir.string_to_quoted(to_char_list(string), line, file, opts)
  end

  @doc """
  Converts the given string to its quoted form.

  It returns the ast if it succeeds,
  raises an exception otherwise. The exception is a `TokenMissingError`
  in case a token is missing (usually because the expression is incomplete),
  `SyntaxError` otherwise.

  Check `string_to_quoted/2` for options information.
  """
  def string_to_quoted!(string, opts \\ []) when is_list(opts) do
    file = Keyword.get opts, :file, "nofile"
    line = Keyword.get opts, :line, 1
    :elixir.string_to_quoted!(to_char_list(string), line, file, opts)
  end

  @doc """
  Evals the given file.

  Accepts `relative_to` as an argument to tell where the file is located.

  While `load_file` loads a file and returns the loaded modules and their
  byte code, `eval_file` simply evaluates the file contents and returns the
  evaluation result and its bindings.
  """
  def eval_file(file, relative_to \\ nil) do
    file = find_file(file, relative_to)
    eval_string File.read!(file), [], [file: file, line: 1]
  end

  @doc """
  Loads the given file.

  Accepts `relative_to` as an argument to tell where the file is located.
  If the file was already required/loaded, loads it again.

  It returns a list of tuples `{ModuleName, <<byte_code>>}`, one tuple for
  each module defined in the file.

  Notice that if `load_file` is invoked by different processes concurrently,
  the target file will be loaded concurrently many times. Check `require_file/2`
  if you don't want a file to be loaded concurrently.

  ## Examples

      Code.load_file("eex_test.exs","../eex/test") |> List.first
      #=> {EExTest.Compiled, <<70, 79, 82, 49, ...>>}

  """
  def load_file(file, relative_to \\ nil) when is_binary(file) do
    file = find_file(file, relative_to)
    :elixir_code_server.call {:acquire, file}
    loaded = :elixir_compiler.file file
    :elixir_code_server.cast {:loaded, file}
    loaded
  end

  @doc """
  Requires the given `file`.

  Accepts `relative_to` as an argument to tell where the file is located.
  The return value is the same as that of `load_file/2`. If the file was already
  required/loaded, doesn't do anything and returns `nil`.

  Notice that if `require_file` is invoked by different processes concurrently,
  the first process to invoke `require_file` acquires a lock and the remaining
  ones will block until the file is available. I.e. if `require_file` is called
  N times with a given file, it will be loaded only once. The first process to
  call `require_file` will get the list of loaded modules, others will get `nil`.

  Check `load_file/2` if you want a file to be loaded multiple times. See also
  `unload_files/1`

  ## Examples

  If the code is already loaded, it returns `nil`:

      Code.require_file("eex_test.exs","../eex/test") #=> nil

  If the code is not loaded yet, it returns the same as `load_file/2`:

      Code.require_file("eex_test.exs","../eex/test") |> List.first
      #=> {EExTest.Compiled, <<70, 79, 82, 49, ...>>}

  """
  def require_file(file, relative_to \\ nil) when is_binary(file) do
    file = find_file(file, relative_to)

    case :elixir_code_server.call({:acquire, file}) do
      :loaded  ->
        nil
      {:queued, ref}  ->
        receive do {:elixir_code_server, ^ref, :loaded} -> nil end
      :proceed ->
        loaded = :elixir_compiler.file file
        :elixir_code_server.cast {:loaded, file}
        loaded
    end
  end

  @doc """
  Gets the compilation options from the code server.

  Check `compiler_options/1` for more information.

  ## Examples

      Code.compiler_options
      #=> [debug_info: true, docs: true, warnings_as_errors: false]

  """
  def compiler_options do
    :elixir_config.get :compiler_options
  end

  @doc """
  Returns a list with the available compiler options.

  See `Code.compiler_options/1` for more info.

  ## Examples

      Code.available_compiler_options
      #=> [:docs, :debug_info, :ignore_module_conflict, :warnings_as_errors]

  """
  def available_compiler_options do
    [:docs, :debug_info, :ignore_module_conflict, :warnings_as_errors]
  end

  @doc """
  Sets compilation options.

  These options are global since they are stored by Elixir's Code Server.

  Available options are:

    * `:docs` - when `true`, retain documentation in the compiled module,
      `true` by default

    * `:debug_info` - when `true`, retain debug information in the compiled
      module; this allows a developer to reconstruct the original source
      code, `false` by default

    * `:ignore_module_conflict` - when `true`, override modules that were
      already defined without raising errors, `false` by default

    * `:warnings_as_errors` - cause compilation to fail when warnings are
      generated

  It returns the new list of compiler options.

  ## Examples

      Code.compiler_options(debug_info: true)
      #=> [debug_info: true, docs: true, warnings_as_errors: false]

  """
  def compiler_options(opts) do
    {opts, bad} = Keyword.split(opts, available_compiler_options)
    if bad != [] do
      bad = bad |> Keyword.keys |> Enum.join(", ")
      raise ArgumentError, message: "unknown compiler options: #{bad}"
    end
    update = &:orddict.merge(fn(_, _, value) -> value end, &1, opts)
    :elixir_config.update :compiler_options, update
  end

  @doc """
  Compiles the given string.

  Returns a list of tuples where the first element is the module name
  and the second one is its byte code (as a binary).

  For compiling many files at once, check `Kernel.ParallelCompiler.files/2`.
  """
  def compile_string(string, file \\ "nofile") when is_binary(file) do
    :elixir_compiler.string to_char_list(string), file
  end

  @doc """
  Compiles the quoted expression.

  Returns a list of tuples where the first element is the module name and
  the second one is its byte code (as a binary).
  """
  def compile_quoted(quoted, file \\ "nofile") when is_binary(file) do
    :elixir_compiler.quoted quoted, file
  end

  @doc """
  Ensures the given module is loaded.

  If the module is already loaded, this works as no-op. If the module
  was not yet loaded, it tries to load it.

  If it succeeds loading the module, it returns `{:module, module}`.
  If not, returns `{:error, reason}` with the error reason.

  ## Code loading on the Erlang VM

  Erlang has two modes to load code: interactive and embedded.

  By default, the Erlang VM runs in interactive mode, where modules
  are loaded as needed. In embedded mode the opposite happens, as all
  modules need to be loaded upfront or explicitly.

  Therefore, this function is used to check if a module is loaded
  before using it and allows one to react accordingly. For example, the `URI`
  module uses this function to check if a specific parser exists for a given
  URI scheme.

  ## Code.ensure_compiled/1

  Elixir also contains an `ensure_compiled/1` function that is a
  superset of `ensure_loaded/1`.

  Since Elixir's compilation happens in parallel, in some situations
  you may need to use a module that was not yet compiled, therefore
  it can't even be loaded.

  `ensure_compiled/1` halts the current process until the
  module we are depending on is available.

  In most cases, `ensure_loaded/1` is enough. `ensure_compiled/1`
  must be used in rare cases, usually involving macros that need to
  invoke a module for callback information.

  ## Examples

      iex> Code.ensure_loaded(Atom)
      {:module, Atom}

      iex> Code.ensure_loaded(DoesNotExist)
      {:error, :nofile}

  """
  def ensure_loaded(module) when is_atom(module) do
    :code.ensure_loaded(module)
  end

  @doc """
  Ensures the given module is loaded.

  Similar to `ensure_loaded/1`, but returns `true` if the module
  is already loaded or was successfully loaded. Returns `false`
  otherwise.

  ## Examples

      iex> Code.ensure_loaded?(Atom)
      true

  """
  def ensure_loaded?(module) do
    match?({:module, ^module}, ensure_loaded(module))
  end

  @doc """
  Ensures the given module is compiled and loaded.

  If the module is already loaded, it works as no-op. If the module was
  not loaded yet, it checks if it needs to be compiled first and then
  tries to load it.

  If it succeeds loading the module, it returns `{:module, module}`.
  If not, returns `{:error, reason}` with the error reason.

  Check `ensure_loaded/1` for more information on module loading
  and when to use `ensure_loaded/1` or `ensure_compiled/1`.
  """
  def ensure_compiled(module) when is_atom(module) do
    case :code.ensure_loaded(module) do
      {:error, :nofile} = error ->
        case :erlang.get(:elixir_ensure_compiled) do
          :undefined -> error
          _ ->
            try do
              module.__info__(:module)
              {:module, module}
            rescue
              UndefinedFunctionError -> error
            end
        end
      other -> other
    end
  end

  @doc """
  Ensures the given module is compiled and loaded.

  Similar to `ensure_compiled/1`, but returns `true` if the module
  is already loaded or was successfully loaded and compiled.
  Returns `false` otherwise.
  """
  def ensure_compiled?(module) do
    match?({:module, ^module}, ensure_compiled(module))
  end

  @doc ~S"""
  Returns the docs for the given module.

  When given a module name, it finds its BEAM code and reads the docs from it.

  When given a path to a .beam file, it will load the docs directly from that
  file.

  The return value depends on the `kind` value:

    * `:docs` - list of all docstrings attached to functions and macros
      using the `@doc` attribute

    * `:moduledoc` - tuple `{<line>, <doc>}` where `line` is the line on
      which module definition starts and `doc` is the string
      attached to the module using the `@moduledoc` attribute

    * `:callback_docs` - list of all docstrings attached to
      `@callbacks` using the `@doc` attribute

    * `:type_docs` - list of all docstrings attached to
      `@type` callbacks using the `@typedoc` attribute

    * `:all` - a keyword list with both `:docs` and `:moduledoc`

  ## Examples

      # Get the documentation for the first function listed
      iex> [fun|_] = Code.get_docs(Atom, :docs) |> Enum.sort()
      iex> {{_function, _arity}, _line, _kind, _signature, text} = fun
      iex> String.split(text, "\n") |> Enum.at(0)
      "Converts an atom to a char list."

  """
  def get_docs(module, kind) when is_atom(module) do
    case :code.get_object_code(module) do
      {_module, bin, _beam_path} ->
        do_get_docs(bin, kind)

      :error -> nil
    end
  end

  def get_docs(binpath, kind) when is_binary(binpath) do
    do_get_docs(String.to_char_list(binpath), kind)
  end

  @docs_chunk 'ExDc'

  defp do_get_docs(bin_or_path, kind) do
    case :beam_lib.chunks(bin_or_path, [@docs_chunk]) do
      {:ok, {_module, [{@docs_chunk, bin}]}} ->
        lookup_docs(:erlang.binary_to_term(bin), kind)

      {:error, :beam_lib, {:missing_chunk, _, @docs_chunk}} -> nil
    end
  end

  defp lookup_docs({:elixir_docs_v1, docs}, kind),
    do: do_lookup_docs(docs, kind)

  # unsupported chunk version
  defp lookup_docs(_, _), do: nil

  @doc_sections [:docs, :moduledoc, :callback_docs, :type_docs]

  defp do_lookup_docs(docs, :all), do: docs
  defp do_lookup_docs(docs, kind) when kind in @doc_sections,
    do: Keyword.get(docs, kind)

  ## Helpers

  # Finds the file given the relative_to path.
  #
  # If the file is found, returns its path in binary, fails otherwise.
  defp find_file(file, relative_to) do
    file = if relative_to do
      Path.expand(file, relative_to)
    else
      Path.expand(file)
    end

    if File.regular?(file) do
      file
    else
      raise Code.LoadError, file: file
    end
  end
end

defmodule Code do
  @moduledoc """
  Utilities for managing code compilation, code evaluation and code loading.

  This module complements [Erlang's code module](http://www.erlang.org/doc/man/code.html)
  to add behaviour which is specific to Elixir. Almost all of the functions in this module
  have global side effects on the behaviour of Elixir.
  """

  @doc """
  Lists all loaded files.

  ## Examples

      Code.require_file("../eex/test/eex_test.exs")
      List.first(Code.loaded_files) =~ "eex_test.exs" #=> true

  """
  def loaded_files do
    :elixir_code_server.call :loaded
  end

  @doc """
  Removes files from the loaded files list.

  The modules defined in the file are not removed;
  calling this function only removes them from the list,
  allowing them to be required again.

  ## Examples

      # Load EEx test code, unload file, check for functions still available
      Code.load_file("../eex/test/eex_test.exs")
      Code.unload_files(Code.loaded_files)
      function_exported?(EExTest.Compiled, :before_compile, 0) #=> true

  """
  def unload_files(files) do
    :elixir_code_server.cast {:unload_files, files}
  end

  @doc """
  Appends a path to the end of the Erlang VM code path list.

  This is the list of directories the Erlang VM uses for
  finding module code.

  The path is expanded with `Path.expand/1` before being appended.
  If this path does not exist, an error is returned.

  ## Examples

      Code.append_path(".") #=> true

      Code.append_path("/does_not_exist") #=> {:error, :bad_directory}

  """
  def append_path(path) do
    :code.add_pathz(to_char_list(Path.expand path))
  end

  @doc """
  Prepends a path to the begining of the Erlang VM code path list.

  This is the list of directories the Erlang VM uses for finding
  module code.

  The path is expanded with `Path.expand/1` before being prepended.
  If this path does not exist, an error is returned.

  ## Examples

      Code.prepend_path(".") #=> true

      Code.prepend_path("/does_not_exist") #=> {:error, :bad_directory}

  """
  def prepend_path(path) do
    :code.add_patha(to_char_list(Path.expand path))
  end

  @doc """
  Deletes a path from the Erlang VM code path list. This is the list of
  directories the Erlang VM uses for finding module code.

  The path is expanded with `Path.expand/1` before being deleted. If the
  path does not exist it returns `false`.

  ## Examples

      Code.prepend_path(".")
      Code.delete_path(".") #=> true

      Code.delete_path("/does_not_exist") #=> false

  """
  def delete_path(path) do
    :code.del_path(to_char_list(Path.expand path))
  end

  @doc """
  Evaluates the contents given by `string`.

  The `binding` argument is a keyword list of variable bindings.
  The `opts` argument is a keyword list of environment options.

  Those options can be:

    * `:file` - the file to be considered in the evaluation
    * `:line` - the line on which the script starts

  Additionally, the following scope values can be configured:

    * `:aliases` - a list of tuples with the alias and its target

    * `:requires` - a list of modules required

    * `:functions` - a list of tuples where the first element is a module
      and the second a list of imported function names and arity; the list
      of function names and arity must be sorted

    * `:macros` - a list of tuples where the first element is a module
      and the second a list of imported macro names and arity; the list
      of function names and arity must be sorted

  Notice that setting any of the values above overrides Elixir's default
  values. For example, setting `:requires` to `[]`, will no longer
  automatically require the `Kernel` module; in the same way setting
  `:macros` will no longer auto-import `Kernel` macros like `if`, `case`,
  etc.

  Returns a tuple of the form `{value, binding}`,
  where `value` is the value returned from evaluating `string`.
  If an error occurs while evaluating `string` an exception will be raised.

  `binding` is a keyword list with the value of all variable bindings
  after evaluating `string`. The binding key is usually an atom, but it
  may be a tuple for variables defined in a different context.

  ## Examples

      iex> Code.eval_string("a + b", [a: 1, b: 2], file: __ENV__.file, line: __ENV__.line)
      {3, [a: 1, b: 2]}

      iex> Code.eval_string("c = a + b", [a: 1, b: 2], __ENV__)
      {3, [a: 1, b: 2, c: 3]}

      iex> Code.eval_string("a = a + b", [a: 1, b: 2])
      {3, [a: 3, b: 2]}

  For convenience, you can pass `__ENV__` as the `opts` argument and
  all imports, requires and aliases defined in the current environment
  will be automatically carried over:

      iex> Code.eval_string("a + b", [a: 1, b: 2], __ENV__)
      {3, [a: 1, b: 2]}

  """
  def eval_string(string, binding \\ [], opts \\ [])

  def eval_string(string, binding, %Macro.Env{} = env) do
    {value, binding, _env, _scope} = :elixir.eval to_char_list(string), binding, Map.to_list(env)
    {value, binding}
  end

  def eval_string(string, binding, opts) when is_list(opts) do
    validate_eval_opts(opts)
    {value, binding, _env, _scope} = :elixir.eval to_char_list(string), binding, opts
    {value, binding}
  end

  @doc """
  Evaluates the quoted contents.

  See `eval_string/3` for a description of arguments and return values.

  ## Examples

      iex> contents = quote(do: var!(a) + var!(b))
      iex> Code.eval_quoted(contents, [a: 1, b: 2], file: __ENV__.file, line: __ENV__.line)
      {3, [a: 1, b: 2]}

  For convenience, you can pass `__ENV__` as the `opts` argument and
  all options will be automatically extracted from the current environment:

      iex> contents = quote(do: var!(a) + var!(b))
      iex> Code.eval_quoted(contents, [a: 1, b: 2], __ENV__)
      {3, [a: 1, b: 2]}

  """
  def eval_quoted(quoted, binding \\ [], opts \\ [])

  def eval_quoted(quoted, binding, %Macro.Env{} = env) do
    {value, binding, _env, _scope} = :elixir.eval_quoted quoted, binding, Map.to_list(env)
    {value, binding}
  end

  def eval_quoted(quoted, binding, opts) when is_list(opts) do
    validate_eval_opts(opts)
    {value, binding, _env, _scope} = :elixir.eval_quoted quoted, binding, opts
    {value, binding}
  end

  defp validate_eval_opts(opts) do
    if f = opts[:functions], do: validate_imports(:functions, f)
    if m = opts[:macros],    do: validate_imports(:macros, m)
    if a = opts[:aliases],   do: validate_aliases(:aliases, a)
    if r = opts[:requires],  do: validate_requires(:requires, r)
  end

  defp validate_requires(kind, requires) do
    valid = is_list(requires) and Enum.all?(requires, &is_atom(&1))

    unless valid do
      raise ArgumentError, "expected :#{kind} option given to eval in the format: [module]"
    end
  end

  defp validate_aliases(kind, aliases) do
    valid = is_list(aliases) and Enum.all?(aliases, fn {k, v} ->
      is_atom(k) and is_atom(v)
    end)

    unless valid do
      raise ArgumentError, "expected :#{kind} option given to eval in the format: [{module, module}]"
    end
  end

  defp validate_imports(kind, imports) do
    valid = is_list(imports) and Enum.all?(imports, fn {k, v} ->
      is_atom(k) and is_list(v) and Enum.all?(v, fn {name, arity} ->
        is_atom(name) and is_integer(arity)
      end)
    end)

    unless valid do
      raise ArgumentError, "expected :#{kind} option given to eval in the format: [{module, [{name, arity}]}]"
    end
  end

  @doc """
  Converts the given string to its quoted form.

  Returns `{:ok, quoted_form}`
  if it succeeds, `{:error, {line, error, token}}` otherwise.

  ## Options

    * `:file` - the filename to be used in stacktraces
      and the file reported in the `__ENV__` variable

    * `:line` - the line reported in the `__ENV__` variable

    * `:existing_atoms_only` - when `true`, raises an error
      when non-existing atoms are found by the tokenizer

  ## Macro.to_string/2

  The opposite of converting a string to its quoted form is
  `Macro.to_string/2`, which converts a quoted form to a string/binary
  representation.
  """
  def string_to_quoted(string, opts \\ []) when is_list(opts) do
    file = Keyword.get opts, :file, "nofile"
    line = Keyword.get opts, :line, 1
    :elixir.string_to_quoted(to_char_list(string), line, file, opts)
  end

  @doc """
  Converts the given string to its quoted form.

  It returns the ast if it succeeds,
  raises an exception otherwise. The exception is a `TokenMissingError`
  in case a token is missing (usually because the expression is incomplete),
  `SyntaxError` otherwise.

  Check `string_to_quoted/2` for options information.
  """
  def string_to_quoted!(string, opts \\ []) when is_list(opts) do
    file = Keyword.get opts, :file, "nofile"
    line = Keyword.get opts, :line, 1
    :elixir.string_to_quoted!(to_char_list(string), line, file, opts)
  end

  @doc """
  Evals the given file.

  Accepts `relative_to` as an argument to tell where the file is located.

  While `load_file` loads a file and returns the loaded modules and their
  byte code, `eval_file` simply evaluates the file contents and returns the
  evaluation result and its bindings.
  """
  def eval_file(file, relative_to \\ nil) do
    file = find_file(file, relative_to)
    eval_string File.read!(file), [], [file: file, line: 1]
  end

  @doc """
  Loads the given file.

  Accepts `relative_to` as an argument to tell where the file is located.
  If the file was already required/loaded, loads it again.

  It returns a list of tuples `{ModuleName, <<byte_code>>}`, one tuple for
  each module defined in the file.

  Notice that if `load_file` is invoked by different processes concurrently,
  the target file will be loaded concurrently many times. Check `require_file/2`
  if you don't want a file to be loaded concurrently.

  ## Examples

      Code.load_file("eex_test.exs","../eex/test") |> List.first
      #=> {EExTest.Compiled, <<70, 79, 82, 49, ...>>}

  """
  def load_file(file, relative_to \\ nil) when is_binary(file) do
    file = find_file(file, relative_to)
    :elixir_code_server.call {:acquire, file}
    loaded = :elixir_compiler.file file
    :elixir_code_server.cast {:loaded, file}
    loaded
  end

  @doc """
  Requires the given `file`.

  Accepts `relative_to` as an argument to tell where the file is located.
  The return value is the same as that of `load_file/2`. If the file was already
  required/loaded, doesn't do anything and returns `nil`.

  Notice that if `require_file` is invoked by different processes concurrently,
  the first process to invoke `require_file` acquires a lock and the remaining
  ones will block until the file is available. I.e. if `require_file` is called
  N times with a given file, it will be loaded only once. The first process to
  call `require_file` will get the list of loaded modules, others will get `nil`.

  Check `load_file/2` if you want a file to be loaded multiple times. See also
  `unload_files/1`

  ## Examples

  If the code is already loaded, it returns `nil`:

      Code.require_file("eex_test.exs","../eex/test") #=> nil

  If the code is not already loaded, it returns the same as `load_file/2`:

      Code.require_file("eex_test.exs","../eex/test") |> List.first
      #=> {EExTest.Compiled, <<70, 79, 82, 49, ...>>}

  """
  def require_file(file, relative_to \\ nil) when is_binary(file) do
    file = find_file(file, relative_to)

    case :elixir_code_server.call({:acquire, file}) do
      :loaded  ->
        nil
      {:queued, ref}  ->
        receive do {:elixir_code_server, ^ref, :loaded} -> nil end
      :proceed ->
        loaded = :elixir_compiler.file file
        :elixir_code_server.cast {:loaded, file}
        loaded
    end
  end

  @doc """
  Gets the compilation options from the code server.

  Check `compiler_options/1` for more information.

  ## Examples

      Code.compiler_options
      #=> [debug_info: true, docs: true, warnings_as_errors: false]

  """
  def compiler_options do
    :elixir_config.get :compiler_options
  end

  @doc """
  Returns a list with the available compiler options.

  See `Code.compiler_options/1` for more info.

  ## Examples

      Code.available_compiler_options
      #=> [:docs, :debug_info, :ignore_module_conflict, :warnings_as_errors]

  """
  def available_compiler_options do
    [:docs, :debug_info, :ignore_module_conflict, :warnings_as_errors]
  end

  @doc """
  Sets compilation options.

  These options are global since they are stored by Elixir's Code Server.

  Available options are:

    * `:docs` - when `true`, retain documentation in the compiled module,
      `true` by default

    * `:debug_info` - when `true`, retain debug information in the compiled
      module; this allows a developer to reconstruct the original source
      code, `false` by default

    * `:ignore_module_conflict` - when `true`, override modules that were
      already defined without raising errors, `false` by default

    * `:warnings_as_errors` - cause compilation to fail when warnings are
      generated

  It returns the new list of compiler options.

  ## Examples

      Code.compiler_options(debug_info: true)
      #=> [debug_info: true, docs: true, warnings_as_errors: false]

  """
  def compiler_options(opts) do
    {opts, bad} = Keyword.split(opts, available_compiler_options)
    if bad != [] do
      bad = bad |> Keyword.keys |> Enum.join(", ")
      raise ArgumentError, message: "unknown compiler options: #{bad}"
    end
    update = &:orddict.merge(fn(_, _, value) -> value end, &1, opts)
    :elixir_config.update :compiler_options, update
  end

  @doc """
  Compiles the given string.

  Returns a list of tuples where the first element is the module name
  and the second one is its byte code (as a binary).

  For compiling many files at once, check `Kernel.ParallelCompiler.files/2`.
  """
  def compile_string(string, file \\ "nofile") when is_binary(file) do
    :elixir_compiler.string to_char_list(string), file
  end

  @doc """
  Compiles the quoted expression.

  Returns a list of tuples where the first element is the module name and
  the second one is its byte code (as a binary).
  """
  def compile_quoted(quoted, file \\ "nofile") when is_binary(file) do
    :elixir_compiler.quoted quoted, file
  end

  @doc """
  Ensures the given module is loaded.

  If the module is already loaded, this works as no-op. If the module
  was not yet loaded, it tries to load it.

  If it succeeds loading the module, it returns `{:module, module}`.
  If not, returns `{:error, reason}` with the error reason.

  ## Code loading on the Erlang VM

  Erlang has two modes to load code: interactive and embedded.

  By default, the Erlang VM runs in interactive mode, where modules
  are loaded as needed. In embedded mode the opposite happens, as all
  modules need to be loaded upfront or explicitly.

  Therefore, this function is used to check if a module is loaded
  before using it and allows one to react accordingly. For example, the `URI`
  module uses this function to check if a specific parser exists for a given
  URI scheme.

  ## Code.ensure_compiled/1

  Elixir also contains an `ensure_compiled/1` function that is a
  superset of `ensure_loaded/1`.

  Since Elixir's compilation happens in parallel, in some situations
  you may need to use a module that was not yet compiled, therefore
  it can't even be loaded.

  `ensure_compiled/1` halts the current process until the
  module we are depending on is available.

  In most cases, `ensure_loaded/1` is enough. `ensure_compiled/1`
  must be used in rare cases, usually involving macros that need to
  invoke a module for callback information.

  ## Examples

      iex> Code.ensure_loaded(Atom)
      {:module, Atom}

      iex> Code.ensure_loaded(DoesNotExist)
      {:error, :nofile}

  """
  def ensure_loaded(module) when is_atom(module) do
    :code.ensure_loaded(module)
  end

  @doc """
  Ensures the given module is loaded.

  Similar to `ensure_loaded/1`, but returns `true` if the module
  is already loaded or was successfully loaded. Returns `false`
  otherwise.

  ## Examples

      iex> Code.ensure_loaded?(Atom)
      true

  """
  def ensure_loaded?(module) do
    match?({:module, ^module}, ensure_loaded(module))
  end

  @doc """
  Ensures the given module is compiled and loaded.

  If the module is already loaded, it works as no-op. If the module was
  not loaded yet, it checks if it needs to be compiled first and then
  tries to load it.

  If it succeeds loading the module, it returns `{:module, module}`.
  If not, returns `{:error, reason}` with the error reason.

  Check `ensure_loaded/1` for more information on module loading
  and when to use `ensure_loaded/1` or `ensure_compiled/1`.
  """
  def ensure_compiled(module) when is_atom(module) do
    case :code.ensure_loaded(module) do
      {:error, :nofile} = error ->
        case :erlang.get(:elixir_ensure_compiled) do
          :undefined -> error
          _ ->
            try do
              module.__info__(:module)
              {:module, module}
            rescue
              UndefinedFunctionError -> error
            end
        end
      other -> other
    end
  end

  @doc """
  Ensures the given module is compiled and loaded.

  Similar to `ensure_compiled/1`, but returns `true` if the module
  is already loaded or was successfully loaded and compiled.
  Returns `false` otherwise.
  """
  def ensure_compiled?(module) do
    match?({:module, ^module}, ensure_compiled(module))
  end

  @doc ~S"""
  Returns the docs for the given module.

  When given a module name, it finds its BEAM code and reads the docs from it.

  When given a path to a .beam file, it will load the docs directly from that
  file.

  The return value depends on the `kind` value:

    * `:docs` - list of all docstrings attached to functions and macros
      using the `@doc` attribute

    * `:moduledoc` - tuple `{<line>, <doc>}` where `line` is the line on
      which module definition starts and `doc` is the string
      attached to the module using the `@moduledoc` attribute

    * `:callback_docs` - list of all docstrings attached to
      `@callbacks` using the `@doc` attribute

    * `:type_docs` - list of all docstrings attached to
      `@type` callbacks using the `@typedoc` attribute

    * `:all` - a keyword list with both `:docs` and `:moduledoc`

  ## Examples

      # Get the documentation for the first function listed
      iex> [fun|_] = Code.get_docs(Atom, :docs) |> Enum.sort()
      iex> {{_function, _arity}, _line, _kind, _signature, text} = fun
      iex> String.split(text, "\n") |> Enum.at(0)
      "Converts an atom to a char list."

  """
  def get_docs(module, kind) when is_atom(module) do
    case :code.get_object_code(module) do
      {_module, bin, _beam_path} ->
        do_get_docs(bin, kind)

      :error -> nil
    end
  end

  def get_docs(binpath, kind) when is_binary(binpath) do
    do_get_docs(String.to_char_list(binpath), kind)
  end

  @docs_chunk 'ExDc'

  defp do_get_docs(bin_or_path, kind) do
    case :beam_lib.chunks(bin_or_path, [@docs_chunk]) do
      {:ok, {_module, [{@docs_chunk, bin}]}} ->
        lookup_docs(:erlang.binary_to_term(bin), kind)

      {:error, :beam_lib, {:missing_chunk, _, @docs_chunk}} -> nil
    end
  end

  defp lookup_docs({:elixir_docs_v1, docs}, kind),
    do: do_lookup_docs(docs, kind)

  # unsupported chunk version
  defp lookup_docs(_, _), do: nil

  @doc_sections [:docs, :moduledoc, :callback_docs, :type_docs]

  defp do_lookup_docs(docs, :all), do: docs
  defp do_lookup_docs(docs, kind) when kind in @doc_sections,
    do: Keyword.get(docs, kind)

  ## Helpers

  # Finds the file given the relative_to path.
  #
  # If the file is found, returns its path in binary, fails otherwise.
  defp find_file(file, relative_to) do
    file = if relative_to do
      Path.expand(file, relative_to)
    else
      Path.expand(file)
    end

    if File.regular?(file) do
      file
    else
      raise Code.LoadError, file: file
    end
  end
end


Added typespec for Port.info/1 (#8720)

Added typespec for Port.info/1 (#8720)

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

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

  ## Example

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

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

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

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

  Once everything was done, we closed the port.

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

  ## Message and function APIs

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

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

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

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

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

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

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

  ## Open mechanisms

  The port can be opened through four main mechanisms.

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

  ### spawn

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

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

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

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

  ### spawn_executable

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

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

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

  ### fd

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

  ## Zombie operating system processes

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

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

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

  Now instead of:

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

  You may invoke:

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

  """

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

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

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

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

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

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

  @doc """
  Closes the `port`.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  where:

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

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

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

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

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

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

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

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

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

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

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

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

  ## Example

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

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

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

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

  Once everything was done, we closed the port.

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

  ## Message and function APIs

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

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

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

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

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

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

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

  ## Open mechanisms

  The port can be opened through four main mechanisms.

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

  ### spawn

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

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

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

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

  ### spawn_executable

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

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

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

  ### fd

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

  ## Zombie operating system processes

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

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

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

  Now instead of:

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

  You may invoke:

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

  """

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

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

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

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

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

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

  @doc """
  Closes the `port`.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  where:

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

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

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

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

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

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

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

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

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

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


Update Port ### spawn example (#6630)

Update Port ### spawn example (#6630)

The explanation used `echo hello` so it would make sense for the example to use the same.

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

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

  ## Example

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

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

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

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

  Once everything was done, we closed the port.

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

  ## Message and function APIs

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

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

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

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

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

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

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

  ## Open mechanisms

  The port can be opened through four main mechanisms.

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

  ### spawn

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

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

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

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

  ### spawn_executable

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

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

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

  ### fd

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

  ## Zombie processes

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

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

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

  Now instead of:

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

  You may invoke:

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

  """

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

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

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

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

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

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

  @doc """
  Closes the `port`.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  where:

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

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

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

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

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

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

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

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

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

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

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

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

  ## Example

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

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

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

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

  Once everything was done, we closed the port.

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

  ## Message and function APIs

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

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

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

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

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

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

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

  ## Open mechanisms

  The port can be opened through four main mechanisms.

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

  ### spawn

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

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

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

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

  ### spawn_executable

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

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

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

  ### fd

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

  ## Zombie processes

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

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

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

  Now instead of:

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

  You may invoke:

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

  """

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

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

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

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

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

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

  @doc """
  Closes the `port`.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  where:

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

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

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

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

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

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

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

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

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

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


hist now returns the list it was given, so you can pipe with it

defmodule Expyplot.Plot do
  @moduledoc """
  This is the end-user API for pyplot.
  """

  @doc """
  Convenience method to get or set axis properties.
  """
  def axis(kwargs \\ []) do
    code = Codebuilder.build_code(funcname: "plt.axis", nonnamed: [], named: kwargs)
    Server.Commapi.add_code code
  end

  @doc """
  Histogram function. See matplotlib docs for hist:
  http://matplotlib.org/api/pyplot_api.html

  #TODO: This needs to return something - so it needs to communicate with a python process that is running as a server
  """
  def hist(x, opts \\ [bins: nil, range: nil, normed: false, weights: nil, cumulative: false, bottom: nil, histtype: :bar, align: :mid, orientation: :vertical,
                       rwidth: nil, log: false, color: nil, label: nil, stacked: false, hold: nil, data: nil], kwargs \\ []) do
    code = Codebuilder.build_code(funcname: "plt.hist", nonnamed: [x], named: opts ++ kwargs)
    Server.Commapi.add_code code
    x
  end

  @doc """
  Turn the axes grids on or off.

  Set the axes grids on or off; b is a boolean. (For MATLAB compatibility, b may also be a string, ‘on’ or ‘off’.)

  If b is None and len(kwargs)==0, toggle the grid state. If kwargs are supplied, it is assumed that you want a grid and b is thus set to True.

  which can be ‘major’ (default), ‘minor’, or ‘both’ to control whether major tick grids, minor tick grids, or both are affected.

  axis can be ‘both’ (default), ‘x’, or ‘y’ to control which set of gridlines are drawn.
  """
  def grid(opts \\ [b: nil, which: :major, axis: :both], kwargs \\ []) do
    code = Codebuilder.build_code(funcname: "plt.grid", nonnamed: [], named: opts ++ kwargs)
    Server.Commapi.add_code code
  end

  @doc """
  Set a title of the current axes.

  Set one of hte three available axes titles. The available titles are positioned above the aces in the center, flush with the left edge, and flush
  with the right edge.
  """
  def title(s, kwargs \\ []) do
    code = Codebuilder.build_code(funcname: "plt.title", nonnamed: [s], named: kwargs)
    Server.Commapi.add_code code
  end

  @doc """
  Set the x axis label of the current axis.
  """
  def xlabel(s, kwargs \\ []) do
    code = Codebuilder.build_code(funcname: "plt.xlabel", nonnamed: [s], named: kwargs)
    Server.Commapi.add_code code
  end

  @doc """
  Set the y axis label of the current axis.
  """
  def ylabel(s, kwargs \\ []) do
    code = Codebuilder.build_code(funcname: "plt.ylabel", nonnamed: [s], named: kwargs)
    Server.Commapi.add_code code
  end

  def show do
    Server.Commapi.get_code() |> IO.inspect
    Server.Commapi.execute()
  end
end

defmodule Expyplot.Plot do
  @moduledoc """
  This is the end-user API for pyplot.
  """

  @doc """
  Convenience method to get or set axis properties.
  """
  def axis(kwargs \\ []) do
    code = Codebuilder.build_code(funcname: "plt.axis", nonnamed: [], named: kwargs)
    Server.Commapi.add_code code
  end

  @doc """
  Histogram function. See matplotlib docs for hist:
  http://matplotlib.org/api/pyplot_api.html

  #TODO: This needs to return something - so it needs to communicate with a python process that is running as a server

  ## Examples

      iex> Expyplot.Plot.hist([50, 100], normed: 1, facecolor: :green, alpha: 0.75)
      :ok

      iex> Expyplot.Plot.hist([20])
      :ok

      iex> Expyplot.Plot.hist([5, 10, 15], log: true)
      :ok

      iex> Expyplot.Plot.hist([23, 45], data: %{money: 5, people: [24, "money"]})
      :ok
  """
  def hist(x, opts \\ [bins: nil, range: nil, normed: false, weights: nil, cumulative: false, bottom: nil, histtype: :bar, align: :mid, orientation: :vertical,
                       rwidth: nil, log: false, color: nil, label: nil, stacked: false, hold: nil, data: nil], kwargs \\ []) do
    code = Codebuilder.build_code(funcname: "plt.hist", nonnamed: [x], named: opts ++ kwargs)
    Server.Commapi.add_code code
    :ok
  end

  @doc """
  Turn the axes grids on or off.

  Set the axes grids on or off; b is a boolean. (For MATLAB compatibility, b may also be a string, ‘on’ or ‘off’.)

  If b is None and len(kwargs)==0, toggle the grid state. If kwargs are supplied, it is assumed that you want a grid and b is thus set to True.

  which can be ‘major’ (default), ‘minor’, or ‘both’ to control whether major tick grids, minor tick grids, or both are affected.

  axis can be ‘both’ (default), ‘x’, or ‘y’ to control which set of gridlines are drawn.
  """
  def grid(opts \\ [b: nil, which: :major, axis: :both], kwargs \\ []) do
    code = Codebuilder.build_code(funcname: "plt.grid", nonnamed: [], named: opts ++ kwargs)
    Server.Commapi.add_code code
  end

  @doc """
  Set a title of the current axes.

  Set one of hte three available axes titles. The available titles are positioned above the aces in the center, flush with the left edge, and flush
  with the right edge.
  """
  def title(s, kwargs \\ []) do
    code = Codebuilder.build_code(funcname: "plt.title", nonnamed: [s], named: kwargs)
    Server.Commapi.add_code code
  end

  @doc """
  Set the x axis label of the current axis.
  """
  def xlabel(s, kwargs \\ []) do
    code = Codebuilder.build_code(funcname: "plt.xlabel", nonnamed: [s], named: kwargs)
    Server.Commapi.add_code code
  end

  @doc """
  Set the y axis label of the current axis.
  """
  def ylabel(s, kwargs \\ []) do
    code = Codebuilder.build_code(funcname: "plt.ylabel", nonnamed: [s], named: kwargs)
    Server.Commapi.add_code code
  end

  def show do
    Server.Commapi.get_code() |> IO.inspect
    Server.Commapi.execute()
  end
end