defmodule Combo.Template.CEExEngine.Assigns do @moduledoc """ Provides `assigns` related helpers. """ alias Combo.Template.CEExEngine.Compiler @doc """ Adds a key-value pair to `assigns`. ## Examples iex> assign(assigns, :name, "Combo") """ def assign(assigns, key, value) when is_map(assigns) do validate_assign_key!(key) case assigns do %{^key => ^value} -> assigns _ -> Map.put(assigns, key, value) end end @doc """ Adds key-value pairs to `assigns`. Accepts a keyword list, a map, or a single-argument function. When a keyword list or map is given as the second argument, it merges into the existing `assigns`. When a function is given as the second argument, it takes the current `assigns` as an argument and merges its return value into the existing `assigns`. ## Examples iex> assign(assigns, name: "Combo", lang: "Elixir") iex> assign(assigns, %{name: "Combo", lang: "Elixir"}) iex> assign(assigns, fn %{name: name, lang: lang} -> ...> %{title: Enum.join([name, lang], " | ")} ...> end) """ def assign(conn, keyword_or_map_or_fun) def assign(assigns, keyword_or_map) when is_map(assigns) and (is_list(keyword_or_map) or is_map(keyword_or_map)) do Enum.reduce(keyword_or_map, assigns, fn {key, value}, acc -> assign(acc, key, value) end) end def assign(assigns, fun) when is_function(fun, 1) do assign(assigns, fun.(assigns)) end @doc ~S''' Adds the given `key` with `value` from `fun` into `assigns` if one does not yet exist. This function is useful for lazily assigning values. ## Examples iex> assign_new(assigns, :name, fn -> "Combo" end) iex> assign_new(assigns, :new_name, fn assigns -> "new" <> assigns[:name] end) ## Use cases - lazy assigns Imagine a card component: ```ceex <.card bg_color="red" /> ``` The `bg_color` is optional, so you can skip it: ```ceex <.card /> ``` In such case, the implementation can use `assign_new/1` to lazily assign a color if none is given. ```elixir def card(assigns) do assigns = assign_new(assigns, :bg_color, fn -> "green" end) ~CE"""