Sort reviewers & members lists

Sort reviewers & members lists

This simply sorts the lists of reviewers and members on the
project settings page.  The logged-in user is always first,
and otherwise users are sorted by their login.

defmodule BorsNG.ProjectController do
  @moduledoc """
  Shows a list of repositories, a single repository,
  and the repository's settings page.

  n.b.
  We call it a project internally, though it corresponds
  to a GitHub repository. This is to avoid confusing
  a GitHub repo with an Ecto repo.
  """

  use BorsNG.Web, :controller

  alias BorsNG.Worker.Batcher
  alias BorsNG.Database.Context.Permission
  alias BorsNG.Database.Repo
  alias BorsNG.Database.LinkMemberProject
  alias BorsNG.Database.LinkUserProject
  alias BorsNG.Database.Project
  alias BorsNG.Database.Batch
  alias BorsNG.Database.Crash
  alias BorsNG.Database.Patch
  alias BorsNG.Database.User
  alias BorsNG.GitHub
  alias BorsNG.Worker.Syncer

  # Auto-grab the project and check the permissions

  def action(conn, _) do
    do_action(conn, action_name(conn), conn.params)
  end

  defp do_action(conn, action, %{"id" => id} = params) do
    allow_private_repos = Confex.fetch_env!(
      :bors, BorsNG)[:allow_private_repos]
    project = Project
    |> from(preload: [:installation])
    |> Repo.get!(id)
    mode = cond do
      User.has_perm(Repo, conn.assigns.user, project.id) -> :rw
      !allow_private_repos -> :ro
      true -> raise BorsNG.PermissionDeniedError
    end
    apply(__MODULE__, action, [conn, mode, project, params])
  end
  defp do_action(conn, action, params) do
    apply(__MODULE__, action, [conn, params])
  end

  # The actual handlers
  # Two-item ones have a project ID inputed
  # One-item ones don't

  def index(conn, _params) do
    projects = Repo.all(Project.by_owner(conn.assigns.user.id))
    render conn, "index.html", projects: projects
  end

  defp batch_info(batch) do
    %{
      commit: batch.commit,
      patches: Repo.all(Patch.all_for_batch(batch.id)),
      state: batch.state}
  end

  def show(conn, mode, project, _params) do
    batches = project.id
    |> Batch.all_for_project(:incomplete)
    |> Repo.all()
    |> Enum.map(&batch_info/1)
    unbatched_patches = project.id
    |> Patch.all_for_project(:awaiting_review)
    |> Repo.all()
    is_synchronizing = match?(
      [{_, _}],
      Registry.lookup(Syncer.Registry, project.id))
    render conn, "show.html",
      project: project,
      batches: batches,
      is_synchronizing: is_synchronizing,
      unbatched_patches: unbatched_patches,
      mode: mode
  end

  def settings(_, :ro, _, _), do: raise BorsNG.PermissionDeniedError
  def settings(conn, :rw, project, _params) do
    reviewers = Permission.list_users_for_project(:reviewer, project.id)
    |> Enum.sort_by(fn %User{login: login} ->
      case conn.assigns[:user].login do
        ^login -> ""
        _ -> login
      end
    end)
    members = Permission.list_users_for_project(:member, project.id)
    |> Enum.sort_by(fn %User{login: login} ->
      case conn.assigns[:user].login do
        ^login -> ""
        _ -> login
      end
    end)
    render conn, "settings.html",
      project: project,
      reviewers: reviewers,
      members: members,
      current_user_id: conn.assigns.user.id,
      update_branches: Project.changeset_branches(project)
  end

  def log(_, :ro, _, _), do: raise BorsNG.PermissionDeniedError
  def log(conn, :rw, project, _params) do
    batches = project.id
    |> Batch.all_for_project()
    |> Repo.all()
    |> Enum.map(fn
      %Batch{id: id} = batch ->
        %{batch | patches: Repo.all(Patch.all_for_batch(id))}
    end)
    crashes = Repo.all(Crash.all_for_project(project.id))
    entries = crashes ++ batches
    |> Enum.sort_by(fn %{updated_at: at} -> Date.to_erl(at) end)
    |> Enum.reverse()
    render conn, "log.html",
      project: project,
      current_user_id: conn.assigns.user.id,
      entries: entries
  end

  def cancel_all(_, :ro, _, _), do: raise BorsNG.PermissionDeniedError
  def cancel_all(conn, :rw, project, _params) do
    project.id
    |> Batcher.Registry.get()
    |> Batcher.cancel_all()
    conn
    |> put_flash(:ok, "Canceled all running batches")
    |> redirect(to: project_path(conn, :show, project))
  end

  def update_branches(_, :ro, _, _), do: raise BorsNG.PermissionDeniedError
  def update_branches(conn, :rw, project, %{"project" => pdef}) do
    result = project
    |> Project.changeset_branches(pdef)
    |> Repo.update()
    case result do
      {:ok, _} ->
        conn
        |> put_flash(:ok, "Successfully updated branches")
        |> redirect(to: project_path(conn, :settings, project))
      {:error, changeset} ->
        reviewers = Permission.list_users_for_project(:reviewer, project.id)
        members = Permission.list_users_for_project(:member, project.id)
        conn
        |> put_flash(:error, "Cannot update branches")
        |> render("settings.html",
          project: project,
          reviewers: reviewers,
          members: members,
          current_user_id: conn.assigns.user.id,
          update_branches: changeset)
    end
  end

  def add_reviewer(_, :ro, _, _), do: raise BorsNG.PermissionDeniedError
  def add_reviewer(conn, :rw, project, %{"reviewer" => %{"login" => ""}}) do
    conn
    |> put_flash(:error, "Please enter a GitHub user's nickname")
    |> redirect(to: project_path(conn, :settings, project))
  end
  def add_reviewer(conn, :rw, project, %{"reviewer" => %{"login" => login}}) do
    user = case Repo.get_by(User, login: login) do
      nil ->
        {:installation, project.installation.installation_xref}
        |> GitHub.get_user_by_login!(login)
        |> case do
          nil -> nil
          gh_user ->
            case Repo.get_by(User, user_xref: gh_user.id) do
              nil ->
                User.changeset(%User{}, %{
                  user_xref: gh_user.id,
                  login: gh_user.login
                })
                |> Repo.insert!()
              user -> user
            end
        end
      user -> user
    end
    {state, msg} = case user do
      nil ->
        {:error, "GitHub user not found; maybe you typo-ed?"}
      user ->
        %LinkUserProject{}
        |> LinkUserProject.changeset(%{
          user_id: user.id,
          project_id: project.id})
        |> Repo.insert()
        |> case do
          {:error, _} ->
            {:error, "This user is already a reviewer"}
          {:ok, _login} ->
            {:ok, "Successfully added #{user.login} as a reviewer"}
        end
    end
    conn
    |> put_flash(state, msg)
    |> redirect(to: project_path(conn, :settings, project))
  end

  def add_member(_, :ro, _, _), do: raise BorsNG.PermissionDeniedError
  def add_member(conn, :rw, project, %{"member" => %{"login" => ""}}) do
    conn
    |> put_flash(:error, "Please enter a GitHub user's nickname")
    |> redirect(to: project_path(conn, :settings, project))
  end
  def add_member(conn, :rw, project, %{"member" => %{"login" => login}}) do
    user = case Repo.get_by(User, login: login) do
      nil ->
        {:installation, project.installation.installation_xref}
        |> GitHub.get_user_by_login!(login)
        |> case do
          nil -> nil
          gh_user ->
            case Repo.get_by(User, user_xref: gh_user.id) do
              nil ->
                User.changeset(%User{}, %{
                  user_xref: gh_user.id,
                  login: gh_user.login
                })
                |> Repo.insert!()
              user -> user
            end
        end
      user -> user
    end
    {state, msg} = case user do
      nil ->
        {:error, "GitHub user not found; maybe you typo-ed?"}
      user ->
        %LinkMemberProject{}
        |> LinkMemberProject.changeset(%{
          user_id: user.id,
          project_id: project.id})
        |> Repo.insert()
        |> case do
          {:error, _} ->
            {:error, "This user is already a member"}
          {:ok, _login} ->
            {:ok, "Successfully added #{user.login} as a member"}
        end
    end
    conn
    |> put_flash(state, msg)
    |> redirect(to: project_path(conn, :settings, project))
  end

  def confirm_add_reviewer(_, :ro, _, _) do
    raise BorsNG.PermissionDeniedError
  end
  def confirm_add_reviewer(conn, :rw, project, %{"login" => login}) do
    render conn, "confirm-add-reviewer.html",
      project: project,
      current_user_id: conn.assigns.user.id,
      login: login
  end

  def remove_reviewer(_, :ro, _, _), do: raise BorsNG.PermissionDeniedError
  def remove_reviewer(conn, :rw, project, %{"user_id" => user_id}) do
    link = Repo.get_by!(
      LinkUserProject,
      project_id: project.id,
      user_id: user_id)
    Repo.delete!(link)
    conn
    |> put_flash(:ok, "Removed reviewer")
    |> redirect(to: project_path(conn, :settings, project))
  end

  def remove_member(_, :ro, _, _), do: raise BorsNG.PermissionDeniedError
  def remove_member(conn, :rw, project, %{"user_id" => user_id}) do
    link = Repo.get_by!(
      LinkMemberProject,
      project_id: project.id,
      user_id: user_id)
    Repo.delete!(link)
    conn
    |> put_flash(:ok, "Removed member")
    |> redirect(to: project_path(conn, :settings, project))
  end

  def synchronize(_, :ro, _, _), do: raise BorsNG.PermissionDeniedError
  def synchronize(conn, :rw, project, _params) do
    Syncer.start_synchronize_project(project.id)
    conn
    |> put_flash(:ok, "Started synchronizing")
    |> redirect(to: project_path(conn, :show, project))
  end
end

