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_byandupdated_byfields 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"}
]
endQuick 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
end2. 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
end3. 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
endCustom 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
endDisable 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
endCustom References
defmodule MyApp.Post do
use Ecto.Schema
use EctoIRS.Schema
schema "posts" do
field :title, :string
audits MyApp.User, references: :user_id
end
endAutomatic 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
endPre-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
endMigration 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
endCustom 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
endNullable 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
endAdding 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
endCustom 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
endConfiguration
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.