Packages
track_indices
0.1.0
Document and audit database indices in your Ecto schemas. TrackIndices provides a way to document database indices directly in your schema modules and automatically verify that your documentation matches the actual database indices.
Current section
Files
Jump to
Current section
Files
lib/track_indices.ex
defmodule TrackIndices do
@moduledoc """
Provides index documentation for schema modules.
## Usage
Add `use TrackIndices` to your schema module and define @indices:
defmodule MyApp.MySchema do
use Ecto.Schema
use TrackIndices
@indices [
%{columns: [:user_id], unique: false},
%{columns: [:email], unique: true}
]
schema "my_table" do
# ...
end
end
This will generate a `__indices__/0` function that returns the documented indices.
## Index Format
Each index is a map with the following keys:
* `:columns` - List of column names (atoms)
* `:unique` - Boolean indicating if the index is unique
* `:where` - (Optional) Partial index condition as a string
## Example
@indices [
%{columns: [:email], unique: true},
%{columns: [:event_id, :email], unique: true, where: "email IS NOT NULL"}
]
"""
defmacro __using__(_opts) do
quote do
Module.register_attribute(__MODULE__, :track_indices_table, persist: true)
@before_compile TrackIndices
end
end
defmacro __before_compile__(env) do
indices = Module.get_attribute(env.module, :indices, [])
quote do
@doc """
Returns the documented indices for this schema.
"""
def __indices__ do
unquote(Macro.escape(indices))
end
@doc """
Returns the table name for this schema.
"""
def __table_name__ do
__schema__(:source)
end
end
end
@doc """
Returns all modules that use TrackIndices along with their table names.
"""
def all_schemas do
# Get all loaded applications and their modules
:application.loaded_applications()
|> Enum.flat_map(fn {app, _, _} ->
case :application.get_key(app, :modules) do
{:ok, modules} -> modules
_ -> []
end
end)
|> Enum.filter(fn module ->
Code.ensure_loaded?(module) and function_exported?(module, :__table_name__, 0)
end)
|> Enum.map(fn module ->
{module, module.__table_name__()}
end)
|> Enum.reject(fn {_module, table} -> is_nil(table) end)
end
@doc """
Gets the Ecto repo from application config.
"""
def get_repo do
case Application.get_env(:track_indices, :repo) do
nil -> raise_no_config_error()
repo when is_atom(repo) -> repo
other -> raise_invalid_repo_error(other)
end
end
defp raise_no_config_error do
raise """
No repo configured for TrackIndices.
Please add to your config/config.exs:
config :track_indices, repo: YourApp.Repo
"""
end
defp raise_invalid_repo_error(other) do
raise "Expected :repo to be a module, got: #{inspect(other)}"
end
@doc """
Retrieves documented indices from all schema modules.
Returns a map of table_name => [documented_index]
"""
def get_documented_indices do
all_schemas()
|> Enum.reduce(%{}, fn {module, table_name}, acc ->
indices = module.__indices__()
Map.put(acc, table_name, indices)
end)
end
end