fix a nil case for hash state

fix a nil case for hash state

defmodule Service.Domo do
  @behaviour Reducer

  import DB.Validation, only: [flip_v1_uuid: 1]

  alias DB.Event
  alias Reducer.State
  require Logger

  @domains [:domo]
  @types [:pull]
  def domains, do: @domains
  def types, do: @types

  def correct_type?(event) do
    Enum.member?([
      "pull"], event.type)
  end

  @spec call(list(Event.t), State.t) :: State.t
  def call(events, state)do
    {model, new_events} = Enum.filter(events, fn(event) -> correct_type?(event) end)
     |> domo_service_recursive({state.model, []})
    %State{model: model, new_events: new_events}
  end

  def domo_service_recursive([], {model, new_events}), do: {model, new_events}
  def domo_service_recursive([event | remaining_events], {model, new_events}) do
    domo_service_recursive(remaining_events, domo_service(event, {model, new_events}))
  end

  def domo_service(event, {model, new_events}) do
    store_ids = get_store_ids()
    meta = event.meta
    dataset_id = meta["dataset_id"]
    client_id = meta["client_id"]
    client_secret = meta["client_secret"]
    type = meta["out_going_type"]
    last_played = flip_v1_uuid(event.event_id)

    domo_dataset(dataset_id, client_id, client_secret)
      |> hash_file(model, type, store_ids)
      |> return_model_and_events(last_played)
  end

  def domo_dataset(dataset_id, client_id, client_secret) do
   get_access_token(client_id, client_secret)
    |> get_dataset(dataset_id)
  end

  def get_access_token(client_id, client_secret) do
    url = "https://api.domo.com/oauth/token?grant_type=client_credentials&scope=data"
    case HTTPoison.get(url, %{}, [hackney: [basic_auth: {client_id, client_secret}]]) do
      {:ok, %HTTPoison.Response{status_code: 200, body: body}} ->
        response = Poison.Parser.parse!(body)
        response["access_token"]
      {:ok, %HTTPoison.Response{status_code: code}} ->
        Logger.error("token error, status_code#{code}")
        :error
      {:error, %HTTPoison.Error{reason: reason}} ->
        Logger.error("token error, #{reason}")
        :error
    end
  end

  def get_dataset(access_token, dataset_id) do
    url = "https://api.domo.com/v1/datasets/#{dataset_id}/data?includeHeader=true&fileName=data_dump.csv"
    case HTTPoison.get(url, [{"Authorization", "bearer #{access_token}"}]) do
      {:ok, %HTTPoison.Response{status_code: 200, body: body}} ->
        body
        {:ok, %HTTPoison.Response{status_code: code}} ->
          Logger.error("dataset error, status_code#{code}")
          :error
        {:error, %HTTPoison.Error{reason: reason}} ->
          Logger.error("dataset error, #{reason}")
          :error
    end
  end

  def hash_file(body, model, type, store_ids) do
    case body do
      :error ->
        {}
      _ ->
        hash_state = is_empty?(model)
        [col_heads | values] = String.split(body, "\n", parts: 2)
        file = String.split(to_string(values), "\n")
        file |> Enum.reduce({[], col_heads, type, hash_state, store_ids}, &reducer/2)
    end
  end

  def reducer(row, {events, col_heads, type, hash_state, store_ids}) when row != "" do
    {new_events, col_heads, type, new_hash_state, store_ids} =
      case Donethat.new?(row, hash_state) do
        {true, new_hash_state} ->
          {[make_event(col_heads, type, row, store_ids) | events], col_heads, type, new_hash_state, store_ids}
        {_, new_hash_state} ->
          {events, col_heads, type, new_hash_state, store_ids}
      end
    {new_events, col_heads, type, new_hash_state, store_ids}
  end

  def reducer(row, {events, col_heads, type, hash_state, store_ids}) do
    {events, col_heads, type, hash_state, store_ids}
  end

  def is_empty?(model) when map_size(model) == 0 do
    Donethat.empty_state
  end

  def is_empty?(model) do
    case is_nil(model) or is_nil(model["hash_state"]) do
      true -> Donethat.empty_state
      false -> to_struct(HashState, model["hash_state"])
    end
  end

  def to_struct(kind, attrs) do
    struct = struct(kind)
    Enum.reduce Map.to_list(struct), struct, fn {k, _}, acc ->
      case Map.fetch(attrs, Atom.to_string(k)) do
        {:ok, v} -> %{acc | k => v}
        :error -> acc
      end
  end
end

  def make_event(col_heads, type, row, store_ids) do
    [store | _t] = String.split(row, ",", parts: 2)
    meta = build_meta_map(col_heads, row)
    event = %Event{domain: "stats",
                      meta: meta,
                      entity_id: get_entity_id(meta["STORE"] || meta["Store"], store_ids),
                      event_id: gen_event_id(),
                      realm: "nike",
                      remote_ip: "127.0.0.1",
                      type: type}
    event
  end

  def build_meta_map(col_heads, row) do
    [dc] = CSV.decode([col_heads], separator: ?,)
      |> Enum.to_list
    [dr] = CSV.decode([row], separatot: ?,)
      |> Enum.to_list
    Enum.zip(dc, dr)
      |> Map.new
  end

  def get_entity_id(store_id, stores) do
    store = to_string(store_id)
    stores[store]
  end

  def get_store_ids() do
    perhap_base_url = Application.get_env(:reducers, :perhap_base_url)
    url = perhap_base_url <> "/v1/model/storeindex/100077bd-5b34-41ac-b37b-62adbf86c1a5"
    {:ok, response} = case Mix.env do
      :test ->
        HTTPoison.get(url, [], hackney: [:insecure])
      _ ->
        HTTPoison.get(url)
    end
    {:ok, body} = Poison.decode(response.body)
    body["stores"]
  end

  def gen_event_id() do
    {uuid_v1, _} = :uuid.get_v1(:uuid.new(self(), :erlang))
    event_id = :uuid.uuid_to_string(uuid_v1)
    event_id
  end

  # case when hash_file doesn't work right, should we just record last played?
  def return_model_and_events({}, last_played) do
    model = Map.put(%{}, :last_played, last_played)
    {model, []}
  end
  def return_model_and_events({new_events, _col_heads, _type, new_hash_state, store_ids}, last_played) do
    model = Map.put(%{}, :last_played, last_played)
      |> Map.put(:hash_state, new_hash_state)
    {model, new_events}
  end

  def flipper(uuidv1) do
    [time_low, time_mid, time_high, node_hi, node_low] = String.split(uuidv1, "-")
    time_high <> "-" <> time_mid <> "-" <> time_low <> "-" <> node_hi <> "-" <> node_low
  end
end

defmodule Service.Domo do
  @behaviour Reducer

  import DB.Validation, only: [flip_v1_uuid: 1]

  alias DB.Event
  alias Reducer.State
  require Logger

  @domains [:domo]
  @types [:pull]
  def domains, do: @domains
  def types, do: @types

  def correct_type?(event) do
    Enum.member?([
      "pull"], event.type)
  end

  @spec call(list(Event.t), State.t) :: State.t
  def call(events, state)do
    {model, new_events} = Enum.filter(events, fn(event) -> correct_type?(event) end)
     |> domo_service_recursive({state.model, []})
    %State{model: model, new_events: new_events}
  end

  def domo_service_recursive([], {model, new_events}), do: {model, new_events}
  def domo_service_recursive([event | remaining_events], {model, new_events}) do
    domo_service_recursive(remaining_events, domo_service(event, {model, new_events}))
  end

  def domo_service(event, {model, new_events}) do
    store_ids = get_store_ids()
    meta = event.meta
    dataset_id = meta["dataset_id"]
    client_id = meta["client_id"]
    client_secret = meta["client_secret"]
    type = meta["out_going_type"]
    last_played = flip_v1_uuid(event.event_id)

    domo_dataset(dataset_id, client_id, client_secret)
      |> hash_file(model, type, store_ids)
      |> return_model_and_events(last_played)
  end

  def domo_dataset(dataset_id, client_id, client_secret) do
   get_access_token(client_id, client_secret)
    |> get_dataset(dataset_id)
  end

  def get_access_token(client_id, client_secret) do
    url = "https://api.domo.com/oauth/token?grant_type=client_credentials&scope=data"
    case HTTPoison.get(url, %{}, [hackney: [basic_auth: {client_id, client_secret}]]) do
      {:ok, %HTTPoison.Response{status_code: 200, body: body}} ->
        response = Poison.Parser.parse!(body)
        response["access_token"]
      {:ok, %HTTPoison.Response{status_code: code}} ->
        Logger.error("token error, status_code#{code}")
        :error
      {:error, %HTTPoison.Error{reason: reason}} ->
        Logger.error("token error, #{reason}")
        :error
    end
  end

  def get_dataset(access_token, dataset_id) do
    url = "https://api.domo.com/v1/datasets/#{dataset_id}/data?includeHeader=true&fileName=data_dump.csv"
    case HTTPoison.get(url, [{"Authorization", "bearer #{access_token}"}]) do
      {:ok, %HTTPoison.Response{status_code: 200, body: body}} ->
        body
        {:ok, %HTTPoison.Response{status_code: code}} ->
          Logger.error("dataset error, status_code#{code}")
          :error
        {:error, %HTTPoison.Error{reason: reason}} ->
          Logger.error("dataset error, #{reason}")
          :error
    end
  end

  def hash_file(body, model, type, store_ids) do
    case body do
      :error ->
        {}
      _ ->
        hash_state = is_empty?(model)
        [col_heads | values] = String.split(body, "\n", parts: 2)
        file = String.split(to_string(values), "\n")
        file |> Enum.reduce({[], col_heads, type, hash_state, store_ids}, &reducer/2)
    end
  end

  def reducer(row, {events, col_heads, type, hash_state, store_ids}) when row != "" do
    {new_events, col_heads, type, new_hash_state, store_ids} =
      case Donethat.new?(row, hash_state) do
        {true, new_hash_state} ->
          {[make_event(col_heads, type, row, store_ids) | events], col_heads, type, new_hash_state, store_ids}
        {_, new_hash_state} ->
          {events, col_heads, type, new_hash_state, store_ids}
      end
    {new_events, col_heads, type, new_hash_state, store_ids}
  end

  def reducer(row, {events, col_heads, type, hash_state, store_ids}) do
    {events, col_heads, type, hash_state, store_ids}
  end

  def is_empty?(model) when map_size(model) == 0 do
    Donethat.empty_state
  end

  def is_empty?(model) do
    to_struct(HashState, model["hash_state"])
  end

  def to_struct(kind, attrs) do
  struct = struct(kind)
  Enum.reduce Map.to_list(struct), struct, fn {k, _}, acc ->
    case Map.fetch(attrs, Atom.to_string(k)) do
      {:ok, v} -> %{acc | k => v}
      :error -> acc
    end
  end
