EctoIRS
View SourceEctoIRS is an Elixir library that provides easy auditing capabilities for Ecto schemas and migrations. The library allows developers to automatically track who created or modified database records.
Features
- 🔍 Automatic Audit Fields: Automatically add
inserted_by
andupdated_by
fields to your schemas - 🗃️ Migration Helpers: Easy-to-use migration functions for adding audit columns
- ⚙️ Flexible Configuration: Customize field names, references, and behavior
- 🚀 Auto-population: Support for automatic field population via MFA tuples
- 📦 Zero Dependencies: Built on top of Ecto with minimal external dependencies
Installation
Add ecto_irs
to your list of dependencies in mix.exs
:
def deps do
[
{:ecto_irs, "~> 0.1.0"}
]
end
Quick Start
1. Add Audit Columns to Your Database
In your migration:
defmodule MyApp.Repo.Migrations.CreatePosts do
use Ecto.Migration
import EctoIRS.Migration
def change do
create table(:posts) do
add :title, :string
add :content, :text
# Adds :inserted_by_id and :updated_by_id columns
audits :users
timestamps()
end
end
end
2. Add Audit Fields to Your Schema
In your schema:
defmodule MyApp.Post do
use Ecto.Schema
use EctoIRS.Schema
schema "posts" do
field :title, :string
field :content, :text
# Generates :inserted_by and :updated_by associations
audits MyApp.User
timestamps()
end
end
3. Query with Audit Information
# Preload audit associations
post = MyApp.Repo.get(MyApp.Post, 1) |> MyApp.Repo.preload([:inserted_by, :updated_by])
# Access audit information
IO.puts "Created by: #{post.inserted_by.name}"
IO.puts "Last updated by: #{post.updated_by.name}"
Schema Usage
Basic Usage
defmodule MyApp.Post do
use Ecto.Schema
use EctoIRS.Schema
schema "posts" do
field :title, :string
audits MyApp.User
timestamps()
end
end
Custom Field Names
defmodule MyApp.Post do
use Ecto.Schema
use EctoIRS.Schema
schema "posts" do
field :title, :string
audits MyApp.User,
inserted_by: :created_by,
updated_by: :modified_by
end
end
Disable Specific Fields
defmodule MyApp.Post do
use Ecto.Schema
use EctoIRS.Schema
schema "posts" do
field :title, :string
# Only track who created, not who updated
audits MyApp.User, updated_by: false
end
end
Custom References
defmodule MyApp.Post do
use Ecto.Schema
use EctoIRS.Schema
schema "posts" do
field :title, :string
audits MyApp.User, references: :user_id
end
end
Automatic Field Population
defmodule MyApp.Post do
use Ecto.Schema
use EctoIRS.Schema
schema "posts" do
field :title, :string
audits MyApp.User, autogenerate: {MyApp.Context, :current_user_id, []}
end
end
Pre-configuration
defmodule MyApp.Post do
use Ecto.Schema
use EctoIRS.Schema
@audits_opts [autogenerate: {MyApp.Context, :current_user_id, []}]
schema "posts" do
field :title, :string
audits MyApp.User
end
end
Migration Usage
Basic Migration
defmodule MyApp.Repo.Migrations.CreatePosts do
use Ecto.Migration
import EctoIRS.Migration
def change do
create table(:posts) do
add :title, :string
audits :users
timestamps()
end
end
end
Custom Column Names
defmodule MyApp.Repo.Migrations.CreatePosts do
use Ecto.Migration
import EctoIRS.Migration
def change do
create table(:posts) do
add :title, :string
audits :users,
inserted_by: :created_by,
updated_by: :modified_by
timestamps()
end
end
end
Nullable Columns
defmodule MyApp.Repo.Migrations.CreatePosts do
use Ecto.Migration
import EctoIRS.Migration
def change do
create table(:posts) do
add :title, :string
audits :users, null: true
timestamps()
end
end
end
Adding to Existing Tables
defmodule MyApp.Repo.Migrations.AddAuditsToExistingTable do
use Ecto.Migration
import EctoIRS.Migration
def change do
alter table(:existing_posts) do
audits :users
end
end
end
Custom Reference Options
defmodule MyApp.Repo.Migrations.CreatePosts do
use Ecto.Migration
import EctoIRS.Migration
def change do
create table(:posts) do
add :title, :string
audits :users,
column: :user_id,
on_delete: :nilify_all,
on_update: :update_all
timestamps()
end
end
end
Configuration
Repository Configuration
Configure default audit options at the repository level:
config :my_app, MyApp.Repo,
migration_audits: [
inserted_by: :created_by,
updated_by: :modified_by,
null: false
]
Schema Configuration Options
inserted_by
: The field name for insertion audit (default::inserted_by
)updated_by
: The field name for update audit (default::updated_by
)references
: The field on the referenced table (default::id
)autogenerate
: MFA tuple for automatic field population
Migration Configuration Options
inserted_by
: Column name prefix for insertion audit (default::inserted_by
)updated_by
: Column name prefix for update audit (default::updated_by
)null
: Whether columns accept null values (default:false
)
Best Practices
1. Add Indexes for Performance
create index(:posts, [:inserted_by_id])
create index(:posts, [:updated_by_id])
2. Use Consistent Naming
Stick to either the default names or establish a consistent naming convention across your application.
3. Consider Nullable Columns
Decide whether audit fields should be required based on your application's security requirements.
4. Preload Associations
Always preload audit associations when you need to access the audit information:
posts = MyApp.Repo.all(MyApp.Post) |> MyApp.Repo.preload([:inserted_by, :updated_by])
Development
Running Tests
mix test
Code Quality
mix credo # Run static code analysis
mix dialyzer # Run type checking
Documentation
mix docs # Generate documentation
Contributing
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature
) - Commit your changes (
git commit -m 'Add some amazing feature'
) - Push to the branch (
git push origin feature/amazing-feature
) - Open a Pull Request
License
This project is licensed under the MIT License - see the LICENSE file for details.