README.md

# ExPirate

Yo-ho-ho! `ExPirate` is a layer on top of the `AgentMap` library that provides
TTL and statistics.

See [documentation](https://hexdocs.pm/ex_pirate) for the details.

## Supported attributes

Each stored value is wrapped in a map ("item"). This item may contain the
following keys ("attributes"):

  * `value: term`;

  * `ttl: pos_integer` — the period (in ms) counted from the insertion or the
    last update during which this item can be used. A default TTL value can be
    given as option to `start/1` or `start_link/1` and updated via `set_prop/3`;

  * `expired?: (item -> boolean)` — indicator of item to be expired (used
    instead of TTL).

— this attributes are user customized. Also, `ExPirate` recognizes and
automatically updates the following properties of item:

  * `inserted_at: integer` — is stamped with
    `System.monotonic_time(:millisecond)` the first time item appears;

  * `updated: pos_integer` (times) — is an update count;

  * `updated_at: integer` — is stamped every time when item is changed;

  * `:used(_at)` — is stamped every time when value is requested;

  * `:missed(_at)` — is stamped every time when value is missing.

By default, `ExPirate` stores only `:inserted_at` (and later `:updated_at`)
attribute for items with TTL. To turn on other statistics provide `attrs:
[:updated | :updated_at | :inserted_at | :used | …]` option on
start (see docs).

```elixir
iex> {:ok, ep} = ExPirate.start() # ttl: ∞
...>
iex> ep
...> |> put(:key, 42)
...> |> get(:key)
42
iex> get(ep, :key, raw: true)
%{value: 42}
#
iex> ep
...> |> put(:key, 24, ttl: 5000)
...> |> get(:key, raw: true)
...> |> Map.keys()
[:ttl, :updated_at, :value]
```

## Expired items

To decide which item is expired, either "time to live" attribute (`:ttl`) is
used or a custom indicator (`:expired?`).

TTL is a period of time in milliseconds, counted *from insert* (`:inserted_at`
attribute) or *from the last update* (`:updated_at`). `ExPirate` will never
return or use an expired item in calculations:

```elixir
iex> {:ok, ep} = ExPirate.start()
...>
iex> ep
...> |> put(:key, 42, ttl: 20)
...> |> fetch(:key)
{:ok, 42}
#
iex> sleep(30)
...>
iex> fetch(ep, :key)
{:error, :expired}
#
iex> put(ep, :key, 43)  # no TTL is given
...>                    #
iex> sleep(30)
iex> fetch(ep, :key)
{:ok, 43}
```

Also, can be used a custom indicator — an unary function that takes item as an
argument:

```elixir
iex> {:ok, ep} =
...>   ExPirate.start(ttl: 20, attrs: [:used])
...>
iex> ep
...> |> put(:key, 42, expired?: & &1[:used] > 1)
...> |> fetch(:key)
{:ok, 42}             # used: 1
#
iex> sleep(50)
iex> fetch(ep, :key)
{:ok, 42}             # used: 2, … still there!
#
iex> sleep(50)        # !
iex> fetch(ep, :key)
{:error, :expired}    # used: 2
```

## Installation

If [available in Hex](https://hex.pm/docs/publish), the package can be installed
by adding `ex_pirate` to your list of dependencies in `mix.exs`:

```elixir
def deps do
  [
    {:ex_pirate, "~> 0.1.0"}
  ]
end
```