View Source Bonfire.Common.Utils (Bonfire v0.9.11-social-beta.6)

Various very commonly used utility functions for the Bonfire application.

This module should contain only a few generic and/or heavily-used functions, and any other functions should be in more specific modules (or in other extensions altogether) for e.g.:

We may also want to consider reusing functions from existing utils libraries when possible and contributing missing ones there, for example:

Summary

Functions

Runs a function asynchronously in a Task. Simply a shorthand for calling functions in Task and Task.Supervisor but with support for multi-tenancy in the spawned process.

Runs a function asynchronously using Task.Supervisor. This is similar to apply_task/3 but specifically uses Task.Supervisor for supervision.

Returns the current account from socket, assigns, or options.

Returns a list of current account IDs and/or user IDs.

(Re)authenticates the current account using the provided password.

Returns the current account ID from socket, assigns, or options.

Returns the current user from socket, assigns, or options.

(Re)authenticates the current user using the provided password.

Returns the current user ID from socket, assigns, or options.

Ensures that the current user is present and raises an exception if not logged in.

Checks if the given value is nil, an empty enumerable, or an empty string.

Applies the given function if the first parameter is not nil.

Helper for calling hypothetical functions another modules.

Returns the value of a key from options keyword list or map, or a fallback if not present or empty.

Checks if the given value is nil, false, 0, or an empty value (using empty?/1).

Unwraps an {:ok, val} tuple, returning the value. If not OK, returns a fallback value (default is nil).

Rounds a number and uses Bonfire.Common.Localise.Cldr.Number.to_string/2 function to format into a human readable string.

Rounds a number to the nearest specified target.

Converts a map, user, socket, tuple, etc, to a keyword list for standardised use as function options.

Functions

apply_error(error, args, opts)

apply_task(function \\ :async, fun, opts \\ [])