end

  def make_event(col_heads, type, row, store_ids) do
    [store | _t] = String.split(row, ",", parts: 2)
    meta = build_meta_map(col_heads, row)
    event = %Event{domain: "stats",
                      meta: meta,
                      entity_id: get_entity_id(meta["STORE"] || meta["Store"], store_ids),
                      event_id: gen_event_id(),
                      realm: "nike",
                      remote_ip: "127.0.0.1",
                      type: type}
    event
  end

  def build_meta_map(col_heads, row) do
    [dc] = CSV.decode([col_heads], separator: ?,)
      |> Enum.to_list
    [dr] = CSV.decode([row], separatot: ?,)
      |> Enum.to_list
    Enum.zip(dc, dr)
      |> Map.new
  end

  def get_entity_id(store_id, stores) do
    store = to_string(store_id)
    stores[store]
  end

  def get_store_ids() do
    perhap_base_url = Application.get_env(:reducers, :perhap_base_url)
    url = perhap_base_url <> "/v1/model/storeindex/100077bd-5b34-41ac-b37b-62adbf86c1a5"
    {:ok, response} = case Mix.env do
      :test ->
        HTTPoison.get(url, [], hackney: [:insecure])
      _ ->
        HTTPoison.get(url)
    end
    {:ok, body} = Poison.decode(response.body)
    body["stores"]
  end

  def gen_event_id() do
    {uuid_v1, _} = :uuid.get_v1(:uuid.new(self(), :erlang))
    event_id = :uuid.uuid_to_string(uuid_v1)
    event_id
  end

  # case when hash_file doesn't work right, should we just record last played?
  def return_model_and_events({}, last_played) do
    model = Map.put(%{}, :last_played, last_played)
    {model, []}
  end
  def return_model_and_events({new_events, _col_heads, _type, new_hash_state, store_ids}, last_played) do
    model = Map.put(%{}, :last_played, last_played)
      |> Map.put(:hash_state, new_hash_state)
    {model, new_events}
  end

  def flipper(uuidv1) do
    [time_low, time_mid, time_high, node_hi, node_low] = String.split(uuidv1, "-")
    time_high <> "-" <> time_mid <> "-" <> time_low <> "-" <> node_hi <> "-" <> node_low
  end
end


Refactor pick_starting_region

Refactor pick_starting_region

defmodule CommandParser do

  def start(game_engine, logger) do
      {:ok, parser} = Task.start_link(fn->parse(game_engine, logger) end)
      parser
  end

  def send_message(parser, m) do
      send parser, {:message, m}
  end

  def send_single_int(engine, regex, msg, atom) do
    matches = Regex.run(regex, msg)
    send engine, {atom, String.to_integer List.last(matches)}
  end

  def send_single_string(engine, regex, msg, atom) do
    matches = Regex.run(regex, msg)
    send engine, {atom, List.last(matches)}
  end

  def send_list(engine, regex, msg, atom) do
     matches = Regex.run(regex, msg)
     send engine, {atom, String.split(List.last(matches))}
  end

  def parse(game_engine, logger) do
     receive do
        {:message, msg} ->
            CustomLogger.write(logger, "Command received: " <> msg)
            cond do
            Regex.match?(~r/settings timebank (\d+)/, msg) ->
                 send_single_int game_engine, ~r/settings timebank (\d+)/, msg, :initial_timebank
            Regex.match?(~r/settings time_per_move (\d+)/, msg) ->
                 send_single_int game_engine, ~r/settings time_per_move (\d+)/, msg, :time_per_move
            Regex.match?(~r/settings max_rounds (\d+)/, msg) ->
                send_single_int game_engine, ~r/settings max_rounds (\d+)/, msg, :max_rounds
            Regex.match?(~r/settings your_bot (\w+)/, msg) ->
                send_single_string game_engine, ~r/settings your_bot (\w+)/, msg, :bot_name
            Regex.match?(~r/settings opponent_bot (\w+)/, msg) ->
                send_single_string game_engine, ~r/settings opponent_bot (\w+)/, msg, :opponent_bot_name
            Regex.match?(~r/settings starting_armies (\d+)/, msg) ->
                 send_single_int game_engine, ~r/settings starting_armies (\d+)/, msg, :starting_armies
            Regex.match?(~r/settings starting_regions ((?:\d+\s*)+)/, msg) ->
                 send_list game_engine, ~r/settings starting_regions ((?:\d+\s*)+)/, msg, :starting_regions
            Regex.match?(~r/settings/, msg) -> nil
            Regex.match?(~r/setup_map/, msg) -> nil
            Regex.match?(~r/update_map/, msg) -> nil
            Regex.match?(~r/opponent_moves/, msg) -> nil
            Regex.match?(~r/go place_armies \d+/, msg) ->
                send game_engine, {:place_armies, ""}
            Regex.match?(~r/go attack\/transfer \d+/, msg) ->
                send game_engine, {:attack_transfer, ""}
            Regex.match?(~r/pick_starting_region \d+ ((?:\d+ )+)/, msg) ->
                send_list game_engine, ~r/pick_starting_region \d+ ((?:\d+\s*)+)/, msg, :starting_region_choice
            true->
              CustomLogger.write(logger, "Command Parser didn't know how to parse message: " <> msg)
              send game_engine, {:error, "Invalid Message Received"}
            end
        _ -> CustomLogger.write(logger, "Command Parser received invalid command ")
             send game_engine, {:error, "Invalid Message Received"}
     end
     parse(game_engine, logger)
  end

end

defmodule CommandParser do

  def start(game_engine, logger) do
      {:ok, parser} = Task.start_link(fn->parse(game_engine, logger) end)
      parser
  end

  def send_message(parser, m) do
      send parser, {:message, m}
  end

  def send_single_int(engine, regex, msg, atom) do
    matches = Regex.run(regex, msg)
    send engine, {atom, String.to_integer List.last(matches)}
  end

  def send_single_string(engine, regex, msg, atom) do
    matches = Regex.run(regex, msg)
    send engine, {atom, List.last(matches)}
  end

  def send_list(engine, regex, msg, atom) do
     matches = Regex.run(regex, msg)
     send engine, {atom, String.split(List.last(matches))}
  end

  def parse(game_engine, logger) do
     receive do
        {:message, msg} ->
            CustomLogger.write(logger, "Command received: " <> msg)
            cond do
            Regex.match?(~r/settings timebank (\d+)/, msg) ->
                 send_single_int game_engine, ~r/settings timebank (\d+)/, msg, :initial_timebank
            Regex.match?(~r/settings time_per_move (\d+)/, msg) ->
                 send_single_int game_engine, ~r/settings time_per_move (\d+)/, msg, :time_per_move
            Regex.match?(~r/settings max_rounds (\d+)/, msg) ->
                send_single_int game_engine, ~r/settings max_rounds (\d+)/, msg, :max_rounds
            Regex.match?(~r/settings your_bot (\w+)/, msg) ->
                send_single_string game_engine, ~r/settings your_bot (\w+)/, msg, :bot_name
            Regex.match?(~r/settings opponent_bot (\w+)/, msg) ->
                send_single_string game_engine, ~r/settings opponent_bot (\w+)/, msg, :opponent_bot_name
            Regex.match?(~r/settings starting_armies (\d+)/, msg) ->
                 send_single_int game_engine, ~r/settings starting_armies (\d+)/, msg, :starting_armies
            Regex.match?(~r/settings starting_regions ((?:\d+\s*)+)/, msg) ->
                 send_list game_engine, ~r/settings starting_regions ((?:\d+\s*)+)/, msg, :starting_regions
            Regex.match?(~r/settings/, msg) -> nil
            Regex.match?(~r/setup_map/, msg) -> nil
            Regex.match?(~r/update_map/, msg) -> nil
            Regex.match?(~r/opponent_moves/, msg) -> nil
            Regex.match?(~r/go place_armies \d+/, msg) ->
                send game_engine, {:place_armies, ""}
            Regex.match?(~r/go attack\/transfer \d+/, msg) ->
                send game_engine, {:attack_transfer, ""}
            Regex.match?(~r/pick_starting_region \d+ ((?:\d+ )+)/, msg) ->
                send_list ~r/pick_starting_region \d+ ((?:\d+\s*)+)/, msg, :starting_region_choice
            true->
              CustomLogger.write(logger, "Command Parser didn't know how to parse message: " <> msg)
              send game_engine, {:error, "Invalid Message Received"}
            end
        _ -> CustomLogger.write(logger, "Command Parser received invalid command ")
             send game_engine, {:error, "Invalid Message Received"}
     end
     parse(game_engine, logger)
  end

end


:ok should the default gen_fsm reply

:ok should the default gen_fsm reply

