Use Version.parse in check whether string is a semver

defmodule ReleaseManager.Utils do
  @moduledoc """
  This module provides helper functions for the `mix release` and
  `mix release.clean` tasks.
  """
  import Mix.Shell,    only: [cmd: 2]

  # Relx constants
  @relx_output_path      "rel"

  @doc """
  Perform some actions within the context of a specific mix environment
  """
  def with_env(env, fun) do
    old_env = Mix.env
    try do
      # Change env
      Mix.env(env)
      fun.()
    after
      # Change back
      Mix.env(old_env)
    end
  end

  @doc """
  Load the current project's configuration
  """
  def load_config(env) do
    with_env env, fn ->
      if File.regular?("config/config.exs") do
        Mix.Config.read! "config/config.exs"
      else
        []
      end
    end
  end

  @doc """
  Call the _elixir mix binary with the given arguments
  """
  def mix(command, :quiet),        do: mix(command, :dev, :quiet)
  def mix(command, :verbose),      do: mix(command, :dev, :verbose)
  def mix(command, env),           do: mix(command, env, :quiet)
  def mix(command, env, :quiet),   do: do_cmd("MIX_ENV=#{env} mix #{command}", &ignore/1)
  def mix(command, env, :verbose), do: do_cmd("MIX_ENV=#{env} mix #{command}", &IO.write/1)
  @doc """
  Change user permissions for a target file or directory
  """
  def chmod(target, flags), do: do_cmd("chmod #{flags} #{target}", &ignore/1)
  @doc """
  Execute `relx`
  """
  def relx(name, version, verbosity, upgrade?, dev) do
    # Setup paths
    config     = rel_file_dest_path "relx.config"
    output_dir = @relx_output_path |> Path.expand
    # Determine whether to pass --dev-mode or not
    dev_mode?  = case dev do
      true  -> "--dev-mode"
      false -> ""
    end
    # Convert friendly verbosity names to relx values
    v = case verbosity do
      :silent  -> 0
      :quiet   -> 1
      :normal  -> 2
      :verbose -> 3
      _        -> 2 # Normal if we get an odd value
    end
    # Let relx do the heavy lifting
    relx_path = Path.join([priv_path, "bin", "relx"])
    command = case upgrade? do
      false -> "#{relx_path} release tar -V #{v} --root #{File.cwd!} --config #{config} --relname #{name} --relvsn #{version} --output-dir #{output_dir} #{dev_mode?}"
      true  ->
        last_release = get_last_release(name)
        "#{relx_path} release relup tar -V #{v} --root #{File.cwd!} --config #{config} --relname #{name} --relvsn #{version} --output-dir #{output_dir} --upfrom \"#{last_release}\" #{dev_mode?}"
    end
    case do_cmd command do
      :ok         -> :ok
      {:error, _} ->
        {:error, "Failed to build release. Please fix any errors and try again."}
    end
  end

  @doc "Print an informational message without color"
  def debug(message), do: IO.puts "==> #{message}"
  @doc "Print an informational message in green"
  def info(message),  do: IO.puts "==> #{IO.ANSI.green}#{message}#{IO.ANSI.reset}"
  @doc "Print a warning message in yellow"
  def warn(message),  do: IO.puts "==> #{IO.ANSI.yellow}#{message}#{IO.ANSI.reset}"
  @doc "Print a notice in yellow"
  def notice(message), do: IO.puts "#{IO.ANSI.yellow}#{message}#{IO.ANSI.reset}"
  @doc "Print an error message in red"
  def error(message), do: IO.puts "==> #{IO.ANSI.red}#{message}#{IO.ANSI.reset}"

  @doc "Exits with exit status 1"
  def abort!, do: exit({:shutdown, 1})

  @doc """
  Get a list of tuples representing the previous releases:

  ## Examples

      get_releases #=> [{"test", "0.0.1"}, {"test", "0.0.2"}]

  """
  def get_releases(project) do
    release_path = Path.join([File.cwd!, "rel", project, "releases"])
    case release_path |> File.exists? do
      false -> []
      true  ->
        release_path
        |> File.ls!
        |> Enum.reject(fn entry -> entry in ["RELEASES", "start_erl.data"] end)
        |> Enum.map(fn version -> {project, version} end)
    end
  end

  @doc """
  Get the most recent release prior to the current one
  """
  def get_last_release(project) do
    hd(project |> get_releases |> Enum.map(fn {_, v} -> v end) |> sort_versions)
  end

  @doc """
  Sort a list of versions, latest one first. Tries to use semver version
  compare, but can fall back to regular string compare.
  """
  def sort_versions(versions) do
    versions |> Enum.sort(
      fn v1, v2 ->
        case is_semver?(v1) and is_semver?(v2) do
          true ->
            {:ok, v1} = Version.parse(v1)
            {:ok, v2} = Version.parse(v2)
            case Version.compare(v1, v2) do
              :gt -> true
              _ -> false
            end;
          false ->
            v1 > v2
        end
      end)
  end

  defp is_semver?(vsn) do
    Version.parse(vsn) !== :error
  end

  @doc """
  Get the local path of the current elixir executable
  """
  def get_elixir_path() do
    System.find_executable("elixir") |> get_real_path
  end

  @doc """
  Writes an Elixir/Erlang term to the provided path
  """
  def write_term(path, term) do
    :file.write_file('#{path}', :io_lib.fwrite('~p.\n', [term]))
  end

  @doc """
  Writes a collection of Elixir/Erlang terms to the provided path
  """
  def write_terms(path, terms) when is_list(terms) do
    format_str = String.duplicate("~p.\n\n", Enum.count(terms)) |> String.to_char_list
    :file.write_file('#{path}', :io_lib.fwrite(format_str, terms |> Enum.reverse))
  end

  @doc """
  Reads a file as Erlang terms
  """
  def read_terms(path) do
    result = case '#{path}' |> :file.consult do
      {:ok, terms} ->
        terms
      {:error, {line, type, msg}} ->
        error "Unable to parse #{path}: Line #{line}, #{type}, - #{msg}"
        abort!
      {:error, reason} ->
        error "Unable to access #{path}: #{reason}"
        abort!
    end
    result
  end

  @doc """
  Convert a string to Erlang terms
  """
  def string_to_terms(str) do
    str
    |> String.split("}.")
    |> Stream.map(&(String.strip(&1, ?\n)))
    |> Stream.map(&String.strip/1)
    |> Stream.map(&('#{&1}}.'))
    |> Stream.map(&(:erl_scan.string(&1)))
    |> Stream.map(fn {:ok, tokens, _} -> :erl_parse.parse_term(tokens) end)
    |> Stream.filter(fn {:ok, _} -> true; {:error, _} -> false end)
    |> Enum.reduce([], fn {:ok, term}, acc -> [term|acc] end)
    |> Enum.reverse
  end

  @doc """
  Merges two sets of Elixir/Erlang terms, where the terms come
  in the form of lists of tuples. For example, such as is found
  in the relx.config file
  """
  def merge(old, new) when is_list(old) and is_list(new) do
    merge(old, new, [])
  end

  defp merge([h|t], new, acc) when is_tuple(h) do
    case :lists.keytake(elem(h, 0), 1, new) do
      {:value, new_value, rest} ->
        # Value is present in new, so merge the value
        merged = merge_term(h, new_value)
        merge(t, rest, [merged|acc])
      false ->
        # Value doesn't exist in new, so add it
        merge(t, new, [h|acc])
    end
  end
  defp merge([], new, acc) do
    Enum.reverse(acc, new)
  end

  defp merge_term(old, new) when is_tuple(old) and is_tuple(new) do
    old
    |> Tuple.to_list
    |> Enum.with_index
    |> Enum.reduce([], fn
        {[], idx}, acc ->
          [elem(new, idx)|acc]
        {val, idx}, acc when is_list(val) ->
          case :io_lib.char_list(val) do
            true ->
              [elem(new, idx)|acc]
            false ->
              merged = val |> Enum.concat(elem(new, idx)) |> Enum.uniq
              [merged|acc]
          end
        {val, idx}, acc when is_tuple(val) ->
          [merge_term(val, elem(new, idx))|acc]
        {_val, idx}, acc ->
          [elem(new, idx)|acc]
       end)
    |> Enum.reverse
    |> List.to_tuple
  end

  @doc "Get the priv path of the exrm dependency"
  def priv_path, do: "#{:code.priv_dir('exrm')}"
  @doc "Get the priv/rel path of the exrm dependency"
  def rel_source_path,       do: Path.join(priv_path, "rel")
  @doc "Get the path to a file located in priv/rel of the exrm dependency"
  def rel_source_path(file), do: Path.join(rel_source_path, file)
  @doc "Get the priv/rel/files path of the exrm dependency"
  def rel_file_source_path,       do: Path.join([priv_path, "rel", "files"])
  @doc "Get the path to a file located in priv/rel/files of the exrm dependency"
  def rel_file_source_path(file), do: Path.join(rel_file_source_path, file)
  @doc """
  Get the path to a file located in the rel directory of the current project.
  You can pass either a file name, or a list of directories to a file, like:

      iex> ReleaseManager.Utils.rel_dest_path "relx.config"
      "path/to/project/rel/relx.config"

      iex> ReleaseManager.Utils.rel_dest_path ["<project>", "lib", "<project>.appup"]
      "path/to/project/rel/<project>/lib/<project>.appup"

  """
  def rel_dest_path(files) when is_list(files), do: Path.join([rel_dest_path] ++ files)
  def rel_dest_path(file),                      do: Path.join(rel_dest_path, file)
  @doc "Get the rel path of the current project."
  def rel_dest_path,                            do: Path.join(File.cwd!, "rel")
  @doc """
  Get the path to a file located in the rel/files directory of the current project.
  You can pass either a file name, or a list of directories to a file, like:

      iex> ReleaseManager.Utils.rel_file_dest_path "sys.config"
      "path/to/project/rel/files/sys.config"

      iex> ReleaseManager.Utils.rel_dest_path ["some", "path", "file.txt"]
      "path/to/project/rel/files/some/path/file.txt"

  """
  def rel_file_dest_path(files) when is_list(files), do: Path.join([rel_file_dest_path] ++ files)
  def rel_file_dest_path(file),                      do: Path.join(rel_file_dest_path, file)
  @doc "Get the rel/files path of the current project."
  def rel_file_dest_path,                            do: Path.join([File.cwd!, "rel", "files"])

  # Ignore a message when used as the callback for Mix.Shell.cmd
  defp ignore(_), do: nil

  defp do_cmd(command), do: do_cmd(command, &IO.write/1)
  defp do_cmd(command, callback) do
    case cmd(command, callback) do
      0 -> :ok
      _ -> {:error, "Release step failed. Please fix any errors and try again."}
    end
  end

  defp get_real_path(path) do
    case path |> String.to_char_list |> :file.read_link_info do
      {:ok, {:file_info, _, :regular, _, _, _, _, _, _, _, _, _, _, _}} ->
        path
      {:ok, {:file_info, _, :symlink, _, _, _, _, _, _, _, _, _, _, _}} ->
        {:ok, sym} = path |> String.to_char_list |> :file.read_link
        case sym |> :filename.pathtype do
          :absolute ->
            sym |> IO.iodata_to_binary
          :relative ->
            symlink = sym |> IO.iodata_to_binary
            path |> Path.dirname |> Path.join(symlink) |> Path.expand
        end
    end |> String.replace("/bin/elixir", "")
  end

end

