lib/adminable.ex

defmodule Adminable do
  @moduledoc """
  Behaviour to capture how to build admin interfaces and which fields to allow to edit

  ## Configuration

  - Add `use Adminable` to your Ecto Schema. Optionally

  ```elixir
  defmodule MyApp.User do
    use Ecto.Schema
    import Ecto.{Query, Changeset}, warn: false
    use Adminable

    ...
  end
  ```

  - optionally implement `fields/0`, `create_changeset/2` and `edit_changeset/2`

  - Forward to `Adminable.Router`

  ```elixir
  scope "/admin" do
    pipe_through [:browser, :my, :other, :pipelines]

    forward("/", Adminable.Plug, [
      otp_app: :my_app,
      repo: MyApp.Repo,
      schemas: [MyApp.User],
      view_module: MyAppWeb.Adminable.AdminView
      layout: {MyAppWeb.LayoutView, "app.html"}
    ])
  end
  ```

  Arguments

  * `otp_app` - Your app
  * `repo` - Your app's Repo
  * `schemas` - The schemas to make Admin sections for
  * `view_module` - (Optional) The view_module to use to display pages. Uses Adminable's view module by default. You can export the view to modify using `mix adminable.gen.view MyWebModule`
  * `layout` - (Optional) The layout to use
  """

  @doc """
  A list of fields for to show and edit in Adminable. The primary key will be excluded from
  create and edit forms
  """
  @callback fields() :: [atom()]

  @doc """
  Returns a changeset used for creating new schemas
  """
  @callback create_changeset(any(), any()) :: Ecto.Changeset.t()

  @doc """
  Returns a changeset used for editing existing schemas
  """
  @callback edit_changeset(any(), any()) :: Ecto.Changeset.t()

  defmacro __using__(_) do
    quote do
      @behaviour Adminable

      def fields() do
        __MODULE__.__schema__(:fields)
      end

      def create_changeset(schema, data) do
        __MODULE__.changeset(schema, data)
      end

      def edit_changeset(schema, data) do
        __MODULE__.changeset(schema, data)
      end

      defoverridable fields: 0, create_changeset: 2, edit_changeset: 2
    end
  end
end