Ootempl.DataAccess (ootempl v0.3.0)
Provides data access for nested Elixir data structures with case-insensitive key matching.
This module enables retrieval of values from nested maps and lists using dot notation paths, with automatic case-insensitive key matching and type conversion to strings.
Examples
iex> data = %{"name" => "John", "age" => 30}
iex> Ootempl.DataAccess.get_value(data, ["name"])
{:ok, "John"}
iex> data = %{"customer" => %{"name" => "Jane"}}
iex> Ootempl.DataAccess.get_value(data, ["customer", "name"])
{:ok, "Jane"}
iex> data = %{"count" => 42}
iex> Ootempl.DataAccess.get_value(data, ["count"])
{:ok, "42"}
iex> data = %{"items" => [%{"price" => 99.99}]}
iex> Ootempl.DataAccess.get_value(data, ["items", "0", "price"])
{:ok, "99.99"}Case-Insensitive Matching
The module matches keys case-insensitively, so {{Name}}, {{name}}, and {{NAME}}
all match the same data key:
iex> data = %{"name" => "John"}
iex> Ootempl.DataAccess.get_value(data, ["Name"])
{:ok, "John"}If multiple case variants exist, an error is returned:
iex> data = %{"name" => "John", "Name" => "Jane"}
iex> Ootempl.DataAccess.get_value(data, ["name"])
{:error, {:ambiguous_key, "name", ["Name", "name"]}}
Summary
Functions
Retrieves the raw (un-stringified) value from nested data using a path.
Retrieves a value from nested data using a path with case-insensitive key matching.
Converts a resolved value to its string representation for the document.
Types
@type error_reason() :: {:path_not_found, path()} | {:ambiguous_key, String.t(), [String.t() | atom()]} | {:conflicting_key_types, String.t(), atom(), String.t()} | {:invalid_index, String.t()} | {:index_out_of_bounds, non_neg_integer(), non_neg_integer()} | {:not_a_list, term()} | :nil_value | :unsupported_type
@type path() :: [String.t()]
Functions
@spec get_raw_value(data(), path()) :: {:ok, term()} | {:error, error_reason()}
Retrieves the raw (un-stringified) value from nested data using a path.
Unlike get_value/2, this returns the value with its original Elixir type
intact (e.g. a %Date{} stays a %Date{}, a number stays a number, nil
is returned as {:ok, nil}). This is used by the filter pipeline so that
filters can operate on real types before the value is converted to a string.
Parameters
data- The data structure to traverse (map or list)path- List of path segments to navigate
Returns
{:ok, term}- The raw value{:error, reason}- Error with details about what went wrong
Examples
iex> Ootempl.DataAccess.get_raw_value(%{"count" => 5}, ["count"])
{:ok, 5}
iex> Ootempl.DataAccess.get_raw_value(%{"name" => nil}, ["name"])
{:ok, nil}
iex> Ootempl.DataAccess.get_raw_value(%{}, ["missing"])
{:error, {:path_not_found, ["missing"]}}
@spec get_value(data(), path()) :: {:ok, String.t()} | {:error, error_reason()}
Retrieves a value from nested data using a path with case-insensitive key matching.
Returns {:ok, value} where the value is converted to a string, or {:error, reason}
if the path cannot be resolved.
Parameters
data- The data structure to traverse (map or list)path- List of path segments to navigate (e.g.,["customer", "name"])
Returns
{:ok, string}- The value converted to a string{:error, reason}- Error with details about what went wrong
Examples
iex> Ootempl.DataAccess.get_value(%{"name" => "John"}, ["name"])
{:ok, "John"}
iex> Ootempl.DataAccess.get_value(%{"count" => 5}, ["count"])
{:ok, "5"}
iex> Ootempl.DataAccess.get_value(%{"active" => true}, ["active"])
{:ok, "true"}
iex> Ootempl.DataAccess.get_value(%{}, ["missing"])
{:error, {:path_not_found, ["missing"]}}
Converts a resolved value to its string representation for the document.
Every value type we support has a sensible default rendering, so a value used without a formatting filter still produces output:
- binaries are used as-is
- numbers and booleans are stringified
Date,Time,NaiveDateTime, andDateTimeuse ISO-style defaults (matching thedate/time/datetimefilters)- any other struct implementing
String.Chars(e.g.Decimal) uses it
Returns {:error, :nil_value} for nil, and {:error, :unsupported_type}
for values with no string representation (maps, lists, structs that don't
implement String.Chars) — these usually indicate a path that stopped short
of a leaf value.