defmodule GenFSM.Behaviour do
  @moduledoc """
  This module is a convenience for defining GenFSM callbacks in Elixir.

  A finite state machine (FSM) is responsible for reacting to events received;
  GenFSM is an OTP behaviour that encapsulates common FSM
  functionalities.

  ## Example

  Below is an example of a GenFSM that runs a very simple minded
  coffee vending machine (CVM). The CVM treats all coins the same. If
  you press the request button then the CVM will brew coffee, if you
  have paid enough coins; if not, it will wait until you have inserted
  enough coins and then it will instantly brew the coffee, since you
  had already pressed the request button. As we told you - a very
  simple minded CVM! And greedy too. If you insert more coins than you
  need it will gladly eat them until you press the request button.

  We will leave it to the service-minded reader to improve the way the CVM
  works - we hereby declare a full disclaimer for any lawsuits that the
  behaviour of the CVM in its original state might encur.

      defmodule MyFsm do
        use GenFSM.Behaviour

          # keeping track of what is going on inside the CVM.
          # 3 is the target price for a cup of coffee
          defrecord StateData, coins: 0, price: 3

          #
          # API functions
          #

          def start_link() do
            :gen_fsm.start_link({:local, :cvm}, __MODULE__, [], [])
          end

          def insert_coin() do
            :gen_fsm.send_event(:cvm, :coin)
          end

          def request_coffee() do
            :gen_fsm.send_event(:cvm, :request_coffee)
          end

          #
          # Callbacks
          #

          def init(_args) do
            { :ok, :short_paid, StateData.new }
          end

          def short_paid(:coin, state_data = StateData[coins: c, price: p])
           when c + 1 < p do
            { :next_state, :short_paid, state_data.coins(c + 1) }
          end

          def short_paid(:coin, state_data) do
            { :next_state, :paid_in_full, &state_data.update_coins(&1 + 1) }
          end

          def short_paid(:request_coffee, state_data) do
            { :next_state, :requested_short_paid, state_data }
          end

          def requested_short_paid(:request_coffee, state_data) do
            {:next_state, :requested_short_paid, state_data }
          end

          def requested_short_paid(:coin, state_data=StateData[coins: c, price: p])
           when c+1 < p do
            { :next_state, :requested_short_paid, state_data.coins(c + 1) }
          end

          def requested_short_paid(:coin, _state_data) do
            IO.puts "Here's your coffee!"
            { :next_state, :short_paid, StateData.new }
          end

          def paid_in_full(:coin, state_data) do
            { :next_state, :paid_in_full, &state_data.update_coins(&1 + 1) }
          end

          def paid_in_full(:request_coffee, _state_data) do
            IO.puts "Here's your coffee!"
            { :next_state, :short_paid, StateData.new }
          end
        end

      { :ok, _pid } = MyFsm.start_link()

      MyFsm.insert_coin
      #=> :ok
      MyFsm.insert_coin
      #=> :ok

      MyFsm.request_coffee
      #=> :ok

      MyFsm.insert_coin
      #=> :ok
      #=> Here's your coffee!

  Notice we never call the GenFSM callbacks directly; they are called by
  OTP whenever we interact with the server throught the API. `send_event` is
  asynchronous, whereas `sync_send_event` is synchronous. For
  a GenFSM, the different values a callback can return depend
  on the type of callback.

  State handling returns for `send_event` callbacks:

      { :next_state, next_state_name, new_state_data }
      { :next_state, next_state_name, new_state_data, timeout }
      { :next_state, next_state_name, new_state_data, :hibernate }
      { :stop, reason, new_state_data }

  State handling returns for `sync_send_event` callbacks:

      { :reply, reply, next_state_name, new_state_data }
      { :reply, reply, next_state_name, new_state_data, timeout }
      { :reply, reply, next_state_name, new_state_data, :hibernate }
      { :next_state, next_state_name, new_state_data }
      { :next_state, next_state_name, new_state_data, timeout }
      { :next_state, next_state_name, new_state_data, :hibernate }
      { :stop, reason, reply, new_state_data }
      { :stop, reason, new_state_date }

  There are 6 callbacks required to be implemented in a GenFsm plus 1
  or 2 for each state. The `GenFSM.Behaviour` module defines
  `handle_sync_event`, `handle_info`, `terminate` and `code_change`
  for you. The list of callbacks are:

  * `init(args)` - invoked when the FSM is started;
  * `handle_sync_event(event, from, state_name, state_data)` - invoked to
  handle `sync_send_all_state_event` messages;
  * `handle_event(event, state_name, state_data)` - invoked to handle
  `send_all_state_event` messages;
  * `handle_info(msg, state_name, state_data)` - handle all other
  messages which are normally received by processes;
  * `terminate(reason, state_name, state_data)` - called when the FSM
  is about to terminate, useful for cleaning up;
  * `code_change(old_vsn, state, extra)` - called when the application
  code is being upgraded live (hot code swap);

  Unlike `GenServer` and `GenEvent`, the callback `init/1` is not
  implemented by default, as it requires the next state to be returned.

  For each state you need to define either or both of these:

  * `state_name(event, state_data)` - invoked to handle
  `send_event` messages;
  * `state_name(event, from, state_data)`- invoked to handle
  `sync_send_event` messages;

  If you send asynchronous events you only need to implement the
  `state_name/2` variant and vice-versa for synchronous events and
  `state_name/3`. Keep in mind that if you mix `send_event` and
  `sync_send_event` the best thing to do is to implement both
  callbacks for all states.

  Starting and sending messages to the GenFSM is done via Erlang's
  `:gen_fsm` module. For more information, please refer to the
  following:

  * http://www.erlang.org/doc/man/gen_fsm.html
  * http://www.erlang.org/doc/design_principles/fsm.html
  * http://learnyousomeerlang.com/finite-state-machines
  """

  @doc false
  defmacro __using__(_) do
    quote location: :keep do
      @behavior :gen_fsm

      @doc false
      def handle_event(_event, state_name, state_data) do
        { :next_state, state_name, state_data }
      end

      @doc false
      def handle_sync_event(_event, from, state_name, state_data) do
        { :reply, :ok, state_name, state_data }
      end

      @doc false
      def handle_info(_msg, state_name, state_data) do
        { :next_state, state_name, state_data }
      end

      @doc false
      def terminate(_reason, _state_name, _state_data) do
        :ok
      end

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

      defoverridable [handle_event: 3, handle_sync_event: 4,
                      handle_info: 3, terminate: 3, code_change: 4]
    end
  end
end

defmodule GenFSM.Behaviour do
  @moduledoc """
  This module is a convenience for defining GenFSM callbacks in Elixir.

  A finite state machine (FSM) is responsible for reacting to events received;
  GenFSM is an OTP behaviour that encapsulates common FSM
  functionalities.

  ## Example

  Below is an example of a GenFSM that runs a very simple minded
  coffee vending machine (CVM). The CVM treats all coins the same. If
  you press the request button then the CVM will brew coffee, if you
  have paid enough coins; if not, it will wait until you have inserted
  enough coins and then it will instantly brew the coffee, since you
  had already pressed the request button. As we told you - a very
  simple minded CVM! And greedy too. If you insert more coins than you
  need it will gladly eat them until you press the request button.

  We will leave it to the service-minded reader to improve the way the CVM
  works - we hereby declare a full disclaimer for any lawsuits that the
  behaviour of the CVM in its original state might encur.

      defmodule MyFsm do
        use GenFSM.Behaviour

          # keeping track of what is going on inside the CVM.
          # 3 is the target price for a cup of coffee
          defrecord StateData, coins: 0, price: 3

          #
          # API functions
          #

          def start_link() do
            :gen_fsm.start_link({:local, :cvm}, __MODULE__, [], [])
          end

          def insert_coin() do
            :gen_fsm.send_event(:cvm, :coin)
          end

          def request_coffee() do
            :gen_fsm.send_event(:cvm, :request_coffee)
          end

          #
          # Callbacks
          #

          def init(_args) do
            { :ok, :short_paid, StateData.new }
          end

          def short_paid(:coin, state_data = StateData[coins: c, price: p])
           when c + 1 < p do
            { :next_state, :short_paid, state_data.coins(c + 1) }
          end

          def short_paid(:coin, state_data) do
            { :next_state, :paid_in_full, &state_data.update_coins(&1 + 1) }
          end

          def short_paid(:request_coffee, state_data) do
            { :next_state, :requested_short_paid, state_data }
          end

          def requested_short_paid(:request_coffee, state_data) do
            {:next_state, :requested_short_paid, state_data }
          end

          def requested_short_paid(:coin, state_data=StateData[coins: c, price: p])
           when c+1 < p do
            { :next_state, :requested_short_paid, state_data.coins(c + 1) }
          end

          def requested_short_paid(:coin, _state_data) do
            IO.puts "Here's your coffee!"
            { :next_state, :short_paid, StateData.new }
          end

          def paid_in_full(:coin, state_data) do
            { :next_state, :paid_in_full, &state_data.update_coins(&1 + 1) }
          end

          def paid_in_full(:request_coffee, _state_data) do
            IO.puts "Here's your coffee!"
            { :next_state, :short_paid, StateData.new }
          end
        end

      { :ok, _pid } = MyFsm.start_link()

      MyFsm.insert_coin
      #=> :ok
      MyFsm.insert_coin
      #=> :ok

      MyFsm.request_coffee
      #=> :ok

      MyFsm.insert_coin
      #=> :ok
      #=> Here's your coffee!

  Notice we never call the GenFSM callbacks directly; they are called by
  OTP whenever we interact with the server throught the API. `send_event` is
  asynchronous, whereas `sync_send_event` is synchronous. For
  a GenFSM, the different values a callback can return depend
  on the type of callback.

  State handling returns for `send_event` callbacks:

      { :next_state, next_state_name, new_state_data }
      { :next_state, next_state_name, new_state_data, timeout }
      { :next_state, next_state_name, new_state_data, :hibernate }
      { :stop, reason, new_state_data }

  State handling returns for `sync_send_event` callbacks:

      { :reply, reply, next_state_name, new_state_data }
      { :reply, reply, next_state_name, new_state_data, timeout }
      { :reply, reply, next_state_name, new_state_data, :hibernate }
      { :next_state, next_state_name, new_state_data }
      { :next_state, next_state_name, new_state_data, timeout }
      { :next_state, next_state_name, new_state_data, :hibernate }
      { :stop, reason, reply, new_state_data }
      { :stop, reason, new_state_date }

  There are 6 callbacks required to be implemented in a GenFsm plus 1
  or 2 for each state. The `GenFSM.Behaviour` module defines
  `handle_sync_event`, `handle_info`, `terminate` and `code_change`
  for you. The list of callbacks are:

  * `init(args)` - invoked when the FSM is started;
  * `handle_sync_event(event, from, state_name, state_data)` - invoked to
  handle `sync_send_all_state_event` messages;
  * `handle_event(event, state_name, state_data)` - invoked to handle
  `send_all_state_event` messages;
  * `handle_info(msg, state_name, state_data)` - handle all other
  messages which are normally received by processes;
  * `terminate(reason, state_name, state_data)` - called when the FSM
  is about to terminate, useful for cleaning up;
  * `code_change(old_vsn, state, extra)` - called when the application
  code is being upgraded live (hot code swap);

  Unlike `GenServer` and `GenEvent`, the callback `init/1` is not
  implemented by default, as it requires the next state to be returned.

  For each state you need to define either or both of these:

  * `state_name(event, state_data)` - invoked to handle
  `send_event` messages;
  * `state_name(event, from, state_data)`- invoked to handle
  `sync_send_event` messages;

  If you send asynchronous events you only need to implement the
  `state_name/2` variant and vice-versa for synchronous events and
  `state_name/3`. Keep in mind that if you mix `send_event` and
  `sync_send_event` the best thing to do is to implement both
  callbacks for all states.

  Starting and sending messages to the GenFSM is done via Erlang's
  `:gen_fsm` module. For more information, please refer to the
  following:

  * http://www.erlang.org/doc/man/gen_fsm.html
  * http://www.erlang.org/doc/design_principles/fsm.html
  * http://learnyousomeerlang.com/finite-state-machines
  """

  @doc false
  defmacro __using__(_) do
    quote location: :keep do
      @behavior :gen_fsm

      @doc false
      def handle_event(_event, state_name, state_data) do
        { :next_state, state_name, state_data }
      end

      @doc false
      def handle_sync_event(_event, from, state_name, state_data) do
        { :reply, :default_implementation, state_name, state_data }
      end

      @doc false
      def handle_info(_msg, state_name, state_data) do
        { :next_state, state_name, state_data }
      end

      @doc false
      def terminate(_reason, _state_name, _state_data) do
        :ok
      end

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

      defoverridable [handle_event: 3, handle_sync_event: 4,
                      handle_info: 3, terminate: 3, code_change: 4]
    end
  end