defmodule ReleaseManager.Utils do
  @moduledoc """
  This module provides helper functions for the `mix release` and
  `mix release.clean` tasks.
  """
  import Mix.Shell,    only: [cmd: 2]

  # Relx constants
  @relx_output_path      "rel"

  @doc """
  Perform some actions within the context of a specific mix environment
  """
  def with_env(env, fun) do
    old_env = Mix.env
    try do
      # Change env
      Mix.env(env)
      fun.()
    after
      # Change back
      Mix.env(old_env)
    end
  end

  @doc """
  Load the current project's configuration
  """
  def load_config(env) do
    with_env env, fn ->
      if File.regular?("config/config.exs") do
        Mix.Config.read! "config/config.exs"
      else
        []
      end
    end
  end

  @doc """
  Call the _elixir mix binary with the given arguments
  """
  def mix(command, :quiet),        do: mix(command, :dev, :quiet)
  def mix(command, :verbose),      do: mix(command, :dev, :verbose)
  def mix(command, env),           do: mix(command, env, :quiet)
  def mix(command, env, :quiet),   do: do_cmd("MIX_ENV=#{env} mix #{command}", &ignore/1)
  def mix(command, env, :verbose), do: do_cmd("MIX_ENV=#{env} mix #{command}", &IO.write/1)
  @doc """
  Change user permissions for a target file or directory
  """
  def chmod(target, flags), do: do_cmd("chmod #{flags} #{target}", &ignore/1)
  @doc """
  Execute `relx`
  """
  def relx(name, version, verbosity, upgrade?, dev) do
    # Setup paths
    config     = rel_file_dest_path "relx.config"
    output_dir = @relx_output_path |> Path.expand
    # Determine whether to pass --dev-mode or not
    dev_mode?  = case dev do
      true  -> "--dev-mode"
      false -> ""
    end
    # Convert friendly verbosity names to relx values
    v = case verbosity do
      :silent  -> 0
      :quiet   -> 1
      :normal  -> 2
      :verbose -> 3
      _        -> 2 # Normal if we get an odd value
    end
    # Let relx do the heavy lifting
    relx_path = Path.join([priv_path, "bin", "relx"])
    command = case upgrade? do
      false -> "#{relx_path} release tar -V #{v} --root #{File.cwd!} --config #{config} --relname #{name} --relvsn #{version} --output-dir #{output_dir} #{dev_mode?}"
      true  ->
        last_release = get_last_release(name)
        "#{relx_path} release relup tar -V #{v} --root #{File.cwd!} --config #{config} --relname #{name} --relvsn #{version} --output-dir #{output_dir} --upfrom \"#{last_release}\" #{dev_mode?}"
    end
    case do_cmd command do
      :ok         -> :ok
      {:error, _} ->
        {:error, "Failed to build release. Please fix any errors and try again."}
    end
  end

  @doc "Print an informational message without color"
  def debug(message), do: IO.puts "==> #{message}"
  @doc "Print an informational message in green"
  def info(message),  do: IO.puts "==> #{IO.ANSI.green}#{message}#{IO.ANSI.reset}"
  @doc "Print a warning message in yellow"
  def warn(message),  do: IO.puts "==> #{IO.ANSI.yellow}#{message}#{IO.ANSI.reset}"
  @doc "Print a notice in yellow"
  def notice(message), do: IO.puts "#{IO.ANSI.yellow}#{message}#{IO.ANSI.reset}"
  @doc "Print an error message in red"
  def error(message), do: IO.puts "==> #{IO.ANSI.red}#{message}#{IO.ANSI.reset}"

  @doc "Exits with exit status 1"
  def abort!, do: exit({:shutdown, 1})

  @doc """
  Get a list of tuples representing the previous releases:

  ## Examples

      get_releases #=> [{"test", "0.0.1"}, {"test", "0.0.2"}]

  """
  def get_releases(project) do
    release_path = Path.join([File.cwd!, "rel", project, "releases"])
    case release_path |> File.exists? do
      false -> []
      true  ->
        release_path
        |> File.ls!
        |> Enum.reject(fn entry -> entry in ["RELEASES", "start_erl.data"] end)
        |> Enum.map(fn version -> {project, version} end)
    end
  end

  @doc """
  Get the most recent release prior to the current one
  """
  def get_last_release(project) do
    hd(project |> get_releases |> Enum.map(fn {_, v} -> v end) |> sort_versions)
  end

  @doc """
  Sort a list of versions, latest one first. Tries to use semver version
  compare, but can fall back to regular string compare.
  """
  def sort_versions(versions) do
    versions |> Enum.sort(
      fn v1, v2 ->
        case is_semver?(v1) and is_semver?(v2) do
          true ->
            {:ok, v1} = Version.parse(v1)
            {:ok, v2} = Version.parse(v2)
            case Version.compare(v1, v2) do
              :gt -> true
              _ -> false
            end;
          false ->
            v1 > v2
        end
      end)
  end

  defp is_semver?(vsn) do
    Regex.match?(~r/^\d+\.\d+\.\d+/, vsn)
  end

  @doc """
  Get the local path of the current elixir executable
  """
  def get_elixir_path() do
    System.find_executable("elixir") |> get_real_path
  end

  @doc """
  Writes an Elixir/Erlang term to the provided path
  """
  def write_term(path, term) do
    :file.write_file('#{path}', :io_lib.fwrite('~p.\n', [term]))
  end

  @doc """
  Writes a collection of Elixir/Erlang terms to the provided path
  """
  def write_terms(path, terms) when is_list(terms) do
    format_str = String.duplicate("~p.\n\n", Enum.count(terms)) |> String.to_char_list
    :file.write_file('#{path}', :io_lib.fwrite(format_str, terms |> Enum.reverse))
  end

  @doc """
  Reads a file as Erlang terms
  """
  def read_terms(path) do
    result = case '#{path}' |> :file.consult do
      {:ok, terms} ->
        terms
      {:error, {line, type, msg}} ->
        error "Unable to parse #{path}: Line #{line}, #{type}, - #{msg}"
        abort!
      {:error, reason} ->
        error "Unable to access #{path}: #{reason}"
        abort!
    end
    result
  end

  @doc """
  Convert a string to Erlang terms
  """
  def string_to_terms(str) do
    str
    |> String.split("}.")
    |> Stream.map(&(String.strip(&1, ?\n)))
    |> Stream.map(&String.strip/1)
    |> Stream.map(&('#{&1}}.'))
    |> Stream.map(&(:erl_scan.string(&1)))
    |> Stream.map(fn {:ok, tokens, _} -> :erl_parse.parse_term(tokens) end)
    |> Stream.filter(fn {:ok, _} -> true; {:error, _} -> false end)
    |> Enum.reduce([], fn {:ok, term}, acc -> [term|acc] end)
    |> Enum.reverse
  end

  @doc """
  Merges two sets of Elixir/Erlang terms, where the terms come
  in the form of lists of tuples. For example, such as is found
  in the relx.config file
  """
  def merge(old, new) when is_list(old) and is_list(new) do
    merge(old, new, [])
  end

  defp merge([h|t], new, acc) when is_tuple(h) do
    case :lists.keytake(elem(h, 0), 1, new) do
      {:value, new_value, rest} ->
        # Value is present in new, so merge the value
        merged = merge_term(h, new_value)
        merge(t, rest, [merged|acc])
      false ->
        # Value doesn't exist in new, so add it
        merge(t, new, [h|acc])
    end
  end
  defp merge([], new, acc) do
    Enum.reverse(acc, new)
  end

  defp merge_term(old, new) when is_tuple(old) and is_tuple(new) do
    old
    |> Tuple.to_list
    |> Enum.with_index
    |> Enum.reduce([], fn
        {[], idx}, acc ->
          [elem(new, idx)|acc]
        {val, idx}, acc when is_list(val) ->
          case :io_lib.char_list(val) do
            true ->
              [elem(new, idx)|acc]
            false ->
              merged = val |> Enum.concat(elem(new, idx)) |> Enum.uniq
              [merged|acc]
          end
        {val, idx}, acc when is_tuple(val) ->
          [merge_term(val, elem(new, idx))|acc]
        {_val, idx}, acc ->
          [elem(new, idx)|acc]
       end)
    |> Enum.reverse
    |> List.to_tuple
  end

  @doc "Get the priv path of the exrm dependency"
  def priv_path, do: "#{:code.priv_dir('exrm')}"
  @doc "Get the priv/rel path of the exrm dependency"
  def rel_source_path,       do: Path.join(priv_path, "rel")
  @doc "Get the path to a file located in priv/rel of the exrm dependency"
  def rel_source_path(file), do: Path.join(rel_source_path, file)
  @doc "Get the priv/rel/files path of the exrm dependency"
  def rel_file_source_path,       do: Path.join([priv_path, "rel", "files"])
  @doc "Get the path to a file located in priv/rel/files of the exrm dependency"
  def rel_file_source_path(file), do: Path.join(rel_file_source_path, file)
  @doc """
  Get the path to a file located in the rel directory of the current project.
  You can pass either a file name, or a list of directories to a file, like:

      iex> ReleaseManager.Utils.rel_dest_path "relx.config"
      "path/to/project/rel/relx.config"

      iex> ReleaseManager.Utils.rel_dest_path ["<project>", "lib", "<project>.appup"]
      "path/to/project/rel/<project>/lib/<project>.appup"

  """
  def rel_dest_path(files) when is_list(files), do: Path.join([rel_dest_path] ++ files)
  def rel_dest_path(file),                      do: Path.join(rel_dest_path, file)
  @doc "Get the rel path of the current project."
  def rel_dest_path,                            do: Path.join(File.cwd!, "rel")
  @doc """
  Get the path to a file located in the rel/files directory of the current project.
  You can pass either a file name, or a list of directories to a file, like:

      iex> ReleaseManager.Utils.rel_file_dest_path "sys.config"
      "path/to/project/rel/files/sys.config"

      iex> ReleaseManager.Utils.rel_dest_path ["some", "path", "file.txt"]
      "path/to/project/rel/files/some/path/file.txt"

  """
  def rel_file_dest_path(files) when is_list(files), do: Path.join([rel_file_dest_path] ++ files)
  def rel_file_dest_path(file),                      do: Path.join(rel_file_dest_path, file)
  @doc "Get the rel/files path of the current project."
  def rel_file_dest_path,                            do: Path.join([File.cwd!, "rel", "files"])

  # Ignore a message when used as the callback for Mix.Shell.cmd
  defp ignore(_), do: nil

  defp do_cmd(command), do: do_cmd(command, &IO.write/1)
  defp do_cmd(command, callback) do
    case cmd(command, callback) do
      0 -> :ok
      _ -> {:error, "Release step failed. Please fix any errors and try again."}
    end
  end

  defp get_real_path(path) do
    case path |> String.to_char_list |> :file.read_link_info do
      {:ok, {:file_info, _, :regular, _, _, _, _, _, _, _, _, _, _, _}} ->
        path
      {:ok, {:file_info, _, :symlink, _, _, _, _, _, _, _, _, _, _, _}} ->
        {:ok, sym} = path |> String.to_char_list |> :file.read_link
        case sym |> :filename.pathtype do
          :absolute ->
            sym |> IO.iodata_to_binary
          :relative ->
            symlink = sym |> IO.iodata_to_binary
            path |> Path.dirname |> Path.join(symlink) |> Path.expand
        end
    end |> String.replace("/bin/elixir", "")
  end

end


Fix 'elixir -v' to display Erlang :system_version info

It fixes a bug accidentally introduced in 8b035311d1b2cadd982491d92bf091ade45d3976
that removed a line of code

Signed-off-by: José Valim <0c2436ea76ed86e37cc05f66dea18d48ef390882@plataformatec.com.br>