defmodule BorsNG.ProjectController do
  @moduledoc """
  Shows a list of repositories, a single repository,
  and the repository's settings page.

  n.b.
  We call it a project internally, though it corresponds
  to a GitHub repository. This is to avoid confusing
  a GitHub repo with an Ecto repo.
  """

  use BorsNG.Web, :controller

  alias BorsNG.Worker.Batcher
  alias BorsNG.Database.Context.Permission
  alias BorsNG.Database.Repo
  alias BorsNG.Database.LinkMemberProject
  alias BorsNG.Database.LinkUserProject
  alias BorsNG.Database.Project
  alias BorsNG.Database.Batch
  alias BorsNG.Database.Crash
  alias BorsNG.Database.Patch
  alias BorsNG.Database.User
  alias BorsNG.GitHub
  alias BorsNG.Worker.Syncer

  # Auto-grab the project and check the permissions

  def action(conn, _) do
    do_action(conn, action_name(conn), conn.params)
  end

  defp do_action(conn, action, %{"id" => id} = params) do
    allow_private_repos = Confex.fetch_env!(
      :bors, BorsNG)[:allow_private_repos]
    project = Project
    |> from(preload: [:installation])
    |> Repo.get!(id)
    mode = cond do
      User.has_perm(Repo, conn.assigns.user, project.id) -> :rw
      !allow_private_repos -> :ro
      true -> raise BorsNG.PermissionDeniedError
    end
    apply(__MODULE__, action, [conn, mode, project, params])
  end
  defp do_action(conn, action, params) do
    apply(__MODULE__, action, [conn, params])
  end

  # The actual handlers
  # Two-item ones have a project ID inputed
  # One-item ones don't

  def index(conn, _params) do
    projects = Repo.all(Project.by_owner(conn.assigns.user.id))
    render conn, "index.html", projects: projects
  end

  defp batch_info(batch) do
    %{
      commit: batch.commit,
      patches: Repo.all(Patch.all_for_batch(batch.id)),
      state: batch.state}
  end

  def show(conn, mode, project, _params) do
    batches = project.id
    |> Batch.all_for_project(:incomplete)
    |> Repo.all()
    |> Enum.map(&batch_info/1)
    unbatched_patches = project.id
    |> Patch.all_for_project(:awaiting_review)
    |> Repo.all()
    is_synchronizing = match?(
      [{_, _}],
      Registry.lookup(Syncer.Registry, project.id))
    render conn, "show.html",
      project: project,
      batches: batches,
      is_synchronizing: is_synchronizing,
      unbatched_patches: unbatched_patches,
      mode: mode
  end

  def settings(_, :ro, _, _), do: raise BorsNG.PermissionDeniedError
  def settings(conn, :rw, project, _params) do
    reviewers = Permission.list_users_for_project(:reviewer, project.id)
    members = Permission.list_users_for_project(:member, project.id)
    render conn, "settings.html",
      project: project,
      reviewers: reviewers,
      members: members,
      current_user_id: conn.assigns.user.id,
      update_branches: Project.changeset_branches(project)
  end

  def log(_, :ro, _, _), do: raise BorsNG.PermissionDeniedError
  def log(conn, :rw, project, _params) do
    batches = project.id
    |> Batch.all_for_project()
    |> Repo.all()
    |> Enum.map(fn
      %Batch{id: id} = batch ->
        %{batch | patches: Repo.all(Patch.all_for_batch(id))}
    end)
    crashes = Repo.all(Crash.all_for_project(project.id))
    entries = crashes ++ batches
    |> Enum.sort_by(fn %{updated_at: at} -> Date.to_erl(at) end)
    |> Enum.reverse()
    render conn, "log.html",
      project: project,
      current_user_id: conn.assigns.user.id,
      entries: entries
  end

  def cancel_all(_, :ro, _, _), do: raise BorsNG.PermissionDeniedError
  def cancel_all(conn, :rw, project, _params) do
    project.id
    |> Batcher.Registry.get()
    |> Batcher.cancel_all()
    conn
    |> put_flash(:ok, "Canceled all running batches")
    |> redirect(to: project_path(conn, :show, project))
  end

  def update_branches(_, :ro, _, _), do: raise BorsNG.PermissionDeniedError
  def update_branches(conn, :rw, project, %{"project" => pdef}) do
    result = project
    |> Project.changeset_branches(pdef)
    |> Repo.update()
    case result do
      {:ok, _} ->
        conn
        |> put_flash(:ok, "Successfully updated branches")
        |> redirect(to: project_path(conn, :settings, project))
      {:error, changeset} ->
        reviewers = Permission.list_users_for_project(:reviewer, project.id)
        members = Permission.list_users_for_project(:member, project.id)
        conn
        |> put_flash(:error, "Cannot update branches")
        |> render("settings.html",
          project: project,
          reviewers: reviewers,
          members: members,
          current_user_id: conn.assigns.user.id,
          update_branches: changeset)
    end
  end

  def add_reviewer(_, :ro, _, _), do: raise BorsNG.PermissionDeniedError
  def add_reviewer(conn, :rw, project, %{"reviewer" => %{"login" => ""}}) do
    conn
    |> put_flash(:error, "Please enter a GitHub user's nickname")
    |> redirect(to: project_path(conn, :settings, project))
  end
  def add_reviewer(conn, :rw, project, %{"reviewer" => %{"login" => login}}) do
    user = case Repo.get_by(User, login: login) do
      nil ->
        {:installation, project.installation.installation_xref}
        |> GitHub.get_user_by_login!(login)
        |> case do
          nil -> nil
          gh_user ->
            case Repo.get_by(User, user_xref: gh_user.id) do
              nil ->
                User.changeset(%User{}, %{
                  user_xref: gh_user.id,
                  login: gh_user.login
                })
                |> Repo.insert!()
              user -> user
            end
        end
      user -> user
    end
    {state, msg} = case user do
      nil ->
        {:error, "GitHub user not found; maybe you typo-ed?"}
      user ->
        %LinkUserProject{}
        |> LinkUserProject.changeset(%{
          user_id: user.id,
          project_id: project.id})
        |> Repo.insert()
        |> case do
          {:error, _} ->
            {:error, "This user is already a reviewer"}
          {:ok, _login} ->
            {:ok, "Successfully added #{user.login} as a reviewer"}
        end
    end
    conn
    |> put_flash(state, msg)
    |> redirect(to: project_path(conn, :settings, project))
  end

  def add_member(_, :ro, _, _), do: raise BorsNG.PermissionDeniedError
  def add_member(conn, :rw, project, %{"member" => %{"login" => ""}}) do
    conn
    |> put_flash(:error, "Please enter a GitHub user's nickname")
    |> redirect(to: project_path(conn, :settings, project))
  end
  def add_member(conn, :rw, project, %{"member" => %{"login" => login}}) do
    user = case Repo.get_by(User, login: login) do
      nil ->
        {:installation, project.installation.installation_xref}
        |> GitHub.get_user_by_login!(login)
        |> case do
          nil -> nil
          gh_user ->
            case Repo.get_by(User, user_xref: gh_user.id) do
              nil ->
                User.changeset(%User{}, %{
                  user_xref: gh_user.id,
                  login: gh_user.login
                })
                |> Repo.insert!()
              user -> user
            end
        end
      user -> user
    end
    {state, msg} = case user do
      nil ->
        {:error, "GitHub user not found; maybe you typo-ed?"}
      user ->
        %LinkMemberProject{}
        |> LinkMemberProject.changeset(%{
          user_id: user.id,
          project_id: project.id})
        |> Repo.insert()
        |> case do
          {:error, _} ->
            {:error, "This user is already a member"}
          {:ok, _login} ->
            {:ok, "Successfully added #{user.login} as a member"}
        end
    end
    conn
    |> put_flash(state, msg)
    |> redirect(to: project_path(conn, :settings, project))
  end

  def confirm_add_reviewer(_, :ro, _, _) do
    raise BorsNG.PermissionDeniedError
  end
  def confirm_add_reviewer(conn, :rw, project, %{"login" => login}) do
    render conn, "confirm-add-reviewer.html",
      project: project,
      current_user_id: conn.assigns.user.id,
      login: login
  end

  def remove_reviewer(_, :ro, _, _), do: raise BorsNG.PermissionDeniedError
  def remove_reviewer(conn, :rw, project, %{"user_id" => user_id}) do
    link = Repo.get_by!(
      LinkUserProject,
      project_id: project.id,
      user_id: user_id)
    Repo.delete!(link)
    conn
    |> put_flash(:ok, "Removed reviewer")
    |> redirect(to: project_path(conn, :settings, project))
  end

  def remove_member(_, :ro, _, _), do: raise BorsNG.PermissionDeniedError
  def remove_member(conn, :rw, project, %{"user_id" => user_id}) do
    link = Repo.get_by!(
      LinkMemberProject,
      project_id: project.id,
      user_id: user_id)
    Repo.delete!(link)
    conn
    |> put_flash(:ok, "Removed member")
    |> redirect(to: project_path(conn, :settings, project))
  end

  def synchronize(_, :ro, _, _), do: raise BorsNG.PermissionDeniedError
  def synchronize(conn, :rw, project, _params) do
    Syncer.start_synchronize_project(project.id)
    conn
    |> put_flash(:ok, "Started synchronizing")
    |> redirect(to: project_path(conn, :show, project))
  end
end


DRY with message handler

DRY with message handler

defmodule Bot.MessageHandler do
  require Logger
  @fb_page_access_token System.get_env("FB_PAGE_ACCESS_TOKEN")

  def handle(msg = %{"message" => %{"text" => _text}}) do
    buttons = [
      %{type: "web_url", title: "hectoripm.com", url: "http://hectoripm.com"},
      %{type: "postback", title: "Héctor Iván Patricio Moreno", payload: "PB_NAME"}
    ]
    IO.inspect msg
    send_button_message msg["sender"]["id"], "Escoge una de las siguientes opciones", buttons
  end

  def handle(msg = %{"postback" => %{"payload"=> "PB_NAME"}}) do
    send_text_message msg["sender"]["id"], "Me llamo Héctor Patricio"
  end

  def handle(msg) do
    Logger.info "I don't know how to handle this message: \n#{inspect msg}"
  end

  def send_button_message(recipient, message, buttons) do
    payload = %{
      recipient: %{id: recipient},
      message: %{
        attachment: %{
          type: "template",
          payload: %{
            template_type: "button",
            text: message,
            buttons: buttons
          }
        }
      }
    }
    send_message payload
  end

  def send_text_message(recipient, message) do
    payload = %{
      recipient: %{id: recipient},
      message: %{
        text: message
      }
    }
    send_message payload
  end

  def send_message(message) do
    IO.puts "Sending message"
    url = "https://graph.facebook.com/v2.6/me/messages?access_token=#{@fb_page_access_token}"
    headers = [{"Content-type", "application/json"}]
    HTTPoison.post! url, Poison.encode!(message), headers
  end
end

defmodule Bot.MessageHandler do
  require Logger
  @fb_page_access_token System.get_env("FB_PAGE_ACCESS_TOKEN")

  def handle(msg = %{"message" => %{"text" => _text}}) do
    buttons = [
      %{type: "web_url", title: "hectoripm.com", url: "http://hectoripm.com"},
      %{type: "postback", title: "Héctor Iván Patricio Moreno", payload: "PB_NAME"}
    ]
    IO.inspect msg
    send_button_message msg["sender"]["id"], "Escoge una de las siguientes opciones", buttons
  end

  def handle(msg = %{"postback" => %{"payload"=> "PB_NAME"}}) do
    send_text_message msg["sender"]["id"], "Me llamo Héctor Patricio"
  end

  def handle(msg) do
    Logger.info "I don't know how to handle this message: \n#{inspect msg}"
  end

  def send_button_message(recipient, message, buttons) do
    payload = %{
      recipient: %{id: recipient},
      message: %{
        attachment: %{
          type: "template",
          payload: %{
            template_type: "button",
            text: message,
            buttons: buttons
          }
        }
      }
    }
    url = "https://graph.facebook.com/v2.6/me/messages?access_token=#{@fb_page_access_token}"
    headers = [{"Content-type", "application/json"}]
    HTTPoison.post! url, Poison.encode!(payload), headers
  end

  def send_text_message(recipient, message) do
    payload = %{
      recipient: %{id: recipient},
      message: %{
        text: message
      }
    }
    url = "https://graph.facebook.com/v2.6/me/messages?access_token=#{@fb_page_access_token}"
    headers = [{"Content-type", "application/json"}]
    HTTPoison.post! url, Poison.encode!(payload), headers
  end

  def send_message() do
    IO.puts "sending"
  end
end


Generate conn for all ConnCase tests

Generate conn for all ConnCase tests

defmodule <%= application_module %>.ConnCase do
  @moduledoc """
  This module defines the test case to be used by
  tests that require setting up a connection.

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

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

  use ExUnit.CaseTemplate

  using do
    quote do
      # Import conveniences for testing with connections
      use Phoenix.ConnTest
<%= if ecto do %>
      alias <%= application_module %>.Repo
      import Ecto.Model
      import Ecto.Query, only: [from: 2]
<% end %>
      import <%= application_module %>.Router.Helpers

      # The default endpoint for testing
      @endpoint <%= application_module %>.Endpoint
    end
  end

  setup tags do
<%= if ecto do %>    unless tags[:async] do
      <%= adapter_config[:test_restart] %>
    end
<% end %>
    {:ok, conn: Phoenix.ConnTest.conn()}
  end
end

defmodule <%= application_module %>.ConnCase do
  @moduledoc """
  This module defines the test case to be used by
  tests that require setting up a connection.

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

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

  use ExUnit.CaseTemplate

  using do
    quote do
      # Import conveniences for testing with connections
      use Phoenix.ConnTest
<%= if ecto do %>
      alias <%= application_module %>.Repo
      import Ecto.Model
      import Ecto.Query, only: [from: 2]
<% end %>
      import <%= application_module %>.Router.Helpers

      # The default endpoint for testing
      @endpoint <%= application_module %>.Endpoint
    end
  end

  setup tags do
<%= if ecto do %>    unless tags[:async] do
      <%= adapter_config[:test_restart] %>
    end
<% end %>
    :ok
  end
end


Update .formatter.exs for defcallback/1 macro

[
  inputs: ["mix.exs", "{config,lib,test}/**/*.{ex,exs}"],
  locals_without_parens: [
    defcallback: 1
  ]
]

[
  inputs: ["mix.exs", "{config,lib,test}/**/*.{ex,exs}"]
]


Cosmetics

Cosmetics

defmodule MicrocrawlerTest.Downloader do
    use ExUnit.Case

    @valid_url "https://google.com"
    @invalid_url "https://invalid.url"
    @xkcd_url "http://xkcd.com/"

    test "Get valid URL - #{@valid_url}" do
        Microcrawler.Downloader.get!(@valid_url)
    end

    test "Get invalid URL - #{@invalid_url}" do
        assert_raise HTTPoison.Error, fn ->
            Microcrawler.Downloader.get!(@invalid_url)
        end
    end

    test "Parse Title of #{@xkcd_url}" do
        res = Microcrawler.Downloader.get!(@xkcd_url)
        assert(res.status_code == 200)

        [{"title", [], [title]}] = Floki.find(res.body, "title")
        assert(title)
    end
end

defmodule MicrocrawlerTest.Downloader do
    use ExUnit.Case

    @valid_url "https://google.com"
    @invalid_url "https://invalid.url"
    @xkcd_url "http://xkcd.com/"

    test "Get valid URL - #{@valid_url}" do
        Microcrawler.Downloader.get!(@valid_url)
    end

    test "Get invalid URL - #{@invalid_url}" do
        assert_raise HTTPoison.Error, fn ->
            Microcrawler.Downloader.get!(@invalid_url)
        end
    end

    test "Parse Title" do
        res = Microcrawler.Downloader.get!(@xkcd_url)
        assert(res.status_code == 200)

        [{"title", [], [title]}] = Floki.find(res.body, "title")
        assert(title)
    end
end


index.ex

index.ex

defmodule Eeb.Index do
  @filename "index.html"
  alias Eeb.BlogPath

  @moduledoc """
  处理博客的首页
  """

  def get_index_file_name do
    Path.join(BlogPath.html_path, @filename)
  end

end

defmodule Eeb.Index do

  @moduledoc """
  处理博客的首页
  """
end


jnpr/check_parity.ex - counting correction

git-svn-id: db5a50a3edfb8610943a268dc77bba63d7e1496b@307 aaeee498-0047-0410-bd38-07bdd6096719

#!/usr/bin/expect -f

# $Id$

set send_slow {5 .02}
set timeout 60
set user "lab"
set pass "lab123"
set pattern "User lab logged in"

if { $argc == 0 } {
  puts "
Usage: $argv0 filename

hosts definitions inside the file should be in a format:
<user>@host<:pass>

examples:
127.0.0.1
user1@127.0.0.2
user2@127.0.0.3:pass2

if the user or pass are not specified the default values are used.
"
  exit
}

