Fix double event sourcing dashboard edit

Was failing at add again same uuid.. which is nice.

require Logger
require Serverboards.Project.Model
alias Serverboards.Project.Model
alias Serverboards.Repo

defmodule Serverboards.Dashboard do

  def start_link(_options) do
    {:ok, es} = EventSourcing.start_link name: :dashboard

    EventSourcing.Model.subscribe :dashboard, :dashboard, Serverboards.Repo
    #EventSourcing.subscribe :project, :debug_full

    Serverboards.Dashboard.setup_eventsourcing(es)
    Serverboards.Dashboard.Widget.setup_eventsourcing(es)

    {:ok, es}
  end

  def setup_eventsourcing(es) do
    EventSourcing.subscribe es, :add_dashboard, fn attr, me ->
      dashboard_add_real(attr, me)
    end
    EventSourcing.subscribe es, :update_dashboard, fn attr, _me ->
      dashboard_update_real(attr)
    end
    EventSourcing.subscribe es, :remove_dashboard, fn attr, _me ->
      dashboard_remove_real(attr.uuid)
    end
  end

  def dashboard_add( attr, me ) do
    data = Map.put( attr, :uuid, UUID.uuid4() )
    EventSourcing.dispatch(:dashboard, :add_dashboard, data, me.email )
    {:ok, data.uuid}
  end

  def dashboard_update( attr, me ) do
    EventSourcing.dispatch(:dashboard, :update_dashboard, attr, me.email )
  end

  def dashboard_remove( uuid, me ) do
    EventSourcing.dispatch(:dashboard, :remove_dashboard, %{ uuid: uuid }, me.email )
    :ok
  end

  def dashboard_list( %{ project: project }, _me \\ nil ) do
    import Ecto.Query
    Repo.all(
      from d in Model.Dashboard,
      join: p in Model.Project, on: p.id == d.project_id,
      where: p.shortname == ^project,
      order_by: :order
      )
  end

  def dashboard_get_model(uuid) do
    import Ecto.Query
    Repo.one(
      from d in Model.Dashboard,
      where: d.uuid == ^uuid
    )
  end

  def dashboard_alias_get( alias_ ) do
    import Ecto.Query
    uuid = Repo.one(
      from d in Model.Dashboard,
      where: d.alias == ^alias_,
      select: d.uuid
    )
    dashboard_get(uuid)
  end

  def dashboard_alias_get( alias_, project ) do
    import Ecto.Query
    uuid = Repo.one(
      from d in Model.Dashboard,
      select: d.uuid,
      join: p in Serverboards.Project.Model.Project,
      on: p.id == d.project_id,

      where: d.alias == ^alias_ and p.shortname == ^project
    )
    if uuid == nil do
      {:error, :unknown_dashboard}
    else
      dashboard_get(uuid)
    end
  end

  def dashboard_get( uuid ) do
    import Ecto.Query
    case dashboard_get_model( uuid ) do
      nil -> nil
      data ->
        widgets = Repo.all(
          from w in Model.Widget,
          where: w.dashboard_id == ^data.id
        )
        %{
          name: data.name,
          uuid: data.uuid,
          config: data.config,
          order: data.order,
          alias: data.alias,
          widgets: for w <- widgets do
            %{
              widget: w.widget,
              config: w.config,
              ui: w.ui,
              uuid: w.uuid
            }
          end
        }
    end
  end

  def dashboard_get_project_shortname( uuid ) do
    import Ecto.Query
    Repo.one(
      from d in Model.Dashboard,
      join: p in Model.Project, on: p.id == d.project_id,
      where: d.uuid == ^uuid,
      select: p.shortname
    )
  end

  def dashboard_count( project ) do
    import Ecto.Query
    Repo.one(
      from d in Model.Dashboard,
      join: p in Model.Project, on: p.id == d.project_id,
      where: p.shortname == ^project,
      select: count(d.id)
    )
  end

  def dashboard_add_real( %{ uuid: _uuid } = attr, me) do
    Logger.debug("Add dashboard #{inspect attr.uuid}")
    {:ok, project} = Serverboards.Project.project_get( attr.project, me  )
    attr = Map.drop( attr, [:project] )
    attr = Map.put(attr, :project_id, project.id )
    attr = Map.put(attr, :order, dashboard_count( project.shortname ) )
    {:ok, _dashboard} = Repo.insert(
      Model.Dashboard.changeset( %Model.Dashboard{}, attr )
    )
    Serverboards.Event.emit("dashboard.created[#{project.shortname}]", attr, ["project.get"])
  end

  def dashboard_update_real( %{ uuid: uuid } = attr ) do
    Repo.update( Model.Dashboard.changeset( dashboard_get_model(uuid), attr ) )

    project_shortname = dashboard_get_project_shortname( uuid )
    Serverboards.Event.emit("dashboard.updated[#{uuid}]", attr, ["project.get"])
    Serverboards.Event.emit("dashboard.updated[#{project_shortname}]", attr, ["project.get"])
  end

  def dashboard_remove_real( uuid ) do
    import Ecto.Query
    project_shortname = dashboard_get_project_shortname( uuid )

    Repo.delete_all(
      from w in Model.Widget,
      join: d in Model.Dashboard, on: d.id == w.dashboard_id,
      where: d.uuid == ^uuid
    )
    Repo.delete_all(
      from d in Model.Dashboard,
      where: d.uuid == ^uuid
    )
    Serverboards.Event.emit("dashboard.deleted[#{uuid}]", %{ uuid: uuid }, ["project.get"])
    Serverboards.Event.emit("dashboard.deleted[#{project_shortname}]", %{ uuid: uuid }, ["project.get"])
  end

end

require Logger
require Serverboards.Project.Model
alias Serverboards.Project.Model
alias Serverboards.Repo

defmodule Serverboards.Dashboard do

  def start_link(_options) do
    {:ok, es} = EventSourcing.start_link name: :dashboard

    EventSourcing.Model.subscribe :dashboard, :dashboard, Serverboards.Repo
    #EventSourcing.subscribe :project, :debug_full

    setup_eventsourcing(es)
    Serverboards.Dashboard.setup_eventsourcing(es)
    Serverboards.Dashboard.Widget.setup_eventsourcing(es)

    {:ok, es}
  end

  def setup_eventsourcing(es) do
    EventSourcing.subscribe es, :add_dashboard, fn attr, me ->
      dashboard_add_real(attr, me)
    end
    EventSourcing.subscribe es, :update_dashboard, fn attr, _me ->
      dashboard_update_real(attr)
    end
    EventSourcing.subscribe es, :remove_dashboard, fn attr, _me ->
      dashboard_remove_real(attr.uuid)
    end
  end

  def dashboard_add( attr, me ) do
    data = Map.put( attr, :uuid, UUID.uuid4() )
    EventSourcing.dispatch(:dashboard, :add_dashboard, data, me.email )
    {:ok, data.uuid}
  end

  def dashboard_update( attr, me ) do
    EventSourcing.dispatch(:dashboard, :update_dashboard, attr, me.email )
  end

  def dashboard_remove( uuid, me ) do
    EventSourcing.dispatch(:dashboard, :remove_dashboard, %{ uuid: uuid }, me.email )
    :ok
  end

  def dashboard_list( %{ project: project }, _me \\ nil ) do
    import Ecto.Query
    Repo.all(
      from d in Model.Dashboard,
      join: p in Model.Project, on: p.id == d.project_id,
      where: p.shortname == ^project,
      order_by: :order
      )
  end

  def dashboard_get_model(uuid) do
    import Ecto.Query
    Repo.one(
      from d in Model.Dashboard,
      where: d.uuid == ^uuid
    )
  end

  def dashboard_alias_get( alias_ ) do
    import Ecto.Query
    uuid = Repo.one(
      from d in Model.Dashboard,
      where: d.alias == ^alias_,
      select: d.uuid
    )
    dashboard_get(uuid)
  end

  def dashboard_alias_get( alias_, project ) do
    import Ecto.Query
    uuid = Repo.one(
      from d in Model.Dashboard,
      select: d.uuid,
      join: p in Serverboards.Project.Model.Project,
      on: p.id == d.project_id,

      where: d.alias == ^alias_ and p.shortname == ^project
    )
    if uuid == nil do
      {:error, :unknown_dashboard}
    else
      dashboard_get(uuid)
    end
  end

  def dashboard_get( uuid ) do
    import Ecto.Query
    case dashboard_get_model( uuid ) do
      nil -> nil
      data ->
        widgets = Repo.all(
          from w in Model.Widget,
          where: w.dashboard_id == ^data.id
        )
        %{
          name: data.name,
          uuid: data.uuid,
          config: data.config,
          order: data.order,
          alias: data.alias,
          widgets: for w <- widgets do
            %{
              widget: w.widget,
              config: w.config,
              ui: w.ui,
              uuid: w.uuid
            }
          end
        }
    end
  end

  def dashboard_get_project_shortname( uuid ) do
    import Ecto.Query
    Repo.one(
      from d in Model.Dashboard,
      join: p in Model.Project, on: p.id == d.project_id,
      where: d.uuid == ^uuid,
      select: p.shortname
    )
  end

  def dashboard_count( project ) do
    import Ecto.Query
    Repo.one(
      from d in Model.Dashboard,
      join: p in Model.Project, on: p.id == d.project_id,
      where: p.shortname == ^project,
      select: count(d.id)
    )
  end

  def dashboard_add_real( %{ uuid: _uuid } = attr, me) do
    {:ok, project} = Serverboards.Project.project_get( attr.project, me  )
    attr = Map.drop( attr, [:project] )
    attr = Map.put(attr, :project_id, project.id )
    attr = Map.put(attr, :order, dashboard_count( project.shortname ) )
    {:ok, _dashboard} = Repo.insert(
      Model.Dashboard.changeset( %Model.Dashboard{}, attr )
    )
    Serverboards.Event.emit("dashboard.created[#{project.shortname}]", attr, ["project.get"])
  end

  def dashboard_update_real( %{ uuid: uuid } = attr ) do
    Repo.update( Model.Dashboard.changeset( dashboard_get_model(uuid), attr ) )

    project_shortname = dashboard_get_project_shortname( uuid )
    Serverboards.Event.emit("dashboard.updated[#{uuid}]", attr, ["project.get"])
    Serverboards.Event.emit("dashboard.updated[#{project_shortname}]", attr, ["project.get"])
  end

  def dashboard_remove_real( uuid ) do
    import Ecto.Query
    project_shortname = dashboard_get_project_shortname( uuid )

    Repo.delete_all(
      from w in Model.Widget,
      join: d in Model.Dashboard, on: d.id == w.dashboard_id,
      where: d.uuid == ^uuid
    )
    Repo.delete_all(
      from d in Model.Dashboard,
      where: d.uuid == ^uuid
    )
    Serverboards.Event.emit("dashboard.deleted[#{uuid}]", %{ uuid: uuid }, ["project.get"])
    Serverboards.Event.emit("dashboard.deleted[#{project_shortname}]", %{ uuid: uuid }, ["project.get"])
  end

end


Add shortdoc

Add shortdoc


Delete log file if a user factory resets.