end


remove unnecessary call

remove unnecessary call

defmodule Glasnost.Web.PageView do
  use Glasnost.Web, :view

  def format_tag(eng_tag, blockchain) do
    case blockchain do
      "golos" -> GenServer.call(GolosJS, {:detransliterate, eng_tag})
      "steem" -> eng_tag
    end
  end

  def format_excerpt(post) do
    post.body_html
    |> Floki.text(sep: " ")
    |> String.slice(0..255)
  end

  def generate_pagination_list(current_page) do
    case current_page do
      x when x <= 2 -> 1..5
      x -> x-2..x+2
    end
    |> Enum.with_index(1)
  end
end

defmodule Glasnost.Web.PageView do
  use Glasnost.Web, :view

  def format_tag(eng_tag, blockchain) do
    case blockchain do
      "golos" -> GenServer.call(GolosJS, {:detransliterate, eng_tag})
      "steem" -> eng_tag
    end
  end

  def format_excerpt(post) do
    post.body_html
    |> Earmark.as_html!()
    |> Floki.text(sep: " ")
    |> String.slice(0..255)
  end

  def generate_pagination_list(current_page) do
    case current_page do
      x when x <= 2 -> 1..5
      x -> x-2..x+2
    end
    |> Enum.with_index(1)
  end
end


Add back GenServer error message translation to support libraries

