Current section
12 Versions
Jump to
Current section
12 Versions
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 | [](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…