set time     [ timestamp -format "%Y/%m/%d %H:%M:%S"]
set filetime [ timestamp -format "%Y%m%d_%H%M%S"]

log_file "~/check_parity-$filetime.log"

proc counterrors {pattern logs} {
  set errorcount 0
  foreach line [split $logs "\n"] {
    if { [regexp "/var/log/messages" $line] } {
      continue
    }
    if { [regexp $pattern $line] } {
      incr errorcount 1
    }
  }
  return $errorcount
}

set file [lindex $argv 0]
set fp [open $file r]
set file_data [read $fp]
close $fp

foreach host [split $file_data "\n"] {

  set errorperhost 0

  if { $host != "" } {

    if { [regexp ":" $host] } {
      regexp {:(\S+)} $host match pass
      regexp {(\S+):} $host match host
    }

    if { [regexp "@" $host] } {
      regexp {(\S+?)@} $host match user
      regexp {@(\S+)} $host match host
    }

    spawn telnet $host

    expect timeout {
      send_user "\n$host: CONNECTION ERROR - connection timeout\n"
      continue
    } eof {
      send_user "\n$host: CONNECTION ERROR - got eof\n"
      continue
    } "Login incorrect" {
      send_user "\n$host: CONNECTION ERROR - wrong credentials\n"
      continue
    } "Permission denied, please try again." {
      send_user "\n$host: CONNECTION ERROR - wrong credentials\n"
      continue
    } "Are you sure you want to continue connecting (yes/no)?" {
      send -s "yes\r"
      exp_continue
    } "ogin:" {
      send -s "$user\r"
      exp_continue
    } "assword:" {
      send -s "$pass\r"
      exp_continue
    } "*> " {
      send -s "\r"
    }

    set master [regexp -inline -- {master:\d+} $expect_out(buffer)]
    set master [regexp -inline -- {\d+} $master]

    set errorpermember 0

    expect timeout {
      send_user "\n$host: CONNECTION ERROR - connection timeout\n"
      break
    } eof {
      send_user "\n$host: CONNECTION ERROR - got eof\n"
      break
    } "*> " {
      send -s "start shell\r"
      exp_continue
    } "*% " {
      send -s "cat /var/log/messages | grep '$pattern'\r"
    }
    set errorpermember [ counterrors $pattern $expect_out(buffer)]
    expect "*% " {send -s "zcat /var/log/messages*gz | grep '$pattern'\r"}
    set moreerror [ counterrors $pattern $expect_out(buffer)]
    set errorpermember [expr $errorpermember+$moreerror]
    set errorperhost [expr $errorperhost+$errorpermember]
    send_user "\n#### $host:$master has $errorpermember errors ####\n"
    expect "*% " {send -s "exit\r"}

    set errorpermember 0
    set done 0
    for { set x 0 } { $x<=9 } { incr x } {
      if { $x != $master } {
        expect timeout {
          send_user "\n$host: CONNECTION ERROR - connection timeout\n"
          break
        } eof {
          send_user "\n$host: CONNECTION ERROR - got eof\n"
          break
        } "*> " {
          send -s "request session member $x\r"
        }

        expect timeout {
          send_user "\n$host: CONNECTION ERROR - connection timeout\n"
          break
        } eof {
          send_user "\n$host: CONNECTION ERROR - got eof\n"
          break
        } "assword: " {
          send -s "$pass\r"
          exp_continue
        } "No route to host" {
          continue
        } "*> " {
          send -s "start shell\r"
          exp_continue
        } "*% " {
          send -s "cat /var/log/messages | grep '$pattern'\r"
        }
        set errorpermember [ counterrors $pattern $expect_out(buffer)]
        expect "*% " {send -s "zcat /var/log/messages*gz | grep '$pattern'\r"}
        set moreerror [ counterrors $pattern $expect_out(buffer)]
        set errorpermember [expr $errorpermember+$moreerror]
        set errorperhost [expr $errorperhost+$errorpermember]
        send_user "\n#### $host:$x has $errorpermember errors ####\n"

        expect "*% " {send -s "exit\r"}
      }
    }
    send_user "\n#### $host in total has $errorperhost errors ####\n"
    expect "*> " { send -s "exit\r"}
  }
}

#!/usr/bin/expect -f

# $Id$

set send_slow {5 .02}
set timeout 60
set user "lab"
set pass "lab123"
set pattern "User lab logged in"

if { $argc == 0 } {
  puts "
Usage: $argv0 filename

hosts definitions inside the file should be in a format:
<user>@host<:pass>

examples:
127.0.0.1
user1@127.0.0.2
user2@127.0.0.3:pass2

if the user or pass are not specified the default values are used.
"
  exit
}

set time     [ timestamp -format "%Y/%m/%d %H:%M:%S"]
set filetime [ timestamp -format "%Y%m%d_%H%M%S"]

log_file "~/check_parity-$filetime.log"

proc counterrors {pattern logs} {
  set errorcount 0
  foreach line [split $logs "\n"] {
    if { [regexp $pattern $line] } {
      incr errorcount 1
    }
  }
  return $errorcount
}

set file [lindex $argv 0]
set fp [open $file r]
set file_data [read $fp]
close $fp

foreach host [split $file_data "\n"] {

  set errorperhost 0

  if { $host != "" } {

    if { [regexp ":" $host] } {
      regexp {:(\S+)} $host match pass
      regexp {(\S+):} $host match host
    }

    if { [regexp "@" $host] } {
      regexp {(\S+?)@} $host match user
      regexp {@(\S+)} $host match host
    }

    spawn telnet $host

    expect timeout {
      send_user "$host: CONNECTION ERROR - connection timeout\n"
      continue
    } eof {
      send_user "$host: CONNECTION ERROR - got eof\n"
      continue
    } "Login incorrect" {
      send_user "$host: CONNECTION ERROR - wrong credentials\n"
      continue
    } "Permission denied, please try again." {
      send_user "$host: CONNECTION ERROR - wrong credentials\n"
      continue
    } "Are you sure you want to continue connecting (yes/no)?" {
      send -s "yes\r"
      exp_continue
    } "ogin:" {
      send -s "$user\r"
      exp_continue
    } "assword:" {
      send -s "$pass\r"
      exp_continue
    } "*> " {
      send -s "\r"
    }

    set master [regexp -inline -- {master:\d+} $expect_out(buffer)]
    set master [regexp -inline -- {\d+} $master]

    set errorpermember 0

    expect timeout {
      send_user "$host: CONNECTION ERROR - connection timeout\n"
      break
    } eof {
      send_user "$host: CONNECTION ERROR - got eof\n"
      break
    } "*> " {
      send -s "start shell\r"
      exp_continue
    } "*% " {
      send -s "cat /var/log/messages | grep '$pattern'\r"
    }
    set errorpermember [ counterrors $pattern $expect_out(buffer)]
    expect "*% " {send -s "zcat /var/log/messages*gz | grep '$pattern'\r"}
    set moreerror [ counterrors $pattern $expect_out(buffer)]
    set errorpermember [expr $errorpermember+$moreerror]
    set errorperhost [expr $errorperhost+$errorpermember]
    send_user "\n#### $host:$master has $errorpermember errors ####\n"
    expect "*% " {send -s "exit\r"}

    set errorpermember 0
    set done 0
    for { set x 0 } { $x<=9 } { incr x } {

      if { $x != $master } {
        expect timeout {
          send_user "$host: CONNECTION ERROR - connection timeout\n"
          break
        } eof {
          send_user "$host: CONNECTION ERROR - got eof\n"
          break
        } "*> " {
          send -s "request session member $x\r"
        }

        expect timeout {
          send_user "$host: CONNECTION ERROR - connection timeout\n"
          break
        } eof {
          send_user "$host: CONNECTION ERROR - got eof\n"
          break
        } "assword: " {
          send -s "$pass\r"
          exp_continue
        } "No route to host" {
          break
        } "*> " {
          send -s "start shell\r"
          exp_continue
        } "*% " {
          send -s "cat /var/log/messages | grep '$pattern'\r"
        }
        set errorpermember [ counterrors $pattern $expect_out(buffer)]
        expect "*% " {send -s "zcat /var/log/messages*gz | grep '$pattern'\r"}
        set moreerror [ counterrors $pattern $expect_out(buffer)]
        set errorpermember [expr $errorpermember+$moreerror]
        set errorperhost [expr $errorperhost+$errorpermember]
        send_user "\n#### $host:$x has $errorpermember errors ####\n"

        expect "*% " {send -s "exit\r"}
      }
    }
     send_user "\n#### $host in total has $errorperhost errors ####\n"
    expect "*> " { send -s "exit\r"}
  }
}


Extended stats tests

Extended stats tests

defmodule Benchee.StatistcsTest do
  use ExUnit.Case
  doctest Benchee.Statistics

  test "statistics computes the statistics for all jobs correctly" do
    suite = %{jobs: [{"Job 1", [600, 470, 170, 430, 300]},
                     {"Job 2", [17, 15, 23, 7, 9, 13]}]}

    [{_, stats_1}, {_, stats_2}] = Benchee.Statistics.statistics suite

    assert stats_1.average == 394
    assert_in_delta stats_1.std_dev, 147.32, 0.01
    assert_in_delta stats_1.std_dev_ratio, 0.37, 0.01
    assert_in_delta stats_1.ips, 2538, 1

    assert stats_2.average == 14
    assert_in_delta stats_2.std_dev, 5.25, 0.01
    assert_in_delta stats_2.std_dev_ratio, 0.37, 0.01
    assert_in_delta stats_2.ips, 71428, 1
  end

  test "sort sorts the benchmarks correctly and retains all date" do
    fourth = {"Fourth", %{average: 400.1, ips: 4_999.0,  std_dev_ratio: 0.7}}
    second = {"Second", %{average: 200.0, ips: 5_000.0,  std_dev_ratio: 2.1}}
    third  = {"Third",  %{average: 400.0, ips: 2_500.0,  std_dev_ratio: 1.5}}
    first  = {"First",  %{average: 100.0, ips: 10_000.0, std_dev_ratio: 0.3}}
    jobs = [fourth, second, third, first]

    assert Benchee.Statistics.sort(jobs) == [first, second, third, fourth]
  end
end

defmodule Benchee.StatistcsTest do
  use ExUnit.Case
  doctest Benchee.Statistics

  test "sort sorts the benchmarks correctly and retains all date" do
    fourth = {"Fourth", %{average: 400.1, ips: 4_999.0,  std_dev_ratio: 0.7}}
    second = {"Second", %{average: 200.0, ips: 5_000.0,  std_dev_ratio: 2.1}}
    third  = {"Third",  %{average: 400.0, ips: 2_500.0,  std_dev_ratio: 1.5}}
    first  = {"First",  %{average: 100.0, ips: 10_000.0, std_dev_ratio: 0.3}}
    jobs = [fourth, second, third, first]

    assert Benchee.Statistics.sort(jobs) == [first, second, third, fourth]
  end
end


Fixed login fail logging

Fixed login fail logging

Was failing to log log in name when log failed. Should log only if login
succeeds. If not succeed, log failure.

(was accessing :name of false)

require Logger