defmodule Logger.Translator do
  @moduledoc """
  Default translation for Erlang log messages.

  Logger allows developers to rewrite log messages provided by
  OTP applications into a format more compatible with Elixir
  log messages by providing a translator.

  A translator is simply a tuple containing a module and a function
  that can be added and removed via the `Logger.add_translator/1` and
  `Logger.remove_translator/1` functions and is invoked for every Erlang
  message above the minimum log level with four arguments:

    * `min_level` - the current Logger level
    * `level` - the level of the message being translated
    * `kind` - if the message is a `:report` or `:format`
    * `message` - the message to format. If it is `:report`, it is a tuple
      with `{report_type, report_data}`, if it is `:format`, it is a
      tuple with `{format_message, format_args}`.

  The function must return:

    * `{:ok, chardata, metadata}` - if the message translation with its metadata
    * `{:ok, chardata}` - the translated message
    * `:skip` - if the message is not meant to be translated nor logged
    * `:none` - if there is no translation, which triggers the next translator

  See the function `translate/4` in this module for an example implementation
  and the default messages translated by Logger.
  """

  @doc """
  Built-in translation function.
  """
  def translate(min_level, level, kind, message)

  ## Erlang/OTP 21 and after

  def translate(min_level, _level, :report, {:logger, %{label: label} = report}) do
    case label do
      {:gen_server, :terminate} ->
        report_gen_server_terminate(min_level, report)

      {:gen_event, :terminate} ->
        report_gen_event_terminate(min_level, report)

      _ ->
        :skip
    end
  end

  def translate(min_level, _level, :report, {{:proc_lib, :crash}, data}) do
    report_crash(min_level, data)
  end

  def translate(min_level, _level, :report, {{:supervisor, :progress}, data}) do
    report_supervisor_progress(min_level, data)
  end

  def translate(min_level, _level, :report, {{:supervisor, _}, data}) do
    report_supervisor(min_level, data)
  end

  def translate(
        _min_level,
        _level,
        :report,
        {{:application_controller, :progress}, [application: app, started_at: node]}
      ) do
    {:ok, ["Application ", Atom.to_string(app), " started at " | inspect(node)]}
  end

  def translate(
        _min_level,
        _level,
        :report,
        {{:application_controller, :exit}, [application: app, exited: reason, type: _type]}
      ) do
    {:ok, ["Application ", Atom.to_string(app), " exited: " | Application.format_error(reason)]}
  end

  def translate(
        _min_level,
        :error,
        :report,
        {{Task.Supervisor, :terminating},
         %{
           name: name,
           starter: starter,
           function: function,
           args: args,
           reason: reason
         }}
      ) do
    opts = Application.get_env(:logger, :translator_inspect_opts)

    {formatted, reason} = format_reason(reason)
    metadata = [crash_reason: reason] ++ registered_name(name)

    msg =
      ["Task #{inspect(name)} started from #{inspect(starter)} terminating"] ++
        [formatted, "\nFunction: #{inspect(function, opts)}"] ++
        ["\n    Args: #{inspect(args, opts)}"]

    {:ok, msg, metadata}
  end

  def translate(min_level, :error, :format, message) do
    case message do
      # This is no longer emitted by Erlang/OTP but it may be
      # manually emitted by libraries like connection.
      {'** Generic server ' ++ _, [name, last, state, reason | client]} ->
        opts = Application.get_env(:logger, :translator_inspect_opts)
        {formatted, reason} = format_reason(reason)
        metadata = [crash_reason: reason] ++ registered_name(name)

        msg =
          ["GenServer #{inspect(name)} terminating", formatted] ++
            ["\nLast message#{format_from(client)}: #{inspect(last, opts)}"]

        if min_level == :debug do
          msg = [msg, "\nState: #{inspect(state, opts)}" | format_client(client)]
          {:ok, msg, metadata}
        else
          {:ok, msg, metadata}
        end

      {'Error in process ' ++ _, [pid, node, {reason, stack}]} ->
        reason = Exception.normalize(:error, reason, stack)

        msg = [
          "Process ",
          inspect(pid),
          " on node ",
          inspect(node),
          " raised an exception" | format(:error, reason, stack)
        ]

        {:ok, msg, [crash_reason: exit_reason(:error, reason, stack)]}

      {'Error in process ' ++ _, [pid, {reason, stack}]} ->
        reason = Exception.normalize(:error, reason, stack)
        msg = ["Process ", inspect(pid), " raised an exception" | format(:error, reason, stack)]
        {:ok, msg, [crash_reason: exit_reason(:error, reason, stack)]}

      _ ->
        :none
    end
  end

  def translate(_min_level, :info, :report, {
        :std_info,
        [application: app, exited: reason, type: _type]
      }) do
    {:ok, ["Application ", Atom.to_string(app), " exited: " | Application.format_error(reason)]}
  end

  def translate(min_level, :error, :report, {{:error_logger, :error_report}, data}) do
    report_supervisor(min_level, data)
  end

  def translate(min_level, :error, :report, {:supervisor_report, data}) do
    report_supervisor(min_level, data)
  end

  def translate(min_level, :error, :report, {:crash_report, data}) do
    report_crash(min_level, data)
  end

  def translate(min_level, :info, :report, {:progress, [{:supervisor, _} | _] = data}) do
    report_supervisor_progress(min_level, data)
  end

  def translate(_min_level, :info, :report, {:progress, [application: app, started_at: node]}) do
    {:ok, ["Application ", Atom.to_string(app), " started at " | inspect(node)]}
  end

  ## Helpers

  def translate(_min_level, _level, _kind, _message) do
    :none
  end

  defp report_gen_server_terminate(min_level, report) do
    inspect_opts = Application.get_env(:logger, :translator_inspect_opts)

    %{
      client_info: client,
      last_message: last,
      name: name,
      reason: reason,
      state: state
    } = report

    {formatted, reason} = format_reason(reason)
    metadata = [crash_reason: reason] ++ registered_name(name)

    msg =
      ["GenServer ", inspect(name), " terminating", formatted] ++
        ["\nLast message", format_last_message_from(client), ": ", inspect(last, inspect_opts)]

    if min_level == :debug do
      msg = [msg, "\nState: ", inspect(state, inspect_opts) | format_client_info(client)]
      {:ok, msg, metadata}
    else
      {:ok, msg, metadata}
    end
  end

  defp report_gen_event_terminate(min_level, report) do
    inspect_opts = Application.get_env(:logger, :translator_inspect_opts)

    %{
      handler: handler,
      last_message: last,
      name: name,
      reason: reason,
      state: state
    } = report

    reason =
      case reason do
        {:EXIT, why} -> why
        _ -> reason
      end

    {formatted, reason} = format_reason(reason)
    metadata = [crash_reason: reason] ++ registered_name(name)

    msg =
      [":gen_event handler ", inspect(handler), " installed in ", inspect(name), " terminating"] ++
        [formatted, "\nLast message: ", inspect(last, inspect_opts)]

    if min_level == :debug do
      {:ok, [msg, "\nState: ", inspect(state, inspect_opts)], metadata}
    else
      {:ok, msg, metadata}
    end
  end

  defp report_supervisor_progress(
         min_level,
         supervisor: sup,
         started: [{:pid, pid}, {:id, id} | started]
       ) do
    msg =
      ["Child ", inspect(id), " of Supervisor ", sup_name(sup), " started"] ++
        ["\nPid: ", inspect(pid)] ++ child_info(min_level, started)

    {:ok, msg}
  end

  defp report_supervisor_progress(
         min_level,
         supervisor: sup,
         started: [{:pid, pid} | started]
       ) do
    msg =
      ["Child of Supervisor ", sup_name(sup), " started", "\nPid: ", inspect(pid)] ++
        child_info(min_level, started)

    {:ok, msg}
  end

  defp report_supervisor_progress(_min_level, _other), do: :none

  defp report_supervisor(
         min_level,
         supervisor: sup,
         errorContext: context,
         reason: reason,
         offender: [{:pid, pid}, {:id, id} | offender]
       ) do
    pid_info =
      if is_pid(pid) and context != :shutdown do
        ["\nPid: ", inspect(pid)]
      else
        []
      end

    msg =
      ["Child ", inspect(id), " of Supervisor ", sup_name(sup)] ++
        [?\s, sup_context(context), "\n** (exit) ", offender_reason(reason, context)] ++
        pid_info ++ child_info(min_level, offender)

    {:ok, msg}
  end

  defp report_supervisor(
         min_level,
         supervisor: sup,
         errorContext: context,
         reason: reason,
         offender: [{:nb_children, n}, {:id, id} | offender]
       ) do
    msg =
      ["Children ", inspect(id), " of Supervisor ", sup_name(sup), ?\s, sup_context(context)] ++
        ["\n** (exit) ", offender_reason(reason, context), "\nNumber: ", Integer.to_string(n)] ++
        child_info(min_level, offender)

    {:ok, msg}
  end

  defp report_supervisor(
         min_level,
         supervisor: sup,
         errorContext: context,
         reason: reason,
         offender: [{:pid, pid} | offender]
       ) do
    msg =
      ["Child of Supervisor ", sup_name(sup), ?\s, sup_context(context)] ++
        ["\n** (exit) ", offender_reason(reason, context), "\nPid: ", inspect(pid)] ++
        child_info(min_level, offender)

    {:ok, msg}
  end

  defp report_supervisor(_min_level, _other), do: :none

  # If start call raises reason will be of form {:EXIT, reason}
  defp offender_reason({:EXIT, reason}, :start_error) do
    Exception.format_exit(reason)
  end

  defp offender_reason(reason, _context) do
    Exception.format_exit(reason)
  end

  defp sup_name({:local, name}), do: inspect(name)
  defp sup_name({:global, name}), do: inspect(name)
  defp sup_name({:via, _mod, name}), do: inspect(name)
  defp sup_name({pid, mod}), do: [inspect(pid), " (", inspect(mod), ?)]
  defp sup_name(unknown_name), do: inspect(unknown_name)

  defp sup_context(:start_error), do: "failed to start"
  defp sup_context(:child_terminated), do: "terminated"
  defp sup_context(:shutdown), do: "caused shutdown"
  defp sup_context(:shutdown_error), do: "shut down abnormally"

  defp child_info(min_level, [{:mfargs, {mod, fun, args}} | debug]) do
    ["\nStart Call: ", format_mfa(mod, fun, args) | child_debug(min_level, debug)]
  end

  # Comes from bridge with MFA
  defp child_info(min_level, [{:mfa, {mod, fun, args}} | debug]) do
    ["\nStart Call: ", format_mfa(mod, fun, args) | child_debug(min_level, debug)]
  end

  # Comes from bridge with Mod
  defp child_info(min_level, [{:mod, mod} | debug]) do
    ["\nStart Module: ", inspect(mod) | child_debug(min_level, debug)]
  end

  defp child_info(_min_level, _child) do
    []
  end

  defp child_debug(:debug, restart_type: restart, shutdown: shutdown, child_type: type) do
    ["\nRestart: ", inspect(restart), "\nShutdown: ", inspect(shutdown)] ++
      ["\nType: ", inspect(type)]
  end

  defp child_debug(_min_level, _child) do
    []
  end

  defp report_crash(min_level, [[{:initial_call, initial_call} | crashed], linked]) do
    mfa = initial_call_to_mfa(initial_call)
    report_crash(min_level, crashed, [{:initial_call, mfa}], linked)
  end

  defp report_crash(min_level, [crashed, linked]) do
    report_crash(min_level, crashed, [], linked)
  end

  defp report_crash(min_level, crashed, extra, linked) do
    [
      {:pid, pid},
      {:registered_name, name},
      {:error_info, {kind, reason, stack}} | crashed
    ] = crashed

    dictionary = crashed[:dictionary]
    reason = Exception.normalize(kind, reason, stack)

    case Keyword.get(dictionary, :logger_metadata, {true, []}) do
      {false, _} ->
        :skip

      {true, _user_metadata} ->
        user_metadata = Keyword.get(dictionary, :"$logger_metadata$", %{}) |> Map.to_list()

        msg =
          ["Process ", crash_name(pid, name), " terminating", format(kind, reason, stack)] ++
            [crash_info(min_level, extra ++ crashed, [?\n]), crash_linked(min_level, linked)]

        extra =
          if ancestors = crashed[:ancestors], do: [{:ancestors, ancestors} | extra], else: extra

        extra =
          if callers = dictionary[:"$callers"], do: [{:callers, callers} | extra], else: extra

        extra = [{:crash_reason, exit_reason(kind, reason, stack)} | extra]
        {:ok, msg, registered_name(name) ++ extra ++ user_metadata}
    end
  end

  defp initial_call_to_mfa({:supervisor, module, _}), do: {module, :init, 1}
  defp initial_call_to_mfa({:supervisor_bridge, module, _}), do: {module, :init, 1}
  defp initial_call_to_mfa({mod, fun, args}) when is_list(args), do: {mod, fun, length(args)}
  defp initial_call_to_mfa(mfa), do: mfa

  defp crash_name(pid, []), do: inspect(pid)
  defp crash_name(pid, name), do: [inspect(name), " (", inspect(pid), ?)]

  defp crash_info(min_level, [{:initial_call, {mod, fun, args}} | info], prefix) do
    [prefix, "Initial Call: ", crash_call(mod, fun, args) | crash_info(min_level, info, prefix)]
  end

  defp crash_info(min_level, [{:current_function, {mod, fun, args}} | info], prefix) do
    [prefix, "Current Call: ", crash_call(mod, fun, args) | crash_info(min_level, info, prefix)]
  end

  defp crash_info(min_level, [{:current_function, []} | info], prefix) do
    crash_info(min_level, info, prefix)
  end

  defp crash_info(min_level, [{:ancestors, ancestors} | debug], prefix) do
    [prefix, "Ancestors: ", inspect(ancestors) | crash_info(min_level, debug, prefix)]
  end

  defp crash_info(:debug, debug, prefix) do
    for {key, value} <- debug do
      crash_debug(key, value, prefix)
    end
  end

  defp crash_info(_, _, _) do
    []
  end

  defp crash_call(mod, fun, arity) when is_integer(arity) do
    format_mfa(mod, fun, arity)
  end

  defp crash_call(mod, fun, args) do
    format_mfa(mod, fun, length(args))
  end

  defp crash_debug(:current_stacktrace, stack, prefix) do
    stack_prefix = [prefix | "    "]
    stacktrace = Enum.map(stack, &[stack_prefix | Exception.format_stacktrace_entry(&1)])
    [prefix, "Current Stacktrace:" | stacktrace]
  end

  defp crash_debug(key, value, prefix) do
    [prefix, crash_debug_key(key), ?:, ?\s, inspect(value)]
  end

  defp crash_debug_key(key) do
    case key do
      :message_queue_len -> "Message Queue Length"
      :messages -> "Messages"
      :links -> "Links"
      :dictionary -> "Dictionary"
      :trap_exit -> "Trapping Exits"
      :status -> "Status"
      :heap_size -> "Heap Size"
      :stack_size -> "Stack Size"
      :reductions -> "Reductions"
    end
  end

  defp crash_linked(_min_level, []), do: []

  defp crash_linked(min_level, neighbours) do
    Enum.reduce(neighbours, "\nNeighbours:", fn {:neighbour, info}, acc ->
      [acc | crash_neighbour(min_level, info)]
    end)
  end

  @indent "    "

  defp crash_neighbour(min_level, [{:pid, pid}, {:registered_name, []} | info]) do
    [?\n, @indent, inspect(pid) | crash_info(min_level, info, [?\n, @indent | @indent])]
  end

  defp crash_neighbour(min_level, [{:pid, pid}, {:registered_name, name} | info]) do
    [?\n, @indent, inspect(name), " (", inspect(pid), ")"] ++
      crash_info(min_level, info, [?\n, @indent | @indent])
  end

  defp format_last_message_from({_, {name, _}}), do: [" (from ", inspect(name), ")"]
  defp format_last_message_from({from, _}), do: [" (from ", inspect(from), ")"]
  defp format_last_message_from(_), do: []

  defp format_client_info({from, :dead}),
    do: ["\nClient ", inspect(from), " is dead"]

  defp format_client_info({from, :remote}),
    do: ["\nClient ", inspect(from), " is remote on node ", inspect(node(from))]

  defp format_client_info({_, {name, stacktrace}}),
    do: ["\nClient ", inspect(name), " is alive\n" | format_stacktrace(stacktrace)]

  defp format_client_info(_),
    do: []

  defp format_reason({maybe_exception, [_ | _] = maybe_stacktrace} = reason) do
    try do
      format_stacktrace(maybe_stacktrace)
    catch
      :error, _ ->
        {format_stop(reason), {reason, []}}
    else
      formatted_stacktrace ->
        {formatted, reason} = maybe_normalize(maybe_exception, maybe_stacktrace)
        {[formatted | formatted_stacktrace], {reason, maybe_stacktrace}}
    end
  end

  defp format_reason(reason) do
    {format_stop(reason), {reason, []}}
  end

  defp format_stop(reason) do
    ["\n** (stop) " | Exception.format_exit(reason)]
  end

  # Erlang processes rewrite the :undef error to these reasons when logging
  @gen_undef [:"module could not be loaded", :"function not exported"]

  defp maybe_normalize(undef, [{mod, fun, args, _info} | _] = stacktrace)
       when undef in @gen_undef and is_atom(mod) and is_atom(fun) do
    cond do
      is_list(args) ->
        format_undef(mod, fun, length(args), undef, stacktrace)

      is_integer(args) ->
        format_undef(mod, fun, args, undef, stacktrace)

      true ->
        {format_stop(undef), undef}
    end
  end

  defp maybe_normalize(reason, stacktrace) do
    # If this is already an exception (even an ErlangError), we format it as an
    # exception. Otherwise, we try to normalize it, and if it's normalized as an
    # ErlangError we instead format it as an exit.
    if Exception.exception?(reason) do
      {[?\n | Exception.format_banner(:error, reason, stacktrace)], reason}
    else
      case Exception.normalize(:error, reason, stacktrace) do
        %ErlangError{} ->
          {format_stop(reason), reason}

        exception ->
          {[?\n | Exception.format_banner(:error, exception, stacktrace)], exception}
      end
    end
  end

  defp format(kind, payload, stacktrace) do
    [?\n, Exception.format_banner(kind, payload, stacktrace) | format_stacktrace(stacktrace)]
  end

  defp format_stacktrace(stacktrace) do
    for entry <- stacktrace do
      ["\n    " | Exception.format_stacktrace_entry(entry)]
    end
  end

  defp registered_name(name) when is_atom(name), do: [registered_name: name]
  defp registered_name(_name), do: []

  defp format_mfa(mod, fun, :undefined),
    do: [inspect(mod), ?., Code.Identifier.inspect_as_function(fun) | "/?"]

  defp format_mfa(mod, fun, args),
    do: Exception.format_mfa(mod, fun, args)

  defp exit_reason(:exit, reason, stack), do: {reason, stack}
  defp exit_reason(:error, reason, stack), do: {reason, stack}
  defp exit_reason(:throw, value, stack), do: {{:nocatch, value}, stack}

  ## Deprecated helpers

  defp format_from([]), do: ""
  defp format_from([from]), do: " (from #{inspect(from)})"
  defp format_from([from, stacktrace]) when is_list(stacktrace), do: " (from #{inspect(from)})"

  defp format_from([from, node_name]) when is_atom(node_name),
    do: " (from #{inspect(from)} on #{inspect(node_name)})"

  defp format_client([from]) do
    "\nClient #{inspect(from)} is dead"
  end

  defp format_client([from, stacktrace]) when is_list(stacktrace) do
    ["\nClient #{inspect(from)} is alive\n" | format_stacktrace(stacktrace)]
  end

  defp format_client(_) do
    []
  end

  defp format_undef(mod, fun, arity, undef, stacktrace) do
    opts = [module: mod, function: fun, arity: arity, reason: undef]
    exception = UndefinedFunctionError.exception(opts)
    {[?\n | Exception.format_banner(:error, exception, stacktrace)], exception}
  end
