Packages

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

Current section

12 Versions

Jump to

Compare versions

6 files changed
+113 additions
-8 deletions
  @@ -1,5 +1,19 @@
1 1 # Used by "mix format"
2 2 [
3 3 inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"],
4 - import_deps: [:ecto]
4 + import_deps: [:ecto],
5 + locals_without_parens: [
6 + # Partial
7 + defpartial: 2,
8 + # Schema
9 + field!: 1,
10 + field!: 2,
11 + field!: 3,
12 + embeds_one!: 2,
13 + embeds_one!: 3,
14 + embeds_one!: 4,
15 + embeds_many!: 2,
16 + embeds_many!: 3,
17 + embeds_many!: 4
18 + ]
5 19 ]
  @@ -6,7 +6,7 @@
6 6 [![Twitter Follow](https://img.shields.io/twitter/follow/ac_alejos?style=social)](https://twitter.com/ac_alejos)
7 7 <!-- BEGIN MODULEDOC -->
8 8
9 - Practical [`Ecto`](https://github.com/elixir-ecto/ecto) `embedded_schema`s for data validation, coercion, and manipulation.
9 + Declarative [`Ecto`](https://github.com/elixir-ecto/ecto) `embedded_schema`s for data validation, coercion, and manipulation.
10 10
11 11 ## Feature Overview
  @@ -1,8 +1,8 @@
1 1 {<<"links">>,[{<<"GitHub">>,<<"https://github.com/acalejos/flint">>}]}.
2 2 {<<"name">>,<<"flint">>}.
3 - {<<"version">>,<<"0.1.0">>}.
3 + {<<"version">>,<<"0.2.0">>}.
4 4 {<<"description">>,
5 - <<"Practical Ecto embedded schemas for data validation, coercion, and manipulation.">>}.
5 + <<"Declarative Ecto embedded schemas for data validation, coercion, and manipulation.">>}.
6 6 {<<"elixir">>,<<"~> 1.14">>}.
7 7 {<<"app">>,<<"flint">>}.
8 8 {<<"licenses">>,[<<"MIT">>]}.
  @@ -19,6 +19,7 @@
19 19 {<<"repository">>,<<"hexpm">>}]]}.
20 20 {<<"files">>,
21 21 [<<"lib">>,<<"lib/schema.ex">>,<<"lib/flint.ex">>,<<"lib/flint">>,
22 - <<"lib/flint/types">>,<<"lib/flint/types/union.ex">>,<<"lib/pipeline.ex">>,
23 - <<".formatter.exs">>,<<"mix.exs">>,<<"README.md">>,<<"LICENSE.md">>]}.
22 + <<"lib/flint/types">>,<<"lib/flint/types/union.ex">>,
23 + <<"lib/flint/partial.ex">>,<<"lib/pipeline.ex">>,<<".formatter.exs">>,
24 + <<"mix.exs">>,<<"README.md">>,<<"LICENSE.md">>]}.
24 25 {<<"build_tools">>,[<<"mix">>]}.
  @@ -23,6 +23,7 @@ defmodule Flint do
23 23 prelude =
24 24 quote do
25 25 alias Flint.Types.Union
26 + import Flint.Partial
26 27
27 28 @after_compile Flint.Schema
  @@ -0,0 +1,89 @@
1 + defmodule Flint.Partial do
2 + @moduledoc """
3 + Can be `use`d to create an `Ecto.ParameterizedType` based on another `Ecto.ParameterizedType`,
4 + applying default parameters. You may supply any number of default parameters. This essentially provides a new
5 + `init/1` implementation for the type, supplying the default values, while not affecting any of the
6 + other `Ecto.ParameterizedType` callbacks.
7 +
8 + You may still override the newly set defaults at the local level.
9 +
10 + You may override any of the inherited `Ecto.ParameterizedType` callbacks inherity from the extended module
11 + in the case that you wish to customize the module further.
12 +
13 + ## Examples
14 +
15 + ``` elixir
16 + defmodule Category do
17 + use Flint.Partial, extends: Ecto.Enum, values: [:folder, :file]
18 + end
19 + ```
20 +
21 + This will apply default `values` to `Ecto.Enum` when you supply a `Cateogory` type
22 + to an Ecto schema. You may still override the values if you supply the `:values`
23 + option for the field.
24 + """
25 + defmacro __using__(opts) do
26 + {extends, opts} = Keyword.pop!(opts, :extends)
27 + extends = Macro.expand_literals(extends, __CALLER__)
28 +
29 + unless Code.ensure_loaded?(extends) do
30 + raise ArgumentError, "Cannot load module #{inspect(extends)}!"
31 + end
32 +
33 + callbacks =
34 + Enum.map(
35 + [
36 + {:autogenerate, 1},
37 + {:embed_as, 2},
38 + {:format, 1},
39 + {:type, 1},
40 + {:cast, 2},
41 + {:load, 3},
42 + {:dump, 3},
43 + {:equal?, 3}
44 + ],
45 + fn {name, arity} ->
46 + if function_exported?(extends, name, arity) do
47 + args = Macro.generate_arguments(arity, nil)
48 +
49 + quote do
50 + defdelegate unquote(name)(unquote_splicing(args)), to: unquote(extends)
51 + defoverridable [{unquote(name), unquote(arity)}]
52 + end
53 + end
54 + end
55 + )
56 + |> Enum.filter(& &1)
57 +
58 + quote do
59 + use Ecto.ParameterizedType
60 +
61 + def init(opts) do
62 + opts = opts ++ unquote(opts)
63 + unquote(extends).init(opts)
64 + end
65 +
66 + unquote_splicing(callbacks)
67 + end
68 + end
69 +
70 + @doc """
71 + A shorthand for creating a new module to represent a partial `Ecto.ParameterizedType`. This is equivalent to
72 + creating a new module with name `module` and calling `use Flint.Partial` and passing `opts`.
73 +
74 + ## Example
75 +
76 + ```elixir
77 + defpartial Vehicle, extends: Ecto.Enum, values: [:car, :motorcycle, :truck]
78 + ```
79 + """
80 + def defpartial(module, opts) do
81 + quoted =
82 + quote do
83 + require Flint.Partial
84 + Flint.Partial.__using__(unquote(opts))
85 + end
86 +
87 + Module.create(module, quoted, Macro.Env.location(__ENV__))
88 + end
89 + end
Loading more files…