defmodule Serverboards.Auth do
	@moduledoc ~S"""
	Manages authentication of users.

	It can use custom auth methods (`add_auth`) in elixir code, or plugins,
	which is any component with the `auth` trait.

	## Example

		iex> auth %{ "type" => "invalid" }
		{:error, :unknown_user}

	Using the fake auth plugin:

		iex> {:ok, ret} = auth %{ "type" => "serverboards.test.auth/auth", "token" => "XXX" }
		iex> ret.email
		"dmoreno@serverboards.io"
		iex> auth %{ "type" => "fake", "token" => "xxx" }
		{:error, :unknown_user}

	"""
	use GenServer

	alias MOM.RPC

	defstruct []

	def email_auth(%{ "email" => email, "password" => password }) do
		case Serverboards.Auth.User.Password.auth(email, password) do
			{:error, _} -> false
			false -> false
			user -> user
				#%{ email: user.email, permissions: user.perms, name: user.name }
		end
	end

	def token_auth(%{ "token" => token }) do
		case Serverboards.Auth.User.Token.auth(token) do
			{:error, _} -> false
			false -> false
			user -> user
				#%{ email: user.email, permissions: user.perms, name: user.name }
		end
	end

	@doc ~S"""
	Check is gien params allow for login.

	Params must contain `type`.

	Returns:
		* false -- no login
		* email -- Uses that email to get all the permissions and data from database

	"""
	def auth(%{ "type" => type } = params) do
		user = case type do
			"basic" ->
				(try_login_default_plugins(params)
					|> ensure_exists_in_db(type) # requires update db
				) || email_auth(params)
			"token" ->
				token_auth(params)
			other ->
				auth = auth_components()
					|> Enum.find( &(&1.id == other))

				if auth do
					try_login_by_auth(auth, params)
						|> ensure_exists_in_db(type) # requires update db
				else
					Logger.warn("Unknown auth method #{inspect type}", type: type)
					false
				end
		end

		if user do
			Logger.info("Log in user #{user[:name]} <#{user[:email]}>", user: user)
			{:ok,
				user
					|> decorate
				}
		else
			Logger.error("Login failed, type #{inspect type}", type: type)
			{:error, :unknown_user}
		end
	end

	# Regets the full user, with perms, id and so on
	defp decorate(user) do
		{:ok, user} = Serverboards.Auth.User.user_info(user.email)
		user
	end

	# all perms from the given groups
	def perms_from_groups(groups) do
		import Ecto.Query
		Serverboards.Repo.all(
			from perms in Serverboards.Auth.Model.Permission,
			join: pg in Serverboards.Auth.Model.GroupPerms,
			  on: pg.perm_id == perms.id,
			join: group in Serverboards.Auth.Model.Group,
			  on: pg.group_id == group.id,
			where: group.name in ^groups,
			select: perms.code,
			distinct: true
			)
	end

	defp ensure_exists_in_db(false, _creator), do: false
	defp ensure_exists_in_db(user, creator) do
		Logger.debug("Ensure in db #{inspect user}")
		# check if exists, or create
		nuser = case Serverboards.Auth.User.user_info(user.email) do
			{:error, :unknown_user} ->
				Logger.info("Automatically creating the user #{inspect user.email}", user: user)
				Serverboards.Auth.User.user_add(user, %{ email: creator, perms: ["auth.create_user"]})
				%{ user | groups: [] }
			{:ok, user} ->
				# If exists, check it is active, nuser here only for test
				if user.is_active do
					user
				else
					nil
				end
		end

		# update groups
		nuser = if nuser do
			newgroups = MapSet.new user.groups
			oldgroups = MapSet.new nuser.groups
			if not MapSet.equal?(newgroups, oldgroups)  do
				group_list = Serverboards.Auth.Group.group_list(nuser)
				for g <- MapSet.difference(newgroups, oldgroups) do
					if g in group_list do
						Logger.debug("Add #{inspect nuser.email} to group #{inspect g}")
						:ok = Serverboards.Auth.Group.user_add( g, nuser.email, %{ email: creator, perms: ["auth.manage_groups"]})
					end
				end
				for g <- MapSet.difference(oldgroups, newgroups) do
					Logger.debug("Remove #{inspect nuser.email} from group #{inspect g}")
					:ok = Serverboards.Auth.Group.user_remove( g, nuser.email, %{ email: creator, perms: ["auth.manage_groups"]})
				end
			end
			%{ nuser | groups: user.groups }
		end

		Logger.debug("Ready user #{inspect nuser}")

		nuser
	end

	def client_set_user(client, user) do
		#Logger.debug("Setting user: #{inspect user}")
		RPC.Client.set client, :user, user
		MOM.Channel.send(:auth_authenticated, %MOM.Message{ payload: %{ client: client, user: user } }, [sync: true])
	end

	defp try_login_default_plugins(params) do
		#Logger.debug("Try login with params #{inspect params}")
		auth_components()
			|> Enum.filter(&(&1.login.params == "default"))
			|> try_login_by_plugins(params)
	end

	defp try_login_by_plugins([], _params), do: false
	defp try_login_by_plugins([ auth | rest], params) do
		case try_login_by_auth(auth, params) do
			false ->
				try_login_by_plugins(rest, params)
			user ->
				user
		end
	end

	defp try_login_by_auth(%{ command: command, login: %{ call: call }, id: id } = auth, params) when is_binary(command) and is_binary(call) do
		#Logger.debug("Try login at #{inspect id}: #{inspect command}.#{inspect call}(#{inspect params})")
		 case Serverboards.Plugin.Runner.start_call_stop(command, call, params, "system/auth") do
		  {:ok, email} when is_binary(email) ->
				case Serverboards.Auth.User.user_info(email) do
					{:ok, user}  ->
						Logger.info("Login via #{inspect id} for user #{inspect email}", user: email, auth: auth)
						user
					_ -> false
				end
		 	{:ok, %{} = user} ->
				Logger.info("Login via #{inspect id} for user #{inspect user["email"]}", user: user, auth: auth)
				%{
					email: user["email"],
					name: user["name"],
					perms: user["perms"],
					groups: ["user"] ++ user["groups"],
					is_active: true
				}
			o ->
				Logger.debug("Could not load at #{inspect id} because of #{inspect o}")
				false
		 end
	end

	@doc ~S"""
	Sets up the client for authentication.

	Can call a continuation function f(user) when authentication succeds.
	"""
	def authenticate(client, cont \\ nil) do
		#Logger.debug("Asking for authentication #{inspect client}")

		RPC.Client.add_method(client, "auth.auth", fn
			%{ "type" => _ } = params ->
				case auth(params) do
					{:ok, user} ->
						client_set_user(client, user)
						if cont do
							cont.(user)
						end
						user
					_ ->
						false
				end
		end)
		RPC.Client.add_method(client, "auth.reset_password", fn
			[email] -> # Request token
				Logger.debug("Reset password for #{email}")
				# Check user exists, and is active
				case Serverboards.Auth.User.user_info(email, [require_active: true], %{email: email}) do
					{:error, :unknown_user} ->
						Logger.error("Requested password reset for unknown user #{email}")
						{:ok, :ok}
					{:ok, me} ->
						Logger.info("Password reset link requested for #{email}")
						token = Serverboards.Auth.User.Token.create(me, ["auth.reset_password"])
						base_url=Serverboards.Settings.get("serverboards.core.settings/base",%{})
							|> Map.get("base_url", "http://localhost:8080")
						link="#{base_url}/#?pr=#{token}"
						Serverboards.Notifications.notify(
							email,
							"Password reset link",
							"Please click on the following link to reset your password.\n\n#{link}\n\nThis link is valid for 24 hours.",
							[], me
							)
						{:ok, :ok}
				end
			[email, token, new_password] -> # Reset password
				case Serverboards.Auth.User.Token.auth(token) do
					false -> {:error, :not_allowed}
					user ->
						if "auth.reset_password" in user.perms and user.email==email do
							:ok = Serverboards.Auth.User.Password.password_set(user, new_password, user)
							:ok = Serverboards.Auth.User.Token.invalidate(token)
							Logger.info("Password reset for #{user.email}")
							{:ok, :ok}
						else
							Logger.error("Requested password reset for unknown user #{email}")
							{:ok, :ok}
						end
				end
		end)

		RPC.Client.event( client, "auth.required", list_auth() )
		:ok
	end

	@doc ~S"""
	Returns the list of known authentications

		iex> l = list_auth()
		iex> (Enum.count l) >= 1
		true
		iex> "basic" in l
		true
	"""
	def list_auth do
		auths = (
			["basic", "token"] ++
			(for c <- auth_components() do
				c.id
			end))
		Logger.debug("Found auth types: #{inspect auths}")

		auths
	end

	def auth_components() do
		Serverboards.Plugin.Registry.filter_component(type: "auth")
		 |> Enum.map(fn c ->
			 try do
				 %{
					 id: c.id,
					 name: c.name,
					 description: c.description,
					 command: c.extra["command"],
					 login: %{
						 call: c.extra["login"]["call"],
						 params: c.extra["login"]["params"]
					 }
				 }
			 catch
				 _, _ ->
				 	Logger.error("Bad formed auth component #{c.id}")
				 	nil
			 end
		 end)
		 |> Enum.filter(&(&1!=nil))
	end
end

require Logger

defmodule Serverboards.Auth do
	@moduledoc ~S"""
	Manages authentication of users.

	It can use custom auth methods (`add_auth`) in elixir code, or plugins,
	which is any component with the `auth` trait.

	## Example

		iex> auth %{ "type" => "invalid" }
		{:error, :unknown_user}

	Using the fake auth plugin:

		iex> {:ok, ret} = auth %{ "type" => "serverboards.test.auth/auth", "token" => "XXX" }
		iex> ret.email
		"dmoreno@serverboards.io"
		iex> auth %{ "type" => "fake", "token" => "xxx" }
		{:error, :unknown_user}

	"""
	use GenServer

	alias MOM.RPC

	defstruct []

	def email_auth(%{ "email" => email, "password" => password }) do
		case Serverboards.Auth.User.Password.auth(email, password) do
			{:error, _} -> false
			false -> false
			user -> user
				#%{ email: user.email, permissions: user.perms, name: user.name }
		end
	end

	def token_auth(%{ "token" => token }) do
		case Serverboards.Auth.User.Token.auth(token) do
			{:error, _} -> false
			false -> false
			user -> user
				#%{ email: user.email, permissions: user.perms, name: user.name }
		end
	end

	@doc ~S"""
	Check is gien params allow for login.

	Params must contain `type`.

	Returns:
		* false -- no login
		* email -- Uses that email to get all the permissions and data from database

	"""
	def auth(%{ "type" => type } = params) do
		user = case type do
			"basic" ->
				(try_login_default_plugins(params)
					|> ensure_exists_in_db(type) # requires update db
				) || email_auth(params)
			"token" ->
				token_auth(params)
			other ->
				auth = auth_components()
					|> Enum.find( &(&1.id == other))

				if auth do
					try_login_by_auth(auth, params)
						|> ensure_exists_in_db(type) # requires update db
				else
					Logger.warn("Unknown auth method #{inspect type}", type: type)
					false
				end
		end
		Logger.info("Log in user #{user[:name]} <#{user[:email]}>", user: user)

		if user do
			{:ok,
				user
					|> decorate
				}
		else
			Logger.error("Login failed, type #{inspect type}", type: type)
			{:error, :unknown_user}
		end
	end

	# Regets the full user, with perms, id and so on
	defp decorate(user) do
		{:ok, user} = Serverboards.Auth.User.user_info(user.email)
		user
	end

	# all perms from the given groups
	def perms_from_groups(groups) do
		import Ecto.Query
		Serverboards.Repo.all(
			from perms in Serverboards.Auth.Model.Permission,
			join: pg in Serverboards.Auth.Model.GroupPerms,
			  on: pg.perm_id == perms.id,
			join: group in Serverboards.Auth.Model.Group,
			  on: pg.group_id == group.id,
			where: group.name in ^groups,
			select: perms.code,
			distinct: true
			)
	end

	defp ensure_exists_in_db(false, _creator), do: false
	defp ensure_exists_in_db(user, creator) do
		Logger.debug("Ensure in db #{inspect user}")
		# check if exists, or create
		nuser = case Serverboards.Auth.User.user_info(user.email) do
			{:error, :unknown_user} ->
				Logger.info("Automatically creating the user #{inspect user.email}", user: user)
				Serverboards.Auth.User.user_add(user, %{ email: creator, perms: ["auth.create_user"]})
				%{ user | groups: [] }
			{:ok, user} ->
				# If exists, check it is active, nuser here only for test
				if user.is_active do
					user
				else
					nil
				end
		end

		# update groups
		nuser = if nuser do
			newgroups = MapSet.new user.groups
			oldgroups = MapSet.new nuser.groups
			if not MapSet.equal?(newgroups, oldgroups)  do
				group_list = Serverboards.Auth.Group.group_list(nuser)
				for g <- MapSet.difference(newgroups, oldgroups) do
					if g in group_list do
						Logger.debug("Add #{inspect nuser.email} to group #{inspect g}")
						:ok = Serverboards.Auth.Group.user_add( g, nuser.email, %{ email: creator, perms: ["auth.manage_groups"]})
					end
				end
				for g <- MapSet.difference(oldgroups, newgroups) do
					Logger.debug("Remove #{inspect nuser.email} from group #{inspect g}")
					:ok = Serverboards.Auth.Group.user_remove( g, nuser.email, %{ email: creator, perms: ["auth.manage_groups"]})
				end
			end
			%{ nuser | groups: user.groups }
		end

		Logger.debug("Ready user #{inspect nuser}")

		nuser
	end

	def client_set_user(client, user) do
		#Logger.debug("Setting user: #{inspect user}")
		RPC.Client.set client, :user, user
		MOM.Channel.send(:auth_authenticated, %MOM.Message{ payload: %{ client: client, user: user } }, [sync: true])
	end

	defp try_login_default_plugins(params) do
		#Logger.debug("Try login with params #{inspect params}")
		auth_components()
			|> Enum.filter(&(&1.login.params == "default"))
			|> try_login_by_plugins(params)
	end

	defp try_login_by_plugins([], _params), do: false
	defp try_login_by_plugins([ auth | rest], params) do
		case try_login_by_auth(auth, params) do
			false ->
				try_login_by_plugins(rest, params)
			user ->
				user
		end
	end

	defp try_login_by_auth(%{ command: command, login: %{ call: call }, id: id } = auth, params) when is_binary(command) and is_binary(call) do
		#Logger.debug("Try login at #{inspect id}: #{inspect command}.#{inspect call}(#{inspect params})")
		 case Serverboards.Plugin.Runner.start_call_stop(command, call, params, "system/auth") do
		  {:ok, email} when is_binary(email) ->
				case Serverboards.Auth.User.user_info(email) do
					{:ok, user}  ->
						Logger.info("Login via #{inspect id} for user #{inspect email}", user: email, auth: auth)
						user
					_ -> false
				end
		 	{:ok, %{} = user} ->
				Logger.info("Login via #{inspect id} for user #{inspect user["email"]}", user: user, auth: auth)
				%{
					email: user["email"],
					name: user["name"],
					perms: user["perms"],
					groups: ["user"] ++ user["groups"],
					is_active: true
				}
			o ->
				Logger.debug("Could not load at #{inspect id} because of #{inspect o}")
				false
		 end
	end

	@doc ~S"""
	Sets up the client for authentication.

	Can call a continuation function f(user) when authentication succeds.
	"""
	def authenticate(client, cont \\ nil) do
		#Logger.debug("Asking for authentication #{inspect client}")

		RPC.Client.add_method(client, "auth.auth", fn
			%{ "type" => _ } = params ->
				case auth(params) do
					{:ok, user} ->
						client_set_user(client, user)
						if cont do
							cont.(user)
						end
						user
					_ ->
						false
				end
		end)
		RPC.Client.add_method(client, "auth.reset_password", fn
			[email] -> # Request token
				Logger.debug("Reset password for #{email}")
				# Check user exists, and is active
				case Serverboards.Auth.User.user_info(email, [require_active: true], %{email: email}) do
					{:error, :unknown_user} ->
						Logger.error("Requested password reset for unknown user #{email}")
						{:ok, :ok}
					{:ok, me} ->
						Logger.info("Password reset link requested for #{email}")
						token = Serverboards.Auth.User.Token.create(me, ["auth.reset_password"])
						base_url=Serverboards.Settings.get("serverboards.core.settings/base",%{})
							|> Map.get("base_url", "http://localhost:8080")
						link="#{base_url}/#?pr=#{token}"
						Serverboards.Notifications.notify(
							email,
							"Password reset link",
							"Please click on the following link to reset your password.\n\n#{link}\n\nThis link is valid for 24 hours.",
							[], me
							)
						{:ok, :ok}
				end
			[email, token, new_password] -> # Reset password
				case Serverboards.Auth.User.Token.auth(token) do
					false -> {:error, :not_allowed}
					user ->
						if "auth.reset_password" in user.perms and user.email==email do
							:ok = Serverboards.Auth.User.Password.password_set(user, new_password, user)
							:ok = Serverboards.Auth.User.Token.invalidate(token)
							Logger.info("Password reset for #{user.email}")
							{:ok, :ok}
						else
							Logger.error("Requested password reset for unknown user #{email}")
							{:ok, :ok}
						end
				end
		end)

		RPC.Client.event( client, "auth.required", list_auth() )
		:ok
	end

	@doc ~S"""
	Returns the list of known authentications

		iex> l = list_auth()
		iex> (Enum.count l) >= 1
		true
		iex> "basic" in l
		true
	"""
	def list_auth do
		auths = (
			["basic", "token"] ++
			(for c <- auth_components() do
				c.id
			end))
		Logger.debug("Found auth types: #{inspect auths}")

		auths
	end

	def auth_components() do
		Serverboards.Plugin.Registry.filter_component(type: "auth")
		 |> Enum.map(fn c ->
			 try do
				 %{
					 id: c.id,
					 name: c.name,
					 description: c.description,
					 command: c.extra["command"],
					 login: %{
						 call: c.extra["login"]["call"],
						 params: c.extra["login"]["params"]
					 }
				 }
			 catch
				 _, _ ->
				 	Logger.error("Bad formed auth component #{c.id}")
				 	nil
			 end
		 end)
		 |> Enum.filter(&(&1!=nil))
	end