end

defmodule Logger.Translator do
  @moduledoc """
  Default translation for Erlang log messages.

  Logger allows developers to rewrite log messages provided by
  OTP applications into a format more compatible with Elixir
  log messages by providing a translator.

  A translator is simply a tuple containing a module and a function
  that can be added and removed via the `Logger.add_translator/1` and
  `Logger.remove_translator/1` functions and is invoked for every Erlang
  message above the minimum log level with four arguments:

    * `min_level` - the current Logger level
    * `level` - the level of the message being translated
    * `kind` - if the message is a `:report` or `:format`
    * `message` - the message to format. If it is `:report`, it is a tuple
      with `{report_type, report_data}`, if it is `:format`, it is a
      tuple with `{format_message, format_args}`.

  The function must return:

    * `{:ok, chardata, metadata}` - if the message translation with its metadata
    * `{:ok, chardata}` - the translated message
    * `:skip` - if the message is not meant to be translated nor logged
    * `:none` - if there is no translation, which triggers the next translator

  See the function `translate/4` in this module for an example implementation
  and the default messages translated by Logger.
  """

  @doc """
  Built-in translation function.
  """
  def translate(min_level, level, kind, message)

  ## Erlang/OTP 21 and after

  def translate(min_level, _level, :report, {:logger, %{label: label} = report}) do
    case label do
      {:gen_server, :terminate} ->
        report_gen_server_terminate(min_level, report)

      {:gen_event, :terminate} ->
        report_gen_event_terminate(min_level, report)

      _ ->
        :skip
    end
  end

  def translate(min_level, _level, :report, {{:proc_lib, :crash}, data}) do
    report_crash(min_level, data)
  end

  def translate(min_level, _level, :report, {{:supervisor, :progress}, data}) do
    report_supervisor_progress(min_level, data)
  end

  def translate(min_level, _level, :report, {{:supervisor, _}, data}) do
    report_supervisor(min_level, data)
  end

  def translate(
        _min_level,
        _level,
        :report,
        {{:application_controller, :progress}, [application: app, started_at: node]}
      ) do
    {:ok, ["Application ", Atom.to_string(app), " started at " | inspect(node)]}
  end

  def translate(
        _min_level,
        _level,
        :report,
        {{:application_controller, :exit}, [application: app, exited: reason, type: _type]}
      ) do
    {:ok, ["Application ", Atom.to_string(app), " exited: " | Application.format_error(reason)]}
  end

  def translate(
        _min_level,
        :error,
        :report,
        {{Task.Supervisor, :terminating},
         %{
           name: name,
           starter: starter,
           function: function,
           args: args,
           reason: reason
         }}
      ) do
    opts = Application.get_env(:logger, :translator_inspect_opts)

    {formatted, reason} = format_reason(reason)
    metadata = [crash_reason: reason] ++ registered_name(name)

    msg =
      ["Task #{inspect(name)} started from #{inspect(starter)} terminating"] ++
        [formatted, "\nFunction: #{inspect(function, opts)}"] ++
        ["\n    Args: #{inspect(args, opts)}"]

    {:ok, msg, metadata}
  end

  def translate(_min_level, :error, :format, message) do
    case message do
      {'Error in process ' ++ _, [pid, node, {reason, stack}]} ->
        reason = Exception.normalize(:error, reason, stack)

        msg = [
          "Process ",
          inspect(pid),
          " on node ",
          inspect(node),
          " raised an exception" | format(:error, reason, stack)
        ]

        {:ok, msg, [crash_reason: exit_reason(:error, reason, stack)]}

      {'Error in process ' ++ _, [pid, {reason, stack}]} ->
        reason = Exception.normalize(:error, reason, stack)
        msg = ["Process ", inspect(pid), " raised an exception" | format(:error, reason, stack)]
        {:ok, msg, [crash_reason: exit_reason(:error, reason, stack)]}

      _ ->
        :none
    end
  end

  def translate(_min_level, :info, :report, {
        :std_info,
        [application: app, exited: reason, type: _type]
      }) do
    {:ok, ["Application ", Atom.to_string(app), " exited: " | Application.format_error(reason)]}
  end

  def translate(min_level, :error, :report, {{:error_logger, :error_report}, data}) do
    report_supervisor(min_level, data)
  end

  def translate(min_level, :error, :report, {:supervisor_report, data}) do
    report_supervisor(min_level, data)
  end

  def translate(min_level, :error, :report, {:crash_report, data}) do
    report_crash(min_level, data)
  end

  def translate(min_level, :info, :report, {:progress, [{:supervisor, _} | _] = data}) do
    report_supervisor_progress(min_level, data)
  end

  def translate(_min_level, :info, :report, {:progress, [application: app, started_at: node]}) do
    {:ok, ["Application ", Atom.to_string(app), " started at " | inspect(node)]}
  end

  ## Helpers

  def translate(_min_level, _level, _kind, _message) do
    :none
  end

  defp report_gen_server_terminate(min_level, report) do
    inspect_opts = Application.get_env(:logger, :translator_inspect_opts)

    %{
      client_info: client,
      last_message: last,
      name: name,
      reason: reason,
      state: state
    } = report

    {formatted, reason} = format_reason(reason)
    metadata = [crash_reason: reason] ++ registered_name(name)

    msg =
      ["GenServer ", inspect(name), " terminating", formatted] ++
        ["\nLast message", format_last_message_from(client), ": ", inspect(last, inspect_opts)]

    if min_level == :debug do
      msg = [msg, "\nState: ", inspect(state, inspect_opts) | format_client_info(client)]
      {:ok, msg, metadata}
    else
      {:ok, msg, metadata}
    end
  end

  defp report_gen_event_terminate(min_level, report) do
    inspect_opts = Application.get_env(:logger, :translator_inspect_opts)

    %{
      handler: handler,
      last_message: last,
      name: name,
      reason: reason,
      state: state
    } = report

    reason =
      case reason do
        {:EXIT, why} -> why
        _ -> reason
      end

    {formatted, reason} = format_reason(reason)
    metadata = [crash_reason: reason] ++ registered_name(name)

    msg =
      [":gen_event handler ", inspect(handler), " installed in ", inspect(name), " terminating"] ++
        [formatted, "\nLast message: ", inspect(last, inspect_opts)]

    if min_level == :debug do
      {:ok, [msg, "\nState: ", inspect(state, inspect_opts)], metadata}
    else
      {:ok, msg, metadata}
    end
  end

  defp report_supervisor_progress(
         min_level,
         supervisor: sup,
         started: [{:pid, pid}, {:id, id} | started]
       ) do
    msg =
      ["Child ", inspect(id), " of Supervisor ", sup_name(sup), " started"] ++
        ["\nPid: ", inspect(pid)] ++ child_info(min_level, started)

    {:ok, msg}
  end

  defp report_supervisor_progress(
         min_level,
         supervisor: sup,
         started: [{:pid, pid} | started]
       ) do
    msg =
      ["Child of Supervisor ", sup_name(sup), " started", "\nPid: ", inspect(pid)] ++
        child_info(min_level, started)

    {:ok, msg}
  end

  defp report_supervisor_progress(_min_level, _other), do: :none

  defp report_supervisor(
         min_level,
         supervisor: sup,
         errorContext: context,
         reason: reason,
         offender: [{:pid, pid}, {:id, id} | offender]
       ) do
    pid_info =
      if is_pid(pid) and context != :shutdown do
        ["\nPid: ", inspect(pid)]
      else
        []
      end

    msg =
      ["Child ", inspect(id), " of Supervisor ", sup_name(sup)] ++
        [?\s, sup_context(context), "\n** (exit) ", offender_reason(reason, context)] ++
        pid_info ++ child_info(min_level, offender)

    {:ok, msg}
  end

  defp report_supervisor(
         min_level,
         supervisor: sup,
         errorContext: context,
         reason: reason,
         offender: [{:nb_children, n}, {:id, id} | offender]
       ) do
    msg =
      ["Children ", inspect(id), " of Supervisor ", sup_name(sup), ?\s, sup_context(context)] ++
        ["\n** (exit) ", offender_reason(reason, context), "\nNumber: ", Integer.to_string(n)] ++
        child_info(min_level, offender)

    {:ok, msg}
  end

  defp report_supervisor(
         min_level,
         supervisor: sup,
         errorContext: context,
         reason: reason,
         offender: [{:pid, pid} | offender]
       ) do
    msg =
      ["Child of Supervisor ", sup_name(sup), ?\s, sup_context(context)] ++
        ["\n** (exit) ", offender_reason(reason, context), "\nPid: ", inspect(pid)] ++
        child_info(min_level, offender)

    {:ok, msg}
  end

  defp report_supervisor(_min_level, _other), do: :none

  # If start call raises reason will be of form {:EXIT, reason}
  defp offender_reason({:EXIT, reason}, :start_error) do
    Exception.format_exit(reason)
  end

  defp offender_reason(reason, _context) do
    Exception.format_exit(reason)
  end

  defp sup_name({:local, name}), do: inspect(name)
  defp sup_name({:global, name}), do: inspect(name)
  defp sup_name({:via, _mod, name}), do: inspect(name)
  defp sup_name({pid, mod}), do: [inspect(pid), " (", inspect(mod), ?)]
  defp sup_name(unknown_name), do: inspect(unknown_name)

  defp sup_context(:start_error), do: "failed to start"
  defp sup_context(:child_terminated), do: "terminated"
  defp sup_context(:shutdown), do: "caused shutdown"
  defp sup_context(:shutdown_error), do: "shut down abnormally"

  defp child_info(min_level, [{:mfargs, {mod, fun, args}} | debug]) do
    ["\nStart Call: ", format_mfa(mod, fun, args) | child_debug(min_level, debug)]
  end

  # Comes from bridge with MFA
  defp child_info(min_level, [{:mfa, {mod, fun, args}} | debug]) do
    ["\nStart Call: ", format_mfa(mod, fun, args) | child_debug(min_level, debug)]
  end

  # Comes from bridge with Mod
  defp child_info(min_level, [{:mod, mod} | debug]) do
    ["\nStart Module: ", inspect(mod) | child_debug(min_level, debug)]
  end

  defp child_info(_min_level, _child) do
    []
  end

  defp child_debug(:debug, restart_type: restart, shutdown: shutdown, child_type: type) do
    ["\nRestart: ", inspect(restart), "\nShutdown: ", inspect(shutdown)] ++
      ["\nType: ", inspect(type)]
  end

  defp child_debug(_min_level, _child) do
    []
  end

  defp report_crash(min_level, [[{:initial_call, initial_call} | crashed], linked]) do
    mfa = initial_call_to_mfa(initial_call)
    report_crash(min_level, crashed, [{:initial_call, mfa}], linked)
  end

  defp report_crash(min_level, [crashed, linked]) do
    report_crash(min_level, crashed, [], linked)
  end

  defp report_crash(min_level, crashed, extra, linked) do
    [
      {:pid, pid},
      {:registered_name, name},
      {:error_info, {kind, reason, stack}} | crashed
    ] = crashed

    dictionary = crashed[:dictionary]
    reason = Exception.normalize(kind, reason, stack)

    case Keyword.get(dictionary, :logger_metadata, {true, []}) do
      {false, _} ->
        :skip

      {true, _user_metadata} ->
        user_metadata = Keyword.get(dictionary, :"$logger_metadata$", %{}) |> Map.to_list()

        msg =
          ["Process ", crash_name(pid, name), " terminating", format(kind, reason, stack)] ++
            [crash_info(min_level, extra ++ crashed, [?\n]), crash_linked(min_level, linked)]

        extra =
          if ancestors = crashed[:ancestors], do: [{:ancestors, ancestors} | extra], else: extra

        extra =
          if callers = dictionary[:"$callers"], do: [{:callers, callers} | extra], else: extra

        extra = [{:crash_reason, exit_reason(kind, reason, stack)} | extra]
        {:ok, msg, registered_name(name) ++ extra ++ user_metadata}
    end
  end

  defp initial_call_to_mfa({:supervisor, module, _}), do: {module, :init, 1}
  defp initial_call_to_mfa({:supervisor_bridge, module, _}), do: {module, :init, 1}
  defp initial_call_to_mfa({mod, fun, args}) when is_list(args), do: {mod, fun, length(args)}
  defp initial_call_to_mfa(mfa), do: mfa

  defp crash_name(pid, []), do: inspect(pid)
  defp crash_name(pid, name), do: [inspect(name), " (", inspect(pid), ?)]

  defp crash_info(min_level, [{:initial_call, {mod, fun, args}} | info], prefix) do
    [prefix, "Initial Call: ", crash_call(mod, fun, args) | crash_info(min_level, info, prefix)]
  end

  defp crash_info(min_level, [{:current_function, {mod, fun, args}} | info], prefix) do
    [prefix, "Current Call: ", crash_call(mod, fun, args) | crash_info(min_level, info, prefix)]
  end

  defp crash_info(min_level, [{:current_function, []} | info], prefix) do
    crash_info(min_level, info, prefix)
  end

  defp crash_info(min_level, [{:ancestors, ancestors} | debug], prefix) do
    [prefix, "Ancestors: ", inspect(ancestors) | crash_info(min_level, debug, prefix)]
  end

  defp crash_info(:debug, debug, prefix) do
    for {key, value} <- debug do
      crash_debug(key, value, prefix)
    end
  end

  defp crash_info(_, _, _) do
    []
  end

  defp crash_call(mod, fun, arity) when is_integer(arity) do
    format_mfa(mod, fun, arity)
  end

  defp crash_call(mod, fun, args) do
    format_mfa(mod, fun, length(args))
  end

  defp crash_debug(:current_stacktrace, stack, prefix) do
    stack_prefix = [prefix | "    "]
    stacktrace = Enum.map(stack, &[stack_prefix | Exception.format_stacktrace_entry(&1)])
    [prefix, "Current Stacktrace:" | stacktrace]
  end

  defp crash_debug(key, value, prefix) do
    [prefix, crash_debug_key(key), ?:, ?\s, inspect(value)]
  end

  defp crash_debug_key(key) do
    case key do
      :message_queue_len -> "Message Queue Length"
      :messages -> "Messages"
      :links -> "Links"
      :dictionary -> "Dictionary"
      :trap_exit -> "Trapping Exits"
      :status -> "Status"
      :heap_size -> "Heap Size"
      :stack_size -> "Stack Size"
      :reductions -> "Reductions"
    end
  end

  defp crash_linked(_min_level, []), do: []

  defp crash_linked(min_level, neighbours) do
    Enum.reduce(neighbours, "\nNeighbours:", fn {:neighbour, info}, acc ->
      [acc | crash_neighbour(min_level, info)]
    end)
  end

  @indent "    "

  defp crash_neighbour(min_level, [{:pid, pid}, {:registered_name, []} | info]) do
    [?\n, @indent, inspect(pid) | crash_info(min_level, info, [?\n, @indent | @indent])]
  end

  defp crash_neighbour(min_level, [{:pid, pid}, {:registered_name, name} | info]) do
    [?\n, @indent, inspect(name), " (", inspect(pid), ")"] ++
      crash_info(min_level, info, [?\n, @indent | @indent])
  end

  defp format_last_message_from({_, {name, _}}), do: [" (from ", inspect(name), ")"]
  defp format_last_message_from({from, _}), do: [" (from ", inspect(from), ")"]
  defp format_last_message_from(_), do: []

  defp format_client_info({from, :dead}),
    do: ["\nClient ", inspect(from), " is dead"]

  defp format_client_info({from, :remote}),
    do: ["\nClient ", inspect(from), " is remote on node ", inspect(node(from))]

  defp format_client_info({_, {name, stacktrace}}),
    do: ["\nClient ", inspect(name), " is alive\n" | format_stacktrace(stacktrace)]

  defp format_client_info(_),
    do: []

  defp format_reason({maybe_exception, [_ | _] = maybe_stacktrace} = reason) do
    try do
      format_stacktrace(maybe_stacktrace)
    catch
      :error, _ ->
        {format_stop(reason), {reason, []}}
    else
      formatted_stacktrace ->
        {formatted, reason} = maybe_normalize(maybe_exception, maybe_stacktrace)
        {[formatted | formatted_stacktrace], {reason, maybe_stacktrace}}
    end
  end

  defp format_reason(reason) do
    {format_stop(reason), {reason, []}}
  end

  defp format_stop(reason) do
    ["\n** (stop) " | Exception.format_exit(reason)]
  end

  # Erlang processes rewrite the :undef error to these reasons when logging
  @gen_undef [:"module could not be loaded", :"function not exported"]

  defp maybe_normalize(undef, [{mod, fun, args, _info} | _] = stacktrace)
       when undef in @gen_undef and is_atom(mod) and is_atom(fun) do
    cond do
      is_list(args) ->
        format_undef(mod, fun, length(args), undef, stacktrace)

      is_integer(args) ->
        format_undef(mod, fun, args, undef, stacktrace)

      true ->
        {format_stop(undef), undef}
    end
  end

  defp maybe_normalize(reason, stacktrace) do
    # If this is already an exception (even an ErlangError), we format it as an
    # exception. Otherwise, we try to normalize it, and if it's normalized as an
    # ErlangError we instead format it as an exit.
    if Exception.exception?(reason) do
      {[?\n | Exception.format_banner(:error, reason, stacktrace)], reason}
    else
      case Exception.normalize(:error, reason, stacktrace) do
        %ErlangError{} ->
          {format_stop(reason), reason}

        exception ->
          {[?\n | Exception.format_banner(:error, exception, stacktrace)], exception}
      end
    end
  end

  defp format(kind, payload, stacktrace) do
    [?\n, Exception.format_banner(kind, payload, stacktrace) | format_stacktrace(stacktrace)]
  end

  defp format_stacktrace(stacktrace) do
    for entry <- stacktrace do
      ["\n    " | Exception.format_stacktrace_entry(entry)]
    end
  end

  defp registered_name(name) when is_atom(name), do: [registered_name: name]
  defp registered_name(_name), do: []

  defp format_mfa(mod, fun, :undefined),
    do: [inspect(mod), ?., Code.Identifier.inspect_as_function(fun) | "/?"]

  defp format_mfa(mod, fun, args),
    do: Exception.format_mfa(mod, fun, args)

  defp exit_reason(:exit, reason, stack), do: {reason, stack}
  defp exit_reason(:error, reason, stack), do: {reason, stack}
  defp exit_reason(:throw, value, stack), do: {{:nocatch, value}, stack}

  ## Deprecated helpers

  defp format_undef(mod, fun, arity, undef, stacktrace) do
    opts = [module: mod, function: fun, arity: arity, reason: undef]
    exception = UndefinedFunctionError.exception(opts)
    {[?\n | Exception.format_banner(:error, exception, stacktrace)], exception}
  end