defmodule Farmbot.CeleryScript.AST.Node.FactoryReset do
  @moduledoc false
  use Farmbot.CeleryScript.AST.Node
  allow_args [:package]
  use Farmbot.Logger

  def execute(%{package: :farmbot_os}, _, env) do
    env = mutate_env(env)
    Farmbot.BotState.set_sync_status(:maintenance)
    Farmbot.BotState.force_state_push()
    Farmbot.System.ConfigStorage.update_config_value(:bool, "settings", "disable_factory_reset", false)
    clean_log_file()
    Logger.warn 1, "Farmbot OS going down for factory reset!"
    Farmbot.System.factory_reset "CeleryScript request."
    {:ok, env}
  end

  def execute(%{package: :arduino_firmware}, _, env) do
    env = mutate_env(env)
    Farmbot.BotState.set_sync_status(:maintenance)
    Logger.warn 1, "Arduino Firmware going down for factory reset!"
    Farmbot.HTTP.delete!("/api/firmware_config")
    pl = Poison.encode!(%{"api_migrated" => true})
    Farmbot.HTTP.put!("/api/firmware_config", pl)
    Farmbot.Bootstrap.SettingsSync.do_sync_fw_configs()
    Farmbot.BotState.reset_sync_status()
    {:ok, env}
  end

  def clean_log_file do
    try do
      Elixir.Logger.remove_backend(LoggerBackendSqlite)
      db = Application.get_env(:logger, LoggerBackendSqlite)[:database]
      db && File.rm!(db)
    catch
      er, reason ->
        Logger.error 1, "Failed to remove log file: #{inspect(er)} #{inspect(reason)}" <>
                        "\r\n \r\n" <> Exception.format_stacktrace()
    end
  end
end

defmodule Farmbot.CeleryScript.AST.Node.FactoryReset do
  @moduledoc false
  use Farmbot.CeleryScript.AST.Node
  allow_args [:package]
  use Farmbot.Logger

  def execute(%{package: :farmbot_os}, _, env) do
    env = mutate_env(env)
    Farmbot.BotState.set_sync_status(:maintenance)
    Farmbot.BotState.force_state_push()
    Farmbot.System.ConfigStorage.update_config_value(:bool, "settings", "disable_factory_reset", false)
    Logger.warn 1, "Farmbot OS going down for factory reset!"
    Farmbot.System.factory_reset "CeleryScript request."
    {:ok, env}
  end

  def execute(%{package: :arduino_firmware}, _, env) do
    env = mutate_env(env)
    Farmbot.BotState.set_sync_status(:maintenance)
    Logger.warn 1, "Arduino Firmware going down for factory reset!"
    Farmbot.HTTP.delete!("/api/firmware_config")
    pl = Poison.encode!(%{"api_migrated" => true})
    Farmbot.HTTP.put!("/api/firmware_config", pl)
    Farmbot.Bootstrap.SettingsSync.do_sync_fw_configs()
    Farmbot.BotState.reset_sync_status()
    {:ok, env}
  end
end


Add users resources to router

Add users resources to router

defmodule SecretKeeper.Router do
  use SecretKeeper.Web, :router

  pipeline :browser do
    plug :accepts, ["html"]
    plug :fetch_session
    plug :fetch_flash
    plug :protect_from_forgery
    plug :put_secure_browser_headers
  end

  pipeline :api do
    plug :accepts, ["json"]
  end

  scope "/", SecretKeeper do
    pipe_through :browser # Use the default browser stack

    get "/", PageController, :index
    resources "/users", UserController
  end

  # Other scopes may use custom stacks.
  # scope "/api", SecretKeeper do
  #   pipe_through :api
  # end
end

defmodule SecretKeeper.Router do
  use SecretKeeper.Web, :router

  pipeline :browser do
    plug :accepts, ["html"]
    plug :fetch_session
    plug :fetch_flash
    plug :protect_from_forgery
    plug :put_secure_browser_headers
  end

  pipeline :api do
    plug :accepts, ["json"]
  end

  scope "/", SecretKeeper do
    pipe_through :browser # Use the default browser stack

    get "/", PageController, :index
  end

  # Other scopes may use custom stacks.
  # scope "/api", SecretKeeper do
  #   pipe_through :api
  # end
end


Add note on ssl

Add note on ssl

use Mix.Config

"""
NOTE: To get SSL working, you will need to set:

    ssl: true,
    keyfile: System.get_env("SOME_APP_SSL_KEY_PATH"),
    certfile: System.get_env("SOME_APP_SSL_CERT_PATH"),

Where those two env variables point to a file on disk
for the key and cert
"""

config :phoenix, <%= application_module %>.Router,
  port: System.get_env("PORT"),
  ssl: false,
  code_reload: false,
  cookies: true,
  session_key: "_<%= Mix.Utils.underscore(application_module) %>_key",
  session_secret: "<%= session_secret %>"

config :phoenix, :logger,
  level: :error

use Mix.Config

config :phoenix, <%= application_module %>.Router,
  port: System.get_env("PORT"),
  ssl: false,
  code_reload: false,
  cookies: true,
  session_key: "_<%= Mix.Utils.underscore(application_module) %>_key",
  session_secret: "<%= session_secret %>"

config :phoenix, :logger,
  level: :error


Assume application/octet-stream in Plug.Upload

defmodule Asciinema.Fixtures do
  alias Asciinema.{Repo, Asciicasts}
  alias Asciinema.Accounts.User

  def fixture(what, attrs \\ %{})

  def fixture(:upload, attrs) do
    fixture(:upload_v1, attrs)
  end

  def fixture(:upload_v1, attrs) do
    path = Map.get(attrs, :path) || "1/asciicast.json"
    filename = Path.basename(path)
    %Plug.Upload{path: "spec/fixtures/#{path}",
                 filename: filename,
                 content_type: "application/json"}
  end

  def fixture(:upload_v2, attrs) do
    path = Map.get(attrs, :path) || "2/full.cast"
    filename = Path.basename(path)
    %Plug.Upload{path: "spec/fixtures/#{path}",
                 filename: filename,
                 content_type: "application/octet-stream"}
  end

  def fixture(:user, attrs) do
    attrs = Map.merge(%{username: "test",
                        email: "test@example.com"}, attrs)
    %User{}
    |> User.create_changeset(attrs)
    |> Repo.insert!
  end

  def fixture(:asciicast, attrs) do
    fixture(:asciicast_v2, attrs)
  end

  def fixture(:asciicast_v1, _attrs) do
    user = fixture(:user)
    upload = fixture(:upload_v1)
    {:ok, asciicast} = Asciicasts.create_asciicast(user, upload)
    asciicast
  end

  def fixture(:asciicast_v2, _attrs) do
    user = fixture(:user)
    upload = fixture(:upload_v2)
    {:ok, asciicast} = Asciicasts.create_asciicast(user, upload)
    asciicast
  end
end

defmodule Asciinema.Fixtures do
  alias Asciinema.{Repo, Asciicasts}
  alias Asciinema.Accounts.User

  def fixture(what, attrs \\ %{})

  def fixture(:upload, attrs) do
    fixture(:upload_v1, attrs)
  end

  def fixture(:upload_v1, attrs) do
    path = Map.get(attrs, :path) || "1/asciicast.json"
    filename = Path.basename(path)
    %Plug.Upload{path: "spec/fixtures/#{path}",
                 filename: filename,
                 content_type: "application/json"}
  end

  def fixture(:upload_v2, attrs) do
    path = Map.get(attrs, :path) || "2/full.cast"
    filename = Path.basename(path)
    %Plug.Upload{path: "spec/fixtures/#{path}",
                 filename: filename,
                 content_type: "application/json"} # TODO change
  end

  def fixture(:user, attrs) do
    attrs = Map.merge(%{username: "test",
                        email: "test@example.com"}, attrs)
    %User{}
    |> User.create_changeset(attrs)
    |> Repo.insert!
  end

  def fixture(:asciicast, attrs) do
    fixture(:asciicast_v2, attrs)
  end

  def fixture(:asciicast_v1, _attrs) do
    user = fixture(:user)
    upload = fixture(:upload_v1)
    {:ok, asciicast} = Asciicasts.create_asciicast(user, upload)
    asciicast
  end

  def fixture(:asciicast_v2, _attrs) do
    user = fixture(:user)
    upload = fixture(:upload_v2)
    {:ok, asciicast} = Asciicasts.create_asciicast(user, upload)
    asciicast
  end
end


Remove warning

Remove warning

# https://github.com/scrogson/oauth2_example/blob/214f84aa690dbdc640613a8673c90c79d8f5767a/web/oauth/github.ex

defmodule GitHub do
  @moduledoc """
  An OAuth2 strategy for GitHub.
  """
  use OAuth2.Strategy

  alias OAuth2.Strategy.AuthCode

  defp config do
    [strategy: GitHub,
     site: "https://api.github.com",
     authorize_url: "https://github.com/login/oauth/authorize",
     token_url: "https://github.com/login/oauth/access_token"]
  end

  # Public API

  def client do
    Application.get_env(:mr_rebase, GitHub)
    |> Keyword.merge(config())
    |> OAuth2.Client.new()
  end

  def authorize_url!(params \\ []) do
    OAuth2.Client.authorize_url!(client(), params)
  end

  def get_token!(params \\ [], _headers \\ []) do
    OAuth2.Client.get_token!(client(), params)
  end

  # Strategy Callbacks

  def authorize_url(client, params) do
    AuthCode.authorize_url(client, params)
  end

  def get_token(client, params, headers) do
    client
    |> put_header("Accept", "application/json")
    |> AuthCode.get_token(params, headers)
  end
end

# https://github.com/scrogson/oauth2_example/blob/214f84aa690dbdc640613a8673c90c79d8f5767a/web/oauth/github.ex

defmodule GitHub do
  @moduledoc """
  An OAuth2 strategy for GitHub.
  """
  use OAuth2.Strategy

  alias OAuth2.Strategy.AuthCode

  defp config do
    [strategy: GitHub,
     site: "https://api.github.com",
     authorize_url: "https://github.com/login/oauth/authorize",
     token_url: "https://github.com/login/oauth/access_token"]
  end

  # Public API

  def client do
    Application.get_env(:mr_rebase, GitHub)
    |> Keyword.merge(config())
    |> OAuth2.Client.new()
  end

  def authorize_url!(params \\ []) do
    OAuth2.Client.authorize_url!(client(), params)
  end

  def get_token!(params \\ [], headers \\ []) do
    OAuth2.Client.get_token!(client(), params)
  end

  # Strategy Callbacks

  def authorize_url(client, params) do
    AuthCode.authorize_url(client, params)
  end

  def get_token(client, params, headers) do
    client
    |> put_header("Accept", "application/json")
    |> AuthCode.get_token(params, headers)
  end
end