end


Refactor PusherEvent tests

Refactor PusherEvent tests

defmodule Poxa.PusherEventTest do
  use ExUnit.Case
  import :meck
  import Poxa.PusherEvent
  alias Poxa.PusherEvent

  setup do
    on_exit fn -> unload end
    :ok
  end

  doctest Poxa.PusherEvent

  test "connection established output" do
    json ="{\"data\":\"{\\\"activity_timeout\\\":120,\\\"socket_id\\\":\\\"SocketId\\\"}\",\"event\":\"pusher:connection_established\"}"
    assert connection_established("SocketId") == json
  end

  test "subscription established output" do
    json = "{\"channel\":\"channel\",\"data\":{},\"event\":\"pusher_internal:subscription_succeeded\"}"
    assert subscription_succeeded("channel") == json
  end

  test "subscription error output" do
    json = "{\"data\":{},\"event\":\"pusher:subscription_error\"}"
    assert subscription_error == json
  end

  test "pusher error output" do
    json = "{\"data\":{\"code\":null,\"message\":\"An error\"},\"event\":\"pusher:error\"}"
    assert pusher_error("An error") == json
  end

  test "pusher error with code output" do
    json = "{\"data\":{\"code\":123,\"message\":\"An error\"},\"event\":\"pusher:error\"}"
    assert pusher_error("An error", 123) == json
  end

  test "pong output" do
    json ="{\"data\":{},\"event\":\"pusher:pong\"}"
    assert pong == json
  end

  test "presence member added output" do
    json ="{\"channel\":\"channel\",\"data\":\"{\\\"user_id\\\":\\\"userid\\\",\\\"user_info\\\":\\\"userinfo\\\"}\",\"event\":\"pusher_internal:member_added\"}"
    assert presence_member_added("channel", "userid", "userinfo") == json
  end

  test "presence member removed output" do
    json ="{\"channel\":\"channel\",\"data\":\"{\\\"user_id\\\":\\\"userid\\\"}\",\"event\":\"pusher_internal:member_removed\"}"
    assert presence_member_removed("channel", "userid") == json
  end

  test "presence subscription succeeded" do
    json = "{\"channel\":\"presence-channel\",\"data\":\"{\\\"presence\\\":{\\\"count\\\":1,\\\"hash\\\":{\\\"userid\\\":\\\"userinfo\\\"},\\\"ids\\\":[\\\"userid\\\"]}}\",\"event\":\"pusher_internal:subscription_succeeded\"}"
    subscription = %Poxa.PresenceSubscription{channel: "presence-channel",
                                              channel_data: [{"userid", "userinfo"}]}
    assert subscription_succeeded(subscription) == json
  end

  test "build PusherEvent with 1 channel" do
    event = %{"channel" => "channel_name",
              "data" => "event_data",
              "name" => "event_etc"}
    assert build(event) == {:ok, %PusherEvent{name: "event_etc", data: "event_data", channels: ["channel_name"]}}
  end

  test "build PusherEvent with multiple channels" do
    event = %{"channels" => ["channel_name1", "channel_name2"],
              "data" => "event_data",
              "name" => "event_etc"}
    assert build(event) == {:ok, %PusherEvent{name: "event_etc", data: "event_data", channels: ["channel_name1","channel_name2"]}}
  end

  test "build PusherEvent with excluding socket_id" do
    event = %{"channel" => "channel_name",
              "data" => "event_data",
              "socket_id" => "123.456",
              "name" => "event_etc"}
    assert build(event) == {:ok, %PusherEvent{name: "event_etc", data: "event_data", channels: ["channel_name"], socket_id: "123.456"}}
  end

  test "build PusherEvent with invalid socket_id" do
    event = %{"channel" => "channel_name",
              "data" => "event_data",
              "socket_id" => "123:456",
              "name" => "event_etc"}
    assert build(event) == {:error, :invalid_event}
  end

  test "build PusherEvent with invalid channel name" do
    event = %{"channel" => "channel:name",
              "data" => "event_data",
              "socket_id" => "123.456",
              "name" => "event_etc"}
    assert build(event) == {:error, :invalid_event}
  end

  test "build_client_event with excluding socket_id" do
    event = %{"channel" => "channel_name",
              "data" => "event_data",
              "name" => "event_etc"}
    assert build_client_event(event, "123.456") == {:ok, %PusherEvent{name: "event_etc", data: "event_data", channels: ["channel_name"], socket_id: "123.456"}}
  end

  test "build_client_event with invalid excluding socket_id" do
    event = %{"channel" => "channel_name",
              "data" => "event_data",
              "name" => "event_etc"}
    assert build_client_event(event, "123:456") == {:error, :invalid_event}
  end

  test "build_client_event with invalid channel name" do
    event = %{"channel" => "channel:name",
              "data" => "event_data",
              "name" => "event_etc"}
    assert build_client_event(event, "123.456") == {:error, :invalid_event}
  end

  test "sending message to a channel" do
    expect(:gproc, :send, [{[{:p, :l, {:pusher, "channel123"}}, {self, :msg, nil}], :ok}])
    expected = %{channel: "channel123", data: "data", event: "event"}
    expect(JSX, :encode!, [{[expected], :msg}])
    event = %PusherEvent{channels: ["channel123"], data: "data", name: "event"}

    assert publish(event) == :ok

    assert validate [:gproc, JSX]
  end

  test "sending message to channels excluding a socket id" do
    expected = %{channel: "channel123", data: %{}, event: "event"}
    expect(JSX, :encode!, [{[expected], :msg}])
    expect(:gproc, :send, [{[{:p, :l, {:pusher, "channel123"}}, {self, :msg, "SocketId"}], :ok}])

    assert publish(%PusherEvent{data: %{}, channels: ["channel123"], name: "event", socket_id: "SocketId"}) == :ok

    assert validate [:gproc, JSX]
  end
end

defmodule Poxa.PusherEventTest do
  use ExUnit.Case
  import :meck
  import Poxa.PusherEvent
  alias Poxa.PusherEvent

  setup do
    new :gproc
    on_exit fn -> unload end
    :ok
  end

  doctest Poxa.PusherEvent

  test "connection established output" do
    json ="{\"data\":\"{\\\"activity_timeout\\\":120,\\\"socket_id\\\":\\\"SocketId\\\"}\",\"event\":\"pusher:connection_established\"}"
    assert connection_established("SocketId") == json
  end

  test "subscription established output" do
    json = "{\"channel\":\"channel\",\"data\":{},\"event\":\"pusher_internal:subscription_succeeded\"}"
    assert subscription_succeeded("channel") == json
  end

  test "subscription error output" do
    json = "{\"data\":{},\"event\":\"pusher:subscription_error\"}"
    assert subscription_error == json
  end

  test "pusher error output" do
    json = "{\"data\":{\"code\":null,\"message\":\"An error\"},\"event\":\"pusher:error\"}"
    assert pusher_error("An error") == json
  end

  test "pusher error with code output" do
    json = "{\"data\":{\"code\":123,\"message\":\"An error\"},\"event\":\"pusher:error\"}"
    assert pusher_error("An error", 123) == json
  end

  test "pong output" do
    json ="{\"data\":{},\"event\":\"pusher:pong\"}"
    assert pong == json
  end

  test "presence member added output" do
    json ="{\"channel\":\"channel\",\"data\":\"{\\\"user_id\\\":\\\"userid\\\",\\\"user_info\\\":\\\"userinfo\\\"}\",\"event\":\"pusher_internal:member_added\"}"
    assert presence_member_added("channel", "userid", "userinfo") == json
  end

  test "presence member removed output" do
    json ="{\"channel\":\"channel\",\"data\":\"{\\\"user_id\\\":\\\"userid\\\"}\",\"event\":\"pusher_internal:member_removed\"}"
    assert presence_member_removed("channel", "userid") == json
  end

  test "presence subscription succeeded" do
    json = "{\"channel\":\"presence-channel\",\"data\":\"{\\\"presence\\\":{\\\"count\\\":1,\\\"hash\\\":{\\\"userid\\\":\\\"userinfo\\\"},\\\"ids\\\":[\\\"userid\\\"]}}\",\"event\":\"pusher_internal:subscription_succeeded\"}"
    subscription = %Poxa.PresenceSubscription{channel: "presence-channel",
                                              channel_data: [{"userid", "userinfo"}]}
    assert subscription_succeeded(subscription) == json
  end

  test "build PusherEvent with 1 channel" do
    event = %{"channel" => "channel_name",
              "data" => "event_data",
              "name" => "event_etc"}
    assert build(event) == {:ok, %PusherEvent{name: "event_etc", data: "event_data", channels: ["channel_name"]}}
  end

  test "build PusherEvent with multiple channels" do
    event = %{"channels" => ["channel_name1", "channel_name2"],
              "data" => "event_data",
              "name" => "event_etc"}
    assert build(event) == {:ok, %PusherEvent{name: "event_etc", data: "event_data", channels: ["channel_name1","channel_name2"]}}
  end

  test "build PusherEvent with excluding socket_id" do
    event = %{"channel" => "channel_name",
              "data" => "event_data",
              "socket_id" => "123.456",
              "name" => "event_etc"}
    assert build(event) == {:ok, %PusherEvent{name: "event_etc", data: "event_data", channels: ["channel_name"], socket_id: "123.456"}}
  end

  test "build PusherEvent with invalid socket_id" do
    event = %{"channel" => "channel_name",
              "data" => "event_data",
              "socket_id" => "123:456",
              "name" => "event_etc"}
    assert build(event) == {:error, :invalid_event}
  end

  test "build PusherEvent with invalid channel name" do
    event = %{"channel" => "channel:name",
              "data" => "event_data",
              "socket_id" => "123.456",
              "name" => "event_etc"}
    assert build(event) == {:error, :invalid_event}
  end

  test "build_client_event with excluding socket_id" do
    event = %{"channel" => "channel_name",
              "data" => "event_data",
              "name" => "event_etc"}
    assert build_client_event(event, "123.456") == {:ok, %PusherEvent{name: "event_etc", data: "event_data", channels: ["channel_name"], socket_id: "123.456"}}
  end

  test "build_client_event with invalid excluding socket_id" do
    event = %{"channel" => "channel_name",
              "data" => "event_data",
              "name" => "event_etc"}
    assert build_client_event(event, "123:456") == {:error, :invalid_event}
  end

  test "build_client_event with invalid channel name" do
    event = %{"channel" => "channel:name",
              "data" => "event_data",
              "name" => "event_etc"}
    assert build_client_event(event, "123.456") == {:error, :invalid_event}
  end

  test "sending message to a channel" do
    expect(:gproc, :send, [{[{:p, :l, {:pusher, "channel123"}}, {self, :msg, nil}], :ok}])
    expected = %{channel: "channel123", data: "data", event: "event"}
    expect(JSX, :encode!, [{[expected], :msg}])
    event = %PusherEvent{channels: ["channel123"], data: "data", name: "event"}

    assert publish(event) == :ok

    assert validate :gproc
    assert validate JSX
  end

  test "sending message to channels excluding a socket id" do
    expected = %{channel: "channel123", data: %{}, event: "event"}
    expect(JSX, :encode!, [{[expected], :msg}])
    expect(:gproc, :send, [{[{:p, :l, {:pusher, "channel123"}}, {self, :msg, "SocketId"}], :ok}])

    assert publish(%PusherEvent{data: %{}, channels: ["channel123"], name: "event", socket_id: "SocketId"}) == :ok

    assert validate :gproc
    assert validate JSX
  end