defmodule Kernel.CLI do
  @moduledoc false

  @blank_config %{commands: [], output: ".", compile: [],
                  halt: true, compiler_options: [], errors: [],
                  pa: [], pz: [], verbose_compile: false}

  @doc """
  This is the API invoked by Elixir boot process.
  """
  def main(argv) do
    argv = for arg <- argv, do: IO.chardata_to_string(arg)

    {config, argv} = parse_argv(argv)
    System.argv(argv)

    run fn _ ->
      errors = process_commands(config)

      if errors != [] do
        Enum.each(errors, &IO.puts(:stderr, &1))
        System.halt(1)
      end
    end, config.halt
  end

  @doc """
  Runs the given function by catching any failure
  and printing them to stdout. `at_exit` hooks are
  also invoked before exiting.

  This function is used by Elixir's CLI and also
  by escripts generated by Elixir.
  """
  def run(fun, halt \\ true) do
    {ok_or_shutdown, status} = exec_fun(fun, {:ok, 0})
    if ok_or_shutdown == :shutdown or halt do
      {_, status} = at_exit({ok_or_shutdown, status})

      # Ensure Logger messages are flushed before halting
      case :erlang.whereis(Logger) do
        pid when is_pid(pid) -> Logger.flush()
        _ -> :ok
      end

      System.halt(status)
    end
  end

  @doc false
  def parse_argv(argv) do
    parse_argv(argv, @blank_config)
  end

  @doc false
  def process_commands(config) do
    results = Enum.map(Enum.reverse(config.commands), &process_command(&1, config))
    errors  = for {:error, msg} <- results, do: msg
    Enum.reverse(config.errors, errors)
  end

  ## Helpers

  defp at_exit(res) do
    hooks = :elixir_config.get_and_put(:at_exit, [])
    res = Enum.reduce(hooks, res, &exec_fun/2)
    if hooks == [], do: res, else: at_exit(res)
  end

  defp exec_fun(fun, res) when is_function(fun, 1) and is_tuple(res) do
    parent = self()

    {pid, ref} =
      spawn_monitor(fn ->
        try do
          fun.(elem(res, 1))
        catch
          :exit, {:shutdown, int} when is_integer(int) ->
            send parent, {self, {:shutdown, int}}
            exit({:shutdown, int})
          :exit, reason
              when reason == :normal
              when reason == :shutdown
              when tuple_size(reason) == 2 and elem(reason, 0) == :shutdown ->
            send parent, {self, {:shutdown, 0}}
            exit(reason)
          kind, reason ->
            stack = System.stacktrace
            print_error(kind, reason, stack)
            send parent, {self, {:shutdown, 1}}
            exit(to_exit(kind, reason, stack))
        else
          _ ->
            send parent, {self, res}
        end
      end)

    receive do
      {^pid, res} ->
        :erlang.demonitor(ref, [:flush])
        res
      {:DOWN, ^ref, _, _, other} ->
        print_error({:EXIT, pid}, other, [])
        {:shutdown, 1}
    end
  end

  defp to_exit(:throw, reason, stack), do: {{:nocatch, reason}, stack}
  defp to_exit(:error, reason, stack), do: {reason, stack}
  defp to_exit(:exit, reason, _stack), do: reason

  defp shared_option?(list, config, callback) do
    case parse_shared(list, config) do
      {[h|hs], _} when h == hd(list) ->
        new_config = %{config | errors: ["#{h} : Unknown option" | config.errors]}
        callback.(hs, new_config)
      {new_list, new_config} ->
        callback.(new_list, new_config)
    end
  end

  defp print_error(kind, reason, trace) do
    IO.puts :stderr, Exception.format(kind, reason, prune_stacktrace(trace))
  end

  @elixir_internals [:elixir, :elixir_exp, :elixir_compiler, :elixir_module, :elixir_clauses,
                     :elixir_translator, :elixir_expand, :elixir_lexical, :elixir_exp_clauses,
                     :elixir_def, :elixir_map]

  defp prune_stacktrace([{mod, _, _, _}|t]) when mod in @elixir_internals do
    prune_stacktrace(t)
  end

  defp prune_stacktrace([{__MODULE__, :wrapper, 1, _}|_]) do
    []
  end

  defp prune_stacktrace([h|t]) do
    [h|prune_stacktrace(t)]
  end

  defp prune_stacktrace([]) do
    []
  end

  # Parse shared options

  defp parse_shared([opt|_t], _config) when opt in ["-v", "--version"] do
    if function_exported?(IEx, :started?, 0) and IEx.started? do
      IO.puts "IEx " <> System.build_info[:build]
    else
      IO.puts :erlang.system_info(:system_version)
      IO.puts "Elixir " <> System.build_info[:build]
    end

    System.halt 0
  end

  defp parse_shared(["-pa", h|t], config) do
    paths = expand_code_path(h)
    Enum.each(paths, &:code.add_patha/1)
    parse_shared t, %{config | pa: config.pa ++ paths}
  end

  defp parse_shared(["-pz", h|t], config) do
    paths = expand_code_path(h)
    Enum.each(paths, &:code.add_pathz/1)
    parse_shared t, %{config | pz: config.pz ++ paths}
  end

  defp parse_shared(["--app", h|t], config) do
    parse_shared t, %{config | commands: [{:app, h} | config.commands]}
  end

  defp parse_shared(["--no-halt"|t], config) do
    parse_shared t, %{config | halt: false}
  end

  defp parse_shared(["-e", h|t], config) do
    parse_shared t, %{config | commands: [{:eval, h} | config.commands]}
  end

  defp parse_shared(["-r", h|t], config) do
    parse_shared t, %{config | commands: [{:require, h} | config.commands]}
  end

  defp parse_shared(["-pr", h|t], config) do
    parse_shared t, %{config | commands: [{:parallel_require, h} | config.commands]}
  end

  defp parse_shared([erl, _|t], config) when erl in ["--erl", "--sname", "--name", "--cookie", "--logger-otp-reports", "--logger-sasl-reports"] do
    parse_shared t, config
  end

  defp parse_shared([erl|t], config) when erl in ["--detached", "--hidden", "--werl"] do
    parse_shared t, config
  end

  defp parse_shared(list, config) do
    {list, config}
  end

  defp expand_code_path(path) do
    path = Path.expand(path)
    case Path.wildcard(path) do
      []   -> [to_char_list(path)]
      list -> Enum.map(list, &to_char_list/1)
    end
  end

  # Process init options

  defp parse_argv(["--"|t], config) do
    {config, t}
  end

  defp parse_argv(["+elixirc"|t], config) do
    parse_compiler t, config
  end

  defp parse_argv(["+iex"|t], config) do
    parse_iex t, config
  end

  defp parse_argv(["-S", h|t], config) do
    {%{config | commands: [{:script, h} | config.commands]}, t}
  end

  defp parse_argv([h|t] = list, config) do
    case h do
      "-" <> _ ->
        shared_option? list, config, &parse_argv(&1, &2)
      _ ->
        if Keyword.has_key?(config.commands, :eval) do
          {config, list}
        else
          {%{config | commands: [{:file, h} | config.commands]}, t}
        end
    end
  end

  defp parse_argv([], config) do
    {config, []}
  end

  # Parse compiler options

  defp parse_compiler(["--"|t], config) do
    {config, t}
  end

  defp parse_compiler(["-o", h|t], config) do
    parse_compiler t, %{config | output: h}
  end

  defp parse_compiler(["--no-docs"|t], config) do
    parse_compiler t, %{config | compiler_options: [{:docs, false} | config.compiler_options]}
  end

  defp parse_compiler(["--no-debug-info"|t], config) do
    parse_compiler t, %{config | compiler_options: [{:debug_info, false} | config.compiler_options]}
  end

  defp parse_compiler(["--ignore-module-conflict"|t], config) do
    parse_compiler t, %{config | compiler_options: [{:ignore_module_conflict, true} | config.compiler_options]}
  end

  defp parse_compiler(["--warnings-as-errors"|t], config) do
    parse_compiler t, %{config | compiler_options: [{:warnings_as_errors, true} | config.compiler_options]}
  end

  defp parse_compiler(["--verbose"|t], config) do
    parse_compiler t, %{config | verbose_compile: true}
  end

  defp parse_compiler([h|t] = list, config) do
    case h do
      "-" <> _ ->
        shared_option? list, config, &parse_compiler(&1, &2)
      _ ->
        pattern = if File.dir?(h), do: "#{h}/**/*.ex", else: h
        parse_compiler t, %{config | compile: [pattern | config.compile]}
    end
  end

  defp parse_compiler([], config) do
    {%{config | commands: [{:compile, config.compile}|config.commands]}, []}
  end

  # Parse iex options

  defp parse_iex(["--"|t], config) do
    {config, t}
  end

  # This clause is here so that Kernel.CLI does not
  # error out with "unknown option"
  defp parse_iex(["--dot-iex", _|t], config) do
    parse_iex t, config
  end

  defp parse_iex([opt, _|t], config) when opt in ["--remsh"] do
    parse_iex t, config
  end

  defp parse_iex(["-S", h|t], config) do
    {%{config | commands: [{:script, h} | config.commands]}, t}
  end

  defp parse_iex([h|t] = list, config) do
    case h do
      "-" <> _ ->
        shared_option? list, config, &parse_iex(&1, &2)
      _ ->
        {%{config | commands: [{:file, h} | config.commands]}, t}
    end
  end

  defp parse_iex([], config) do
    {config, []}
  end

  # Process commands

  defp process_command({:cookie, h}, _config) do
    if Node.alive? do
      wrapper fn -> Node.set_cookie(String.to_atom(h)) end
    else
      {:error, "--cookie : Cannot set cookie if the node is not alive (set --name or --sname)"}
    end
  end

  defp process_command({:eval, expr}, _config) when is_binary(expr) do
    wrapper fn -> Code.eval_string(expr, []) end
  end

  defp process_command({:app, app}, _config) when is_binary(app) do
    case Application.ensure_all_started(String.to_atom(app)) do
      {:error, {app, reason}} ->
        {:error, "--app : Could not start application #{app}: " <>
          Application.format_error(reason)}
      {:ok, _} ->
        :ok
    end
  end

  defp process_command({:script, file}, _config) when is_binary(file) do
    if exec = find_elixir_executable(file) do
      wrapper fn -> Code.require_file(exec) end
    else
      {:error, "-S : Could not find executable #{file}"}
    end
  end

  defp process_command({:file, file}, _config) when is_binary(file) do
    if File.regular?(file) do
      wrapper fn -> Code.require_file(file) end
    else
      {:error, "No file named #{file}"}
    end
  end

  defp process_command({:require, pattern}, _config) when is_binary(pattern) do
    files = filter_patterns(pattern)

    if files != [] do
      wrapper fn -> Enum.map files, &Code.require_file(&1) end
    else
      {:error, "-r : No files matched pattern #{pattern}"}
    end
  end

  defp process_command({:parallel_require, pattern}, _config) when is_binary(pattern) do
    files = filter_patterns(pattern)

    if files != [] do
      wrapper fn -> Kernel.ParallelRequire.files(files) end
    else
      {:error, "-pr : No files matched pattern #{pattern}"}
    end
  end

  defp process_command({:compile, patterns}, config) do
    # If ensuring the dir returns an error no files will be found.
    _ = :filelib.ensure_dir(:filename.join(config.output, "."))

    case filter_multiple_patterns(patterns) do
      {:ok, []} ->
        {:error, "No files matched provided patterns"}
      {:ok, files} ->
        wrapper fn ->
          Code.compiler_options(config.compiler_options)
          Kernel.ParallelCompiler.files_to_path(files, config.output,
            each_file: fn file -> if config.verbose_compile do IO.puts "Compiled #{file}" end end)
        end
      {:missing, missing} ->
        {:error, "No files matched pattern(s) #{Enum.join(missing, ",")}"}
    end
  end

  defp filter_patterns(pattern) do
    pattern
    |> Path.wildcard
    |> :lists.usort
    |> Enum.filter(&File.regular?/1)
  end

  defp filter_multiple_patterns(patterns) do
    matched_files = Enum.map patterns, fn(pattern) ->
      case filter_patterns(pattern) do
        []    -> {:missing, pattern}
        files -> {:ok, files}
      end
    end

    files = Enum.filter_map matched_files,
       fn(match) -> elem(match, 0) == :ok end,
       &elem(&1, 1)

    missing_patterns = Enum.filter_map matched_files,
       fn(match) -> elem(match, 0) == :missing end,
       &elem(&1, 1)

    if missing_patterns == [] do
      {:ok, :lists.usort(Enum.concat(files))}
    else
      {:missing, :lists.usort(missing_patterns)}
    end
  end

  defp wrapper(fun) do
    _ = fun.()
    :ok
  end

  defp find_elixir_executable(file) do
    if exec = System.find_executable(file) do
      # If we are on Windows, the executable is going to be
      # a .bat file that must be in the same directory as
      # the actual Elixir executable.
      case :os.type() do
        {:win32, _} ->
          base = Path.rootname(exec)
          if File.regular?(base), do: base, else: exec
        _ ->
          exec
      end
    end
  end
end

