Packages

`MapBot` builds Elixir Maps/Structs based on factory definitions and attributes.

Current section

Files

Jump to
map_bot lib map_bot.ex
Raw

lib/map_bot.ex

defmodule MapBot do
@moduledoc """
`MapBot` builds Elixir Maps/Structs based on factory definitions and attributes.
## Factories Definition:
Factories are defined in a single module such as:
```elixir
defmodule MyApp.Factory do
def new(:greenish), do: %{color: :green}
def new(:tomato), do: %{name: "Tomato", color: :red}
def new(MapBot.Car), do: %MapBot.Car{model: "SUV", color: :black}
def new(:with_code), do: %{code: &"CODE-\#{&1}"}
end
```
## Configuration:
Then configure `:map_bot` to use that factories definition:
```elixir
config :map_bot, factories: MyApp.Factory
```
And now you can start using the `MapBot.build/2` function.
"""
@type name :: module() | atom()
@type attributes :: map() | keyword()
@type result :: struct() | map()
@doc """
Builds an Elixir Map/Struct.
## Examples
iex> MapBot.build(:tomato)
%{name: "Tomato", color: :red}
iex> MapBot.build(:tomato, color: :green)
%{name: "Tomato", color: :green}
iex> MapBot.build(MapBot.Car, color: :yellow)
%MapBot.Car{model: "SUV", color: :yellow}
iex> MapBot.build(MapBot.Car, %{color: :yellow})
%MapBot.Car{model: "SUV", color: :yellow}
iex> MapBot.build(MapBot.Car, [:greenish, model: "Sport"])
%MapBot.Car{model: "Sport", color: :green}
"""
@spec build(name, attributes) :: result
def build(name, attrs \\ [])
def build(name, %{} = attrs), do: build(name, Map.to_list(attrs))
def build(name, attrs) do
map = Enum.reduce([name | attrs], %{}, &apply_attr/2)
Enum.reduce(Map.to_list(map), map, &apply_sequence/2)
end
defp apply_attr({key, value}, map), do: Map.put(map, key, value)
defp apply_attr(name, map), do: Map.merge(map, factories().new(name))
defp apply_sequence({key, func}, map) when is_function(func) do
value = func.(MapBot.Sequence.next_int())
apply_attr({key, value}, map)
end
defp apply_sequence({_key, _value}, map), do: map
defp factories(), do: Application.get_env(:map_bot, :factories)
end