Packages

Accessor is an Elixir data structure manipulator wrapper

Current section

Files

Jump to
accessor lib accessor.ex
Raw

lib/accessor.ex

defmodule Accessor do
@moduledoc """
Accessor is an Elixir data structure manipulator wrapper
"""
@doc """
Update a value in a very deep nested map or list
iex> Accessor.deep_put(%{a: %{"b" => %{c: 42}}}, [:a, "b", :c], 177)
%{a: %{"b" => %{c: 177}}}
iex> Accessor.deep_put(%{a: %{"b" => %{c: [42, 43, 44]}}}, [:a, "b", :c, 1], 177)
%{a: %{"b" => %{c: [42, 177, 44]}}}
"""
@spec deep_put(map | list, list, any) :: map | list
defdelegate deep_put(data, keys, value), to: Accessor.DeepPut
@doc ~S"""
Map or list value validation. The function is very useful to check a value under a very deep and nested map or list.
Evaluate these following functions:
# This is the spec that will be used as reference to validate data within a map or list
def spec do
%{
[:bio, "name", "first"] => [:is_it_string],
[:bio, :age] => [:is_it_integer],
[:position, 0] => [&is_it_software_engineer/2]
}
end
# This is a custom validator function
def is_it_software_engineer(keys, value) do
if value == "software engineer" do
:ok
else
{:error, "#{Enum.join(keys, ".")} is not a software engineer"}
end
end
These are the results when you call the function:
iex> data =
...> %{
...> bio: %{
...> "name" => %{"first" => "irfan", "last" => "hanif"}, age: 22
...> },
...> position: ["software engineer", "junior architect"]
...> }
iex> Accessor.validation(data, spec)
:ok
iex> data =
...> %{
...> bio: %{
...> "name" => %{"first" => 112548, "last" => "hanif"}, age: 22
...> },
...> position: ["software developer", "junior architect"]
...> }
iex> Accessor.validation(data, spec)
{:error, ["bio.name.first is not a string", "position.0 is not a software engineer"]}
This is a list of built-in validator function which you can use directly:
- `:is_it_string` to check whether a value is in the form of string
- `:is_it_integer` to check whether a value is in the form of integer
- `:is_it_boolean` to check whether a value is in the form of boolean
As you can see, it is possible for you to use your own custom validator. If you choose to make your own validator,
the function must be capable of receiving two arguments `keys` and `value` and two returns possibilities: `:ok` or `{:error, error_message}`.
At example above, `is_it_software_engineer/2` is a custom function validator.
"""
@spec validation(map | list, map) :: :ok | {:error, [String.t]}
defdelegate validation(data, spec), to: Accessor.Validation
@doc """
Get a value from a very deep nested map or list
iex> Accessor.deep_get(%{bio: %{name: %{first: "irfan", last: "hanif"}, age: 22}, position: "software engineer"}, [:bio, :name, :last])
"hanif"
iex> Accessor.deep_get(%{bio: %{name: %{first: "irfan", last: "hanif"}, age: 22}, position: "software engineer"}, [:bio, :name, :middle], "power ranger")
"power ranger"
"""
@spec deep_get(map | list, list, any) :: any
defdelegate deep_get(data, keys, default \\ nil), to: Accessor.DeepGet
end