end


version 0.8.15

version 0.8.15

defmodule Wwwtech.MixProject do
  use Mix.Project

  def project do
    [
      app: :wwwtech,
      version: "0.8.15",
      elixir: "~> 1.5",
      elixirc_paths: elixirc_paths(Mix.env()),
      compilers: [:phoenix, :gettext] ++ Mix.compilers(),
      start_permanent: Mix.env() == :prod,
      aliases: aliases(),
      deps: deps()
    ]
  end

  # Configuration for the OTP application.
  #
  # Type `mix help compile.app` for more information.
  def application do
    [
      mod: {Wwwtech.Application, []},
      extra_applications: [:logger, :runtime_tools, :os_mon]
    ]
  end

  # Specifies which paths to compile per environment.
  defp elixirc_paths(:test), do: ["lib", "test/support"]
  defp elixirc_paths(_), do: ["lib"]

  # Specifies your project dependencies.
  #
  # Type `mix help deps` for examples and options.
  defp deps do
    [
      {:phoenix, "~> 1.5.0"},
      {:phoenix_pubsub, "~> 2.0"},
      {:phoenix_ecto, "~> 4.0"},
      {:ecto_sql, "~> 3.1"},
      {:postgrex, ">= 0.0.0"},
      {:phoenix_html, "~> 2.11"},
      {:phoenix_live_reload, "~> 1.2", only: :dev},
      {:gettext, "~> 0.11"},
      {:jason, "~> 1.0"},
      {:plug_cowboy, "~> 2.1"},
      {:argon2_elixir, "~> 2.0"},
      {:timex, "~> 3.5"},
      {:earmark, "~> 1.4"},
      {:xml_builder, "~> 2.1"},
      {:exexif, "~> 0.0.5"},
      {:mogrify, "~> 0.7.3"},
      {:microformats2, "~> 0.1"},
      {:webmentions, "~> 0.3"},
      {:floki, "~> 0.23"},
      {:swoosh, "~> 0.25"},
      {:gen_smtp, "~> 0.15"},
      {:phoenix_swoosh, "~> 0.2"},
      {:appsignal, "~> 1.0"},
      {:tesla, "~> 1.3"},
      {:hackney, "~> 1.15"},
      {:phoenix_live_dashboard, "~> 0.1"},
      {:telemetry_poller, "~> 0.4"},
      {:telemetry_metrics, "~> 0.4"}
    ]
  end

  # Aliases are shortcuts or tasks specific to the current project.
  # For example, to create, migrate and run the seeds file at once:
  #
  #     $ mix ecto.setup
  #
  # See the documentation for `Mix` for more info on aliases.
  defp aliases do
    [
      "ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"],
      "ecto.reset": ["ecto.drop", "ecto.setup"],
      test: ["ecto.create --quiet", "ecto.migrate", "test"],
      build: "cmd ./.build/build",
      deploy: "cmd ./.build/deploy"
    ]
  end
end

defmodule Wwwtech.MixProject do
  use Mix.Project

  def project do
    [
      app: :wwwtech,
      version: "0.8.14",
      elixir: "~> 1.5",
      elixirc_paths: elixirc_paths(Mix.env()),
      compilers: [:phoenix, :gettext] ++ Mix.compilers(),
      start_permanent: Mix.env() == :prod,
      aliases: aliases(),
      deps: deps()
    ]
  end

  # Configuration for the OTP application.
  #
  # Type `mix help compile.app` for more information.
  def application do
    [
      mod: {Wwwtech.Application, []},
      extra_applications: [:logger, :runtime_tools, :os_mon]
    ]
  end

  # Specifies which paths to compile per environment.
  defp elixirc_paths(:test), do: ["lib", "test/support"]
  defp elixirc_paths(_), do: ["lib"]

  # Specifies your project dependencies.
  #
  # Type `mix help deps` for examples and options.
  defp deps do
    [
      {:phoenix, "~> 1.5.0"},
      {:phoenix_pubsub, "~> 2.0"},
      {:phoenix_ecto, "~> 4.0"},
      {:ecto_sql, "~> 3.1"},
      {:postgrex, ">= 0.0.0"},
      {:phoenix_html, "~> 2.11"},
      {:phoenix_live_reload, "~> 1.2", only: :dev},
      {:gettext, "~> 0.11"},
      {:jason, "~> 1.0"},
      {:plug_cowboy, "~> 2.1"},
      {:argon2_elixir, "~> 2.0"},
      {:timex, "~> 3.5"},
      {:earmark, "~> 1.4"},
      {:xml_builder, "~> 2.1"},
      {:exexif, "~> 0.0.5"},
      {:mogrify, "~> 0.7.3"},
      {:microformats2, "~> 0.1"},
      {:webmentions, "~> 0.3"},
      {:floki, "~> 0.23"},
      {:swoosh, "~> 0.25"},
      {:gen_smtp, "~> 0.15"},
      {:phoenix_swoosh, "~> 0.2"},
      {:appsignal, "~> 1.0"},
      {:tesla, "~> 1.3"},
      {:hackney, "~> 1.15"},
      {:phoenix_live_dashboard, "~> 0.1"},
      {:telemetry_poller, "~> 0.4"},
      {:telemetry_metrics, "~> 0.4"}
    ]
  end

  # Aliases are shortcuts or tasks specific to the current project.
  # For example, to create, migrate and run the seeds file at once:
  #
  #     $ mix ecto.setup
  #
  # See the documentation for `Mix` for more info on aliases.
  defp aliases do
    [
      "ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"],
      "ecto.reset": ["ecto.drop", "ecto.setup"],
      test: ["ecto.create --quiet", "ecto.migrate", "test"],
      build: "cmd ./.build/build",
      deploy: "cmd ./.build/deploy"
    ]
  end
end


Version 1.0.5

Version 1.0.5

defmodule ReleaseManager.Mixfile do
  use Mix.Project

  def project do
    [ app: :exrm,
      version: "1.0.5",
      elixir: "~> 1.0",
      description: description,
      package: package,
      deps: deps,
      docs: docs,
      test_coverage: [tool: Coverex.Task, coveralls: true]]
  end

  def application, do: [
    applications: [:logger, :relx]
  ]

  def deps do
    [{:relx, "~> 3.5" },
     {:earmark, "~> 0.2", only: :dev},
     {:ex_doc, "~> 0.11", only: :dev},
     {:coverex, "~> 1.4", only: :test}]
  end

  defp description do
    """
    Exrm, or Elixir Release Manager, provides mix tasks for building,
    upgrading, and controlling release packages for your application.
    """
  end

  defp package do
    [ files: ["lib", "priv", "mix.exs", "README.md", "LICENSE"],
      maintainers: ["Paul Schoenfelder"],
      licenses: ["MIT"],
      links: %{ "GitHub": "https://github.com/bitwalker/exrm" } ]
  end

  defp docs do
    [main: "getting-started",
     extras: [
        "docs/Getting Started.md",
        "docs/Release Configuration.md",
        "docs/Deployment.md",
        "docs/Upgrades and Downgrades.md",
        "docs/Common Issues.md",
        "docs/Examples.md"
    ]]
  end

end

defmodule ReleaseManager.Mixfile do
  use Mix.Project

  def project do
    [ app: :exrm,
      version: "1.0.4",
      elixir: "~> 1.0",
      description: description,
      package: package,
      deps: deps,
      docs: docs,
      test_coverage: [tool: Coverex.Task, coveralls: true]]
  end

  def application, do: [
    applications: [:logger, :relx]
  ]

  def deps do
    [{:relx, "~> 3.5" },
     {:earmark, "~> 0.2", only: :dev},
     {:ex_doc, "~> 0.11", only: :dev},
     {:coverex, "~> 1.4", only: :test}]
  end

  defp description do
    """
    Exrm, or Elixir Release Manager, provides mix tasks for building,
    upgrading, and controlling release packages for your application.
    """
  end

  defp package do
    [ files: ["lib", "priv", "mix.exs", "README.md", "LICENSE"],
      maintainers: ["Paul Schoenfelder"],
      licenses: ["MIT"],
      links: %{ "GitHub": "https://github.com/bitwalker/exrm" } ]
  end

  defp docs do
    [main: "getting-started",
     extras: [
        "docs/Getting Started.md",
        "docs/Release Configuration.md",
        "docs/Deployment.md",
        "docs/Upgrades and Downgrades.md",
        "docs/Common Issues.md",
        "docs/Examples.md"
    ]]
  end

end


Bump version 0.3.4

Bump version 0.3.4

[ci skip]

defmodule Httparrot.Mixfile do
  use Mix.Project

  @description """
    HTTP Request & Response Server. An incomplete clone of http://httpbin.org
  """

  def project do
    [ app: :httparrot,
      version: "0.3.4",
      elixir: "~> 1.0",
      name: "HTTParrot",
      description: @description,
      package: package,
      deps: deps ]
  end

  def application do
    [ applications: [ :compiler,
                      :syntax_tools,
                      :cowboy,
                      :jsex ],
      mod: { HTTParrot, [] },
      env: [ http_port: 8080, ssl: true, https_port: 8433 ] ]
  end

  defp deps do
    [ {:cowboy, "~> 1.0.0"},
      {:jsex, "~> 2.0"},
      {:meck, "~> 0.8.2", only: :test } ]
  end

  defp package do
    [ contributors: ["Eduardo Gurgel Pinho"],
      licenses: ["MIT"],
      links: %{ "Github" => "https://github.com/edgurgel/httparrot",
                "HTTParrot" => "http://httparrot.herokuapp.com",
                "httpbin" => "http://httpbin.org" } ]
  end
end

defmodule Httparrot.Mixfile do
  use Mix.Project

  @description """
    HTTP Request & Response Server. An incomplete clone of http://httpbin.org
  """

  def project do
    [ app: :httparrot,
      version: "0.3.3",
      elixir: "~> 1.0",
      name: "HTTParrot",
      description: @description,
      package: package,
      deps: deps ]
  end

  def application do
    [ applications: [ :compiler,
                      :syntax_tools,
                      :cowboy,
                      :jsex ],
      mod: { HTTParrot, [] },
      env: [ http_port: 8080, ssl: true, https_port: 8433 ] ]
  end

  defp deps do
    [ {:cowboy, "~> 1.0.0"},
      {:jsex, "~> 2.0"},
      {:meck, "~> 0.8.2", only: :test } ]
  end

  defp package do
    [ contributors: ["Eduardo Gurgel Pinho"],
      licenses: ["MIT"],
      links: %{ "Github" => "https://github.com/edgurgel/httparrot",
                "HTTParrot" => "http://httparrot.herokuapp.com",
                "httpbin" => "http://httpbin.org" } ]
  end
end


add files to include in project

add files to include in project

defmodule PortMidi.Mixfile do
  use Mix.Project

  def project do
    [app: :portmidi,
     version: "3.1.0",
     elixir: "~> 1.2",
     description: "Elixir bindings to the portmidi C library",
     package: package,
     compilers: [:port_midi, :elixir, :app],
     build_embedded: Mix.env == :prod,
     start_permanent: Mix.env == :prod,
     deps: deps]
  end

  # Configuration for the OTP application
  #
  # Type "mix help compile.app" for more information
  def application do
    [applications: [:logger],
     mod: {PortMidi, []}]
  end

  # Dependencies can be Hex packages:
  #
  #   {:mydep, "~> 0.3.0"}
  #
  # Or git/path repositories:
  #
  #   {:mydep, git: "https://github.com/elixir-lang/mydep.git", tag: "0.1.0"}
  #
  # Type "mix help deps" for more examples and options
  defp deps do
    [{:credo, "~> 0.3", only: [:dev, :test]},
     {:mock, "~> 0.1.1", only: :test},
     {:ex_doc, github: "elixir-lang/ex_doc", only: :dev},
     {:earmark, ">= 0.0.0"}]
  end

  defp package do
    [maintainers: ["Andrea Rossi"],
     files: ["priv", "lib", "src", "Makefile", "mix.exs", "README.md", "LICENSE"],
     licenses: ["MIT"],
     links: %{"Github" => "https://github.com/lucidstack/ex-portmidi"}]
  end

end

defmodule Mix.Tasks.Compile.PortMidi do
  @shortdoc "Compiles portmidi bindings"
  def run(_) do
    if Mix.shell.cmd("make") != 0 do
      raise Mix.Error, message: "could not run `make`. Do you have make, gcc and libportmidi installed?"
    end
  end
end