Runs a function asynchronously in a Task. Simply a shorthand for calling functions in Task and Task.Supervisor but with support for multi-tenancy in the spawned process.

  • Task.async/1 the caller creates a new process links and monitors it. Once the task action finishes, a message is sent to the caller with the result. Task.await/2 is used to read the message sent by the task. When using async, you must await a reply as they are always sent.

  • Task.start_link/1 is suggested instead if you are not expecting a reply. It starts a statically supervised task as part of a supervision tree, linked to the calling process (meaning it will be stopped when the caller stops).

  • Task.start/1 can be used for fire-and-forget tasks, like side-effects, when you have no interest on its results nor if it completes successfully (because if the server is shut down it won't be restarted).

For more serious tasks, consider using Oban or apply_task_supervised/3 for supervised tasks when possible:

Parameters

  • function: The type of task to start (e.g. :async, :start_link, or :start).
  • fun: The function to execute async.
  • opts: Options for task execution, including:
    • :module - The module to use for task execution (defaults to Task).

Examples

> apply_task(:async, fn -> IO.puts("Async task") end)
# Output: "Async task"

> {apply_task(:start, fn -> IO.puts("Fire-and-forget task") end)
# Output: "Fire-and-forget task"

> apply_task(:start_link, fn -> IO.puts("Supervised task") end)
# Output: "Supervised task"

apply_task_supervised(supervisor, fun, opts \\ [])

Runs a function asynchronously using Task.Supervisor. This is similar to apply_task/3 but specifically uses Task.Supervisor for supervision.

Parameters

  • module: The supervisor module to use for task execution
  • fun: The function to execute async
  • opts: Options for task execution, including:
    • :function - The Task.Supervisor function to use for task execution (defaults to :async).

Examples

> apply_task_supervised(MySupervisor, fn -> IO.puts("Supervised async task") end)
** (EXIT) no process: the process is not alive or there's no process currently associated with the given name, possibly because its application isn't started
# because `MySupervisor` is not defined and/or started ^

common_utils(opts \\ [])

(macro)

current_account(current_account_or_socket_or_opts, recursing \\ false)

Returns the current account from socket, assigns, or options.

This function traverses various possible structures to find and return the current account.

Examples

iex> Bonfire.Common.Utils.current_account(%{current_account: %{id: "account1"}})
%{id: "account1"}

iex> Bonfire.Common.Utils.current_account(%{assigns: %{current_account: %{id: "account2"}}})
%{id: "account2"}

iex> Bonfire.Common.Utils.current_account(%{socket: %{assigns: %{current_account: %{id: "account3"}}}})
%{id: "account3"}

iex> Bonfire.Common.Utils.current_account([current_account: %{id: "account4"}])
%{id: "account4"}

current_account_and_or_user_ids(assigns)

Returns a list of current account IDs and/or user IDs.

This function returns a keyword list with the current account IDs and/or user IDs.

Examples

iex> Bonfire.Common.Utils.current_account_and_or_user_ids(%{current_account_id: "2CC0VNTSARE1S01AT10NGR0VPS", current_user_id: "5EVSER1S0STENS1B1YHVMAN01D"})
[account: "2CC0VNTSARE1S01AT10NGR0VPS", user: "5EVSER1S0STENS1B1YHVMAN01D"]

iex> Bonfire.Common.Utils.current_account_and_or_user_ids(%{current_account: %{id: "2CC0VNTSARE1S01AT10NGR0VPS"}, current_user: %{id: "5EVSER1S0STENS1B1YHVMAN01D"}})
[account: "2CC0VNTSARE1S01AT10NGR0VPS", user: "5EVSER1S0STENS1B1YHVMAN01D"]

iex> Bonfire.Common.Utils.current_account_and_or_user_ids(%{current_account_id: "2CC0VNTSARE1S01AT10NGR0VPS"})
[account: "2CC0VNTSARE1S01AT10NGR0VPS"]

iex> Bonfire.Common.Utils.current_account_and_or_user_ids(%{current_user_id: "5EVSER1S0STENS1B1YHVMAN01D"})
[user: "5EVSER1S0STENS1B1YHVMAN01D"]

iex> Bonfire.Common.Utils.current_account_and_or_user_ids(%{})
[]

current_account_auth!(context, password)

(Re)authenticates the current account using the provided password.

Raises an exception if the credentials are invalid.

Examples

> Bonfire.Common.Utils.current_account_auth!(%{current_account: %{id: "2CC0VNTSARE1S01AT10NGR0VPS"}}, "wrong-password")
** (Bonfire.Fail.Auth) We couldn't find an account with the details you provided.

current_account_id(current_account_or_socket_or_opts, recursing \\ false)

Returns the current account ID from socket, assigns, or options.

This function traverses various possible structures to find and return the current account ID.

Examples

iex> Bonfire.Common.Utils.current_account_id(%{current_account_id: "2CC0VNTSARE1S01AT10NGR0VPS"})
"2CC0VNTSARE1S01AT10NGR0VPS"

iex> Bonfire.Common.Utils.current_account_id(%{assigns: %{current_account_id: "2CC0VNTSARE1S01AT10NGR0VPS"}})
"2CC0VNTSARE1S01AT10NGR0VPS"

iex> Bonfire.Common.Utils.current_account_id(%{socket: %{assigns: %{current_account_id: "2CC0VNTSARE1S01AT10NGR0VPS"}}})
"2CC0VNTSARE1S01AT10NGR0VPS"

iex> Bonfire.Common.Utils.current_account_id("2CC0VNTSARE1S01AT10NGR0VPS")
"2CC0VNTSARE1S01AT10NGR0VPS"

current_user(current_user_or_socket_or_opts, recursing \\ false)

Returns the current user from socket, assigns, or options.

This function traverses various possible structures to find and return the current user (or current user ID if that's all that's available).

Examples

iex> Bonfire.Common.Utils.current_user(%{current_user: %{id: "user1"}})
%{id: "user1"}

iex> Bonfire.Common.Utils.current_user(%{assigns: %{current_user: %{id: "user2"}}})
%{id: "user2"}

iex> Bonfire.Common.Utils.current_user(%{socket: %{assigns: %{current_user: %{id: "user3"}}}})
%{id: "user3"}

iex> Bonfire.Common.Utils.current_user([current_user: %{id: "user4"}])
%{id: "user4"}

iex> Bonfire.Common.Utils.current_user(%{current_user_id: "5EVSER1S0STENS1B1YHVMAN01D"})
"5EVSER1S0STENS1B1YHVMAN01D"

current_user_auth!(context, password)

(Re)authenticates the current user using the provided password.

Raises an exception if the credentials are invalid.

Examples

> Bonfire.Common.Utils.current_user_auth!(%{current_user: %{id: "user1"}}, "password123")
** (Bonfire.Fail.Auth) We couldn't find an account with the details you provided. 

current_user_id(current_user_or_socket_or_opts, recursing \\ false)

Returns the current user ID from socket, assigns, or options.

This function traverses various possible structures to find and return the current user ID.

Examples

iex> Bonfire.Common.Utils.current_user_id(%{current_user_id: "5EVSER1S0STENS1B1YHVMAN01D"})
"5EVSER1S0STENS1B1YHVMAN01D"

iex> Bonfire.Common.Utils.current_user_id(%{assigns: %{current_user_id: "5EVSER1S0STENS1B1YHVMAN01D"}})
"5EVSER1S0STENS1B1YHVMAN01D"

iex> Bonfire.Common.Utils.current_user_id(%{assigns: %{__context__: %{current_user_id: "5EVSER1S0STENS1B1YHVMAN01D"}}})
"5EVSER1S0STENS1B1YHVMAN01D"

iex> Bonfire.Common.Utils.current_user_id("5EVSER1S0STENS1B1YHVMAN01D")
"5EVSER1S0STENS1B1YHVMAN01D"

iex> Bonfire.Common.Utils.current_user_id("invalid id")
nil

current_user_required!(context)

Ensures that the current user is present and raises an exception if not logged in.

Examples

iex> Bonfire.Common.Utils.current_user_required!(%{current_user: %{id: "user1"}})
%{id: "user1"}

> Bonfire.Common.Utils.current_user_required!(%{})
** (Bonfire.Fail.Auth) You need to log in first. 

declared_extension()

Callback implementation for Bonfire.Common.ExtensionModule.declared_extension/0.

empty?(v)

Checks if the given value is nil, an empty enumerable, or an empty string.

Parameters

  • v: The value to check.

Examples

iex> empty?(nil)
true

iex> empty?("")
true

iex> empty?([])
true

iex> empty?([1, 2, 3])
false

iex> empty?("hello")
false

maybe(val, change_fn)

Applies the given function if the first parameter is not nil.

Parameters

  • val: The value to check.
  • change_fn: A function to apply if val is not nil.

Examples

iex> maybe(nil, fn x -> x * 2 end)
nil

iex> maybe(3, fn x -> x * 2 end)
6

maybe_apply(module, fun, args \\ [], opts \\ [])

Helper for calling hypothetical functions another modules.

Attempts to apply a function from a specified module with the given arguments and returns the result.

Returns an error if the function is not defined, unless a fallback function was provided to be invoked, or a fallback value to be returned.

Parameters

  • module: The module to check for the function.
  • funs: A list of function names (atoms) to try.
  • args: Arguments to pass to the function.
  • opts: Options for error handling and fallback. Options include:
    • :fallback_fun - A function to call if the primary function is not found.
    • :fallback_return - A default return value if the function cannot be applied.

Examples

iex> maybe_apply(Enum, :map, [[1, 2, 3], &(&1 * 2)])
[2, 4, 6]

iex> maybe_apply(Enum, [:nonexistent_fun], [])
{:error, "None of the functions [:nonexistent_fun] are defined at Elixir.Enum with arity 0"}

iex> maybe_apply(Enum, [:nonexistent_fun], [], fallback_fun: fn error, _args, _opts -> raise "Failed" end)
** (RuntimeError) Failed

iex> maybe_apply(SomeModule, [:some_fun], [1, 2, 3], fallback_return: "Failed")
# Output: [warning] maybe_apply: No such module (Elixir.SomeModule) could be loaded. - with args: ([1, 2, 3])
"Failed"

maybe_apply_fallback(error, args, opts)

maybe_from_opts(opts, key, fallback \\ nil)

Returns the value of a key from options keyword list or map, or a fallback if not present or empty.

nothing?(v)

Checks if the given value is nil, false, 0, or an empty value (using empty?/1).

Parameters

  • v: The value to check.

Examples

iex> nothing?(nil)
true

iex> nothing?(false)
true

iex> nothing?(0)
true

iex> nothing?("")
true

iex> nothing?([1, 2, 3])
false

iex> nothing?("hello")
false

ok_unwrap(val, fallback \\ nil)

Unwraps an {:ok, val} tuple, returning the value. If not OK, returns a fallback value (default is nil).

Parameters

  • val: The value or tuple to unwrap.
  • fallback: The fallback value if the tuple is an error.

Examples

iex> ok_unwrap({:ok, 42})
42

iex> ok_unwrap({:error, "something went wrong"}, "default")
"default"

iex> ok_unwrap(:error, "default")
"default"

iex> ok_unwrap(nil, "default")
"default"

round_nearest(num)

Rounds a number and uses Bonfire.Common.Localise.Cldr.Number.to_string/2 function to format into a human readable string.

Examples

iex> round_nearest(1234)
"1K"

iex> round_nearest(1600000)
"2M"

round_nearest(num, target)

Rounds a number to the nearest specified target.

Parameters

  • num: The number to round.
  • target: The target to round to (optional).

Examples

iex> round_nearest(1234, 10)
1230

iex> round_nearest(1234, 100)
1200

iex> round_nearest(1234, 1000)
1000

to_options(user_or_socket_or_opts)

Converts a map, user, socket, tuple, etc, to a keyword list for standardised use as function options.