Packages

Declarative Ecto embedded schemas for data validation, coercion, and manipulation.

Current section

Files

Jump to
flint lib extension.ex
Raw

lib/extension.ex

defmodule Flint.Extension do
@moduledoc """
`Flint` extensions allow developers to easily hook into `Flint` metaprogramming lifecycle to add extra data into the embedded
schema reflection functions.
Flint currently offers three ways to extend behavior:
1. Schema-level attributes
2. Field-level additional options
3. Injected Code
Extension authors define what fields / options / attributes `Flint` should look for in the module / schema definition and
strip out and store in a schema reflection function, but it is still the resposibility of either the extension author or
the end user to make use of the stored information.
## Schema-Level Attributes
These are simply module attributes that are pre-registered with `Flint`, and can be given a default value
as well as a validation function. When you use an extension that registers an attribute, then a new `__schema__`
reflection function is added for each attribute name, with the attribute name as the argument.
**Note that the validation occurs at compile time**
### Example
Given the following extension:
```elixir
defmodule Returnable do
use Flint.Extension
attribute :returns, validator: fn returns -> is_binary(returns) end
end
```
And the schema
```elixir
defmodule Schema do
use Flint.Schema, extensions: [Returnable]
@returns "something"
embedded_schema do
...
end
end
```
Then you can reflect on this new attribute with:
```elixir
Schema.__schema__(:returns)
```
## Field-Level Options
You can also register additional field-level keyword options to be consumed in a downstream function.
These function similarly to the built-in extra options that `Flint` provides, where the options are
stripped and stored in a module attribute (and subsequently in a `__schema__` reflection function)
before passing the valid `Ecto.Schema` options to `Ecto` itself.
**Note that the validation occurs at compile time**
### Example
Given the following extension that enables Go-like JSON marshalling options:
```elixir
defmodule JSON do
use Flint.Extension
option :name, required: false, validator: &is_binary/1
option :omitempty, required: false, default: false, validator: &is_boolean/1
option :ignore, required: false, default: false, validator: &is_boolean/1
end
```
And the following schema:
```elixir
defmodule Schema do
use Flint.Schema, extensions: [JSON]
embedded_schema do
field :myfield, :string, name: "my_field", omitempty: true
end
end
```
Then you can access these specific fields with:
```elixir
Schema.__schema__(:extra_options)
```
```elixir
[
myfield: [ignore: false, omitempty: true, name: "my_field"],
]
```
## Injected Code
Lastly, extensions allow you to define custom `__using__/1` macros which will be passed through
to the target schema module. This is one of the core functionalities of extensions, and works the
same as you would normally `use` a module, and helps compartmentalize similar functionality.
## Default Extensions
By default, `Flint` will enable the following extensions:
* `Flint.Extensions.PreTransforms`,
* `Flint.Extensions.When`,
* `Flint.Extensions.EctoValidations`,
* `Flint.Extensions.PostTransforms`,
* `Flint.Extensions.Accessible`,
* `Flint.Extensions.Embedded`,
* `Flint.Extensions.JSON`
If you want to pass your own list of extensions for a module, you will need to explicitly pass the defaults
as well if you would like to keep them. You can use the convenience `Flint.default_extensions/0` constant
if you want to include all of the defaults.
"""
use Spark.Dsl,
many_extension_kinds: [:extensions],
default_extensions: [extensions: Flint.Extension.Dsl]
defmacro __using__(opts) do
quote do
unquote_splicing(super(opts))
defmacro __using__(opts) do
quote do
end
end
defoverridable __using__: 1
@doc false
def option_names(),
do: Spark.Dsl.Extension.get_entities(__MODULE__, :options) |> Enum.map(& &1.name)
@doc false
def attribute_names(),
do: Spark.Dsl.Extension.get_entities(__MODULE__, :attributes) |> Enum.map(& &1.name)
end
end
end