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"}
  ]
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.

Existing TypedStruct users

If you already depend on TypedStruct, you will need to remove that dependency from your mix.exs until the plugin system is released. You can track progress here