Packages

A macro module and behaviour with default mapping implementations.

Current section

Files

Jump to
data_mapper lib data_mapper.ex
Raw

lib/data_mapper.ex

defmodule DataMapper do
@moduledoc """
Define behaviours for a data-mapping module.
"""
@callback map_list(input :: any) :: [map()]
@callback map(input :: any) :: map()
@callback map(input :: any, mappings :: map()) :: map()
@callback map_field({key :: atom, value :: any}) :: {key :: atom, value :: any}
@callback pre_map_list_transform(any) :: any
@callback post_map_list_transform(any) :: [map()]
@callback pre_map_transform(any) :: any
@callback post_map_transform(any) :: map()
defmacro __using__(opts) do
mappings = Keyword.fetch!(opts, :mappings)
quote do
@behaviour DataMapper
@doc """
Map a list of inputs.
"""
@impl true
@spec map_list([map() | keyword()]) :: [map()]
def map_list(inputs) do
inputs
|> pre_map_list_transform()
|> Enum.map(&map/1)
|> post_map_list_transform()
end
@doc """
Maps input to the mappings described in the `opts`.
"""
@impl true
@spec map(map() | keyword()) :: map()
def map(input) do
input
|> pre_map_transform()
|> map(unquote(mappings))
|> post_map_transform()
end
## Callbacks
@doc """
Maps the input to the given mappings.
"""
@impl true
@spec map(input :: map() | keyword(), mappings :: map()) :: map()
def map(input, mappings) do
Enum.into(mappings, %{}, fn
# Handle a nested map.
{from_key, sub_mappings} when is_map(sub_mappings) ->
map(input[from_key], sub_mappings)
# The normal case, mapping the input key to the output key.
{from_key, to_key} ->
map_field({to_key, input[from_key]})
end)
end
@doc """
Maps a field.
"""
@impl true
@spec map_field({atom, any}) :: {atom, any}
def map_field(field), do: field
@doc """
Transform the list before mapping.
"""
@impl true
def pre_map_list_transform(input), do: input
@doc """
Transform the list after mapping.
"""
@impl true
def post_map_list_transform(output), do: output
@doc """
Transform the input before mapping.
"""
@impl true
def pre_map_transform(input), do: input
@doc """
Transform the output after mapping.
"""
@impl true
def post_map_transform(output), do: output
defoverridable(
map_list: 1,
map: 1,
map: 2,
map_field: 1,
pre_map_list_transform: 1,
post_map_list_transform: 1,
pre_map_transform: 1,
post_map_transform: 1
)
end
end
end