end


Avoid side effect in tests.

Avoid side effect in tests.

Code.require_file "../../test_helper", __FILE__

require Erlang.os, as: OS

defmodule Elixir.CLI.InitTest do
  use ExUnit.Case

  test :code_init do
    assert_equal '3\n', OS.cmd('bin/elixir -e "IO.puts 1 + 2"')

    expected = '#{inspect ["-o", "1", "2", "3"]}\n3\n'
    assert_equal expected, OS.cmd('bin/elixir -e "IO.puts inspect(System.argv)" test/elixir/fixtures/init_sample.exs -o 1 2 3')
  end
end

defmodule Elixir.CLI.OptionParsingTest do
  use ExUnit.Case

  test :path do
    list = OS.cmd('bin/elixir -e "IO.inspect Erlang.code.get_path" -pa "*" -pz "exbin/*"')
    { path, _ } = Code.eval list, []

    # pa
    assert_member 'bin', path
    assert_member 'ebin', path
    assert_member 'exbin', path
    assert_member 'src', path
    assert_member 'lib', path
    assert_member 'include', path
    assert_member 'test', path

    # pz
    assert_member 'exbin/__MAIN__', path
  end

  test :require do
    options = ['-r', 'lib/list/*', '-r', '/never/gonna/*/up']
    { config, _argv } = Elixir.CLI.process_options(options, Elixir.CLI.Config.new)
    assert_member {:require, 'lib/list/chars.ex'}, config.commands
  end
end

defmodule Elixir.CLI.AtExitTest do
  use ExUnit.Case

  test :at_exit do
    assert_equal 'goodbye cruel world with status 0\n', OS.cmd('bin/elixir test/elixir/fixtures/at_exit.exs')
  end
end

defmodule Elixir.CLI.ErrorTest do
  use ExUnit.Case

  test :code_error do
    assert Erlang.string.str('** (throw) 1', OS.cmd('bin/elixir -e "throw 1"')) == 0
    assert Erlang.string.str('** (ErlangError) erlang error: 1', OS.cmd('bin/elixir -e "error 1"')) == 0

    # It does not catch exits with integers nor strings...
    assert_equal '', OS.cmd('bin/elixir -e "exit 1"')
  end
end

defmodule Elixir.CLI.SyntaxErrorTest do
  use ExUnit.Case

  test :syntax_code_error do
    message = '** (TokenMissingError) nofile:1: syntax error: expression is incomplete'
    assert Erlang.string.str(message, OS.cmd('bin/elixir -e "[1,2"')) == 0
    message = '** (SyntaxError) nofile:1: syntax error before: \'end\''
    assert Erlang.string.str(message, OS.cmd('bin/elixir -e "case 1 end"')) == 0
  end
