Packages
estructura
0.6.0
1.14.0
1.13.0
1.12.0
1.11.0
1.10.2
1.10.1
1.10.0
1.9.0
1.8.3
1.8.2
1.8.1
1.8.0
1.7.2
1.7.1
1.7.0
1.6.1
1.6.0
1.5.0
1.4.2
1.4.1
1.4.0
1.3.3
1.3.2
1.3.1
1.3.0
1.2.12
1.2.11
1.2.10
1.2.9
1.2.8
1.2.7
1.2.6
1.2.5
1.2.4
1.2.3
1.2.2
1.2.1
1.2.0
1.1.1
1.1.0
1.0.0
0.6.0
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.2
0.4.1
0.4.0
0.3.2
0.3.1
0.3.0
0.2.0
0.1.0
Extensions for Elixir structures.
Current section
Files
Jump to
Current section
Files
lib/estructura/lazy.ex
defmodule Estructura.Lazy do
@moduledoc """
The field stub allowing lazy instantiation of `Estructura` fields.
"""
alias Estructura.Lazy
@type key :: Map.key()
@type value :: Map.value()
@type cached :: {:ok, value()} | {:error, any()}
@type getter :: (value() -> cached()) | (key(), value() -> cached())
@type t :: %{
__struct__: __MODULE__,
expires_in: non_neg_integer() | :instantly | :never,
timestamp: nil | DateTime.t(),
payload: any(),
value: cached(),
getter: getter()
}
defstruct getter: &Estructura.Lazy.id/1,
expires_in: :never,
timestamp: nil,
payload: nil,
value: {:error, :not_loaded},
error: nil
@spec new(getter(), non_neg_integer() | :instantly | :never) :: t()
@doc "Create the new struct with the getter passed as an argument"
def new(getter, expires_in \\ :never) when is_function(getter, 1) or is_function(getter, 2),
do: struct!(__MODULE__, getter: getter, expires_in: expires_in)
@spec apply(t(), %{__lazy_data__: term()} | term(), key()) :: t()
@doc """
Apply the lazy getter to the data passed as an argument
## Examples
iex> lazy = Estructura.Lazy.new(&System.fetch_env/1)
...> Estructura.Lazy.apply(lazy, "LANG").value
System.fetch_env("LANG")
"""
def apply(lazy, lazy_data, key \\ nil)
def apply(%Lazy{expires_in: :never, timestamp: timestamp} = lazy, %{__lazy_data__: _data}, _key)
when not is_nil(timestamp),
do: lazy
def apply(%Lazy{} = lazy, %{__lazy_data__: data}, key) do
if stale?(lazy),
do: %Lazy{lazy | timestamp: DateTime.utc_now(), value: apply_getter(lazy.getter, key, data)},
else: lazy
end
def apply(%Lazy{} = lazy, data, key), do: __MODULE__.apply(lazy, %{__lazy_data__: data}, key)
defp apply_getter(getter, _key, data) when is_function(getter, 1), do: getter.(data)
defp apply_getter(getter, key, data) when is_function(getter, 2), do: getter.(key, data)
@spec stale?(t()) :: boolean()
@doc "Validates if the value is not stale yet according to `expires_in` setting"
def stale?(%Lazy{timestamp: nil}), do: true
def stale?(%Lazy{expires_in: :instantly}), do: true
def stale?(%Lazy{expires_in: :never}), do: false
def stale?(%Lazy{expires_in: expires_in, timestamp: timestamp}) when is_integer(expires_in),
do: DateTime.diff(DateTime.utc_now(), timestamp, :millisecond) > expires_in
@doc false
@spec id(data) :: {:ok, data} when data: value()
def id(data), do: {:ok, data}
@doc false
@spec put(t(), value()) :: t()
def put(lazy, value), do: %Lazy{lazy | timestamp: DateTime.utc_now(), value: {:ok, value}}
defimpl Inspect do
@moduledoc false
import Inspect.Algebra
def inspect(%Lazy{value: {:ok, value}, expires_in: :never}, opts) do
if Keyword.get(opts.custom_options, :lazy_marker, false),
do: concat(["‹✓", to_doc(value, opts), "›"]),
else: to_doc(value, opts)
end
def inspect(%Lazy{value: {:error, error}}, opts),
do: concat(["‹✗", to_doc([error: error], opts), "›"])
def inspect(%Lazy{value: {:ok, value}} = lazy, opts) do
if Lazy.stale?(lazy) do
concat(["‹✗", to_doc(value, opts), "›"])
else
if Keyword.get(opts.custom_options, :lazy_marker, false),
do: concat(["‹✓", to_doc(value, opts), "›"]),
else: to_doc(value, opts)
end
end
end
end