Bonfire.Common.Utils (Bonfire v1.0.0-social)
View SourceVarious 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.:
Bonfire.Common.Enumsfor functions around maps, structs, keyword lists, and the likeBonfire.Common.Typesfor object typesBonfire.Common.URIsandLinkifyfor URI handlingBonfire.Common.DatesTimesfor date/time helpersBonfire.Common.Eto extract nested data from an objectBonfire.Common.ErrorsandBonfire.Failfor error handlingBonfire.Common.Extendfor functions around modularityBonfire.Common.Optsfor handling function optionsBonfire.Common.Configfor handling app-wide configBonfire.Common.Settingsfor handling account/user/instance level settingsBonfire.Common.HTTPfor HTTP requestsBonfire.Common.Cachefor cachingBonfire.Common.Textfor plain or rich textBonfire.Common.Localisefor app localisationBonfire.Common.MediaandBonfire.Filesfor avatars/images/videos/etcBonfire.Common.RepoandNeedlefor database accessBonfire.Common.PubSubfor pub/sub
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(:start_async, fun, opts) 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.
Callback implementation for Bonfire.Common.ExtensionModule.declared_extension/0.
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).
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
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/1the 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/2is used to read the message sent by the task. When usingasync, you mustawaita reply as they are always sent.Task.start_link/1is 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/1can 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:
Task.Supervisor.start_child/2allows you to start a fire-and-forget task when you don't care about its results or if it completes successfully or not.Task.Supervisor.async/2+Task.await/2allows you to execute tasks concurrently and retrieve its result. If the task fails, the caller will also fail.
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 toTask).
Examples
# Spawn an unsupervised Task for side effects
> {apply_task(:start, fn -> IO.puts("Fire-and-forget task") end)
# Output: "Fire-and-forget task"
# Spawn a supervised Task
> apply_task(:start_link, fn -> IO.puts("Supervised task") end)
# Output: "Supervised task"
# Start an async Task (which you must await to get the result)
> apply_task(:async, fn -> IO.puts("Async task") end)
# Output: "Async task"
# Using LiveView's start_async
> apply_task(:start_async, fn -> {:ok, %{result: "done"}} end, socket: socket, id: "task-123")
# Returns updated socket with async operation
# Using LiveView's assign_async
> apply_task(:assign_async, fn -> {:ok, %{key_to_assign: "value"}} end, socket: socket, keys: [:key_to_assign])
# Returns updated socket with async assignment
Runs a function asynchronously using Task.Supervisor. This is similar to apply_task(:start_async, fun, opts) but specifically uses Task.Supervisor for supervision.
Parameters
module: The supervisor module to use for task executionfun: The function to execute asyncopts: Options for task execution, including::function- TheTask.Supervisorfunction 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 ^
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"}
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(%{})
[]
(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.
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"
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"
(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.
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
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.
Callback implementation for Bonfire.Common.ExtensionModule.declared_extension/0.
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
Applies the given function if the first parameter is not nil.
Parameters
val: The value to check.change_fn: A function to apply ifvalis notnil.
Examples
iex> maybe(nil, fn x -> x * 2 end)
nil
iex> maybe(3, fn x -> x * 2 end)
6
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"
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).
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
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"
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
Converts a map, user, socket, tuple, etc, to a keyword list for standardised use as function options.