Constructor

An Elixir DSL for defining and validating structs.

Usage

defmodule ConstructorExampleUser do
use Constructor
constructor do
field :id, :integer, constructor: &is_integer/1, enforce: true
field :role, :user | :admin, constructor: &is_valid_role/1, enforce: true
field :first_name, :string, default: "", constructor: &is_string/1
field :last_name, :string, default: "", constructor: &is_string/1
end
def is_valid_role(value) do
case value do
:admin -> {:ok, value}
:user -> {:ok, value}
_ -> {:error, "invalid role!"}
end
end
end
iex> ConstructorExampleUser.new(id: "foo", role: :admin, first_name: 37)
{:error, {:constructor, %{id: "must be an integer", first_name: "must be an integer"}}}
iex> ConstructorExampleUser.new(id: 12, role: :admin, first_name: "Chris")
{:ok, %ConstructorExampleUser{id: 12, first_name: "Chris", last_name: ""}}
iex> ConstructorExampleUser.new!(id: 12, role: :admin, first_name: "Chris")
%ConstructorExampleUser{id: 12, first_name: "Chris", last_name: ""}

Check out the docs to learn more.

Installation

Add constructor to your list of dependencies in mix.exs:

def deps do
[
{:constructor, "~> 1.0.0-rc.6"}
]
end

Documentation can be generated with ExDoc and published on HexDocs. Once published, the docs can be found at https://hexdocs.pm/constructor.

But why?

Before writing the first iteration of this, I was using Vex for projects that didn't have an Ecto dependency, or Ecto.Changeset if I did. It worked, but there were a couple of issues.

  1. Vex has some performance issues that seem difficult to resolve in a way that won't break existing users. More concerning, it seems like Vex is without a clear maintainer.
  2. You can do most of what Constructor does with Ecto.Changesets, but you bring a lot of ORM baggage along with it. Much of it may not be applicable if your project is not using an RDBMS. Constructor provides a richer and more concise way of doing validations and type casting

Acknowledgments

This library was born from the lack of a lightweight and flexible validation library in Elixir. However, the design of the constructor/2 macro and indeed much of the functionality is provided by the excellent TypedStruct library.