defmodule PortMidi.Mixfile do
  use Mix.Project

  def project do
    [app: :portmidi,
     version: "3.1.0",
     elixir: "~> 1.2",
     description: "Elixir bindings to the portmidi C library",
     package: package,
     compilers: [:port_midi, :elixir, :app],
     build_embedded: Mix.env == :prod,
     start_permanent: Mix.env == :prod,
     deps: deps]
  end

  # Configuration for the OTP application
  #
  # Type "mix help compile.app" for more information
  def application do
    [applications: [:logger],
     mod: {PortMidi, []}]
  end

  # Dependencies can be Hex packages:
  #
  #   {:mydep, "~> 0.3.0"}
  #
  # Or git/path repositories:
  #
  #   {:mydep, git: "https://github.com/elixir-lang/mydep.git", tag: "0.1.0"}
  #
  # Type "mix help deps" for more examples and options
  defp deps do
    [{:credo, "~> 0.3", only: [:dev, :test]},
     {:mock, "~> 0.1.1", only: :test},
     {:ex_doc, github: "elixir-lang/ex_doc", only: :dev},
     {:earmark, ">= 0.0.0"}]
  end

  defp package do
    [maintainers: ["Andrea Rossi"],
     licenses: ["MIT"],
     links: %{"Github" => "https://github.com/lucidstack/ex-portmidi"}]
  end

end

defmodule Mix.Tasks.Compile.PortMidi do
  @shortdoc "Compiles portmidi bindings"
  def run(_) do
    if Mix.shell.cmd("make") != 0 do
      raise Mix.Error, message: "could not run `make`. Do you have make, gcc and libportmidi installed?"
    end
  end
end


Relax Poison version requirement

Relax Poison version requirement

defmodule Swoosh.Mixfile do
  use Mix.Project

  @version "0.1.0"

  def project do
    [app: :swoosh,
     version: @version,
     elixir: "~> 1.2",
     elixirc_paths: elixirc_paths(Mix.env),
     build_embedded: Mix.env == :prod,
     start_permanent: Mix.env == :prod,
     deps: deps,

     # Hex
     description: description,
     package: package,

     # Docs
     name: "Swoosh",
     docs: [source_ref: "v#{@version}", main: "Swoosh",
            canonical: "http://hexdocs.pm/swoosh",
            source_url: "https://github.com/swoosh/swoosh"]]
  end

  def application do
    [applications: [:logger, :httpoison],
     mod: {Swoosh.Application, []}]
  end

  defp elixirc_paths(:test), do: ["lib", "test/support"]
  defp elixirc_paths(_),     do: ["lib"]

  defp deps do
    [{:httpoison, "~> 0.8"},
     {:poison, "~> 1.5 or ~> 2.0"},
     {:gen_smtp, "~> 0.9.0"},
     {:cowboy, "~> 1.0.0", optional: true},
     {:plug, "~> 1.1", optional: true},
     {:credo, "~> 0.3", only: [:dev, :test]},
     {:bypass, "~> 0.5", only: [:test]},
     {:ex_doc, "~> 0.10", only: :docs},
     {:earmark, "~> 0.1", only: :docs},
     {:inch_ex, ">= 0.0.0", only: :docs}]
  end

  defp description do
    """
    Compose, deliver and test your emails easily in Elixir. Supports SMTP, Sendgrid, Mandrill, Postmark and Mailgun out of the box.
    Works great with Phoenix.
    """
  end

  defp package do
    [maintainers: ["Steve Domin", "Baris Balic"],
     licenses: ["MIT"],
     links: %{"GitHub" => "https://github.com/swoosh/swoosh"}]
  end
end

defmodule Swoosh.Mixfile do
  use Mix.Project

  @version "0.1.0"

  def project do
    [app: :swoosh,
     version: @version,
     elixir: "~> 1.2",
     elixirc_paths: elixirc_paths(Mix.env),
     build_embedded: Mix.env == :prod,
     start_permanent: Mix.env == :prod,
     deps: deps,

     # Hex
     description: description,
     package: package,

     # Docs
     name: "Swoosh",
     docs: [source_ref: "v#{@version}", main: "Swoosh",
            canonical: "http://hexdocs.pm/swoosh",
            source_url: "https://github.com/swoosh/swoosh"]]
  end

  def application do
    [applications: [:logger, :httpoison],
     mod: {Swoosh.Application, []}]
  end

  defp elixirc_paths(:test), do: ["lib", "test/support"]
  defp elixirc_paths(_),     do: ["lib"]

  defp deps do
    [{:httpoison, "~> 0.8"},
     {:poison, "~> 2.1"},
     {:gen_smtp, "~> 0.9.0"},
     {:cowboy, "~> 1.0.0", optional: true},
     {:plug, "~> 1.1", optional: true},
     {:credo, "~> 0.3", only: [:dev, :test]},
     {:bypass, "~> 0.5", only: [:test]},
     {:ex_doc, "~> 0.10", only: :docs},
     {:earmark, "~> 0.1", only: :docs},
     {:inch_ex, ">= 0.0.0", only: :docs}]
  end

  defp description do
    """
    Compose, deliver and test your emails easily in Elixir. Supports SMTP, Sendgrid, Mandrill, Postmark and Mailgun out of the box.
    Works great with Phoenix.
    """
  end

  defp package do
    [maintainers: ["Steve Domin", "Baris Balic"],
     licenses: ["MIT"],
     links: %{"GitHub" => "https://github.com/swoosh/swoosh"}]
  end
end


[ex] Add validation JSON to package

[ex] Add validation JSON to package

defmodule IFSC.Mixfile do
  use Mix.Project

  def project do
    [
      app: :ifsc,
      version: "0.1.0",
      elixir: "~> 1.5",
      start_permanent: Mix.env == :prod,
      description: description(),
      package: package(),
      deps: deps(),
      source_url: "https://github.com/razorpay/ifsc",
      homepage_url: "https://ifsc.razorpay.com/",
      elixirc_paths: ["src/elixir"],
      test_paths: ["tests/elixir"],
    ]
  end

  def application do
    [
      extra_applications: [:logger],
    ]
  end

  defp description do
    "A simple package by @razorpay to help you validate your IFSC codes. "
    <> "IFSC codes are bank codes within India"
  end

  defp package do
    [
      maintainers: [
        "Nihal Gonsalves <nihal@gonsalves.com>",
        "Abhay Rana <contact@razorpay.com>",
      ],
      licenses: ["MIT"],
      links: %{
        "GitHub" => "https://github.com/razorpay/ifsc",
        "Website" => "https://ifsc.razorpay.com/",
      },
      files: files(),
    ]
  end

  defp files do
    [
      "src/elixir/config/**",
      "src/elixir/lib/**",
      "src/elixir/*.md",
      "tests/elixir/**",
      "tests/*.json",
      "src/*.json",
      "*.md",
    ]
  end

  defp deps do
    [
      {:poison, "~> 3.1"},
      {:httpoison, "~> 0.13"},
      {:memoize, "~> 1.2"},
      {:exvcr, "~> 0.8", only: :test},
      {:ex_doc, ">= 0.0.0", only: :dev},
    ]
  end
end

defmodule IFSC.Mixfile do
  use Mix.Project

  def project do
    [
      app: :ifsc,
      version: "0.1.0",
      elixir: "~> 1.5",
      start_permanent: Mix.env == :prod,
      description: description(),
      package: package(),
      deps: deps(),
      source_url: "https://github.com/razorpay/ifsc",
      homepage_url: "https://ifsc.razorpay.com/",
      elixirc_paths: ["src/elixir"],
      test_paths: ["tests/elixir"],
    ]
  end

  def application do
    [
      extra_applications: [:logger],
    ]
  end

  defp description do
    "A simple package by @razorpay to help you validate your IFSC codes. "
    <> "IFSC codes are bank codes within India"
  end

  defp package do
    [
      maintainers: [
        "Nihal Gonsalves <nihal@gonsalves.com>",
        "Abhay Rana <contact@razorpay.com>",
      ],
      licenses: ["MIT"],
      links: %{
        "GitHub" => "https://github.com/razorpay/ifsc",
        "Website" => "https://ifsc.razorpay.com/",
      },
      files: files(),
    ]
  end

  defp files do
    [
      "src/elixir/config/**",
      "src/elixir/lib/**",
      "src/elixir/*.md",
      "tests/elixir/**",
      "src/*.json",
      "*.md",
    ]
  end

  defp deps do
    [
      {:poison, "~> 3.1"},
      {:httpoison, "~> 0.13"},
      {:memoize, "~> 1.2"},
      {:exvcr, "~> 0.8", only: :test},
      {:ex_doc, ">= 0.0.0", only: :dev},
    ]
  end
end


Remove poison as a explicit dependency.

Remove poison as a explicit dependency.

defmodule SunshinesBattleacademy.Mixfile do
  use Mix.Project

  def project do
    [
      app: :sunshines_battleacademy,
      version: "0.0.2",
      elixir: "~> 1.4",
      elixirc_paths: elixirc_paths(Mix.env()),
      compilers: [:phoenix, :gettext] ++ Mix.compilers(),
      start_permanent: Mix.env() == :prod,
      aliases: aliases(),
      deps: deps()
    ]
  end

  # Configuration for the OTP application.
  #
  # Type `mix help compile.app` for more information.
  def application do
    [
      mod: {SunshinesBattleacademy.Application, []},
      extra_applications: [:logger, :runtime_tools]
    ]
  end

  # Specifies which paths to compile per environment.
  defp elixirc_paths(:test), do: ["lib", "test/support"]
  defp elixirc_paths(_), do: ["lib"]

  # Specifies your project dependencies.
  #
  # Type `mix help deps` for examples and options.
  defp deps do
    [
      {:phoenix, "~> 1.3.0"},
      {:uuid, "~> 1.1"},
      {:distillery, "~> 1.3"},
      {:phoenix_pubsub, "~> 1.0"},
      {:phoenix_ecto, "~> 3.2"},
      {:phoenix_html, "~> 2.6"},
      {:phoenix_live_reload, "~> 1.0", only: :dev},
      {:gettext, "~> 0.11"},
      {:nebulex, "~> 1.0.0-rc.3"},
      {:erlzord, "~> 1.0"},
      {:cowboy, "~> 1.0"}
    ]
  end

  # Aliases are shortcuts or tasks specific to the current project.
  # For example, to create, migrate and run the seeds file at once:
  #
  #     $ mix ecto.setup
  #
  # See the documentation for `Mix` for more info on aliases.
  defp aliases do
    [
      "ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"],
      "ecto.reset": ["ecto.drop", "ecto.setup"],
      test: ["ecto.create --quiet", "ecto.migrate", "test"]
    ]
  end
end

defmodule SunshinesBattleacademy.Mixfile do
  use Mix.Project

  def project do
    [
      app: :sunshines_battleacademy,
      version: "0.0.2",
      elixir: "~> 1.4",
      elixirc_paths: elixirc_paths(Mix.env()),
      compilers: [:phoenix, :gettext] ++ Mix.compilers(),
      start_permanent: Mix.env() == :prod,
      aliases: aliases(),
      deps: deps()
    ]
  end

  # Configuration for the OTP application.
  #
  # Type `mix help compile.app` for more information.
  def application do
    [
      mod: {SunshinesBattleacademy.Application, []},
      extra_applications: [:logger, :runtime_tools]
    ]
  end

  # Specifies which paths to compile per environment.
  defp elixirc_paths(:test), do: ["lib", "test/support"]
  defp elixirc_paths(_), do: ["lib"]

  # Specifies your project dependencies.
  #
  # Type `mix help deps` for examples and options.
  defp deps do
    [
      {:phoenix, "~> 1.3.0"},
      {:uuid, "~> 1.1"},
      {:distillery, "~> 1.3"},
      {:phoenix_pubsub, "~> 1.0"},
      {:phoenix_ecto, "~> 3.2"},
      {:poison, "~> 3.0", override: true},
      {:phoenix_html, "~> 2.6"},
      {:phoenix_live_reload, "~> 1.0", only: :dev},
      {:gettext, "~> 0.11"},
      {:nebulex, "~> 1.0.0-rc.3"},
      {:erlzord, "~> 1.0"},
      {:cowboy, "~> 1.0"}
    ]
  end

  # Aliases are shortcuts or tasks specific to the current project.
  # For example, to create, migrate and run the seeds file at once:
  #
  #     $ mix ecto.setup
  #
  # See the documentation for `Mix` for more info on aliases.
  defp aliases do
    [
      "ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"],
      "ecto.reset": ["ecto.drop", "ecto.setup"],
      test: ["ecto.create --quiet", "ecto.migrate", "test"]
    ]
  end
end


Replace agent.json with agent.ex in mix.exs

defmodule Mix.Tasks.Compile.Appsignal do
  use Mix.Task

  def run(_args) do
    {_, _} = Code.eval_file("mix_helpers.exs")

    unless Code.ensure_loaded?(Appsignal.Agent) do
      {_, _} = Code.eval_file("agent.ex")
    end

    case Mix.Appsignal.Helper.verify_system_architecture() do
      {:ok, arch} ->
        :ok = Mix.Appsignal.Helper.ensure_downloaded(arch)
        :ok = Mix.Appsignal.Helper.compile
      {:error, {:unsupported, arch}} ->
        Mix.Shell.IO.error("Unsupported target platform #{arch}, AppSignal integration disabled!\nPlease check http://docs.appsignal.com/support/operating-systems.html")
        :ok
    end
  end