defmodule Kernel.CLI do
  @moduledoc false

  @blank_config %{commands: [], output: ".", compile: [],
                  halt: true, compiler_options: [], errors: [],
                  pa: [], pz: [], verbose_compile: false}

  @doc """
  This is the API invoked by Elixir boot process.
  """
  def main(argv) do
    argv = for arg <- argv, do: IO.chardata_to_string(arg)

    {config, argv} = parse_argv(argv)
    System.argv(argv)

    run fn _ ->
      errors = process_commands(config)

      if errors != [] do
        Enum.each(errors, &IO.puts(:stderr, &1))
        System.halt(1)
      end
    end, config.halt
  end

  @doc """
  Runs the given function by catching any failure
  and printing them to stdout. `at_exit` hooks are
  also invoked before exiting.

  This function is used by Elixir's CLI and also
  by escripts generated by Elixir.
  """
  def run(fun, halt \\ true) do
    {ok_or_shutdown, status} = exec_fun(fun, {:ok, 0})
    if ok_or_shutdown == :shutdown or halt do
      {_, status} = at_exit({ok_or_shutdown, status})

      # Ensure Logger messages are flushed before halting
      case :erlang.whereis(Logger) do
        pid when is_pid(pid) -> Logger.flush()
        _ -> :ok
      end

      System.halt(status)
    end
  end

  @doc false
  def parse_argv(argv) do
    parse_argv(argv, @blank_config)
  end

  @doc false
  def process_commands(config) do
    results = Enum.map(Enum.reverse(config.commands), &process_command(&1, config))
    errors  = for {:error, msg} <- results, do: msg
    Enum.reverse(config.errors, errors)
  end

  ## Helpers

  defp at_exit(res) do
    hooks = :elixir_config.get_and_put(:at_exit, [])
    res = Enum.reduce(hooks, res, &exec_fun/2)
    if hooks == [], do: res, else: at_exit(res)
  end

  defp exec_fun(fun, res) when is_function(fun, 1) and is_tuple(res) do
    parent = self()

    {pid, ref} =
      spawn_monitor(fn ->
        try do
          fun.(elem(res, 1))
        catch
          :exit, {:shutdown, int} when is_integer(int) ->
            send parent, {self, {:shutdown, int}}
            exit({:shutdown, int})
          :exit, reason
              when reason == :normal
              when reason == :shutdown
              when tuple_size(reason) == 2 and elem(reason, 0) == :shutdown ->
            send parent, {self, {:shutdown, 0}}
            exit(reason)
          kind, reason ->
            stack = System.stacktrace
            print_error(kind, reason, stack)
            send parent, {self, {:shutdown, 1}}
            exit(to_exit(kind, reason, stack))
        else
          _ ->
            send parent, {self, res}
        end
      end)

    receive do
      {^pid, res} ->
        :erlang.demonitor(ref, [:flush])
        res
      {:DOWN, ^ref, _, _, other} ->
        print_error({:EXIT, pid}, other, [])
        {:shutdown, 1}
    end
  end

  defp to_exit(:throw, reason, stack), do: {{:nocatch, reason}, stack}
  defp to_exit(:error, reason, stack), do: {reason, stack}
  defp to_exit(:exit, reason, _stack), do: reason

  defp shared_option?(list, config, callback) do
    case parse_shared(list, config) do
      {[h|hs], _} when h == hd(list) ->
        new_config = %{config | errors: ["#{h} : Unknown option" | config.errors]}
        callback.(hs, new_config)
      {new_list, new_config} ->
        callback.(new_list, new_config)
    end
  end

  defp print_error(kind, reason, trace) do
    IO.puts :stderr, Exception.format(kind, reason, prune_stacktrace(trace))
  end

  @elixir_internals [:elixir, :elixir_exp, :elixir_compiler, :elixir_module, :elixir_clauses,
                     :elixir_translator, :elixir_expand, :elixir_lexical, :elixir_exp_clauses,
                     :elixir_def, :elixir_map]

  defp prune_stacktrace([{mod, _, _, _}|t]) when mod in @elixir_internals do
    prune_stacktrace(t)
  end

  defp prune_stacktrace([{__MODULE__, :wrapper, 1, _}|_]) do
    []
  end

  defp prune_stacktrace([h|t]) do
    [h|prune_stacktrace(t)]
  end

  defp prune_stacktrace([]) do
    []
  end

  # Parse shared options

  defp parse_shared([opt|_t], _config) when opt in ["-v", "--version"] do
    if function_exported?(IEx, :started?, 0) and IEx.started? do
      IO.puts "IEx " <> System.build_info[:build]
    else
      IO.puts "Elixir " <> System.build_info[:build]
    end

    System.halt 0
  end

  defp parse_shared(["-pa", h|t], config) do
    paths = expand_code_path(h)
    Enum.each(paths, &:code.add_patha/1)
    parse_shared t, %{config | pa: config.pa ++ paths}
  end

  defp parse_shared(["-pz", h|t], config) do
    paths = expand_code_path(h)
    Enum.each(paths, &:code.add_pathz/1)
    parse_shared t, %{config | pz: config.pz ++ paths}
  end

  defp parse_shared(["--app", h|t], config) do
    parse_shared t, %{config | commands: [{:app, h} | config.commands]}
  end

  defp parse_shared(["--no-halt"|t], config) do
    parse_shared t, %{config | halt: false}
  end

  defp parse_shared(["-e", h|t], config) do
    parse_shared t, %{config | commands: [{:eval, h} | config.commands]}
  end

  defp parse_shared(["-r", h|t], config) do
    parse_shared t, %{config | commands: [{:require, h} | config.commands]}
  end

  defp parse_shared(["-pr", h|t], config) do
    parse_shared t, %{config | commands: [{:parallel_require, h} | config.commands]}
  end

  defp parse_shared([erl, _|t], config) when erl in ["--erl", "--sname", "--name", "--cookie", "--logger-otp-reports", "--logger-sasl-reports"] do
    parse_shared t, config
  end

  defp parse_shared([erl|t], config) when erl in ["--detached", "--hidden", "--werl"] do
    parse_shared t, config
  end

  defp parse_shared(list, config) do
    {list, config}
  end

  defp expand_code_path(path) do
    path = Path.expand(path)
    case Path.wildcard(path) do
      []   -> [to_char_list(path)]
      list -> Enum.map(list, &to_char_list/1)
    end
  end

  # Process init options

  defp parse_argv(["--"|t], config) do
    {config, t}
  end

  defp parse_argv(["+elixirc"|t], config) do
    parse_compiler t, config
  end

  defp parse_argv(["+iex"|t], config) do
    parse_iex t, config
  end

  defp parse_argv(["-S", h|t], config) do
    {%{config | commands: [{:script, h} | config.commands]}, t}
  end

  defp parse_argv([h|t] = list, config) do
    case h do
      "-" <> _ ->
        shared_option? list, config, &parse_argv(&1, &2)
      _ ->
        if Keyword.has_key?(config.commands, :eval) do
          {config, list}
        else
          {%{config | commands: [{:file, h} | config.commands]}, t}
        end
    end
  end

  defp parse_argv([], config) do
    {config, []}
  end

  # Parse compiler options

  defp parse_compiler(["--"|t], config) do
    {config, t}
  end

  defp parse_compiler(["-o", h|t], config) do
    parse_compiler t, %{config | output: h}
  end

  defp parse_compiler(["--no-docs"|t], config) do
    parse_compiler t, %{config | compiler_options: [{:docs, false} | config.compiler_options]}
  end

  defp parse_compiler(["--no-debug-info"|t], config) do
    parse_compiler t, %{config | compiler_options: [{:debug_info, false} | config.compiler_options]}
  end

  defp parse_compiler(["--ignore-module-conflict"|t], config) do
    parse_compiler t, %{config | compiler_options: [{:ignore_module_conflict, true} | config.compiler_options]}
  end

  defp parse_compiler(["--warnings-as-errors"|t], config) do
    parse_compiler t, %{config | compiler_options: [{:warnings_as_errors, true} | config.compiler_options]}
  end

  defp parse_compiler(["--verbose"|t], config) do
    parse_compiler t, %{config | verbose_compile: true}
  end

  defp parse_compiler([h|t] = list, config) do
    case h do
      "-" <> _ ->
        shared_option? list, config, &parse_compiler(&1, &2)
      _ ->
        pattern = if File.dir?(h), do: "#{h}/**/*.ex", else: h
        parse_compiler t, %{config | compile: [pattern | config.compile]}
    end
  end

  defp parse_compiler([], config) do
    {%{config | commands: [{:compile, config.compile}|config.commands]}, []}
  end

  # Parse iex options

  defp parse_iex(["--"|t], config) do
    {config, t}
  end

  # This clause is here so that Kernel.CLI does not
  # error out with "unknown option"
  defp parse_iex(["--dot-iex", _|t], config) do
    parse_iex t, config
  end

  defp parse_iex([opt, _|t], config) when opt in ["--remsh"] do
    parse_iex t, config
  end

  defp parse_iex(["-S", h|t], config) do
    {%{config | commands: [{:script, h} | config.commands]}, t}
  end

  defp parse_iex([h|t] = list, config) do
    case h do
      "-" <> _ ->
        shared_option? list, config, &parse_iex(&1, &2)
      _ ->
        {%{config | commands: [{:file, h} | config.commands]}, t}
    end
  end

  defp parse_iex([], config) do
    {config, []}
  end

  # Process commands

  defp process_command({:cookie, h}, _config) do
    if Node.alive? do
      wrapper fn -> Node.set_cookie(String.to_atom(h)) end
    else
      {:error, "--cookie : Cannot set cookie if the node is not alive (set --name or --sname)"}
    end
  end

  defp process_command({:eval, expr}, _config) when is_binary(expr) do
    wrapper fn -> Code.eval_string(expr, []) end
  end

  defp process_command({:app, app}, _config) when is_binary(app) do
    case Application.ensure_all_started(String.to_atom(app)) do
      {:error, {app, reason}} ->
        {:error, "--app : Could not start application #{app}: " <>
          Application.format_error(reason)}
      {:ok, _} ->
        :ok
    end
  end

  defp process_command({:script, file}, _config) when is_binary(file) do
    if exec = find_elixir_executable(file) do
      wrapper fn -> Code.require_file(exec) end
    else
      {:error, "-S : Could not find executable #{file}"}
    end
  end

  defp process_command({:file, file}, _config) when is_binary(file) do
    if File.regular?(file) do
      wrapper fn -> Code.require_file(file) end
    else
      {:error, "No file named #{file}"}
    end
  end

  defp process_command({:require, pattern}, _config) when is_binary(pattern) do
    files = filter_patterns(pattern)

    if files != [] do
      wrapper fn -> Enum.map files, &Code.require_file(&1) end
    else
      {:error, "-r : No files matched pattern #{pattern}"}
    end
  end

  defp process_command({:parallel_require, pattern}, _config) when is_binary(pattern) do
    files = filter_patterns(pattern)

    if files != [] do
      wrapper fn -> Kernel.ParallelRequire.files(files) end
    else
      {:error, "-pr : No files matched pattern #{pattern}"}
    end
  end

  defp process_command({:compile, patterns}, config) do
    # If ensuring the dir returns an error no files will be found.
    _ = :filelib.ensure_dir(:filename.join(config.output, "."))

    case filter_multiple_patterns(patterns) do
      {:ok, []} ->
        {:error, "No files matched provided patterns"}
      {:ok, files} ->
        wrapper fn ->
          Code.compiler_options(config.compiler_options)
          Kernel.ParallelCompiler.files_to_path(files, config.output,
            each_file: fn file -> if config.verbose_compile do IO.puts "Compiled #{file}" end end)
        end
      {:missing, missing} ->
        {:error, "No files matched pattern(s) #{Enum.join(missing, ",")}"}
    end
  end

  defp filter_patterns(pattern) do
    pattern
    |> Path.wildcard
    |> :lists.usort
    |> Enum.filter(&File.regular?/1)
  end

  defp filter_multiple_patterns(patterns) do
    matched_files = Enum.map patterns, fn(pattern) ->
      case filter_patterns(pattern) do
        []    -> {:missing, pattern}
        files -> {:ok, files}
      end
    end

    files = Enum.filter_map matched_files,
       fn(match) -> elem(match, 0) == :ok end,
       &elem(&1, 1)

    missing_patterns = Enum.filter_map matched_files,
       fn(match) -> elem(match, 0) == :missing end,
       &elem(&1, 1)

    if missing_patterns == [] do
      {:ok, :lists.usort(Enum.concat(files))}
    else
      {:missing, :lists.usort(missing_patterns)}
    end
  end

  defp wrapper(fun) do
    _ = fun.()
    :ok
  end

  defp find_elixir_executable(file) do
    if exec = System.find_executable(file) do
      # If we are on Windows, the executable is going to be
      # a .bat file that must be in the same directory as
      # the actual Elixir executable.
      case :os.type() do
        {:win32, _} ->
          base = Path.rootname(exec)
          if File.regular?(base), do: base, else: exec
        _ ->
          exec
      end
    end
  end
end


Improve wording

Improve wording