end

defmodule Elixir.CLI.CompileTest do
  use ExUnit.Case

  test :compile_code do
    assert_equal 'Compiling test/elixir/fixtures/compile_sample.exs\n',
      OS.cmd('bin/elixirc test/elixir/fixtures/compile_sample.exs -o test/tmp/')
    assert File.regular?("test/tmp/__MAIN__/CompileSample.beam")
  after:
    Erlang.file.del_dir("test/tmp/")
  end
end

Code.require_file "../../test_helper", __FILE__

require Erlang.os, as: OS

defmodule Elixir.CLI.InitTest do
  use ExUnit.Case

  test :code_init do
    assert_equal '3\n', OS.cmd('bin/elixir -e "IO.puts 1 + 2"')

    expected = '#{inspect ["-o", "1", "2", "3"]}\n3\n'
    assert_equal expected, OS.cmd('bin/elixir -e "IO.puts inspect(System.argv)" test/elixir/fixtures/init_sample.exs -o 1 2 3')
  end
end

defmodule Elixir.CLI.OptionParsingTest do
  use ExUnit.Case

  test :bogus_options do
    options = ['-r','non-existent', '-r', '/never/gonna/*/up', '-pz', 'there is no spoon', '-pa', '*/unshell']
    { config, _argv } = Elixir.CLI.process_options(options, Elixir.CLI.Config.new)
    expected = Elixir.CLI.Config.new commands: [], close: [], output: '.', compile: false, halt: true, compile_options: []
    assert_equal expected, config
  end

  test :wildcard do
    options = ['-r', 'lib/list/*', '-pa', '*', '-pz', 'exbin/*']
    { config, _argv } = Elixir.CLI.process_options(options, Elixir.CLI.Config.new)
    expected = Elixir.CLI.Config.new commands: [{:require, 'lib/list/chars.ex'}],
                 close: [], output: '.', compile: false, halt: true, compile_options: []
    assert_equal expected, config

    path = Erlang.code.get_path
    # -pa
    assert_member 'bin', path
    assert_member 'ebin', path
    assert_member 'exbin', path
    assert_member 'src', path
    assert_member 'lib', path
    assert_member 'include', path
    assert_member 'test', path

    # -pz
    assert_member 'exbin/__MAIN__', path
  end
end

defmodule Elixir.CLI.AtExitTest do
  use ExUnit.Case

  test :at_exit do
    assert_equal 'goodbye cruel world with status 0\n', OS.cmd('bin/elixir test/elixir/fixtures/at_exit.exs')
  end
end

defmodule Elixir.CLI.ErrorTest do
  use ExUnit.Case

  test :code_error do
    assert Erlang.string.str('** (throw) 1', OS.cmd('bin/elixir -e "throw 1"')) == 0
    assert Erlang.string.str('** (ErlangError) erlang error: 1', OS.cmd('bin/elixir -e "error 1"')) == 0

    # It does not catch exits with integers nor strings...
    assert_equal '', OS.cmd('bin/elixir -e "exit 1"')
  end
end

defmodule Elixir.CLI.SyntaxErrorTest do
  use ExUnit.Case

  test :syntax_code_error do
    message = '** (TokenMissingError) nofile:1: syntax error: expression is incomplete'
    assert Erlang.string.str(message, OS.cmd('bin/elixir -e "[1,2"')) == 0
    message = '** (SyntaxError) nofile:1: syntax error before: \'end\''
    assert Erlang.string.str(message, OS.cmd('bin/elixir -e "case 1 end"')) == 0
  end
end

defmodule Elixir.CLI.CompileTest do
  use ExUnit.Case

  test :compile_code do
    assert_equal 'Compiling test/elixir/fixtures/compile_sample.exs\n',
      OS.cmd('bin/elixirc test/elixir/fixtures/compile_sample.exs -o test/tmp/')
    assert File.regular?("test/tmp/__MAIN__/CompileSample.beam")
  after:
    Erlang.file.del_dir("test/tmp/")
  end
end


Bump agent to 6f74473

Bump agent to 6f74473

Fix multi user issue for agent permissions.

defmodule Appsignal.Agent do
  def version, do: "6f74473"

  def triples do
    %{
      "x86_64-darwin" => %{
        checksum: "b97f65dbc21a12ddaeb3c84b69bf1d9d31143f6e094ad4a1c36d09ba35e824a1",
        download_url: "https://appsignal-agent-releases.global.ssl.fastly.net/6f74473/appsignal-x86_64-darwin-all-static.tar.gz"
      },
      "universal-darwin" => %{
        checksum: "b97f65dbc21a12ddaeb3c84b69bf1d9d31143f6e094ad4a1c36d09ba35e824a1",
        download_url: "https://appsignal-agent-releases.global.ssl.fastly.net/6f74473/appsignal-x86_64-darwin-all-static.tar.gz"
      },
      "i686-linux" => %{
        checksum: "7d48b936ff3a5da2a2b92dc55e3666ba80bcbf25ce65b8f496829c26822e2bec",
        download_url: "https://appsignal-agent-releases.global.ssl.fastly.net/6f74473/appsignal-i686-linux-all-static.tar.gz"
      },
      "x86-linux" => %{
        checksum: "7d48b936ff3a5da2a2b92dc55e3666ba80bcbf25ce65b8f496829c26822e2bec",
        download_url: "https://appsignal-agent-releases.global.ssl.fastly.net/6f74473/appsignal-i686-linux-all-static.tar.gz"
      },
      "i686-linux-musl" => %{
        checksum: "fe29178c35e474515d39515e634ea9d24b79a86bb5d1c583482da03347ce4e88",
        download_url: "https://appsignal-agent-releases.global.ssl.fastly.net/6f74473/appsignal-i686-linux-musl-all-static.tar.gz"
      },
      "x86-linux-musl" => %{
        checksum: "fe29178c35e474515d39515e634ea9d24b79a86bb5d1c583482da03347ce4e88",
        download_url: "https://appsignal-agent-releases.global.ssl.fastly.net/6f74473/appsignal-i686-linux-musl-all-static.tar.gz"
      },
      "x86_64-linux" => %{
        checksum: "0642d2616413bef8b607b63f0ac79d64b515b752c782e1bd7c3ff0b4198f3f23",
        download_url: "https://appsignal-agent-releases.global.ssl.fastly.net/6f74473/appsignal-x86_64-linux-all-static.tar.gz"
      },
      "x86_64-linux-musl" => %{
        checksum: "de0da8e59c45710bbf274ec600cd229863965e023f98388ff59edaa4a969e344",
        download_url: "https://appsignal-agent-releases.global.ssl.fastly.net/6f74473/appsignal-x86_64-linux-musl-all-static.tar.gz"
      },
      "x86_64-freebsd" => %{
        checksum: "67814be3779bb06d7ef96fc53eba416a488c8707967112f453c26eee86fe165c",
        download_url: "https://appsignal-agent-releases.global.ssl.fastly.net/6f74473/appsignal-x86_64-freebsd-all-static.tar.gz"
      },
      "amd64-freebsd" => %{
        checksum: "67814be3779bb06d7ef96fc53eba416a488c8707967112f453c26eee86fe165c",
        download_url: "https://appsignal-agent-releases.global.ssl.fastly.net/6f74473/appsignal-x86_64-freebsd-all-static.tar.gz"
      },
    }
  end
end

defmodule Appsignal.Agent do
  def version, do: "86b6269"

  def triples do
    %{
      "x86_64-darwin" => %{
        checksum: "e253a63c279747c52013e7d0a9b402c879a345d5ab9c06c6f50314b6211d9e4e",
        download_url: "https://appsignal-agent-releases.global.ssl.fastly.net/86b6269/appsignal-x86_64-darwin-all-static.tar.gz"
      },
      "universal-darwin" => %{
        checksum: "e253a63c279747c52013e7d0a9b402c879a345d5ab9c06c6f50314b6211d9e4e",
        download_url: "https://appsignal-agent-releases.global.ssl.fastly.net/86b6269/appsignal-x86_64-darwin-all-static.tar.gz"
      },
      "i686-linux" => %{
        checksum: "11bf57ab3f845e41d90d4cf78fc727103c3a214dcbc18aecde9aa5f9bd0a105f",
        download_url: "https://appsignal-agent-releases.global.ssl.fastly.net/86b6269/appsignal-i686-linux-all-static.tar.gz"
      },
      "x86-linux" => %{
        checksum: "11bf57ab3f845e41d90d4cf78fc727103c3a214dcbc18aecde9aa5f9bd0a105f",
        download_url: "https://appsignal-agent-releases.global.ssl.fastly.net/86b6269/appsignal-i686-linux-all-static.tar.gz"
      },
      "i686-linux-musl" => %{
        checksum: "fed88ea2a0a0885fd98e3f52a48b925aaca3946d39150dd9897af0b2a934660a",
        download_url: "https://appsignal-agent-releases.global.ssl.fastly.net/86b6269/appsignal-i686-linux-musl-all-static.tar.gz"
      },
      "x86-linux-musl" => %{
        checksum: "fed88ea2a0a0885fd98e3f52a48b925aaca3946d39150dd9897af0b2a934660a",
        download_url: "https://appsignal-agent-releases.global.ssl.fastly.net/86b6269/appsignal-i686-linux-musl-all-static.tar.gz"
      },
      "x86_64-linux" => %{
        checksum: "6d80a9d03c905926c14ba6ffd5d0d4c736a8d065c9055d8dd6d85e114f2a4d8e",
        download_url: "https://appsignal-agent-releases.global.ssl.fastly.net/86b6269/appsignal-x86_64-linux-all-static.tar.gz"
      },
      "x86_64-linux-musl" => %{
        checksum: "ecde0cc6349b68bf57a632f19a1e2f9fcdc42e8a9ce646fb1bd651837b06d01b",
        download_url: "https://appsignal-agent-releases.global.ssl.fastly.net/86b6269/appsignal-x86_64-linux-musl-all-static.tar.gz"
      },
      "x86_64-freebsd" => %{
        checksum: "4c880e9ef3f1a6da8cd9914ee2346422e6fb927772bd1586c4afbcf990023a69",
        download_url: "https://appsignal-agent-releases.global.ssl.fastly.net/86b6269/appsignal-x86_64-freebsd-all-static.tar.gz"
      },
      "amd64-freebsd" => %{
        checksum: "4c880e9ef3f1a6da8cd9914ee2346422e6fb927772bd1586c4afbcf990023a69",
        download_url: "https://appsignal-agent-releases.global.ssl.fastly.net/86b6269/appsignal-x86_64-freebsd-all-static.tar.gz"
      }
    }
  end
end