end

defmodule Appsignal.Mixfile do
  use Mix.Project

  def project do
    [app: :appsignal,
     version: "0.12.0",
     name: "AppSignal",
     description: description(),
     package: package(),
     source_url: "https://github.com/appsignal/appsignal-elixir",
     homepage_url: "https://appsignal.com",
     test_paths: test_paths(Mix.env),
     elixir: "~> 1.0",
     compilers: compilers(Mix.env),
     elixirc_paths: elixirc_paths(Mix.env),
     deps: deps(),
     docs: [logo: "logo.png", extras: ["Roadmap.md"]]
    ]
  end

  defp description do
    "Collects error and performance data from your Elixir applications and sends it to AppSignal"
  end

  defp package do
    %{files: ["lib", "c_src/*.[ch]", "mix.exs", "mix_helpers.exs",
              "*.md", "LICENSE", "Makefile", "agent.ex"],
      maintainers: ["Arjan Scherpenisse", "Jeff Kreeftmeijer", "Tom de Bruijn"],
      licenses: ["MIT"],
      links: %{"GitHub" => "https://github.com/appsignal/appsignal-elixir"}}
  end

  def application do
    [mod: {Appsignal, []},
     applications: [:logger]]
  end

  defp compilers(:test_phoenix), do: [:phoenix] ++ compilers(:prod)
  defp compilers(_), do: [:appsignal] ++ Mix.compilers

  defp test_paths(:test_phoenix), do: ["test/appsignal", "test/mix", "test/phoenix"]
  defp test_paths(_), do: ["test/appsignal", "test/mix"]

  defp elixirc_paths(env) do
    case test?(env) do
      true -> ["lib", "test/support"]
      false -> ["lib"]
    end
  end

  defp test?(:test), do: true
  defp test?(:test_phoenix), do: true
  defp test?(:test_no_nif), do: true
  defp test?(_), do: false

  defp deps do
    [
      {:httpoison, "~> 0.11"},
      {:decorator, "~> 1.0"},
      {:phoenix, "~> 1.2.0", optional: true, only: [:prod, :test_phoenix]},
      {:mock, "~> 0.2.1", only: [:test, :test_phoenix, :test_no_nif]},
      {:bypass, "~> 0.5", only: [:test, :test_phoenix, :test_no_nif]},
      {:ex_doc, "~> 0.12", only: :dev}
    ]
  end
end

defmodule Mix.Tasks.Compile.Appsignal do
  use Mix.Task

  def run(_args) do
    {_, _} = Code.eval_file("mix_helpers.exs")

    unless Code.ensure_loaded?(Appsignal.Agent) do
      {_, _} = Code.eval_file("agent.ex")
    end

    case Mix.Appsignal.Helper.verify_system_architecture() do
      {:ok, arch} ->
        :ok = Mix.Appsignal.Helper.ensure_downloaded(arch)
        :ok = Mix.Appsignal.Helper.compile
      {:error, {:unsupported, arch}} ->
        Mix.Shell.IO.error("Unsupported target platform #{arch}, AppSignal integration disabled!\nPlease check http://docs.appsignal.com/support/operating-systems.html")
        :ok
    end
  end
end

defmodule Appsignal.Mixfile do
  use Mix.Project

  def project do
    [app: :appsignal,
     version: "0.12.0",
     name: "AppSignal",
     description: description(),
     package: package(),
     source_url: "https://github.com/appsignal/appsignal-elixir",
     homepage_url: "https://appsignal.com",
     test_paths: test_paths(Mix.env),
     elixir: "~> 1.0",
     compilers: compilers(Mix.env),
     elixirc_paths: elixirc_paths(Mix.env),
     deps: deps(),
     docs: [logo: "logo.png", extras: ["Roadmap.md"]]
    ]
  end

  defp description do
    "Collects error and performance data from your Elixir applications and sends it to AppSignal"
  end

  defp package do
    %{files: ["lib", "c_src/*.[ch]", "mix.exs", "mix_helpers.exs",
              "*.md", "LICENSE", "Makefile", "agent.json"],
      maintainers: ["Arjan Scherpenisse", "Jeff Kreeftmeijer", "Tom de Bruijn"],
      licenses: ["MIT"],
      links: %{"GitHub" => "https://github.com/appsignal/appsignal-elixir"}}
  end

  def application do
    [mod: {Appsignal, []},
     applications: [:logger]]
  end

  defp compilers(:test_phoenix), do: [:phoenix] ++ compilers(:prod)
  defp compilers(_), do: [:appsignal] ++ Mix.compilers

  defp test_paths(:test_phoenix), do: ["test/appsignal", "test/mix", "test/phoenix"]
  defp test_paths(_), do: ["test/appsignal", "test/mix"]

  defp elixirc_paths(env) do
    case test?(env) do
      true -> ["lib", "test/support"]
      false -> ["lib"]
    end
  end

  defp test?(:test), do: true
  defp test?(:test_phoenix), do: true
  defp test?(:test_no_nif), do: true
  defp test?(_), do: false

  defp deps do
    [
      {:httpoison, "~> 0.11"},
      {:decorator, "~> 1.0"},
      {:phoenix, "~> 1.2.0", optional: true, only: [:prod, :test_phoenix]},
      {:mock, "~> 0.2.1", only: [:test, :test_phoenix, :test_no_nif]},
      {:bypass, "~> 0.5", only: [:test, :test_phoenix, :test_no_nif]},
      {:ex_doc, "~> 0.12", only: :dev}
    ]
  end
end


Bump version number

Bump version number

defmodule Vutuv.Mixfile do
  use Mix.Project

  def project do
    [app: :vutuv,
     version: "1.0.1",
     elixir: "~> 1.0",
     elixirc_paths: elixirc_paths(Mix.env),
     compilers: [:phoenix, :gettext] ++ Mix.compilers,
     build_embedded: Mix.env == :prod,
     start_permanent: Mix.env == :prod,
     aliases: aliases,
     deps: deps]
  end

  # Configuration for the OTP application.
  #
  # Type `mix help compile.app` for more information.
  def application do
    [mod: {Vutuv, []},
     applications: [:phoenix,
                    :phoenix_pubsub,
                    :phoenix_html,
                    :cowboy,
                    :logger,
                    :gettext,
                    :phoenix_ecto,
                    :ex_machina,
                    :phoenix_html_simplified_helpers,
                    :bamboo,
                    :bamboo_smtp,
                    :mariaex,
                    :httpoison,
                    :slugger,
                    :timex_ecto,
                    :word_smith,
                    :arc,
                    :arc_ecto]]
  end

  # Specifies which paths to compile per environment.
  defp elixirc_paths(:test), do: ["lib", "web", "test/support"]
  defp elixirc_paths(_),     do: ["lib", "web"]

  # Specifies your project dependencies.
  #
  # Type `mix help deps` for examples and options.
  defp deps do
    [{:ecto, "~> 2.0.0", override: true},
     {:bamboo, "~> 0.6"},
     {:bamboo_smtp, "~> 1.1.0"},
     {:phoenix, "~> 1.2.0"},
     {:phoenix_ecto, "~> 3.0.0-rc"},
     {:phoenix_html, "~> 2.8"},
     {:phoenix_live_reload, "~> 1.0", only: :dev},
     {:gettext, "~> 0.12.1"},
     {:cowboy, "~> 1.0"},
     {:arc, "~> 0.5.3"},
     {:arc_ecto, "~> 0.4.2"},
     {:ex_machina, "~> 0.6.1"},
     {:phoenix_html_simplified_helpers, "~> 0.6.0"},
     {:mariaex, ">= 0.0.0"},
     {:word_smith, "~> 0.1.0"},
     {:slugger, "~> 0.1.0"},
     {:httpoison, "~> 0.9.0"},
     {:distillery, "~> 0.10"}]
  end

  # Aliases are shortcut or tasks specific to the current project.
  # For example, to create, migrate and run the seeds file at once:
  #
  #     $ mix ecto.setup
  #
  # See the documentation for `Mix` for more info on aliases.
  defp aliases do
    ["ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"],
     "ecto.reset": ["ecto.drop", "ecto.setup"],
     "test": ["ecto.create --quiet", "ecto.migrate", "test"]]
  end
end

defmodule Vutuv.Mixfile do
  use Mix.Project

  def project do
    [app: :vutuv,
     version: "1.0.0",
     elixir: "~> 1.0",
     elixirc_paths: elixirc_paths(Mix.env),
     compilers: [:phoenix, :gettext] ++ Mix.compilers,
     build_embedded: Mix.env == :prod,
     start_permanent: Mix.env == :prod,
     aliases: aliases,
     deps: deps]
  end

  # Configuration for the OTP application.
  #
  # Type `mix help compile.app` for more information.
  def application do
    [mod: {Vutuv, []},
     applications: [:phoenix,
                    :phoenix_pubsub,
                    :phoenix_html,
                    :cowboy,
                    :logger,
                    :gettext,
                    :phoenix_ecto,
                    :ex_machina,
                    :phoenix_html_simplified_helpers,
                    :bamboo,
                    :bamboo_smtp,
                    :mariaex,
                    :httpoison,
                    :slugger,
                    :timex_ecto,
                    :word_smith,
                    :arc,
                    :arc_ecto]]
  end

  # Specifies which paths to compile per environment.
  defp elixirc_paths(:test), do: ["lib", "web", "test/support"]
  defp elixirc_paths(_),     do: ["lib", "web"]

  # Specifies your project dependencies.
  #
  # Type `mix help deps` for examples and options.
  defp deps do
    [{:ecto, "~> 2.0.0", override: true},
     {:bamboo, "~> 0.6"},
     {:bamboo_smtp, "~> 1.1.0"},
     {:phoenix, "~> 1.2.0"},
     {:phoenix_ecto, "~> 3.0.0-rc"},
     {:phoenix_html, "~> 2.8"},
     {:phoenix_live_reload, "~> 1.0", only: :dev},
     {:gettext, "~> 0.12.1"},
     {:cowboy, "~> 1.0"},
     {:arc, "~> 0.5.3"},
     {:arc_ecto, "~> 0.4.2"},
     {:ex_machina, "~> 0.6.1"},
     {:phoenix_html_simplified_helpers, "~> 0.6.0"},
     {:mariaex, ">= 0.0.0"},
     {:word_smith, "~> 0.1.0"},
     {:slugger, "~> 0.1.0"},
     {:httpoison, "~> 0.9.0"},
     {:distillery, "~> 0.10"}]
  end

  # Aliases are shortcut or tasks specific to the current project.
  # For example, to create, migrate and run the seeds file at once:
  #
  #     $ mix ecto.setup
  #
  # See the documentation for `Mix` for more info on aliases.
  defp aliases do
    ["ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"],
     "ecto.reset": ["ecto.drop", "ecto.setup"],
     "test": ["ecto.create --quiet", "ecto.migrate", "test"]]
  end
end


bumped version

bumped version

defmodule Formex.Mixfile do
  use Mix.Project

  def project do
    [app: :formex,
     version: "0.5.7",
     elixir: "~> 1.3",
     build_embedded: Mix.env == :prod,
     start_permanent: Mix.env == :prod,
     deps: deps(),
     package: package(),
     description: description(),

     docs: [main: "readme",
          extras: ["README.md", "UPGRADE.md"]],
     source_url: "https://github.com/jakub-zawislak/formex",
     elixirc_paths: elixirc_paths(Mix.env)
   ]
  end

  def application do
    []
  end

  defp deps do
    [{:phoenix_html, "~> 2.0"},
     {:ex_doc, ">= 0.0.0", only: :dev},
     {:phoenix, "~> 1.2", only: [:dev, :test]},
    ]
  end

  defp description do
    """
    Form library for Phoenix with Ecto support
    """
  end

  defp package do
    [maintainers: ["Jakub Zawiślak"],
     licenses: ["MIT"],
     files: ~w(lib priv web CHANGELOG.md LICENSE.md mix.exs package.json README.md),
     links: %{github: "https://github.com/jakub-zawislak/formex"}]
  end

  defp elixirc_paths(:test), do: ["lib", "test/support"]
  defp elixirc_paths(_), do: ["lib"]
end

defmodule Formex.Mixfile do
  use Mix.Project

  def project do
    [app: :formex,
     version: "0.5.6",
     elixir: "~> 1.3",
     build_embedded: Mix.env == :prod,
     start_permanent: Mix.env == :prod,
     deps: deps(),
     package: package(),
     description: description(),

     docs: [main: "readme",
          extras: ["README.md", "UPGRADE.md"]],
     source_url: "https://github.com/jakub-zawislak/formex",
     elixirc_paths: elixirc_paths(Mix.env)
   ]
  end

  def application do
    []
  end

  defp deps do
    [{:phoenix_html, "~> 2.0"},
     {:ex_doc, ">= 0.0.0", only: :dev},
     {:phoenix, "~> 1.2", only: [:dev, :test]},
    ]
  end

  defp description do
    """
    Form library for Phoenix with Ecto support
    """
  end

  defp package do
    [maintainers: ["Jakub Zawiślak"],
     licenses: ["MIT"],
     files: ~w(lib priv web CHANGELOG.md LICENSE.md mix.exs package.json README.md),
     links: %{github: "https://github.com/jakub-zawislak/formex"}]
  end

  defp elixirc_paths(:test), do: ["lib", "test/support"]
  defp elixirc_paths(_), do: ["lib"]
end