defmodule Tesla.Mock do
  @moduledoc """
  Mock adapter for better testing.

  ### Setup

  ```
  # config/test.exs
  config :tesla, adapter: :mock

  # in case MyClient defines specific adapter with `adapter :specific`
  config :tesla, MyClient, adapter: :mock
  ```

  ### Example test
  ```
  defmodule MyAppTest do
    use ExUnit.Case

    setup do
      Tesla.Mock.mock fn
        %{method: :get} ->
          %Tesla.Env{status: 200, body: "hello"}
      end

      :ok
    end

    test "list things" do
      assert %Tesla.Env{} = env = MyApp.get("...")
      assert env.status == 200
      assert env.body == "hello"
    end
  end
  ```

  ### Setting up mocks
  ```
  # Match on method & url and return whole Tesla.Env
  Tesla.Mock.mock fn
    %{method: :get,  url: "http://example.com/list"} ->
      %Tesla.Env{status: 200, body: "hello"}
  end

  # You can use any logic required
  Tesla.Mock.mock fn env ->
    case env.url do
      "http://example.com/list" ->
        %Tesla.Env{status: 200, body: "ok!"}
      _ ->
        %Tesla.Env{status: 404, body: "NotFound"}
  end

  # mock will also accept short version of response
  # in the form of {status, headers, body}
  Tesla.Mock.mock fn
    %{method: :post} -> {201, %{}, %{id: 42}}
  end
  ```

  ### Global mocks
  By default, mocks are bound to the current process,
  i.e. the process running a single test case.
  This design allows proper isolation between test cases
  and make testing in parallel (`async: true`) possible.

  While this style is recommended, there is one drawback:
  if Tesla client is called from different process
  it will not use the setup mock.

  To solve this issue it is possible to setup a global mock
  using `mock_global/1` function.

  ```
  defmodule MyTest do
    use ExUnit.Case, async: false # must be false!

    setup_all do
      Tesla.Mock.mock_global fn
        env -> # ...
      end

      :ok
    end

    # ...
  end
  ```

  **WARNING**: Using global mocks may affect tests with local mock
  (because of fallback to global mock in case local one is not found)
  """

  defmodule Error do
    defexception env: nil, ex: nil, stacktrace: []

    def message(%__MODULE__{ex: nil}) do
      """
      There is no mock set for process #{inspect(self())}.
      Use Tesla.Mock.mock/1 to mock HTTP requests.

      See https://github.com/teamon/tesla#testing
      """
    end

    def message(%__MODULE__{env: env, ex: %FunctionClauseError{} = ex, stacktrace: stacktrace}) do
      """
      Request not mocked

      The following request was not mocked:

      #{inspect(env, pretty: true)}

      #{Exception.format(:error, ex, stacktrace)}
      """
    end
  end

  ## PUBLIC API

  @doc """
  Setup mocks for current test.

  This mock will only be available to the current process.
  """
  @spec mock((Tesla.Env.t -> Tesla.Env.t | {integer, map, any})) :: no_return
  def mock(fun) when is_function(fun), do: pdict_set(fun)

  @doc """
  Setup global mocks.

  **WARNING**: This mock will be available to ALL processes.
  It might cause conflicts when running tests in parallel!
  """
  @spec mock_global((Tesla.Env.t -> Tesla.Env.t | {integer, map, any})) :: no_return
  def mock_global(fun) when is_function(fun), do: agent_set(fun)

  ## ADAPTER IMPLEMENTATION

  def call(env, _opts) do
    case pdict_get() || agent_get() do
      nil ->
        raise Tesla.Mock.Error, env: env
      fun ->
        case rescue_call(fun, env) do
          {status, headers, body} ->
            %{env | status: status, headers: headers, body: body}
          %Tesla.Env{} = env ->
            env
        end
    end
  end

  defp pdict_set(fun), do: Process.put(__MODULE__, fun)
  defp pdict_get, do: Process.get(__MODULE__)

  defp agent_set(fun), do: {:ok, _pid} = Agent.start_link(fn -> fun end, name: __MODULE__)
  defp agent_get do
    case Process.whereis(__MODULE__) do
      nil -> nil
      pid -> Agent.get(pid, fn f -> f end)
    end
  end

  defp rescue_call(fun, env) do
    fun.(env)
  rescue
    ex in FunctionClauseError ->
      raise Tesla.Mock.Error, env: env, ex: ex, stacktrace: System.stacktrace()
  end
end

defmodule Tesla.Mock do
  @moduledoc """
  Mock adapter for better testing.

  ### Setup

  ```
  # config/test.exs
  config :tesla, adapter: :mock

  # in case MyClient defines specific adapter with `adapter :specific`
  config :tesla, MyClient, adapter: :mock
  ```

  ### Example test
  ```
  defmodule MyAppTest do
    use ExUnit.Case

    setup do
      Tesla.Mock.mock fn
        %{method: :get} ->
          %Tesla.Env{status: 200, body: "hello"}
      end

      :ok
    end

    test "list things" do
      assert %Tesla.Env{} = env = MyApp.get("...")
      assert env.status == 200
      assert env.body == "hello"
    end
  end
  ```

  ### Setting up mocks
  ```
  # Match on method & url and return whole Tesla.Env
  Tesla.Mock.mock fn
    %{method: :get,  url: "http://example.com/list"} ->
      %Tesla.Env{status: 200, body: "hello"}
  end

  # You can use any logic required
  Tesla.Mock.mock fn env ->
    case env.url do
      "http://example.com/list" ->
        %Tesla.Env{status: 200, body: "ok!"}
      _ ->
        %Tesla.Env{status: 404, body: "NotFound"}
  end

  # mock will also accept short version of response
  # in the form of {status, headers, body}
  Tesla.Mock.mock fn
    %{method: :post} -> {201, %{}, %{id: 42}}
  end
  ```

  ### Global mocks
  By default, mocks are bound to the current process,
  i.e. the process running a single test case.
  This design allows proper isolation between test cases
  and make testing in parallel (`async: true`) possible.

  While this style is recommended, there is one drawback:
  if Tesla client is called from different process then the
  test case it will not get proper mock.

  To solve this issue it is possible to setup a global mock
  using `mock_global/1` function.

  ```
  defmodule MyTest do
    use ExUnit.Case, async: false # must be false!

    setup_all do
      Tesla.Mock.mock_global fn
        env -> # ...
      end

      :ok
    end

    # ...
  end
  ```

  **WARNING**: Using global mocks may affect tests with local mock
  (because of fallback to global mock in case local one is not found)
  """

  defmodule Error do
    defexception env: nil, ex: nil, stacktrace: []

    def message(%__MODULE__{ex: nil}) do
      """
      There is no mock set for process #{inspect(self())}.
      Use Tesla.Mock.mock/1 to mock HTTP requests.

      See https://github.com/teamon/tesla#testing
      """
    end

    def message(%__MODULE__{env: env, ex: %FunctionClauseError{} = ex, stacktrace: stacktrace}) do
      """
      Request not mocked

      The following request was not mocked:

      #{inspect(env, pretty: true)}

      #{Exception.format(:error, ex, stacktrace)}
      """
    end
  end

  ## PUBLIC API

  @doc """
  Setup mocks for current test.

  This mock will only be available to the current process.
  """
  @spec mock((Tesla.Env.t -> Tesla.Env.t | {integer, map, any})) :: no_return
  def mock(fun) when is_function(fun), do: pdict_set(fun)

  @doc """
  Setup global mocks.

  **WARNING**: This mock will be available to ALL process.
  It might cause conflicts when running tests in parallel!
  """
  @spec mock_global((Tesla.Env.t -> Tesla.Env.t | {integer, map, any})) :: no_return
  def mock_global(fun) when is_function(fun), do: agent_set(fun)

  ## ADAPTER IMPLEMENTATION

  def call(env, _opts) do
    case pdict_get() || agent_get() do
      nil ->
        raise Tesla.Mock.Error, env: env
      fun ->
        case rescue_call(fun, env) do
          {status, headers, body} ->
            %{env | status: status, headers: headers, body: body}
          %Tesla.Env{} = env ->
            env
        end
    end
  end

  defp pdict_set(fun), do: Process.put(__MODULE__, fun)
  defp pdict_get, do: Process.get(__MODULE__)

  defp agent_set(fun), do: {:ok, _pid} = Agent.start_link(fn -> fun end, name: __MODULE__)
  defp agent_get do
    case Process.whereis(__MODULE__) do
      nil -> nil
      pid -> Agent.get(pid, fn f -> f end)
    end
  end

  defp rescue_call(fun, env) do
    fun.(env)
  rescue
    ex in FunctionClauseError ->
      raise Tesla.Mock.Error, env: env, ex: ex, stacktrace: System.stacktrace()
  end
end


trailing spaces

trailing spaces

defmodule ExTwilio.Api do
  @moduledoc """
  Provides a basic HTTP interface to allow easy communication with the Twilio
  API, by wrapping `HTTPotion`.

  ## Examples

  Requests are made to the Twilio API by passing in a resource module into one
  of this `Api` module's functions. The correct URL to the resource is inferred
  from the module name.

      ExTwilio.Api.find(Resource, "sid")
      %Resource{ sid: "sid", ... }

  Items are returned as instances of the given module's struct. For more
  details, see the documentation for each function.
  """

  use HTTPoison.Base

  alias ExTwilio.Config
  alias ExTwilio.Parser
  alias ExTwilio.UrlGenerator, as: Url
  alias __MODULE__ # Necessary for mocks in tests

  @type data :: map | list

  @doc """
  Find a given resource in the Twilio API by its SID.

  ## Examples

  If the resource was found, `find/2` will return a two-element tuple in this
  format, `{:ok, item}`.

      ExTwilio.Api.find(ExTwilio.Call, "<sid here>")
      {:ok, %Call{ ... }}

  If the resource could not be loaded, `find/2` will return a 3-element tuple
  in this format, `{:error, message, code}`. The `code` is the HTTP status code
  returned by the Twilio API, for example, 404.

      ExTwilio.Api.find(ExTwilio.Call, "nonexistent sid")
      {:error, "The requested resource couldn't be found...", 404}
  """
  @spec find(atom, String.t | nil, list) :: Parser.success | Parser.error
  def find(module, sid, options \\ []) do
    module
    |> Url.build_url(sid, options)
    |> Api.get!()
    |> Parser.parse(module)
  end

  @doc """
  Create a new resource in the Twilio API with a POST request.

  ## Examples

      ExTwilio.Api.create(ExTwilio.Call, [to: "1112223333", from: "4445556666"])
      {:ok, %Call{ ... }}

      ExTwilio.Api.create(ExTwilio.Call, [])
      {:error, "No 'To' number is specified", 400}
  """
  @spec create(atom, data, list) :: Parser.success | Parser.error
  def create(module, data, options \\ []) do
    data = format_data(data)
    module
    |> Url.build_url(nil, options)
    |> Api.post!(data)
    |> Parser.parse(module)
  end

  @doc """
  Update an existing resource in the Twilio Api.

  ## Examples

      ExTwilio.Api.update(ExTwilio.Call, "<sid>", [status: "canceled"])
      {:ok, %Call{ status: "canceled" ... }}

      ExTwilio.Api.update(ExTwilio.Call, "nonexistent", [status: "complete"])
      {:error, "The requested resource ... was not found", 404}
  """
  @spec update(atom, String.t, data, list) :: Parser.success | Parser.error
  def update(module, sid, data, options \\ [])
  def update(module, sid, data, options) when is_binary(sid), do: do_update(module, sid, data, options)
  def update(module, %{sid: sid}, data, options), do: do_update(module, sid, data, options)
  defp do_update(module, sid, data, options) do
    data = format_data(data)
    module
    |> Url.build_url(sid, options)
    |> Api.post!(data)
    |> Parser.parse(module)
  end

  @doc """
  Destroy an existing resource in the Twilio Api.

  ## Examples

      ExTwilio.Api.destroy(ExTwilio.Call, "<sid>")
      :ok

      ExTwilio.Api.destroy(ExTwilio.Call, "nonexistent")
      {:error, "The requested resource ... was not found", 404}
  """
  @spec destroy(atom, String.t) :: Parser.success_delete | Parser.error
  def destroy(module, sid, options \\ [])
  def destroy(module, sid, options) when is_binary(sid), do: do_destroy(module, sid, options)
  def destroy(module, %{sid: sid}, options), do: do_destroy(module, sid, options)
  defp do_destroy(module, sid, options) do
    module
    |> Url.build_url(sid, options)
    |> Api.delete!
    |> Parser.parse(module)
  end

  @doc """
  Builds custom auth header for subaccounts

  ## Examples
    iex> ExTwilio.Api.auth_header([account: 123, token: 123])
    ["Authorization": "Basic MTIzOjEyMw=="]

    iex> ExTwilio.Api.auth_header([], {nil, 2})
    []

  """
  @spec auth_header(options :: list) :: list
  def auth_header(options)  do
    auth_header([], {options[:account], options[:token]})
  end

  @doc """
  Builds custom auth header for subaccounts
  handles master account case if :"Authorization"
  custom header isn't present

  ## Examples

    iex> ExTwilio.Api.auth_header([], {123, 123})
    ["Authorization": "Basic MTIzOjEyMw=="]

    iex> ExTwilio.Api.auth_header(["Authorization": "Basic BASE64=="], {123, 123})
    ["Authorization": "Basic BASE64=="]

  """
  @spec auth_header(headers :: list, auth :: tuple) :: list
  def auth_header(headers, {sid, token}) when not is_nil(sid) and not is_nil(token) do
    case Keyword.has_key?(headers, :"Authorization") do
      true -> headers
      false ->
        auth = Base.encode64("#{sid}:#{token}")
        headers
        |> Keyword.put(:"Authorization", "Basic #{auth}")
    end
  end

  def auth_header(headers, _), do: headers

  ###
  # HTTPotion API
  ###

  @doc """
  Automatically adds the correct headers to each API request.
  """
  @spec process_request_headers(list) :: list
  def process_request_headers(headers \\ []) do
    headers
    |> Keyword.put(:"Content-Type", "application/x-www-form-urlencoded; charset=UTF-8")
    |> auth_header({Config.account_sid, Config.auth_token})
  end

  @spec format_data(data) :: binary
  def format_data(data) when is_map(data) do
    data
    |> Map.to_list
    |> Url.to_query_string
  end
  def format_data(data) when is_list(data) do
    Url.to_query_string(data)
  end
  def format_data(data), do: data