Link to t:Phoenix.HTML.Form.t/0 in Phoenix.HTML.FormData (#277)

[ci skip]

defprotocol Phoenix.HTML.FormData do
  @moduledoc """
  Converts a data structure into a [`Phoenix.HTML.Form`](`t:Phoenix.HTML.Form.t/0`) struct.
  """

  @doc """
  Converts a data structure into a [`Phoenix.HTML.Form`](`t:Phoenix.HTML.Form.t/0`) struct.

  The options are the same options given to `form_for/4`. It
  can be used by implementations to configure their behaviour
  and it must be stored in the underlying struct, with any
  custom field removed.
  """
  @spec to_form(t, Keyword.t()) :: Phoenix.HTML.Form.t()
  def to_form(data, options)

  @doc """
  Converts the field in the given form based on the data structure
  into a list of [`Phoenix.HTML.Form`](`t:Phoenix.HTML.Form.t/0`) structs.

  The options are the same options given to `inputs_for/4`. It
  can be used by implementations to configure their behaviour
  and it must be stored in the underlying struct, with any
  custom field removed.
  """
  @spec to_form(t, Phoenix.HTML.Form.t(), Phoenix.HTML.Form.field(), Keyword.t()) ::
          [Phoenix.HTML.Form.t()]
  def to_form(data, form, field, options)

  @doc """
  Returns the value for the given field.
  """
  @spec input_value(t, Phoenix.HTML.Form.t(), Phoenix.HTML.Form.field()) :: term
  def input_value(data, form, field)

  @doc """
  Returns the HTML5 validations that would apply to
  the given field.
  """
  @spec input_validations(t, Phoenix.HTML.Form.t(), Phoenix.HTML.Form.field()) :: Keyword.t()
  def input_validations(data, form, field)

  @doc """
  Receives the given field and returns its input type (:text_input,
  :select, etc). Returns `nil` if the type is unknown.
  """
  @spec input_type(t, Phoenix.HTML.Form.t(), Phoenix.HTML.Form.field()) :: atom | nil
  def input_type(data, form, field)
end

defimpl Phoenix.HTML.FormData, for: [Plug.Conn, Atom] do
  def to_form(conn_or_atom, opts) do
    {name, params, opts} = name_params_and_opts(conn_or_atom, opts)
    {errors, opts} = Keyword.pop(opts, :errors, [])

    %Phoenix.HTML.Form{
      source: conn_or_atom,
      impl: __MODULE__,
      id: name,
      name: name,
      params: params,
      data: %{},
      errors: errors,
      options: opts
    }
  end

  case @for do
    Atom ->
      defp name_params_and_opts(atom, opts) do
        {params, opts} = Keyword.pop(opts, :params, %{})
        {Atom.to_string(atom), params, opts}
      end

    Plug.Conn ->
      defp name_params_and_opts(conn, opts) do
        case Keyword.pop(opts, :as) do
          {nil, opts} ->
            {nil, conn.params, opts}

          {name, opts} ->
            name = to_string(name)
            {name, Map.get(conn.params, name) || %{}, opts}
        end
      end
  end

  def to_form(conn_or_atom, form, field, opts) when is_atom(field) or is_binary(field) do
    {default, opts} = Keyword.pop(opts, :default, %{})
    {prepend, opts} = Keyword.pop(opts, :prepend, [])
    {append, opts} = Keyword.pop(opts, :append, [])
    {name, opts} = Keyword.pop(opts, :as)
    {id, opts} = Keyword.pop(opts, :id)
    {hidden, opts} = Keyword.pop(opts, :hidden, [])

    id = to_string(id || form.id <> "_#{field}")
    name = to_string(name || form.name <> "[#{field}]")
    params = Map.get(form.params, field_to_string(field))

    cond do
      # cardinality: one
      is_map(default) ->
        [
          %Phoenix.HTML.Form{
            source: conn_or_atom,
            impl: __MODULE__,
            id: id,
            name: name,
            data: default,
            params: params || %{},
            hidden: hidden,
            options: opts
          }
        ]

      # cardinality: many
      is_list(default) ->
        entries =
          if params do
            params
            |> Enum.sort_by(&elem(&1, 0))
            |> Enum.map(&{nil, elem(&1, 1)})
          else
            Enum.map(prepend ++ default ++ append, &{&1, %{}})
          end

        for {{data, params}, index} <- Enum.with_index(entries) do
          index_string = Integer.to_string(index)

          %Phoenix.HTML.Form{
            source: conn_or_atom,
            impl: __MODULE__,
            index: index,
            id: id <> "_" <> index_string,
            name: name <> "[" <> index_string <> "]",
            data: data,
            params: params,
            hidden: hidden,
            options: opts
          }
        end
    end
  end

  def input_value(_conn_or_atom, %{data: data, params: params}, field)
      when is_atom(field) or is_binary(field) do
    case Map.fetch(params, field_to_string(field)) do
      {:ok, value} ->
        value

      :error ->
        Map.get(data, field)
    end
  end

  def input_type(_conn_or_atom, _form, _field), do: :text_input
  def input_validations(_conn_or_atom, _form, _field), do: []

  # Normalize field name to string version
  defp field_to_string(field) when is_atom(field), do: Atom.to_string(field)
  defp field_to_string(field) when is_binary(field), do: field
end

defprotocol Phoenix.HTML.FormData do
  @moduledoc """
  Converts a data structure into a `Phoenix.HTML.Form` struct.
  """

  @doc """
  Converts a data structure into a `Phoenix.HTML.Form` struct.

  The options are the same options given to `form_for/4`. It
  can be used by implementations to configure their behaviour
  and it must be stored in the underlying struct, with any
  custom field removed.
  """
  @spec to_form(t, Keyword.t()) :: Phoenix.HTML.Form.t()
  def to_form(data, options)

  @doc """
  Converts the field in the given form based on the data structure
  into a list of `Phoenix.HTML.Form` structs.

  The options are the same options given to `inputs_for/4`. It
  can be used by implementations to configure their behaviour
  and it must be stored in the underlying struct, with any
  custom field removed.
  """
  @spec to_form(t, Phoenix.HTML.Form.t(), Phoenix.HTML.Form.field(), Keyword.t()) ::
          [Phoenix.HTML.Form.t()]
  def to_form(data, form, field, options)

  @doc """
  Returns the value for the given field.
  """
  @spec input_value(t, Phoenix.HTML.Form.t(), Phoenix.HTML.Form.field()) :: term
  def input_value(data, form, field)

  @doc """
  Returns the HTML5 validations that would apply to
  the given field.
  """
  @spec input_validations(t, Phoenix.HTML.Form.t(), Phoenix.HTML.Form.field()) :: Keyword.t()
  def input_validations(data, form, field)

  @doc """
  Receives the given field and returns its input type (:text_input,
  :select, etc). Returns `nil` if the type is unknown.
  """
  @spec input_type(t, Phoenix.HTML.Form.t(), Phoenix.HTML.Form.field()) :: atom | nil
  def input_type(data, form, field)
end

defimpl Phoenix.HTML.FormData, for: [Plug.Conn, Atom] do
  def to_form(conn_or_atom, opts) do
    {name, params, opts} = name_params_and_opts(conn_or_atom, opts)
    {errors, opts} = Keyword.pop(opts, :errors, [])

    %Phoenix.HTML.Form{
      source: conn_or_atom,
      impl: __MODULE__,
      id: name,
      name: name,
      params: params,
      data: %{},
      errors: errors,
      options: opts
    }
  end

  case @for do
    Atom ->
      defp name_params_and_opts(atom, opts) do
        {params, opts} = Keyword.pop(opts, :params, %{})
        {Atom.to_string(atom), params, opts}
      end

    Plug.Conn ->
      defp name_params_and_opts(conn, opts) do
        case Keyword.pop(opts, :as) do
          {nil, opts} ->
            {nil, conn.params, opts}

          {name, opts} ->
            name = to_string(name)
            {name, Map.get(conn.params, name) || %{}, opts}
        end
      end
  end

  def to_form(conn_or_atom, form, field, opts) when is_atom(field) or is_binary(field) do
    {default, opts} = Keyword.pop(opts, :default, %{})
    {prepend, opts} = Keyword.pop(opts, :prepend, [])
    {append, opts} = Keyword.pop(opts, :append, [])
    {name, opts} = Keyword.pop(opts, :as)
    {id, opts} = Keyword.pop(opts, :id)
    {hidden, opts} = Keyword.pop(opts, :hidden, [])

    id = to_string(id || form.id <> "_#{field}")
    name = to_string(name || form.name <> "[#{field}]")
    params = Map.get(form.params, field_to_string(field))

    cond do
      # cardinality: one
      is_map(default) ->
        [
          %Phoenix.HTML.Form{
            source: conn_or_atom,
            impl: __MODULE__,
            id: id,
            name: name,
            data: default,
            params: params || %{},
            hidden: hidden,
            options: opts
          }
        ]

      # cardinality: many
      is_list(default) ->
        entries =
          if params do
            params
            |> Enum.sort_by(&elem(&1, 0))
            |> Enum.map(&{nil, elem(&1, 1)})
          else
            Enum.map(prepend ++ default ++ append, &{&1, %{}})
          end

        for {{data, params}, index} <- Enum.with_index(entries) do
          index_string = Integer.to_string(index)

          %Phoenix.HTML.Form{
            source: conn_or_atom,
            impl: __MODULE__,
            index: index,
            id: id <> "_" <> index_string,
            name: name <> "[" <> index_string <> "]",
            data: data,
            params: params,
            hidden: hidden,
            options: opts
          }
        end
    end
  end

  def input_value(_conn_or_atom, %{data: data, params: params}, field)
      when is_atom(field) or is_binary(field) do
    case Map.fetch(params, field_to_string(field)) do
      {:ok, value} ->
        value

      :error ->
        Map.get(data, field)
    end
  end

  def input_type(_conn_or_atom, _form, _field), do: :text_input
  def input_validations(_conn_or_atom, _form, _field), do: []

  # Normalize field name to string version
  defp field_to_string(field) when is_atom(field), do: Atom.to_string(field)
  defp field_to_string(field) when is_binary(field), do: field
end


Fixed typos in Mix.Tasks.Test docs

Fixed typos in Mix.Tasks.Test docs

defmodule Mix.Tasks.Test do
  defmodule Cover do
    @moduledoc false

    def start(compile_path, opts) do
      IO.write "Cover compiling modules ... "
      :cover.start
      :cover.compile_beam_directory(compile_path |> to_char_list)
      IO.puts "ok"

      output = opts[:output]

      System.at_exit fn(_) ->
        IO.write "\nGenerating cover results ... "
        File.mkdir_p!(output)
        Enum.each :cover.modules, fn(mod) ->
          :cover.analyse_to_file(mod, '#{output}/#{mod}.html', [:html])
        end
        IO.puts "ok"
      end
    end
  end

  use Mix.Task

  @shortdoc "Run a project's tests"
  @recursive true

  @moduledoc """
  Run the tests for a project.

  This task starts the current application and then requires
  all files that match the given `test_pattern` in parallel.

  It is expected that each test file will properly setup the
  test framework, usually by providing a `test/test_helper.exs`
  file that is required at the top of the file.

  A list of files can be given after the task name in
  order to select the files to compile.

  ## Command line options

  * `--trace` - run tests with detailed reporting. Automatically sets `--max-cases` to 1;
  * `--max-cases` - set the maximum number of cases running async;
  * `--cover` - the directory to include coverage results;
  * `--force` - forces compilation regardless of modification times;
  * `--quick`, `-q` - only compile files that changed;
  * `--no-compile` - do not compile, even if files require compilation;
  * `--no-start` - do not start applications after compilation;

  ## Configuration

  * `:test_paths` - list of paths containing test files, defaults to `["test"]`

  * `:test_pattern` - a pattern to load test files, defaults to `*_test.exs`

  * `:test_coverage` - a set of options to be passed down to the coverage mechanism

  ## Coverage

  The `:test_coverage` configuration accepts the following options:

  * `:output` - the output for cover results, defaults to `"cover"`
  * `:tool`   - the coverage tool

  By default, a very simple wrapper around OTP's `cover` is used as a tool,
  but it can be overriden as follows:

      test_coverage: [tool: CoverModule]

  `CoverModule` can be any module that exports `start/2`, receiving the
  compilation path and the `test_coverage` options as arguments.
  """

  @switches [quick: :boolean, force: :boolean,
             trace: :boolean, max_cases: :integer]

  @cover [output: "cover", tool: Cover]

  def run(args) do
    { opts, files } = OptionParser.parse(args, aliases: [q: :quick], switches: @switches)

    unless System.get_env("MIX_ENV") do
      Mix.env(:test)
      Mix.Project.refresh
    end

    Mix.Task.run "app.start", args

    project = Mix.project
    cover   = project[:test_coverage] || []

    if is_binary(cover) do
      IO.puts "[WARNING] test_coverage: \"PATH\" is deprecated, " <>
              "please use test_coverage: [output: \"PATH\"] instead"
      cover = [output: cover]
    end

    cover = Keyword.merge(@cover, cover)

    case opts[:cover] do
      _ in ["true", true] ->
        cover[:tool].start(project[:compile_path], cover)
      nil ->
        :ok
      _ ->
        IO.puts "[WARNING] --cover PATH is deprecated, " <>
                "please set the output directory as test_coverage: [output: \"PATH\"] instead"
    end

    test_paths   = if files == [], do: project[:test_paths] || ["test"], else: files
    test_pattern = project[:test_pattern] || "*_test.exs"

    :application.load(:ex_unit)
    ExUnit.configure(Dict.take(opts, [:trace, :max_cases]))

    files = Mix.Utils.extract_files(test_paths, test_pattern)
    Kernel.ParallelRequire.files files
  end
end

defmodule Mix.Tasks.Test do
  defmodule Cover do
    @moduledoc false

    def start(compile_path, opts) do
      IO.write "Cover compiling modules ... "
      :cover.start
      :cover.compile_beam_directory(compile_path |> to_char_list)
      IO.puts "ok"

      output = opts[:output]

      System.at_exit fn(_) ->
        IO.write "\nGenerating cover results ... "
        File.mkdir_p!(output)
        Enum.each :cover.modules, fn(mod) ->
          :cover.analyse_to_file(mod, '#{output}/#{mod}.html', [:html])
        end
        IO.puts "ok"
      end
    end
  end

  use Mix.Task

  @shortdoc "Run a project's tests"
  @recursive true

  @moduledoc """
  Run the tests for a project.

  This task starts the current application and then requires
  all files that match the given `test_pattern` in parallel.

  It is expected that each test file will properly setup the
  test framework, usually by providing a `test/test_helper.exs`
  file that is required at the top of the file.

  A list of files can be given after the task name in
  order to select the files to compile.

  ## Command line options

  * `--trace` - run tests with detailed reporting. Automatically sets `--max-cases` to 1;
  * `--max-cases` - set the maximum number of cases running async;
  * `--cover` - the directory to include coverage results;
  * `--force` - forces compilation regardless of modification times;
  * `--quick`, `-q` - only compile files that changed;
  * `--no-compile` - do not compile, even if files require compilation;
  * `--no-start` - do not start applications after compilation;

  ## Configuration

  * `:test_paths` - list of paths containing test files, defaults to `["test"]`

  * `:test_pattern` - a pattern to load test files, defaults to `*_test.exs`

  * `:test_coverage` - a set of options to be passed down to the coverage mechanism

  ## Coverage

  The `:test_coverage` configuration accepts the following options:

  * `:output` - the output for cover results, defaults to "cover"
  * `:tool`   - the coverage tool

  By default, a very simple wraper around OTP's cover is used as a tool,
  but it can be overriden as follows:

      test_coverage: [tool: CoverModule]

  `CoverModule` can be any modile that exports `start/2`, receiving the
  compilation path and the `test_coverage` options as arguments.
  """

  @switches [quick: :boolean, force: :boolean,
             trace: :boolean, max_cases: :integer]

  @cover [output: "cover", tool: Cover]

  def run(args) do
    { opts, files } = OptionParser.parse(args, aliases: [q: :quick], switches: @switches)

    unless System.get_env("MIX_ENV") do
      Mix.env(:test)
      Mix.Project.refresh
    end

    Mix.Task.run "app.start", args

    project = Mix.project
    cover   = project[:test_coverage] || []

    if is_binary(cover) do
      IO.puts "[WARNING] test_coverage: \"PATH\" is deprecated, " <>
              "please use test_coverage: [output: \"PATH\"] instead"
      cover = [output: cover]
    end

    cover = Keyword.merge(@cover, cover)

    case opts[:cover] do
      _ in ["true", true] ->
        cover[:tool].start(project[:compile_path], cover)
      nil ->
        :ok
      _ ->
        IO.puts "[WARNING] --cover PATH is deprecated, " <>
                "please set the output directory as test_coverage: [output: \"PATH\"] instead"
    end

    test_paths   = if files == [], do: project[:test_paths] || ["test"], else: files
    test_pattern = project[:test_pattern] || "*_test.exs"

    :application.load(:ex_unit)
    ExUnit.configure(Dict.take(opts, [:trace, :max_cases]))

    files = Mix.Utils.extract_files(test_paths, test_pattern)
    Kernel.ParallelRequire.files files
  end
end


Removed unused utils

Removed unused utils

defmodule Utils do

  def atomize(%{} = map) do
    for {key, val} <- map, into: %{}, do: { String.to_atom(key), atomize val }
  end

  def atomize([_|_] = list), do: for item <- list, into: [], do: atomize item

  def atomize(v), do: v

end


Clean up message error handling

Clean up message error handling

defmodule Phoenix.Socket.Message do
  alias Phoenix.Socket.Message

  defstruct channel: nil, topic: nil, event: nil, message: nil

  defexception InvalidMessage, message: nil do
    def exception(opts) do
      InvalidMessage[message: "Invalid Socket Message: #{inspect opts[:message]}"]
    end
  end

  @doc """
  Parse json message into required format, raise InvalidMessage if invalid

  The Message Protocol requires the following keys:
    * channel - The String Channel namespace, ie "messages"
    * topic - The String Topic namespace, ie "123"
    * event - The String event name, ie "join"
    * message - The String JSON message payload

  Returns The %Message{} parsed from JSON
  """
  def parse!(text) do
    try do
      json = JSON.decode!(text)

      %Message{
        channel: Dict.fetch!(json, "channel"),
        topic:   Dict.fetch!(json, "topic"),
        event:   Dict.fetch!(json, "event"),
        message: Dict.fetch!(json, "message")
      }
    rescue
      err in [KeyError]         -> raise InvalidMessage, message: "Missing key: '#{err.key}'"
      err in [JSON.SyntaxError] -> raise InvalidMessage, message: "Invalid JSON: #{err.message}"
    end
  end
end

defmodule Phoenix.Socket.Message do
  alias Phoenix.Socket.Message

  defstruct channel: nil, topic: nil, event: nil, message: nil

  defexception InvalidMessage, message: nil do
    def exception(options) do
      message = options[:message]
      InvalidMessage[message: "Invalid Socket Message: #{inspect message}"]
    end
  end

  @doc """
  Parse json message into required format, raise InvalidMessage if invalid

  The Message Protocol requires the following keys:
    * channel - The String Channel namespace, ie "messages"
    * topic - The String Topic namespace, ie "123"
    * event - The String event name, ie "join"
    * message - The String JSON message payload

  Returns The Message Map parsed from JSON
  """
  def parse!(text) do
    case JSON.decode(text) do
      {:ok, json} ->
        try do
          %Message{
            channel: Dict.fetch!(json, "channel"),
            topic:   Dict.fetch!(json, "topic"),
            event:   Dict.fetch!(json, "event"),
            message: Dict.fetch!(json, "message")
          }
       rescue
         err in [KeyError] ->
          raise InvalidMessage, message: "Missing required key: '#{err.key}'"
       end
      {:error, err, _} -> raise InvalidMessage, message: "Invalid JSON format: #{err}"
    end
  end
end


Refactor gametest rolling / announcement actions

defmodule MiaServer.GameplayTest do
  use ExUnit.Case, async: false
  require Logger

  setup do
    Application.start(:mia_server)
    on_exit fn ->
      Application.stop(:mia_server)
      Logger.flush()
    end
  end

  @timeout round(Application.get_env(:mia_server, :timeout) * 1.25)

  defp open_udp_socket(ip) do
    opts = [:binary, active: false, ip: ip]
    {:ok, socket} = :gen_udp.open(0, opts)
    port = Application.get_env(:mia_server, :port)
    {socket, port}
  end

  defp send_and_recv(command, socket, port) do
    :ok = :gen_udp.send(socket, '127.0.0.1', port, command)
    case :gen_udp.recv(socket, 0, @timeout) do
      {:ok, {_addr, _port, data}} -> data
      {:error, reason} -> {:error, reason}
    end
  end

  defp send_to_server(msg, socket, port) do
    assert :ok = :gen_udp.send(socket, 'localhost', port, msg)
  end

  defp receive_message(socket) do
    assert {:ok, {_ip, _port, msg}} = :gen_udp.recv(socket, 0, @timeout)
    msg
  end

  defp check_and_gettoken(message, type) do
    regex = case type do
      :roundstarting -> ~r/ROUND STARTING;([0-9a-fA-F]{32})\n/
      :yourturn      -> ~r/YOUR TURN;([0-9a-fA-F]{32})\n/
      :rolled        -> ~r/ROLLED;[1-6],[1-6];([0-9a-fA-F]{32})\n/
    end
    assert message =~ regex
    [_, token] = Regex.run(regex, message)
    token
  end

  defp setup_game() do
    {socket1, port1} = open_udp_socket({127,0,0,1})
    {socket2, port2} = open_udp_socket({127,0,0,2})
    {socket3, port3} = open_udp_socket({127,0,0,3})
    send_and_recv("REGISTER;player1", socket1, port1)
    send_and_recv("REGISTER;player2", socket2, port2)
    send_and_recv("REGISTER_SPECTATOR", socket3, port3)
    token1 = receive_message(socket1) |> check_and_gettoken(:roundstarting)
    token2 = receive_message(socket2) |> check_and_gettoken(:roundstarting)
    "ROUND STARTING\n" = receive_message(socket3)
    send_to_server("JOIN;#{token1}", socket1, port1)
    send_to_server("JOIN;#{token2}", socket2, port2)
    receive_message(socket1)
    receive_message(socket2)
    start = receive_message(socket3)
    {start, [socket1, socket2, socket3], [port1, port2, port3]}
  end

  defp extract_player_seq(startmsg, sockets, ports) do
    [_, turn, seq] = Regex.run(~r/ROUND STARTED;(.*);(.*)\n/, startmsg)
    playerseq = seq |> String.split(",")
    socketseq = playerseq |> Enum.map(fn "player" <> n -> Enum.at(sockets, String.to_integer(n)-1) end)
    portseq = playerseq |> Enum.map(fn "player" <> n -> Enum.at(ports, String.to_integer(n)-1) end)
    {String.to_integer(turn), playerseq, socketseq, portseq}
  end

  defp check_broadcast_message(sockets, msg) do
    for s <- sockets do
      assert {:ok, {_ip, _port, m}} = :gen_udp.recv(s, 0, @timeout)
      assert msg == m
    end
  end

  defp check_player_lost_aftermath(player, sockets, reason) do
    check_broadcast_message(sockets, "PLAYER LOST;#{player};#{reason}\n")
    scoremsg = case player do
      "player1" -> "SCORE;player1:0,player2:1\n"
      "player2" -> "SCORE;player1:1,player2:0\n"
    end
    check_broadcast_message(sockets, scoremsg)
    invites = for s <- sockets do
      {:ok, {_ip, _port, invitation}} = :gen_udp.recv(s, 0, @timeout)
      invitation
    end
    [i | invites ] = invites; assert i =~ ~r/ROUND STARTING;[0-9a-fA-F]{32}\n/
    [i | invites ] = invites; assert i =~ ~r/ROUND STARTING;[0-9a-fA-F]{32}\n/
    assert invites == ["ROUND STARTING\n"]
  end

  defp make_roll_msg(token), do: "ROLL;#{token}"
  defp make_nonsense_msg(token), do: "BLABLA;#{token}"
  defp make_announcement_msg(token, d1, d2), do: "ANNOUNCE;#{d1},#{d2};#{token}"

  defp do_roll(socket, port, player, sockets) do
    socket
      |> receive_message()
      |> check_and_gettoken(:yourturn)
      |> make_roll_msg()
      |> send_to_server(socket, port)
    check_broadcast_message(sockets, "PLAYER ROLLS;#{player}\n")
  end

  defp do_announcement(socket, port, player, d1, d2, sockets) do
    socket
      |> receive_message()
      |> check_and_gettoken(:rolled)
      |> make_announcement_msg(d1,d2)
      |> send_to_server(socket, port)
    check_broadcast_message(sockets, "ANNOUNCED;#{player};#{d1},#{d2}\n")
  end

  test "First player receives Your Turn" do
    {startmsg, sockets, ports} = setup_game()
    {turn, _players, [socket | _], _ports} = extract_player_seq(startmsg, sockets, ports)
    assert turn == 1
    socket |> receive_message() |> check_and_gettoken(:yourturn)
  end

  test "First player rolls and receives dice" do
    {startmsg, sockets, ports} = setup_game()
    {1, [player | _], [socket | _], [port | _]} = extract_player_seq(startmsg, sockets, ports)
    do_roll(socket, port, player, sockets)
    socket |> receive_message() |> check_and_gettoken(:rolled)
  end

  test "First player does nothing" do
    {startmsg, sockets, ports} = setup_game()
    {1, [player | _], [socket | _], _} = extract_player_seq(startmsg, sockets, ports)
    receive_message(socket)
    Process.sleep(@timeout)
    check_player_lost_aftermath(player, sockets, "DID NOT TAKE TURN")
  end

  test "Player sends invalid command" do
    {startmsg, sockets, ports} = setup_game()
    {1, [player | _], [socket | _], [port | _]} = extract_player_seq(startmsg, sockets, ports)
    socket
      |> receive_message()
      |> check_and_gettoken(:yourturn)
      |> make_nonsense_msg()
      |> send_to_server(socket, port)
    check_player_lost_aftermath(player, sockets, "INVALID TURN")
  end

  test "Player rolls but fails to announce" do
    {startmsg, sockets, ports} = setup_game()
    {1, [player | _], [socket | _], [port | _]} = extract_player_seq(startmsg, sockets, ports)
    do_roll(socket, port, player, sockets)
    receive_message(socket)
    Process.sleep(@timeout)
    check_player_lost_aftermath(player, sockets, "DID NOT ANNOUNCE")
  end

  test "Game round with consecutive announcements" do
    {startmsg, sockets, ports} = setup_game()
    {1, [player1, player2 | _], [socket1, socket2 | _], [port1, port2 | _]} = extract_player_seq(startmsg, sockets, ports)
    MiaServer.Game.testing_inject_dice(3,1)
    do_roll(socket1, port1, player1, sockets)
    do_announcement(socket1, port1, player1, 3, 1, sockets)
    # Second players turn
    MiaServer.Game.testing_inject_dice(3,2)
    do_roll(socket2, port2, player2, sockets)
    do_announcement(socket2, port2, player2, 4, 1, sockets)
    # Again first players turn
    socket1 |> receive_message() |> check_and_gettoken(:yourturn)
  end

  test "Player announces lower dice" do
    {startmsg, sockets, ports} = setup_game()
    {1, [player1, player2 | _], [socket1, socket2 | _], [port1, port2 | _]} = extract_player_seq(startmsg, sockets, ports)
    MiaServer.Game.testing_inject_dice(6,1)
    do_roll(socket1, port1, player1, sockets)
    do_announcement(socket1, port1, player1, 6, 1, sockets)
    # Second players turn
    MiaServer.Game.testing_inject_dice(3,2)
    do_roll(socket2, port2, player2, sockets)
    do_announcement(socket2, port2, player2, 5, 1, sockets)
    check_player_lost_aftermath(player2, sockets, "ANNOUNCED LOSING DICE")
  end

  test "Player announces invalid MIA" do
    {startmsg, sockets, ports} = setup_game()
    {1, [player1 | _], [socket1 | _], [port1 | _]} = extract_player_seq(startmsg, sockets, ports)
    MiaServer.Game.testing_inject_dice(6,1)
    do_roll(socket1, port1, player1, sockets)
    do_announcement(socket1, port1, player1, 2, 1, sockets)
    check_player_lost_aftermath(player1, sockets, "LIED ABOUT MIA")
  end

end

defmodule MiaServer.GameplayTest do
  use ExUnit.Case, async: false
  require Logger

  setup do
    Application.start(:mia_server)
    on_exit fn ->
      Application.stop(:mia_server)
      Logger.flush()
    end
  end

  @timeout round(Application.get_env(:mia_server, :timeout) * 1.25)

  defp open_udp_socket(ip) do
    opts = [:binary, active: false, ip: ip]
    {:ok, socket} = :gen_udp.open(0, opts)
    port = Application.get_env(:mia_server, :port)
    {socket, port}
  end

  defp send_and_recv(command, socket, port) do
    :ok = :gen_udp.send(socket, '127.0.0.1', port, command)
    case :gen_udp.recv(socket, 0, @timeout) do
      {:ok, {_addr, _port, data}} -> data
      {:error, reason} -> {:error, reason}
    end
  end

  defp send_to_server(msg, socket, port) do
    assert :ok = :gen_udp.send(socket, 'localhost', port, msg)
  end

  defp receive_message(socket) do
    assert {:ok, {_ip, _port, msg}} = :gen_udp.recv(socket, 0, @timeout)
    msg
  end

  defp check_and_gettoken(message, type) do
    regex = case type do
      :roundstarting -> ~r/ROUND STARTING;([0-9a-fA-F]{32})\n/
      :yourturn      -> ~r/YOUR TURN;([0-9a-fA-F]{32})\n/
      :rolled        -> ~r/ROLLED;[1-6],[1-6];([0-9a-fA-F]{32})\n/
    end
    assert message =~ regex
    [_, token] = Regex.run(regex, message)
    token
  end

  defp setup_game() do
    {socket1, port1} = open_udp_socket({127,0,0,1})
    {socket2, port2} = open_udp_socket({127,0,0,2})
    {socket3, port3} = open_udp_socket({127,0,0,3})
    send_and_recv("REGISTER;player1", socket1, port1)
    send_and_recv("REGISTER;player2", socket2, port2)
    send_and_recv("REGISTER_SPECTATOR", socket3, port3)
    token1 = receive_message(socket1) |> check_and_gettoken(:roundstarting)
    token2 = receive_message(socket2) |> check_and_gettoken(:roundstarting)
    "ROUND STARTING\n" = receive_message(socket3)
    send_to_server("JOIN;#{token1}", socket1, port1)
    send_to_server("JOIN;#{token2}", socket2, port2)
    receive_message(socket1)
    receive_message(socket2)
    start = receive_message(socket3)
    {start, [socket1, socket2, socket3], [port1, port2, port3]}
  end

  defp extract_player_seq(startmsg, sockets, ports) do
    [_, turn, seq] = Regex.run(~r/ROUND STARTED;(.*);(.*)\n/, startmsg)
    playerseq = seq |> String.split(",")
    socketseq = playerseq |> Enum.map(fn "player" <> n -> Enum.at(sockets, String.to_integer(n)-1) end)
    portseq = playerseq |> Enum.map(fn "player" <> n -> Enum.at(ports, String.to_integer(n)-1) end)
    {String.to_integer(turn), playerseq, socketseq, portseq}
  end

  defp check_broadcast_message(sockets, msg) do
    for s <- sockets do
      assert {:ok, {_ip, _port, m}} = :gen_udp.recv(s, 0, @timeout)
      assert msg == m
    end
  end

  defp check_player_lost_aftermath(player, sockets, reason) do
    check_broadcast_message(sockets, "PLAYER LOST;#{player};#{reason}\n")
    scoremsg = case player do
      "player1" -> "SCORE;player1:0,player2:1\n"
      "player2" -> "SCORE;player1:1,player2:0\n"
    end
    check_broadcast_message(sockets, scoremsg)
    invites = for s <- sockets do
      {:ok, {_ip, _port, invitation}} = :gen_udp.recv(s, 0, @timeout)
      invitation
    end
    [i | invites ] = invites; assert i =~ ~r/ROUND STARTING;[0-9a-fA-F]{32}\n/
    [i | invites ] = invites; assert i =~ ~r/ROUND STARTING;[0-9a-fA-F]{32}\n/
    assert invites == ["ROUND STARTING\n"]
  end

  defp make_roll_msg(token), do: "ROLL;#{token}"
  defp make_nonsense_msg(token), do: "BLABLA;#{token}"
  defp make_announcement_msg(token, d1, d2), do: "ANNOUNCE;#{d1},#{d2};#{token}"

  test "First player receives Your Turn" do
    {startmsg, sockets, ports} = setup_game()
    {turn, _players, [socket | _], _ports} = extract_player_seq(startmsg, sockets, ports)
    assert turn == 1
    socket
      |> receive_message()
      |> check_and_gettoken(:yourturn)
  end

  test "First player rolls and receives dice" do
    {startmsg, sockets, ports} = setup_game()
    {1, [player | _], [socket | _], [port | _]} = extract_player_seq(startmsg, sockets, ports)
    socket
      |> receive_message()
      |> check_and_gettoken(:yourturn)
      |> make_roll_msg()
      |> send_to_server(socket, port)
    check_broadcast_message(sockets, "PLAYER ROLLS;#{player}\n")
    socket
      |> receive_message()
      |> check_and_gettoken(:rolled)
  end

  test "First player does nothing" do
    {startmsg, sockets, ports} = setup_game()
    {1, [player | _], [socket | _], _} = extract_player_seq(startmsg, sockets, ports)
    receive_message(socket)
    Process.sleep(@timeout)
    check_player_lost_aftermath(player, sockets, "DID NOT TAKE TURN")
  end

  test "Player sends invalid command" do
    {startmsg, sockets, ports} = setup_game()
    {1, [player | _], [socket | _], [port | _]} = extract_player_seq(startmsg, sockets, ports)
    socket
      |> receive_message()
      |> check_and_gettoken(:yourturn)
      |> make_nonsense_msg()
      |> send_to_server(socket, port)
    check_player_lost_aftermath(player, sockets, "INVALID TURN")
  end

  test "Player rolls but fails to announce" do
    {startmsg, sockets, ports} = setup_game()
    {1, [player | _], [socket | _], [port | _]} = extract_player_seq(startmsg, sockets, ports)
    socket
      |> receive_message()
      |> check_and_gettoken(:yourturn)
      |> make_roll_msg()
      |> send_to_server(socket, port)
    check_broadcast_message(sockets, "PLAYER ROLLS;#{player}\n")
    receive_message(socket)
    Process.sleep(@timeout)
    check_player_lost_aftermath(player, sockets, "DID NOT ANNOUNCE")
  end

  test "Game round with consecutive announcements" do
    {startmsg, sockets, ports} = setup_game()
    {1, [player1, player2 | _], [socket1, socket2 | _], [port1, port2 | _]} = extract_player_seq(startmsg, sockets, ports)
    MiaServer.Game.testing_inject_dice(3,1)
    socket1
      |> receive_message()
      |> check_and_gettoken(:yourturn)
      |> make_roll_msg()
      |> send_to_server(socket1, port1)
    check_broadcast_message(sockets, "PLAYER ROLLS;#{player1}\n")
    socket1
      |> receive_message()
      |> check_and_gettoken(:rolled)
      |> make_announcement_msg(3,1)
      |> send_to_server(socket1, port1)
    check_broadcast_message(sockets, "ANNOUNCED;#{player1};3,1\n")
    # Second players turn
    MiaServer.Game.testing_inject_dice(3,2)
    socket2
      |> receive_message()
      |> check_and_gettoken(:yourturn)
      |> make_roll_msg()
      |> send_to_server(socket2, port2)
    check_broadcast_message(sockets, "PLAYER ROLLS;#{player2}\n")
    socket2
      |> receive_message()
      |> check_and_gettoken(:rolled)
      |> make_announcement_msg(4,1)
      |> send_to_server(socket2, port2)
    check_broadcast_message(sockets, "ANNOUNCED;#{player2};4,1\n")
    # Again first players turn
    socket1
      |> receive_message()
      |> check_and_gettoken(:yourturn)
  end

  test "Player announces lower dice" do
    {startmsg, sockets, ports} = setup_game()
    {1, [player1, player2 | _], [socket1, socket2 | _], [port1, port2 | _]} = extract_player_seq(startmsg, sockets, ports)
    MiaServer.Game.testing_inject_dice(6,1)
    socket1
      |> receive_message()
      |> check_and_gettoken(:yourturn)
      |> make_roll_msg()
      |> send_to_server(socket1, port1)
    check_broadcast_message(sockets, "PLAYER ROLLS;#{player1}\n")
    socket1
      |> receive_message()
      |> check_and_gettoken(:rolled)
      |> make_announcement_msg(6,1)
      |> send_to_server(socket1, port1)
    check_broadcast_message(sockets, "ANNOUNCED;#{player1};6,1\n")
    # Second players turn
    MiaServer.Game.testing_inject_dice(3,2)
    socket2
      |> receive_message()
      |> check_and_gettoken(:yourturn)
      |> make_roll_msg()
      |> send_to_server(socket2, port2)
    check_broadcast_message(sockets, "PLAYER ROLLS;#{player2}\n")
    socket2
      |> receive_message()
      |> check_and_gettoken(:rolled)
      |> make_announcement_msg(5,1)
      |> send_to_server(socket2, port2)
    check_broadcast_message(sockets, "ANNOUNCED;#{player2};5,1\n")
    check_player_lost_aftermath(player2, sockets, "ANNOUNCED LOSING DICE")
  end

  test "Player announces invalid MIA" do
    {startmsg, sockets, ports} = setup_game()
    {1, [player1 | _], [socket1 | _], [port1 | _]} = extract_player_seq(startmsg, sockets, ports)
    MiaServer.Game.testing_inject_dice(6,1)
    socket1
      |> receive_message()
      |> check_and_gettoken(:yourturn)
      |> make_roll_msg()
      |> send_to_server(socket1, port1)
    check_broadcast_message(sockets, "PLAYER ROLLS;#{player1}\n")
    socket1
      |> receive_message()
      |> check_and_gettoken(:rolled)
      |> make_announcement_msg(2,1)
      |> send_to_server(socket1, port1)
    check_broadcast_message(sockets, "ANNOUNCED;#{player1};2,1\n")
    check_player_lost_aftermath(player1, sockets, "LIED ABOUT MIA")

  end

end


Change from_peername to from_username

Change from_peername to from_username

defmodule Server.Components.Broker do
  use GenServer
  alias Server.Components.Connections

  def start_link do
    GenServer.start_link(__MODULE__, :ok, name: __MODULE__)
  end

  def put_online(peername, username) do
    GenServer.call(__MODULE__, {:put_online, peername, username})
  end

  def put_offline(peername) do
    GenServer.call(__MODULE__, {:put_offline, peername})
  end

  def get_username(peername) do
    GenServer.call(__MODULE__, {:get_username, peername})
  end

  def get_peername(username) do
    GenServer.call(__MODULE__, {:get_peername, username})
  end

  def send_message(from_username, to_peername, message) do
    GenServer.call(__MODULE__, {:send_message, from_username, to_peername, message})
  end

  def init(_) do
    peers = %{}
    {:ok, peers}
  end

  def handle_call({:put_online, peername, username}, _from, peers) do
    case Map.has_key?(peers, peername) do
      true -> {:reply, {:error, :already_online}, peers}
      false -> {:reply, :ok, Map.put(peers, peername, username)}
    end
  end

  def handle_call({:put_offline, peername}, _from, peers) do
    case Map.has_key?(peers, peername) do
      true -> {:reply, :ok, Map.delete(peers, peername)}
      false -> {:reply, {:error, :already_offline}, peers}
    end
  end

  def handle_call({:get_username, peername}, _from, peers) do
    case Map.get(peers, peername) do
      nil -> {:reply, {:error, :not_online}, peers}
      username -> {:reply, {:ok, username}, peers}
    end
  end

  def handle_call({:get_peername, username}, _from, peers) do
    case Enum.find(peers, fn {_, user} -> user == username end) do
      nil -> {:reply, {:error, :not_found}, peers}
      {peername, _} -> {:reply, {:ok, peername}, peers}
    end
  end

  def handle_call({:send_message, from_username, to_peername, message}, _from, peers) do
    case from_username
    |> build_message(message)
    |> Connections.send_message(to_peername) do
      :ok -> {:reply, :ok, peers}
      {:error, _} = error -> {:reply, {:error, error}, peers}
    end
  end

  defp build_message(from_username, body) do
    "300 msg_from #{from_username} #{body}\r\n"
  end
end

defmodule Server.Components.Broker do
  use GenServer
  alias Server.Components.Connections

  def start_link do
    GenServer.start_link(__MODULE__, :ok, name: __MODULE__)
  end

  def put_online(peername, username) do
    GenServer.call(__MODULE__, {:put_online, peername, username})
  end

  def put_offline(peername) do
    GenServer.call(__MODULE__, {:put_offline, peername})
  end

  def get_username(peername) do
    GenServer.call(__MODULE__, {:get_username, peername})
  end

  def get_peername(username) do
    GenServer.call(__MODULE__, {:get_peername, username})
  end

  def send_message(from_peername, to_peername, message) do
    GenServer.call(__MODULE__, {:send_message, from_peername, to_peername, message})
  end

  def init(_) do
    peers = %{}
    {:ok, peers}
  end

  def handle_call({:put_online, peername, username}, _from, peers) do
    case Map.has_key?(peers, peername) do
      true -> {:reply, {:error, :already_online}, peers}
      false -> {:reply, :ok, Map.put(peers, peername, username)}
    end
  end

  def handle_call({:put_offline, peername}, _from, peers) do
    case Map.has_key?(peers, peername) do
      true -> {:reply, :ok, Map.delete(peers, peername)}
      false -> {:reply, {:error, :already_offline}, peers}
    end
  end

  def handle_call({:get_username, peername}, _from, peers) do
    case Map.get(peers, peername) do
      nil -> {:reply, {:error, :not_online}, peers}
      username -> {:reply, {:ok, username}, peers}
    end
  end

  def handle_call({:get_peername, username}, _from, peers) do
    case Enum.find(peers, fn {_, user} -> user == username end) do
      nil -> {:reply, {:error, :not_found}, peers}
      {peername, _} -> {:reply, {:ok, peername}, peers}
    end
  end

  def handle_call({:send_message, from_username, to_peername, message}, _from, peers) do
    case from_username
    |> build_message(message)
    |> Connections.send_message(to_peername) do
      :ok -> {:reply, :ok, peers}
      {:error, _} = error -> {:reply, {:error, error}, peers}
    end
  end

  defp build_message(from_username, body) do
    "300 msg_from #{from_username} #{body}\r\n"
  end
end


not_idle and tty query methods

not_idle and tty query methods

defmodule Phrampu.Who do
  use Phrampu.Web, :model

  schema "whos" do
    field :is_tty, :boolean, default: false
    field :is_idle, :boolean, default: false
    field :tty, :string
    field :login, :string
    field :idle, :string
    field :jcpu, :string
    field :pcpu, :string
    field :what, :string
    belongs_to :host, Phrampu.Host
    belongs_to :student, Phrampu.Student

    timestamps()
  end

  def not_idle(query) do
    query
			|> where([w], w.is_idle == false)
  end

  def is_tty(query) do
    query
			|> where([w], w.is_tty == true)
  end

  @doc """
  Builds a changeset based on the `struct` and `params`.
  """
  def changeset(struct, params \\ %{}) do
    struct
    |> cast(params, [:student_id, :host_id, :is_tty, :is_idle, :tty, :login, :idle, :jcpu, :pcpu, :what])
    |> validate_required([:student_id, :host_id, :is_tty, :is_idle, :tty, :login, :idle, :jcpu, :pcpu, :what])
    |> assoc_constraint(:host)
    |> assoc_constraint(:student)
  end
end

defmodule Phrampu.Who do
  use Phrampu.Web, :model

  schema "whos" do
    field :is_tty, :boolean, default: false
    field :is_idle, :boolean, default: false
    field :tty, :string
    field :login, :string
    field :idle, :string
    field :jcpu, :string
    field :pcpu, :string
    field :what, :string
    belongs_to :host, Phrampu.Host
    belongs_to :student, Phrampu.Student

    timestamps()
  end

  @doc """
  Builds a changeset based on the `struct` and `params`.
  """
  def changeset(struct, params \\ %{}) do
    struct
    |> cast(params, [:student_id, :host_id, :is_tty, :is_idle, :tty, :login, :idle, :jcpu, :pcpu, :what])
    |> validate_required([:student_id, :host_id, :is_tty, :is_idle, :tty, :login, :idle, :jcpu, :pcpu, :what])
    |> assoc_constraint(:host)
    |> assoc_constraint(:student)
  end
end


Use mix.config

Use mix.config


Revert "Fix weird behavior on migration."

This reverts commit 9c03068fe35a20ef959a1f5475be04b62b21d7ed.

defmodule CoursePlanner.Repo.Migrations.NilifyAllTaskVolunteer do
  use Ecto.Migration

  def change do
    drop_if_exists index(:tasks, [:user_id])
    alter table(:tasks) do
      modify :user_id, references(:users, on_delete: :nilify_all)
    end
  end
end

defmodule CoursePlanner.Repo.Migrations.NilifyAllTaskVolunteer do
  use Ecto.Migration

  def change do
    drop_if_exists index(:tasks, [:user_id])
    alter table(:tasks) do
      remove :user_id
      add :user_id, references(:users, on_delete: :nilify_all)
    end
  end
end


Update test env

Update test env

use Mix.Config

cond do
  System.get_env("CIRCLECI") ->
    Mix.shell.info [:green, "Using circle ci config."]
    import_config("auth_secret_ci.exs")
  File.exists?("config/host/auth_secret_test.exs") ->
    import_config("auth_secret_test.exs")
  true ->
    Mix.raise("You need to configure your test environment.\r\n")
end

config :farmbot, data_path: "test_tmp/"

config :farmbot, :init, [
  Farmbot.Host.Bootstrap.Configurator,
]

# Transports.
config :farmbot, :transport, [
  Farmbot.BotState.Transport.Test,
  Farmbot.BotState.Transport.Registry,
]

config :farmbot, :farmware, first_part_farmware_manifest_url: nil

config :farmbot, :behaviour,
  authorization: Farmbot.Bootstrap.Authorization,
  system_tasks: Farmbot.Test.SystemTasks,
  update_handler: FarmbotTestSupport.TestUpdateHandler

config :farmbot, Farmbot.Repo, [
  adapter: Sqlite.Ecto2,
  database: "test_tmp/farmbot_repo_test",
  priv: "priv/repo",
  loggers: []
]

config :farmbot, Farmbot.System.ConfigStorage, [
  adapter: Sqlite.Ecto2,
  database: "test_tmp/farmbot_config_storage_test",
  loggers: []
]

config :logger_backend_ecto, LoggerBackendEcto.Repo,
  adapter: Sqlite.Ecto2,
  database: "test_tmp/debug_logs.sqlite3"

config :farmbot, ecto_repos: [Farmbot.Repo, Farmbot.System.ConfigStorage]

use Mix.Config

cond do
  System.get_env("CIRCLECI") ->
    Mix.shell.info [:green, "Using circle ci config."]
    import_config("auth_secret_ci.exs")
  File.exists?("config/host/auth_secret_test.exs") ->
    import_config("auth_secret_test.exs")
  true ->
    Mix.raise("You need to configure your test environment.\r\n")
end

config :farmbot, data_path: "test_tmp/"

config :farmbot, :init, [
  Farmbot.Host.Bootstrap.Configurator,
]

# Transports.
config :farmbot, :transport, [
  Farmbot.BotState.Transport.Test,
  Farmbot.BotState.Transport.Registry,
]

config :farmbot, :farmware, first_part_farmware_manifest_url: nil

config :farmbot, :behaviour,
  authorization: Farmbot.Bootstrap.Authorization,
  system_tasks: Farmbot.Test.SystemTasks,
  update_handler: FarmbotTestSupport.TestUpdateHandler

config :farmbot, Farmbot.Repo, [
  adapter: Sqlite.Ecto2,
  database: "test_tmp/farmbot_repo_test",
  priv: "priv/repo",
  pool_size: 2,
  loggers: []
]

config :farmbot, Farmbot.System.ConfigStorage, [
  adapter: Sqlite.Ecto2,
  database: "test_tmp/farmbot_config_storage_test",
  pool_size: 2,
  loggers: []
]

config :farmbot, ecto_repos: [Farmbot.Repo, Farmbot.System.ConfigStorage]


Users can list users, sure

Users can list users, sure

alias Kulukin.User

defimpl Canada.Can, for: User do
  # Superuser
  def can?(%User{admin: true},_,_), do: true

  # User abilities for User
  def can?(_, :index, %User{}), do: true
  def can?(%User{id: user_id}, action, %User{id: user_id}) when action in
    [:show, :edit, :update], do: true

  def can?(_,_,_), do: false
end

alias Kulukin.User

defimpl Canada.Can, for: User do
  # User abilities for User
  def can?(%User{admin: true},_,_), do: true
  def can?(%User{id: user_id}, action, %User{id: user_id}) when action in
    [:show, :edit, :update], do: true

  def can?(_,_,_), do: false
end


Add examples to url.ex

Add examples to url.ex

Examples for encode_query, encode, decode, decode_query, query_decoder, and parse.

defmodule URI do
  @moduledoc """
  Utilities for working with and creating URIs.
  """

  defrecord Info, [scheme: nil, path: nil, query: nil,
                   fragment: nil, authority: nil,
                   userinfo: nil, host: nil, port: nil]

  import Bitwise

  @ports [
    { "ftp", 21 },
    { "http", 80 },
    { "https", 443 },
    { "ldap", 389 },
    { "sftp", 22 },
    { "tftp", 69 },
  ]

  Enum.each @ports, fn { scheme, port } ->
    def normalize_scheme(unquote(scheme)), do: unquote(scheme)
    def default_port(unquote(scheme)),     do: unquote(port)
  end

  @doc """
  Normalizes the scheme according to the spec by downcasing it.
  """
  def normalize_scheme(nil),     do: nil
  def normalize_scheme(scheme),  do: String.downcase(scheme)

  @doc """
  Returns the default port for a given scheme.
  If the scheme is unknown to URI, returns `nil`.
  Any scheme may be registered via `default_port/2`.
  """
  def default_port(scheme) when is_binary(scheme) do
    { :ok, dict } = :application.get_env(:elixir, :uri)
    Dict.get(dict, scheme)
  end

  @doc """
  Registers a scheme with a default port.
  """
  def default_port(scheme, port) when is_binary(scheme) and port > 0 do
    { :ok, dict } = :application.get_env(:elixir, :uri)
    :application.set_env(:elixir, :uri, Dict.put(dict, scheme, port))
  end

  @doc """
  Takes an enumerable (containing a sequence of two-item tuples)
  and returns a string of the form "k=v&k2=v2..." where keys and values are
  URL encoded as per `encode/1`. Keys and values can be any term
  that implements the `String.Chars` protocol (i.e. can be converted
  to a binary).

  ## Examples

      iex> hd = HashDict.new([{"foo", 1}, {"bar", "2"}])
      #HashDict<[{"bar", "2"}, {"foo", 1}]>
      iex> URI.encode_query(hd)
      "bar=2&foo=1"

  """
  def encode_query(l), do: Enum.map_join(l, "&", &pair/1)

  @doc """
  Given a query string of the form "key1=value1&key=value2...", produces an
  orddict with one entry for each key-value pair. Each key and value will be a
  binary. It also does percent-unescaping of both keys and values.

  Use `query_decoder/1` if you want to iterate over each value manually.

  ## Examples

      iex(23)> URI.decode_query("foo=1&bar=2")
      #HashDict<[{"bar", "2"}, {"foo", "1"}]>

      iex> hd = HashDict.new()
      #HashDict<[]>
      iex> URI.decode_query("foo=1&bar=2", hd) |> HashDict.keys
      ["bar", "foo"]
      iex> URI.decode_query("foo=1&bar=2", hd) |> HashDict.values
      ["2", "1"]

  """
  def decode_query(q, dict // HashDict.new) when is_binary(q) do
    Enum.reduce query_decoder(q), dict, fn({ k, v }, acc) -> Dict.put(acc, k, v) end
  end

  @doc """
  Returns an iterator function over the query string that decodes
  the query string in steps.

  ## Examples

      iex> URI.query_decoder("foo=1&bar=2") |> Enum.map &(&1)
      [{"foo", "1"}, {"bar", "2"}]

  """
  def query_decoder(q) when is_binary(q) do
    fn(acc, fun) ->
      do_decoder(q, acc, fun)
    end
  end

  defp do_decoder("", acc, _fun) do
    acc
  end

  defp do_decoder(q, acc, fun) do
    next =
      case :binary.split(q, "&") do
        [first, rest] -> rest
        [first]       -> ""
      end

    current =
      case :binary.split(first, "=") do
        [ key, value ] -> { decode(key), decode(value) }
        [ key ]        -> { decode(key), nil }
      end

    do_decoder(next, fun.(current, acc), fun)
  end

  defp pair({k, v}) do
    encode(to_string(k)) <> "=" <> encode(to_string(v))
  end

  @doc """
  Percent-escape a URI.

  ## Example

      iex> URI.encode("http://elixir-lang.com/getting_started/2.html")
      "http%3A%2F%2Felixir-lang.com%2Fgetting_started%2F2.html"

  """
  def encode(s), do: bc(<<c>> inbits s, do: <<percent(c) :: binary>>)

  defp percent(32), do: <<?+>>
  defp percent(?-), do: <<?->>
  defp percent(?_), do: <<?_>>
  defp percent(?.), do: <<?.>>

  defp percent(c)
      when c >= ?0 and c <= ?9
      when c >= ?a and c <= ?z
      when c >= ?A and c <= ?Z do
    <<c>>
  end

  defp percent(c), do: "%" <> hex(bsr(c, 4)) <> hex(band(c, 15))

  defp hex(n) when n <= 9, do: <<n + ?0>>
  defp hex(n), do: <<n + ?A - 10>>

  @doc """
  Percent-unescape a URI.

  ## Examples

      iex> URI.decode("http%3A%2F%2Felixir-lang.com")
      "http://elixir-lang.com"

  """
  def decode(<<?%, hex1, hex2, tail :: binary >>) do
    << bsl(hex2dec(hex1), 4) + hex2dec(hex2) >> <> decode(tail)
  end

  def decode(<<head, tail :: binary >>) do
    <<check_plus(head)>> <> decode(tail)
  end

  def decode(<<>>), do: <<>>

  defp hex2dec(n) when n in ?A..?F, do: n - ?A + 10
  defp hex2dec(n) when n in ?a..?f, do: n - ?a + 10
  defp hex2dec(n) when n in ?0..?9, do: n - ?0

  defp check_plus(?+), do: 32
  defp check_plus(c),  do: c

  @doc """
  Parses a URI into components.

  URIs have portions that are handled specially for the
  particular scheme of the URI. For example, http and https
  have different default ports. Sometimes the parsing
  of portions themselves are different. This parser
  is extensible via behavior modules. If you have a
  module named `URI.MYSCHEME` with a function called
  `parse` that takes a single argument, the generically
  parsed URI, that function will be called when this
  parse function is passed a URI of that scheme. This
  allows you to build on top of what the URI library
  currently offers. You also need to define `default_port`
  which takes no arguments and returns the default port
  for that particular scheme. Take a look at `URI.HTTPS` for an
  example of one of these extension modules.

  ## Examples

      iex> URI.parse("http://elixir-lang.org/")
      URI.Info[scheme: "http", path: "/", query: nil, fragment: nil, authority: "elixir-lang.org", userinfo: nil, host: "elixir-lang.org", port: 80]

  """
  def parse(s) when is_binary(s) do
    # From http://tools.ietf.org/html/rfc3986#appendix-B
    regex = %r/^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/
    parts = nillify(Regex.run(regex, s))

    destructure [_, _, scheme, _, authority, path, _, query, _, fragment], parts
    { userinfo, host, port } = split_authority(authority)

    if authority do
      authority = ""

      if userinfo, do: authority = authority <> userinfo <> "@"
      if host, do: authority = authority <> host
      if port, do: authority = authority <> ":" <> integer_to_binary(port)
    end

    scheme = normalize_scheme(scheme)

    if nil?(port) and not nil?(scheme) do
      port = default_port(scheme)
    end

    URI.Info[
      scheme: scheme, path: path, query: query,
      fragment: fragment, authority: authority,
      userinfo: userinfo, host: host, port: port
    ]
  end

  # Split an authority into its userinfo, host and port parts.
  defp split_authority(s) do
    s = s || ""
    components = Regex.run %r/(^(.*)@)?(\[[a-zA-Z0-9:.]*\]|[^:]*)(:(\d*))?/, s

    destructure [_, _, userinfo, host, _, port], nillify(components)
    port = if port, do: binary_to_integer(port)
    host = if host, do: host |> String.lstrip(?[) |> String.rstrip(?])

    { userinfo, host, port }
  end

  # Regex.run returns empty strings sometimes. We want
  # to replace those with nil for consistency.
  defp nillify(l) do
    lc s inlist l do
      if size(s) > 0, do: s, else: nil
    end
  end
end

defimpl String.Chars, for: URI.Info do
  def to_string(URI.Info[] = uri) do
    scheme = uri.scheme

    if scheme && (port = URI.default_port(scheme)) do
      if uri.port == port, do: uri = uri.port(nil)
    end

    result = ""

    if uri.scheme,   do: result = result <> uri.scheme <> "://"
    if uri.userinfo, do: result = result <> uri.userinfo <> "@"
    if uri.host,     do: result = result <> uri.host
    if uri.port,     do: result = result <> ":" <> integer_to_binary(uri.port)
    if uri.path,     do: result = result <> uri.path
    if uri.query,    do: result = result <> "?" <> uri.query
    if uri.fragment, do: result = result <> "#" <> uri.fragment

    result
  end
end

defmodule URI do
  @moduledoc """
  Utilities for working with and creating URIs.
  """

  defrecord Info, [scheme: nil, path: nil, query: nil,
                   fragment: nil, authority: nil,
                   userinfo: nil, host: nil, port: nil]

  import Bitwise

  @ports [
    { "ftp", 21 },
    { "http", 80 },
    { "https", 443 },
    { "ldap", 389 },
    { "sftp", 22 },
    { "tftp", 69 },
  ]

  Enum.each @ports, fn { scheme, port } ->
    def normalize_scheme(unquote(scheme)), do: unquote(scheme)
    def default_port(unquote(scheme)),     do: unquote(port)
  end

  @doc """
  Normalizes the scheme according to the spec by downcasing it.
  """
  def normalize_scheme(nil),     do: nil
  def normalize_scheme(scheme),  do: String.downcase(scheme)

  @doc """
  Returns the default port for a given scheme.
  If the scheme is unknown to URI, returns `nil`.
  Any scheme may be registered via `default_port/2`.
  """
  def default_port(scheme) when is_binary(scheme) do
    { :ok, dict } = :application.get_env(:elixir, :uri)
    Dict.get(dict, scheme)
  end

  @doc """
  Registers a scheme with a default port.
  """
  def default_port(scheme, port) when is_binary(scheme) and port > 0 do
    { :ok, dict } = :application.get_env(:elixir, :uri)
    :application.set_env(:elixir, :uri, Dict.put(dict, scheme, port))
  end

  @doc """
  Takes an enumerable (containing a sequence of two-item tuples)
  and returns a string of the form "k=v&k2=v2..." where keys and values are
  URL encoded as per `encode/1`. Keys and values can be any term
  that implements the `String.Chars` protocol (i.e. can be converted
  to a binary).
  """
  def encode_query(l), do: Enum.map_join(l, "&", &pair/1)

  @doc """
  Given a query string of the form "key1=value1&key=value2...", produces an
  orddict with one entry for each key-value pair. Each key and value will be a
  binary. It also does percent-unescaping of both keys and values.

  Use `query_decoder/1` if you want to iterate over each value manually.
  """
  def decode_query(q, dict // HashDict.new) when is_binary(q) do
    Enum.reduce query_decoder(q), dict, fn({ k, v }, acc) -> Dict.put(acc, k, v) end
  end

  @doc """
  Returns an iterator function over the query string that decodes
  the query string in steps.
  """
  def query_decoder(q) when is_binary(q) do
    fn(acc, fun) ->
      do_decoder(q, acc, fun)
    end
  end

  defp do_decoder("", acc, _fun) do
    acc
  end

  defp do_decoder(q, acc, fun) do
    next =
      case :binary.split(q, "&") do
        [first, rest] -> rest
        [first]       -> ""
      end

    current =
      case :binary.split(first, "=") do
        [ key, value ] -> { decode(key), decode(value) }
        [ key ]        -> { decode(key), nil }
      end

    do_decoder(next, fun.(current, acc), fun)
  end

  defp pair({k, v}) do
    encode(to_string(k)) <> "=" <> encode(to_string(v))
  end

  @doc """
  Percent-escape a URI.
  """
  def encode(s), do: bc(<<c>> inbits s, do: <<percent(c) :: binary>>)

  defp percent(32), do: <<?+>>
  defp percent(?-), do: <<?->>
  defp percent(?_), do: <<?_>>
  defp percent(?.), do: <<?.>>

  defp percent(c)
      when c >= ?0 and c <= ?9
      when c >= ?a and c <= ?z
      when c >= ?A and c <= ?Z do
    <<c>>
  end

  defp percent(c), do: "%" <> hex(bsr(c, 4)) <> hex(band(c, 15))

  defp hex(n) when n <= 9, do: <<n + ?0>>
  defp hex(n), do: <<n + ?A - 10>>

  @doc """
  Percent-unescape a URI.
  """
  def decode(<<?%, hex1, hex2, tail :: binary >>) do
    << bsl(hex2dec(hex1), 4) + hex2dec(hex2) >> <> decode(tail)
  end

  def decode(<<head, tail :: binary >>) do
    <<check_plus(head)>> <> decode(tail)
  end

  def decode(<<>>), do: <<>>

  defp hex2dec(n) when n in ?A..?F, do: n - ?A + 10
  defp hex2dec(n) when n in ?a..?f, do: n - ?a + 10
  defp hex2dec(n) when n in ?0..?9, do: n - ?0

  defp check_plus(?+), do: 32
  defp check_plus(c),  do: c

  @doc """
  Parses a URI into components.

  URIs have portions that are handled specially for the
  particular scheme of the URI. For example, http and https
  have different default ports. Sometimes the parsing
  of portions themselves are different. This parser
  is extensible via behavior modules. If you have a
  module named `URI.MYSCHEME` with a function called
  `parse` that takes a single argument, the generically
  parsed URI, that function will be called when this
  parse function is passed a URI of that scheme. This
  allows you to build on top of what the URI library
  currently offers. You also need to define `default_port`
  which takes no arguments and returns the default port
  for that particular scheme. Take a look at `URI.HTTPS` for an
  example of one of these extension modules.
  """
  def parse(s) when is_binary(s) do
    # From http://tools.ietf.org/html/rfc3986#appendix-B
    regex = %r/^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/
    parts = nillify(Regex.run(regex, s))

    destructure [_, _, scheme, _, authority, path, _, query, _, fragment], parts
    { userinfo, host, port } = split_authority(authority)

    if authority do
      authority = ""

      if userinfo, do: authority = authority <> userinfo <> "@"
      if host, do: authority = authority <> host
      if port, do: authority = authority <> ":" <> integer_to_binary(port)
    end

    scheme = normalize_scheme(scheme)

    if nil?(port) and not nil?(scheme) do
      port = default_port(scheme)
    end

    URI.Info[
      scheme: scheme, path: path, query: query,
      fragment: fragment, authority: authority,
      userinfo: userinfo, host: host, port: port
    ]
  end

  # Split an authority into its userinfo, host and port parts.
  defp split_authority(s) do
    s = s || ""
    components = Regex.run %r/(^(.*)@)?(\[[a-zA-Z0-9:.]*\]|[^:]*)(:(\d*))?/, s

    destructure [_, _, userinfo, host, _, port], nillify(components)
    port = if port, do: binary_to_integer(port)
    host = if host, do: host |> String.lstrip(?[) |> String.rstrip(?])

    { userinfo, host, port }
  end

  # Regex.run returns empty strings sometimes. We want
  # to replace those with nil for consistency.
  defp nillify(l) do
    lc s inlist l do
      if size(s) > 0, do: s, else: nil
    end
  end
end

defimpl String.Chars, for: URI.Info do
  def to_string(URI.Info[] = uri) do
    scheme = uri.scheme

    if scheme && (port = URI.default_port(scheme)) do
      if uri.port == port, do: uri = uri.port(nil)
    end

    result = ""

    if uri.scheme,   do: result = result <> uri.scheme <> "://"
    if uri.userinfo, do: result = result <> uri.userinfo <> "@"
    if uri.host,     do: result = result <> uri.host
    if uri.port,     do: result = result <> ":" <> integer_to_binary(uri.port)
    if uri.path,     do: result = result <> uri.path
    if uri.query,    do: result = result <> "?" <> uri.query
    if uri.fragment, do: result = result <> "#" <> uri.fragment

    result
  end
end


Comment out some random stuff

Comment out some random stuff

defmodule FdlMessengerBot.PageController do
  use FdlMessengerBot.Web, :controller

  def index(conn, _params) do
    render conn, "index.html"
  end

  def webhook(conn, %{
    "hub.verify_token" => verify_token,
    "hub.challenge" => challenge
  }) do
    text conn, challenge
  end

  def webhook(conn, %{"entry" => entry}) do
    page_access_token = System.get_env("FACEBOOK_PAGE_ACCESS_TOKEN")
    IO.inspect(entry)
    messaging = hd(entry)["messaging"]
    message = hd(messaging)["message"]["text"]
    # user_id = messaging["sender"]["id"]
    IO.inspect(page_access_token)
    IO.inspect(message)
    # MessengerAPI.start
    # FdlMessengerBot.MessengerAPI.start
    # FdlMessengerBot.MessengerAPI.postMessage(%{:user_id => user_id, :message => message})
    text conn, :ok
  end
end

defmodule FdlMessengerBot.PageController do
  use FdlMessengerBot.Web, :controller

  def index(conn, _params) do
    render conn, "index.html"
  end

  def webhook(conn, %{
    "hub.verify_token" => verify_token,
    "hub.challenge" => challenge
  }) do
    text conn, challenge
  end

  def webhook(conn, %{"entry" => entry}) do
    page_access_token = System.get_env("FACEBOOK_PAGE_ACCESS_TOKEN")
    IO.inspect(entry)
    messaging = hd(entry)["messaging"]
    message = hd(messaging)["message"]["text"]
    user_id = messaging["sender"]["id"]
    IO.inspect(page_access_token)
    IO.inspect(message)
    MessengerAPI.start
    FdlMessengerBot.MessengerAPI.start
    FdlMessengerBot.MessengerAPI.postMessage(%{:user_id => user_id, :message => message})
    text conn, :ok
  end
end