ExEssentials

ExEssentials logo

Hex versionHex docsLicense

ExEssentials is a powerful utility library for Elixir that serves as a true toolbox — bringing together a collection of generic, reusable, and ready-to-use helpers to accelerate Elixir application development.

Designed with a focus on productivity and organization, it helps you write cleaner, more maintainable code while saving valuable development time.


Features


Installation

The package can be installed by adding ex_essentials to your list of dependencies in mix.exs:

def deps do
[
{:ex_essentials, "~> 0.11.0"}
]
end

Usage

🚀 Core Runner (Flow Builder)

Inspired by Ecto.Multi, the Runner allows you to build fail-fast execution flows with named steps.

alias ExEssentials.Core.Runner
runner =
Runner.new(timeout: 5_000)
|> Runner.put(:user_id, 123)
|> Runner.run(:fetch_user, fn %{user_id: id} -> {:ok, %{id: id, name: "John"}} end)
|> Runner.run_async(:send_email, fn _ -> {:ok, :sent} end)
|> Runner.run(:log_action, fn %{fetch_user: user} -> {:ok, "Logged #{user.name}"} end)
case Runner.finish(runner) do
{:ok, changes} ->
IO.puts("User fetched: #{changes.fetch_user.name}")
{:error, step, reason, changes_before} ->
IO.inspect({step, reason}, label: "Flow failed")
end

🔑 Fingerprint Utilities

Generate SHA-256 fingerprint hashes and compare maps or structs based on a subset of keys.

alias ExEssentials.Core.Fingerprint
# Generate hash for selected fields
user = %{id: 1, name: "Alice", email: "alice@example.com"}
Fingerprint.hash(user, [:id, :email])
# => "..."
# Compare two maps or structs by selected keys
user_a = %{id: 1, name: "Alice", role: :admin}
user_b = %{id: 1, name: "Alice", role: :user}
Fingerprint.equal?(user_a, user_b, [:id, :name]) # => true
Fingerprint.equal?(user_a, user_b, [:id, :role]) # => false

🇧🇷 Brazilian Document Validation

Validate, format, and mask CPF and CNPJ numbers effortlessly. Supports alphanumeric CNPJs and automatic digit extraction.

Validation & Formatting

alias ExEssentials.BrazilianDocument.Validator
alias ExEssentials.BrazilianDocument.Formatter
# Validate CPF or CNPJ (Numeric or Alphanumeric)
Validator.valid?("123.456.789-00") # => true
Validator.valid?("12ABC34501DE35") # => true (New Alphanumeric CNPJ)
# Format for display
Formatter.format("39053344705") # => "390.533.447-05"
Formatter.format("11222333000181") # => "11.222.333/0001-81"
# Mask sensitive data
Formatter.mask("44286185060") # => "***.861.850-**"
Formatter.mask("20495056000171") # => "20.***.***/0001-7*"

Ecto Integration

Easily validate documents in your changesets.

defmodule MyApp.User do
use Ecto.Schema
import Ecto.Changeset
import ExEssentials.BrazilianDocument.Changeset
schema "users" do
field :document, :string
end
def changeset(user, attrs) do
user
|> cast(attrs, [:document])
|> validate_brazilian_document(:document) # Validates CPF or CNPJ
# Or specifically:
# |> validate_cpf(:document)
# |> validate_cnpj(:document)
end
end

🛡 Web Utilities (Plugs & Validators)

Request Validation

A Plug for validating request parameters using Ecto.Changeset. If validation fails, it returns a 400 Bad Request following RFC 7807.

# 1. Define your validator (mapping actions to changesets)
defmodule MyAppWeb.Validators.User do
import Ecto.Changeset
def create(params) do
{%{}, %{name: :string, email: :string}}
|> cast(params, [:name, :email])
|> validate_required([:name, :email])
end
end
# 2. Use the plug in your controller
defmodule MyAppWeb.UserController do
use MyAppWeb, :controller
plug ExEssentials.Web.Plugs.RequestValidator, validator: MyAppWeb.Validators.User
def create(conn, params) do
# Only executes if params are valid
text(conn, "User Created")
end
end

Query Parameter Normalization

Automatically converts query string values (like "true", "null", "123", or lists "1,2,3") into their Elixir types.

# In your endpoint.ex or router pipeline
plug ExEssentials.Web.Plugs.NormalizeQueryParams
# Input: /api/search?active=true&tags=elixir,rust&limit=10
# Output: %{"active" => true, "tags" => ["elixir", "rust"], "limit" => 10}

Feature Flag Route Disabling

Disable specific controller actions based on feature flags (supports FunWithFlags).

# In your controller
plug ExEssentials.Web.Plugs.DisableServices,
disabled_actions: [:delete, :update],
flag_name: :maintenance_mode

📑 XML Utilities

A safe wrapper around Saxy to build XML documents with automatic sanitization.

import ExEssentials.XML
# Automatically escapes <, >, &, etc.
element_sanitize("note", [], ["Some <script> & unsafe content"])
# => {"note", [], ["Some &lt;script&gt; &amp; unsafe content"]}

🗺 Map Utilities

Helpers for transforming and cleaning up map structures.

alias ExEssentials.Core.Map, as: MapUtil
# Renaming keys
map = %{name: "Alice", age: 30}
MapUtil.renake(map, [:name, age: :years])
# => %{name: "Alice", years: 30}
# Cleaning up maps (removing nil or blank values)
MapUtil.compact(%{a: 1, b: nil, c: "", d: [], e: %{}})
# => %{a: 1}

Configuration

You can customize the RequestValidator response format:

config :ex_essentials, :web_request_validator,
json_library: Jason,
error_code: :invalid_parameter,
error_title: "Invalid request parameters"

Contributing

  1. Fork it
  2. Create your feature branch (git checkout -b feature/my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin feature/my-new-feature)
  5. Create a new Pull Request

License

ExEssentials is released under the Apache License 2.0. See the LICENSE.txt file for details.