end

defmodule ExTwilio.Api do
  @moduledoc """
  Provides a basic HTTP interface to allow easy communication with the Twilio
  API, by wrapping `HTTPotion`.

  ## Examples

  Requests are made to the Twilio API by passing in a resource module into one
  of this `Api` module's functions. The correct URL to the resource is inferred
  from the module name.

      ExTwilio.Api.find(Resource, "sid")
      %Resource{ sid: "sid", ... }

  Items are returned as instances of the given module's struct. For more
  details, see the documentation for each function.
  """

  use HTTPoison.Base

  alias ExTwilio.Config
  alias ExTwilio.Parser
  alias ExTwilio.UrlGenerator, as: Url
  alias __MODULE__ # Necessary for mocks in tests

  @type data :: map | list

  @doc """
  Find a given resource in the Twilio API by its SID.

  ## Examples

  If the resource was found, `find/2` will return a two-element tuple in this
  format, `{:ok, item}`.

      ExTwilio.Api.find(ExTwilio.Call, "<sid here>")
      {:ok, %Call{ ... }}

  If the resource could not be loaded, `find/2` will return a 3-element tuple
  in this format, `{:error, message, code}`. The `code` is the HTTP status code
  returned by the Twilio API, for example, 404.

      ExTwilio.Api.find(ExTwilio.Call, "nonexistent sid")
      {:error, "The requested resource couldn't be found...", 404}
  """
  @spec find(atom, String.t | nil, list) :: Parser.success | Parser.error
  def find(module, sid, options \\ []) do
    module
    |> Url.build_url(sid, options)
    |> Api.get!()
    |> Parser.parse(module)
  end

  @doc """
  Create a new resource in the Twilio API with a POST request.

  ## Examples

      ExTwilio.Api.create(ExTwilio.Call, [to: "1112223333", from: "4445556666"])
      {:ok, %Call{ ... }}

      ExTwilio.Api.create(ExTwilio.Call, [])
      {:error, "No 'To' number is specified", 400}
  """
  @spec create(atom, data, list) :: Parser.success | Parser.error
  def create(module, data, options \\ []) do
    data = format_data(data)
    module
    |> Url.build_url(nil, options)
    |> Api.post!(data)
    |> Parser.parse(module)
  end

  @doc """
  Update an existing resource in the Twilio Api.

  ## Examples

      ExTwilio.Api.update(ExTwilio.Call, "<sid>", [status: "canceled"])
      {:ok, %Call{ status: "canceled" ... }}

      ExTwilio.Api.update(ExTwilio.Call, "nonexistent", [status: "complete"])
      {:error, "The requested resource ... was not found", 404}
  """
  @spec update(atom, String.t, data, list) :: Parser.success | Parser.error
  def update(module, sid, data, options \\ [])
  def update(module, sid, data, options) when is_binary(sid), do: do_update(module, sid, data, options)
  def update(module, %{sid: sid}, data, options), do: do_update(module, sid, data, options)
  defp do_update(module, sid, data, options) do
    data = format_data(data)
    module
    |> Url.build_url(sid, options)
    |> Api.post!(data)
    |> Parser.parse(module)
  end

  @doc """
  Destroy an existing resource in the Twilio Api.

  ## Examples

      ExTwilio.Api.destroy(ExTwilio.Call, "<sid>")
      :ok

      ExTwilio.Api.destroy(ExTwilio.Call, "nonexistent")
      {:error, "The requested resource ... was not found", 404}
  """
  @spec destroy(atom, String.t) :: Parser.success_delete | Parser.error
  def destroy(module, sid, options \\ [])
  def destroy(module, sid, options) when is_binary(sid), do: do_destroy(module, sid, options)
  def destroy(module, %{sid: sid}, options), do: do_destroy(module, sid, options)
  defp do_destroy(module, sid, options) do
    module
    |> Url.build_url(sid, options)
    |> Api.delete!
    |> Parser.parse(module)
  end

  @doc """
  Builds custom auth header for subaccounts

    iex> ExTwilio.Api.auth_header([account: 123, token: 123])
    ["Authorization": "Basic MTIzOjEyMw=="]

  Missing credential pair

    iex> ExTwilio.Api.auth_header([], {nil, 2})
    []

  """
  @spec auth_header(options :: list) :: list
  def auth_header(options)  do
    auth_header([], {options[:account], options[:token]})
  end

  @doc """
  Builds custom auth header for subaccounts
  handles master account case if :"Authorization"
  custom header isn't present

  Without header defined

    iex> ExTwilio.Api.auth_header([], {123, 123})
    ["Authorization": "Basic MTIzOjEyMw=="]

  With header defined

    iex> ExTwilio.Api.auth_header(["Authorization": "Basic BASE64=="], {123, 123})
    ["Authorization": "Basic BASE64=="]
  """
  @spec auth_header(headers :: list, auth :: tuple) :: list
  def auth_header(headers, {sid, token}) when not is_nil(sid) and not is_nil(token) do
    case Keyword.has_key?(headers, :"Authorization") do
      true -> headers
      false ->
        auth = Base.encode64("#{sid}:#{token}")

        headers
        |> Keyword.put(:"Authorization", "Basic #{auth}")
    end
  end

  def auth_header(headers, _), do: headers

  ###
  # HTTPotion API
  ###

  @doc """
  Automatically adds the correct headers to each API request.
  """
  @spec process_request_headers(list) :: list
  def process_request_headers(headers \\ []) do
    headers
    |> Keyword.put(:"Content-Type", "application/x-www-form-urlencoded; charset=UTF-8")
    |> auth_header({Config.account_sid, Config.auth_token})
  end

  @spec format_data(data) :: binary
  def format_data(data) when is_map(data) do
    data
    |> Map.to_list
    |> Url.to_query_string
  end
  def format_data(data) when is_list(data) do
    Url.to_query_string(data)
  end

  def format_data(data), do: data

end


fix: also ignore style and script elements

defmodule Microformats2 do
  def parse(url) do
    response = HTTPotion.get url

    if HTTPotion.Response.success?(response) do
      parse(response.body, url)
    else
      :error
    end
  end

  def parse(content, url) do
    doc = Floki.parse(content) |>
      Floki.filter_out("template") |>
      Floki.filter_out("style") |>
      Floki.filter_out("script")

    rels = Microformats2.Rels.parse(doc, url)
    items = Microformats2.Items.parse(doc, doc, url)

    %{items: items, rels: rels[:rels], rel_urls: rels[:rel_urls]}
  end

  def attr_list(node, attr \\ "class") do
    Floki.attribute(node, attr) |> List.first |> to_string |> String.split(" ", trim: true)
  end

  def blank?(nil), do: true
  def blank?(""), do: true
  def blank?([]), do: true
  def blank?(_), do: false

  def stripped_or_nil(nil), do: nil
  def stripped_or_nil(val), do: String.strip(val)

  def is_rootlevel?(node) when is_tuple(node) do
    attr_list(node, "class") |>
      Enum.any?(fn(cls) -> is_a?(cls, "h") end)
  end
  def is_rootlevel?(class_name) when is_bitstring(class_name) do
    is_a?(class_name, "h")
  end

  def is_a?("h-" <> _, wanted), do: wanted == "h"
  def is_a?("p-" <> _, wanted), do: wanted == "p"
  def is_a?("e-" <> _, wanted), do: wanted == "e"
  def is_a?("u-" <> _, wanted), do: wanted == "u"
  def is_a?("dt-" <> _, wanted), do: wanted == "dt"
  def is_a?(_, _), do: false

  def abs_uri(url, base_url, doc) do
    parsed = URI.parse(url)
    parsed_base = URI.parse(base_url)

    cond do
      not Microformats2.blank?(parsed.scheme) -> # absolute URI
        url
      Microformats2.blank?(parsed.scheme) and not Microformats2.blank?(parsed.host) -> # protocol relative URI
        URI.to_string(%{parsed | scheme: parsed_base.scheme})
      true ->
        base_element = Floki.find(doc, "base")

        new_base = if base_element == nil or Microformats2.blank?(Floki.attribute(base_element, "href")) do
          base_url
        else
          abs_uri(Floki.attribute(base_element, "href") |> List.first,
                  base_url, [])
        end

        parsed_new_base = URI.parse(new_base)
        new_path = Path.expand(parsed.path || "/", Path.dirname(parsed_new_base.path || "/"))

        URI.to_string(%{parsed | scheme: parsed_new_base.scheme, host: parsed_new_base.host, path: new_path})
    end
  end
end

defmodule Microformats2 do
  def parse(url) do
    response = HTTPotion.get url

    if HTTPotion.Response.success?(response) do
      parse(response.body, url)
    else
      :error
    end
  end

  def parse(content, url) do
    doc = Floki.parse(content) |> Floki.filter_out("template")
    rels = Microformats2.Rels.parse(doc, url)
    items = Microformats2.Items.parse(doc, doc, url)

    %{items: items, rels: rels[:rels], rel_urls: rels[:rel_urls]}
  end

  def attr_list(node, attr \\ "class") do
    Floki.attribute(node, attr) |> List.first |> to_string |> String.split(" ", trim: true)
  end

  def blank?(nil), do: true
  def blank?(""), do: true
  def blank?([]), do: true
  def blank?(_), do: false

  def stripped_or_nil(nil), do: nil
  def stripped_or_nil(val), do: String.strip(val)

  def is_rootlevel?(node) when is_tuple(node) do
    attr_list(node, "class") |>
      Enum.any?(fn(cls) -> is_a?(cls, "h") end)
  end
  def is_rootlevel?(class_name) when is_bitstring(class_name) do
    is_a?(class_name, "h")
  end

  def is_a?("h-" <> _, wanted), do: wanted == "h"
  def is_a?("p-" <> _, wanted), do: wanted == "p"
  def is_a?("e-" <> _, wanted), do: wanted == "e"
  def is_a?("u-" <> _, wanted), do: wanted == "u"
  def is_a?("dt-" <> _, wanted), do: wanted == "dt"
  def is_a?(_, _), do: false

  def abs_uri(url, base_url, doc) do
    parsed = URI.parse(url)
    parsed_base = URI.parse(base_url)

    cond do
      not Microformats2.blank?(parsed.scheme) -> # absolute URI
        url
      Microformats2.blank?(parsed.scheme) and not Microformats2.blank?(parsed.host) -> # protocol relative URI
        URI.to_string(%{parsed | scheme: parsed_base.scheme})
      true ->
        base_element = Floki.find(doc, "base")

        new_base = if base_element == nil or Microformats2.blank?(Floki.attribute(base_element, "href")) do
          base_url
        else
          abs_uri(Floki.attribute(base_element, "href") |> List.first,
                  base_url, [])
        end

        parsed_new_base = URI.parse(new_base)
        new_path = Path.expand(parsed.path || "/", Path.dirname(parsed_new_base.path || "/"))

        URI.to_string(%{parsed | scheme: parsed_new_base.scheme, host: parsed_new_base.host, path: new_path})
    end
  end
