defmodule Nola.Plugins.TempRefHelper do @moduledoc """ This module allows to easily implement local temporary simple references for easy access from IRC. For example, your plugin output could be acted on, and instead of giving the burden for the user to write or copy that uuid, you could give them a small alphanumeric reference to use instead. You can configure how many and for how long the references are kept. ## Usage `import Irc.Plugin.TempRef` ```elixir defmodule Irc.MyPlugin do defstruct [:temprefs] def init(_) do # … {:ok, %__MODULE__{temprefs: new_temp_refs()} end end ``` """ defstruct [:refs, :max, :expire, :build_fun, :build_increase_fun, :build_options] defmodule SimpleAlphaNumericBuilder do def build(options) do length = Keyword.get(options, :length, 3) for _ <- 1..length, into: "", do: <> end def increase(options) do Keyword.put(options, :length, Keyword.get(options, :length, 3) + 1) end end def new_temp_refs(options \\ []) do %__MODULE__{ refs: Keyword.get(options, :init_refs, []), max: Keyword.get(options, :max, []), expire: Keyword.get(options, :expire, :infinity), build_fun: Keyword.get(options, :build_fun, &__MODULE__.SimpleAlphaNumericBuilder.build/1), build_increase_fun: Keyword.get( options, :build_increase_fun, &__MODULE__.SimpleAlphaNumericBuilder.increase/1 ), build_options: Keyword.get(options, :build_options, length: 3) } end def janitor_refs(state = %__MODULE__{}) do if length(state.refs) > state.max do %__MODULE__{refs: state.refs |> Enum.reverse() |> tl() |> Enum.reverse()} else state end end def put_temp_ref(data, state = %__MODULE__{}) do state = janitor_refs(state) key = new_nonexisting_key(state) if key do ref = {key, DateTime.utc_now(), data} {key, %__MODULE__{state | refs: [ref | state.refs]}} else {nil, state} end end def lookup_temp_ref(key, state, default \\ nil) do case List.keyfind(state.refs, key, 0) do {_, _, data} -> data _ -> default end end defp new_nonexisting_key(state, i) when i > 50 do nil end defp new_nonexisting_key(state = %__MODULE__{refs: refs}, i \\ 1) do build_options = if rem(i, 5) == 0 do state.build_increase_fun.(state.build_options) else state.build_options end key = state.build_fun.(state.build_options) if !List.keymember?(refs, key, 0) do key else new_nonexisting_key(state, i + 1) end end end