end


tcp print message

tcp print message

defmodule Tcp do
	use Supervisor
	def atom_join(x, y) do
		a=to_string(x) <> "." <> to_string(y)
		String.to_atom(a)
	end
	def start_link(id, func1, func2) do
		import Supervisor.Spec, warn: false
		children = [
			worker(__MODULE__, [func1, id, "/", {0,0,0,0}], function: :run),
			worker(__MODULE__, [func2, :tcp_internal, "/priv/", {127,0,0,1}], function: :run, id: :tcp_internal),
		]
		opts = [strategy: :one_for_one, name: Tcp.Supervisor]
		Supervisor.start_link(children, opts)
	end
	def run(func, id, r, ip) do
		routes = [ {r, Tcp.Handler, [func, ip]}	]
		dispatch = :cowboy_router.compile([{:_, routes}])
		p = Port.port
		if id == :tcp_internal do p = p+1000 end
		opts = [port: p, ip: ip]
		env = [env: [dispatch: dispatch]]
		{:ok, _pid} = :cowboy.start_http(atom_join(:http, id), 100, opts, env)
	end
	def get_local(ip, port, x) do get(ip, port, x, '/priv') end
	def get(ip, port, a, y \\ '') do
		url = 'http://' ++ to_char_list(ip) ++ ':' ++ to_char_list(to_string(port)) ++ y
		content_type = 'application/octet-stream'
		request = {url, [], content_type, PackWrap.pack(a)}
		#:httpc.request(:post, request, [{:timeout, 2000}], [])#hopefully 2 seconds?
		x = :httpc.request(:post, request, [], [])
		case x do
			{:ok, z} ->
				b = z |> elem(2)
				if b == [] do [] else PackWrap.unpack(b) end
			{:error, :socket_closed_remotely} -> get(ip, port, a, y)
			{:error, {:failed_connect, _}} -> nil
			x ->
				IO.puts("TCP WEIRD #{inspect x}")
				x
		end
	end
end
defmodule Tcp.Handler do
	def init({:tcp, :http}, req, opts) do
		{:ok, req, opts}
	end
	def handle(req, opts) do
		[func, ip] = opts
		f = fn(x) -> tl(x) end
		if ip == {127,0,0,1} do
			f = fn(x) -> tl(tl(tl(tl(tl(tl(x)))))) end
		end
		headers = [{"content-type", "text/plain"},
							 {"access-control-allow-origin", "*"}]
		IO.puts("tcp rec #{inspect req}")
		body = req |> elem(21) |> PackWrap.unpack |> func.() |> PackWrap.pack
		{:ok, resp} = :cowboy_req.reply(200, headers, body, req)
		{:ok, resp, func}#why is func there???
	end
	def terminate(_reason, _req, _state) do
		:ok
	end
end

defmodule Tcp do
	use Supervisor
	def atom_join(x, y) do
		a=to_string(x) <> "." <> to_string(y)
		String.to_atom(a)
	end
	def start_link(id, func1, func2) do
		import Supervisor.Spec, warn: false
		children = [
			worker(__MODULE__, [func1, id, "/", {0,0,0,0}], function: :run),
			worker(__MODULE__, [func2, :tcp_internal, "/priv/", {127,0,0,1}], function: :run, id: :tcp_internal),
		]
		opts = [strategy: :one_for_one, name: Tcp.Supervisor]
		Supervisor.start_link(children, opts)
	end
	def run(func, id, r, ip) do
		routes = [ {r, Tcp.Handler, [func, ip]}	]
		dispatch = :cowboy_router.compile([{:_, routes}])
		p = Port.port
		if id == :tcp_internal do p = p+1000 end
		opts = [port: p, ip: ip]
		env = [env: [dispatch: dispatch]]
		{:ok, _pid} = :cowboy.start_http(atom_join(:http, id), 100, opts, env)
	end
	def get_local(ip, port, x) do get(ip, port, x, '/priv') end
	def get(ip, port, a, y \\ '') do
		url = 'http://' ++ to_char_list(ip) ++ ':' ++ to_char_list(to_string(port)) ++ y
		content_type = 'application/octet-stream'
		request = {url, [], content_type, PackWrap.pack(a)}
		#:httpc.request(:post, request, [{:timeout, 2000}], [])#hopefully 2 seconds?
		x = :httpc.request(:post, request, [], [])
		case x do
			{:ok, z} ->
				b = z |> elem(2)
				if b == [] do [] else PackWrap.unpack(b) end
			{:error, :socket_closed_remotely} -> get(ip, port, a, y)
			{:error, {:failed_connect, _}} -> nil
			x ->
				IO.puts("TCP WEIRD #{inspect x}")
				x
		end
	end
end
defmodule Tcp.Handler do
	def init({:tcp, :http}, req, opts) do
		{:ok, req, opts}
	end
	def handle(req, opts) do
		[func, ip] = opts
		f = fn(x) -> tl(x) end
		if ip == {127,0,0,1} do
			f = fn(x) -> tl(tl(tl(tl(tl(tl(x)))))) end
		end
		headers = [{"content-type", "text/plain"},
							 {"access-control-allow-origin", "*"}]
		#IO.puts("tcp rec #{inspect req}")
		body = req |> elem(21) |> PackWrap.unpack |> func.() |> PackWrap.pack
		{:ok, resp} = :cowboy_req.reply(200, headers, body, req)
		{:ok, resp, func}#why is func there???
	end
	def terminate(_reason, _req, _state) do
		:ok
	end
end


warnings-as-errors

warnings-as-errors

defmodule OpenAperture.Manager.Controllers.ProductDeploymentSteps do
  require Logger

  use OpenAperture.Manager.Web, :controller
  use Timex

  alias OpenAperture.Manager.Repo
  alias OpenAperture.Manager.DB.Queries.ProductDeployment, as: ProductDeploymentQuery
  alias OpenAperture.Manager.DB.Models.ProductDeploymentStep
  alias OpenAperture.Manager.DB.Queries.ProductDeploymentStep, as: ProductDeploymentStepQuery

  @deployment_sendable_fields [:id, :product_id, :product_deployment_plan_id, :product_environment_id, :execution_options, :completed, :duration, :output, :inserted_at, :updated_at]
  @deployment_steps_sendable_fields [:id, :product_deployment_plan_step_id, :product_deployment_plan_step_type, :duration, :successful, :execution_options, :output, :sequence, :inserted_at, :updated_at]

  # GET /products/:product_name/deployments/:deployment_id/steps
  def swaggerdoc_index, do: %{
    description: "Retrieve all ProductDeploymentSteps for a ProductDeployment",
    response_schema: %{"title" => "ProductDeploymentSteps", "type": "array", "items": %{"$ref": "#/definitions/OpenAperture.Manager.DB.Models.ProductDeploymentStep"}},
    parameters: [%{
      "name" => "product_name",
      "in" => "path",
      "description" => "Name of the Product",
      "required" => true,
      "type" => "string"
    },
    %{
      "name" => "deployment_id",
      "in" => "path",
      "description" => "ProductDeployment identifier",
      "required" => true,
      "type" => "integer"
    }]
  }
  @spec index(Plug.Conn.t, [any]) :: Plug.Conn.t
  def index(conn, %{"deployment_id" => deployment_id} = _params) do
    json conn, FormatHelper.to_sendable(Repo.all(ProductDeploymentStepQuery.get_steps_of_deployment(deployment_id)), @deployment_steps_sendable_fields)
  end

  def swaggerdoc_show, do: %{
    description: "Retrieve a ProductDeploymentStep for a ProductDeployment",
    response_schema: %{"title" => "ProductDeploymentSteps", "type": "array", "items": %{"$ref": "#/definitions/OpenAperture.Manager.DB.Models.ProductDeploymentStep"}},
    parameters: [%{
      "name" => "product_name",
      "in" => "path",
      "description" => "Name of the Product",
      "required" => true,
      "type" => "string"
    },
    %{
      "name" => "deployment_id",
      "in" => "path",
      "description" => "ProductDeployment identifier",
      "required" => true,
      "type" => "integer"
    },
    %{
      "name" => "step_id",
      "in" => "path",
      "description" => "ProductDeploymentStep identifier",
      "required" => true,
      "type" => "integer"
    }]
  }
  @spec show(Plug.Conn.t, [any]) :: Plug.Conn.t
  def show(conn, %{"product_name" => _product_name, "deployment_id" => deployment_id, "step_id" => step_id} = _params) do
    case ProductDeploymentStepQuery.get_step_of_deployment(deployment_id, step_id) |> Repo.one do
      nil ->
        conn
        |> put_status(:not_found)
        |> json ResponseBodyFormatter.error_body(:not_found, "ProductDeploymentStep")
      step ->
        json conn, FormatHelper.to_sendable(step, @deployment_steps_sendable_fields)
    end
  end

  # POST /products/:product_name/deployments/:deployment_id/steps
  def swaggerdoc_create, do: %{
    description: "Create a ProductDeploymentStep" ,
    parameters: [%{
      "name" => "product_name",
      "in" => "path",
      "description" => "Name of the Product",
      "required" => true,
      "type" => "string"
    },
    %{
      "name" => "deployment_id",
      "in" => "path",
      "description" => "ProductDeployment identifier",
      "required" => true,
      "type" => "integer"
    },
    %{
      "name" => "type",
      "in" => "body",
      "description" => "The new ProductDeploymentStep",
      "required" => true,
      "schema": %{"$ref": "#/definitions/OpenAperture.Manager.DB.Models.ProductDeploymentStep"}
    }]
  }
  @spec create(Plug.Conn.t, [any]) :: Plug.Conn.t
  def create(conn, %{"product_name" => product_name, "deployment_id" => deployment_id} = params) do
    product_name = URI.decode(product_name)

    case ProductDeploymentQuery.get_product_and_deployment(product_name, deployment_id) |> Repo.one do
      nil ->
        conn
        |> put_status(:not_found)
        |> json ResponseBodyFormatter.error_body(:not_found, "ProductDeployment")
      {_product, deployment} ->
        params = Map.put(params, "product_deployment_id", deployment.id)
        changeset = ProductDeploymentStep.new(params)

        if changeset.valid? do
          step = Repo.insert!(changeset)

          path = "#{step.id}"
          conn
          |> put_resp_header("location", path)
          |> resp :created, ""
        else
          conn
          |> put_status(:bad_request)
          |> json ResponseBodyFormatter.error_body(changeset.errors, "ProductDeploymentStep")
        end
    end
  end

  # This clause will only be hit if the request was missing a "plan_name" field
  # POST /products/:product_name/deployments/:deployment_id/steps
  def create(conn, _params) do
    conn
    |> put_status(:bad_request)
    |> json ResponseBodyFormatter.error_body(:bad_request, "ProductDeployment")
  end

  #PUT /products/:product_name/deployments/:deployment_id/steps/:step_id
  def swaggerdoc_update, do: %{
    description: "Update a ProductDeploymentStep" ,
    parameters: [%{
      "name" => "product_name",
      "in" => "path",
      "description" => "Name of the Product",
      "required" => true,
      "type" => "string"
    },
    %{
      "name" => "deployment_id",
      "in" => "path",
      "description" => "ProductDeployment identifier",
      "required" => true,
      "type" => "integer"
    },
    %{
      "name" => "step_id",
      "in" => "path",
      "description" => "ProductDeploymentStep identifier",
      "required" => true,
      "type" => "integer"
    },
    %{
      "name" => "type",
      "in" => "body",
      "description" => "The updated ProductDeploymentStep",
      "required" => true,
      "schema": %{"$ref": "#/definitions/OpenAperture.Manager.DB.Models.ProductDeploymentStep"}
    }]
  }
  @spec update(Plug.Conn.t, [any]) :: Plug.Conn.t
  def update(conn, %{"deployment_id" => deployment_id, "step_id" => step_id} = params) do
    case ProductDeploymentStepQuery.get_step_of_deployment(deployment_id, step_id) |> Repo.one do
      nil ->
        conn
        |> put_status(:not_found)
        |> json ResponseBodyFormatter.error_body(:not_found, "ProductDeploymentStep")
      step ->
        changeset = ProductDeploymentStep.update(step, params)
        if changeset.valid? do
          Repo.update!(changeset)
          path = "#{step.id}"

          conn
          |> put_resp_header("location", path)
          |> resp :no_content, ""
        else
          conn
          |> put_status(:bad_request)
          |> json ResponseBodyFormatter.error_body(changeset.errors, "ProductEnvironment")
        end
    end
  end

  # DELETE /products/:product_name/deployments/:deployment_id/steps/:step_id
  def swaggerdoc_destroy, do: %{
    description: "Delete a ProductDeploymentStep" ,
    parameters: [%{
      "name" => "product_name",
      "in" => "path",
      "description" => "Name of the Product",
      "required" => true,
      "type" => "string"
    },
    %{
      "name" => "deployment_id",
      "in" => "path",
      "description" => "ProductDeployment identifier",
      "required" => true,
      "type" => "integer"
    },
    %{
      "name" => "step_id",
      "in" => "path",
      "description" => "ProductDeploymentStep identifier",
      "required" => true,
      "type" => "integer"
    }]
  }
  @spec destroy(Plug.Conn.t, [any]) :: Plug.Conn.t
  def destroy(conn, %{"deployment_id" => deployment_id, "step_id" => step_id}) do
    case ProductDeploymentStepQuery.get_step_of_deployment(deployment_id, step_id) |> Repo.one do
      nil ->
        conn
        |> put_status(:not_found)
        |> json ResponseBodyFormatter.error_body(:not_found, "ProductDeploymentStep")
      pds ->
        case ProductDeploymentStep.destroy(pds) do
          {:error, _reason} ->
            conn
            |> put_status(:internal_server_error)
            |> json ResponseBodyFormatter.error_body(:internal_server_error, "ProductDeploymentStep")
          _deleted_pds ->
            conn
            |> resp :no_content, ""
        end
    end
  end
end

defmodule OpenAperture.Manager.Controllers.ProductDeploymentSteps do
  require Logger

  use OpenAperture.Manager.Web, :controller
  use Timex

  alias OpenAperture.Manager.Repo
  alias OpenAperture.Manager.DB.Queries.ProductDeployment, as: ProductDeploymentQuery
  alias OpenAperture.Manager.DB.Models.ProductDeploymentStep
  alias OpenAperture.Manager.DB.Queries.ProductDeploymentStep, as: ProductDeploymentStepQuery

  @deployment_sendable_fields [:id, :product_id, :product_deployment_plan_id, :product_environment_id, :execution_options, :completed, :duration, :output, :inserted_at, :updated_at]
  @deployment_steps_sendable_fields [:id, :product_deployment_plan_step_id, :product_deployment_plan_step_type, :duration, :successful, :execution_options, :output, :sequence, :inserted_at, :updated_at]

  # GET /products/:product_name/deployments/:deployment_id/steps
  def swaggerdoc_index, do: %{
    description: "Retrieve all ProductDeploymentSteps for a ProductDeployment",
    response_schema: %{"title" => "ProductDeploymentSteps", "type": "array", "items": %{"$ref": "#/definitions/OpenAperture.Manager.DB.Models.ProductDeploymentStep"}},
    parameters: [%{
      "name" => "product_name",
      "in" => "path",
      "description" => "Name of the Product",
      "required" => true,
      "type" => "string"
    },
    %{
      "name" => "deployment_id",
      "in" => "path",
      "description" => "ProductDeployment identifier",
      "required" => true,
      "type" => "integer"
    }]
  }
  @spec index(Plug.Conn.t, [any]) :: Plug.Conn.t
  def index(conn, %{"deployment_id" => deployment_id} = _params) do
    json conn, FormatHelper.to_sendable(Repo.all(ProductDeploymentStepQuery.get_steps_of_deployment(deployment_id)), @deployment_steps_sendable_fields)
  end

  def swaggerdoc_show, do: %{
    description: "Retrieve a ProductDeploymentStep for a ProductDeployment",
    response_schema: %{"title" => "ProductDeploymentSteps", "type": "array", "items": %{"$ref": "#/definitions/OpenAperture.Manager.DB.Models.ProductDeploymentStep"}},
    parameters: [%{
      "name" => "product_name",
      "in" => "path",
      "description" => "Name of the Product",
      "required" => true,
      "type" => "string"
    },
    %{
      "name" => "deployment_id",
      "in" => "path",
      "description" => "ProductDeployment identifier",
      "required" => true,
      "type" => "integer"
    },
    %{
      "name" => "step_id",
      "in" => "path",
      "description" => "ProductDeploymentStep identifier",
      "required" => true,
      "type" => "integer"
    }]
  }
  @spec show(Plug.Conn.t, [any]) :: Plug.Conn.t
  def show(conn, %{"product_name" => _product_name, "deployment_id" => deployment_id, "step_id" => step_id} = _params) do
    case ProductDeploymentStepQuery.get_step_of_deployment(deployment_id, step_id) |> Repo.one do
      nil ->
        conn
        |> put_status(:not_found)
        |> json ResponseBodyFormatter.error_body(:not_found, "ProductDeploymentStep")
      step ->
        json conn, FormatHelper.to_sendable(step, @deployment_steps_sendable_fields)
    end
  end

  # POST /products/:product_name/deployments/:deployment_id/steps
  def swaggerdoc_create, do: %{
    description: "Create a ProductDeploymentStep" ,
    parameters: [%{
      "name" => "product_name",
      "in" => "path",
      "description" => "Name of the Product",
      "required" => true,
      "type" => "string"
    },
    %{
      "name" => "deployment_id",
      "in" => "path",
      "description" => "ProductDeployment identifier",
      "required" => true,
      "type" => "integer"
    },
    %{
      "name" => "type",
      "in" => "body",
      "description" => "The new ProductDeploymentStep",
      "required" => true,
      "schema": %{"$ref": "#/definitions/OpenAperture.Manager.DB.Models.ProductDeploymentStep"}
    }]
  }
  @spec create(Plug.Conn.t, [any]) :: Plug.Conn.t
  def create(conn, %{"product_name" => product_name, "deployment_id" => deployment_id} = params) do
    product_name = URI.decode(product_name)

    case ProductDeploymentQuery.get_product_and_deployment(product_name, deployment_id) |> Repo.one do
      nil ->
        conn
        |> put_status(:not_found)
        |> json ResponseBodyFormatter.error_body(:not_found, "ProductDeployment")
      {_product, deployment} ->
        params = Map.put(params, "product_deployment_id", deployment.id)
        changeset = ProductDeploymentStep.new(params)

        if changeset.valid? do
          step = Repo.insert!(changeset)

          path = "#{step.id}"
          conn
          |> put_resp_header("location", path)
          |> resp :created, ""
        else
          conn
          |> put_status(:bad_request)
          |> json ResponseBodyFormatter.error_body(changeset.errors, "ProductDeploymentStep")
        end
    end
  end

  # This clause will only be hit if the request was missing a "plan_name" field
  # POST /products/:product_name/deployments/:deployment_id/steps
  def create(conn, _params) do
    conn
    |> put_status(:bad_request)
    |> json ResponseBodyFormatter.error_body(:bad_request, "ProductDeployment")
  end

  #PUT /products/:product_name/deployments/:deployment_id/steps/:step_id
  def swaggerdoc_update, do: %{
    description: "Update a ProductDeploymentStep" ,
    parameters: [%{
      "name" => "product_name",
      "in" => "path",
      "description" => "Name of the Product",
      "required" => true,
      "type" => "string"
    },
    %{
      "name" => "deployment_id",
      "in" => "path",
      "description" => "ProductDeployment identifier",
      "required" => true,
      "type" => "integer"
    },
    %{
      "name" => "step_id",
      "in" => "path",
      "description" => "ProductDeploymentStep identifier",
      "required" => true,
      "type" => "integer"
    },
    %{
      "name" => "type",
      "in" => "body",
      "description" => "The updated ProductDeploymentStep",
      "required" => true,
      "schema": %{"$ref": "#/definitions/OpenAperture.Manager.DB.Models.ProductDeploymentStep"}
    }]
  }
  @spec update(Plug.Conn.t, [any]) :: Plug.Conn.t
  def update(conn, %{"deployment_id" => deployment_id, "step_id" => step_id} = params) do
    case ProductDeploymentStepQuery.get_step_of_deployment(deployment_id, step_id) |> Repo.one do
      nil ->
        conn
        |> put_status(:not_found)
        |> json ResponseBodyFormatter.error_body(:not_found, "ProductDeploymentStep")
      step ->
        changeset = ProductDeploymentStep.update(step, params)
        if changeset.valid? do
          Repo.update!(changeset)
          path = "#{step.id}"

          conn
          |> put_resp_header("location", path)
          |> resp :no_content, ""
        else
          conn
          |> put_status(:bad_request)
          |> json ResponseBodyFormatter.error_body(changeset.errors, "ProductEnvironment")
        end
    end
  end

  # DELETE /products/:product_name/deployments/:deployment_id/steps/:step_id
  def swaggerdoc_update, do: %{
    description: "Delete a ProductDeploymentStep" ,
    parameters: [%{
      "name" => "product_name",
      "in" => "path",
      "description" => "Name of the Product",
      "required" => true,
      "type" => "string"
    },
    %{
      "name" => "deployment_id",
      "in" => "path",
      "description" => "ProductDeployment identifier",
      "required" => true,
      "type" => "integer"
    },
    %{
      "name" => "step_id",
      "in" => "path",
      "description" => "ProductDeploymentStep identifier",
      "required" => true,
      "type" => "integer"
    }]
  }
  @spec destroy(Plug.Conn.t, [any]) :: Plug.Conn.t
  def destroy(conn, %{"deployment_id" => deployment_id, "step_id" => step_id}) do
    case ProductDeploymentStepQuery.get_step_of_deployment(deployment_id, step_id) |> Repo.one do
      nil ->
        conn
        |> put_status(:not_found)
        |> json ResponseBodyFormatter.error_body(:not_found, "ProductDeploymentStep")
      pds ->
        case ProductDeploymentStep.destroy(pds) do
          {:error, _reason} ->
            conn
            |> put_status(:internal_server_error)
            |> json ResponseBodyFormatter.error_body(:internal_server_error, "ProductDeploymentStep")
          _deleted_pds ->
            conn
            |> resp :no_content, ""
        end
    end
  end
end


Updated elixir anagram test with real words

Code.load_file("anagram.exs")
ExUnit.start

defmodule AnagramTest do
  use ExUnit.Case

  test "no matches" do
    matches = Anagram.match "diaper", ["hello", "world", "zombies", "pants"]
    assert matches == []
  end

  test "detect simple anagram" do
    # matches = Anagram.match "ant", ["tan", "stand", "at"]
    # assert matches == ["tan"]
  end

  test "detect multiple anagrams" do
    # matches = Anagram.match "master", ["stream", "pigeon", "maters"]
    # assert matches == ["stream", "maters"]
  end

  test "detect anagram" do
    # matches = Anagram.match "listen", %w(enlists google inlets banana)
    # assert matches == ["inlets"]
  end

  test "multiple anagrams" do
    # matches = Anagram.match "allergy", %w(gallery ballerina regally clergy largely leading)
    # assert matches == ["gallery", "regally", "largely"]
  end
end

Code.load_file("anagram.exs")
ExUnit.start

defmodule AnagramTest do
  use ExUnit.Case

  test "no matches" do
    matches = Anagram.match "diaper", ["hello", "world", "zombies", "pants"]
    assert matches == []
  end

  test "detect simple anagram" do
    # matches = Anagram.match "ba", ["ab", "abc", "bac"]
    # assert matches == ["ab"]
  end

  test "detect multiple anagrams" do
    # matches = Anagram.match "abc", ["ab", "abc", "bac"]
    # assert matches == ["abc", "bac"]
  end

  test "detect anagram" do
    # matches = Anagram.match "listen", %w(enlists google inlets banana)
    # assert matches == ["inlets"]
  end

  test "multiple anagrams" do
    # matches = Anagram.match "allergy", %w(gallery ballerina regally clergy largely leading)
    # assert matches == ["gallery", "regally", "largely"]
  end
end