FatEcto.SharedHelper (FatEcto v1.4.0)

View Source

Provides utility functions for FatEcto, including handling pagination limits, skip values, dynamic binding, and preloading associations.

Summary

Functions

Checks if a value is an alias tuple: {schema_field_atom, operators}.

Builds a map of API name => schema field atom from a filterable config.

Builds a map of field_name => binding for join filters.

Builds a map of API name => schema field atom from a sortable config.

Extracts field names from join filters as strings for overrideable list.

Converts a filterable option list to a map format if it's a keyword list.

Flattens a filterable config into a map of field => operators.

Like flatten_filterable_to_map/1 but preserves operators for join fields.

Retrieves the primary keys for a given query.

Checks if a module implements the given behaviour by inspecting its __info__/1 metadata.

Checks if a filterable entry is a join filter (nested keyword list).

Converts a keyword list to a map with string keys.

Converts a keyword list to a map with string keys and normalizes operator values to uppercase.

Parses an integer from a string or returns the integer if already an integer.

Separates filterable into direct filters and join filters.

Converts a string to an atom.

Converts a string to an existing atom.

Converts various date/datetime inputs to an Elixir Date struct.

Converts various date/datetime inputs to an Elixir Date struct.

Functions

alias_entry?(arg1)

@spec alias_entry?(any()) :: boolean()

Checks if a value is an alias tuple: {schema_field_atom, operators}.

Alias tuples allow using a different API name than the actual schema field.

Examples

iex> FatEcto.SharedHelper.alias_entry?({:name, ["$ILIKE"]})
true

iex> FatEcto.SharedHelper.alias_entry?({:name, "*"})
true

iex> FatEcto.SharedHelper.alias_entry?(["$ILIKE"])
false

iex> FatEcto.SharedHelper.alias_entry?("*")
false

build_field_aliases(filterable)

@spec build_field_aliases(keyword()) :: map()

Builds a map of API name => schema field atom from a filterable config.

Only includes entries that use the alias tuple syntax {:schema_field, operators}. Non-aliased fields are not included (they use the API name as-is).

Examples

iex> FatEcto.SharedHelper.build_field_aliases([
...>   title: ["$ILIKE"],
...>   author_name: {:name, ["$ILIKE"]},
...>   classifications: [
...>     grade_id: ["$EQUAL"],
...>     subject_name: {:name, ["$EQUAL"]}
...>   ]
...> ])
%{"author_name" => :name, "subject_name" => :name}

iex> FatEcto.SharedHelper.build_field_aliases([title: ["$ILIKE"], rating: ["$GT"]])
%{}

build_join_filters_map(join_filters)

@spec build_join_filters_map(keyword()) :: map()

Builds a map of field_name => binding for join filters.

This map is used by Builder to determine which fields should use JoinOperatorApplier instead of the standard OperatorApplier.

Examples

iex> FatEcto.SharedHelper.build_join_filters_map([classifications: [subject_id: ["$EQUAL"], grade_id: ["$EQUAL"]]])
%{"subject_id" => :classifications, "grade_id" => :classifications}

iex> FatEcto.SharedHelper.build_join_filters_map([])
%{}

build_sort_aliases(sortable)

@spec build_sort_aliases(keyword()) :: map()

Builds a map of API name => schema field atom from a sortable config.

Only includes entries that use the alias tuple syntax {:schema_field, directions}.

Examples

iex> FatEcto.SharedHelper.build_sort_aliases([
...>   inserted_at: "*",
...>   author_name: {:name, "*"}
...> ])
%{"author_name" => :name}

iex> FatEcto.SharedHelper.build_sort_aliases([inserted_at: "*", views_count: "*"])
%{}

extract_join_filter_fields(join_filters)

@spec extract_join_filter_fields(keyword()) :: [String.t()]

Extracts field names from join filters as strings for overrideable list.

Examples

iex> FatEcto.SharedHelper.extract_join_filter_fields([classifications: [subject_id: ["$EQUAL"], grade_id: ["$EQUAL"]]])
["subject_id", "grade_id"]

iex> FatEcto.SharedHelper.extract_join_filter_fields([])
[]

filterable_opt_to_map(list)

@spec filterable_opt_to_map(maybe_improper_list() | any()) ::
  maybe_improper_list() | map()

Converts a filterable option list to a map format if it's a keyword list.

This function checks if the input is a keyword list and converts it to a map. If the input is not a list or not a keyword list, it returns the input unchanged. Alias tuples are unwrapped to extract the operators.

Parameters

  • list - Input that may be a keyword list, regular list, or other type

Examples

iex> FatEcto.SharedHelper.filterable_opt_to_map([name: ["$LIKE"], age: ["$GT"]])
%{"name" => ["$LIKE"], "age" => ["$GT"]}

iex> FatEcto.SharedHelper.filterable_opt_to_map(%{"name" => ["$LIKE"]})
%{"name" => ["$LIKE"]}

iex> FatEcto.SharedHelper.filterable_opt_to_map([author_name: {:name, "*"}])
%{"author_name" => "*"}

flatten_filterable_to_map(filterable)

@spec flatten_filterable_to_map(keyword() | list()) :: map()

Flattens a filterable config into a map of field => operators.

Direct filters: {field, [operators]} -> {"field" => [uppercase_ops]} Join filters: {assoc, [nested]} -> each nested field gets "*" wildcard

This is the main function to convert filterable option to a field map.

Examples

iex> FatEcto.SharedHelper.flatten_filterable_to_map([title: ["$ilike"], classifications: [subject_id: ["$EQUAL"]]])
%{"title" => ["$ILIKE"], "subject_id" => "*"}

iex> FatEcto.SharedHelper.flatten_filterable_to_map([name: ["$equal"], age: ["$gt"]])
%{"name" => ["$EQUAL"], "age" => ["$GT"]}

flatten_sortable_to_map(sortable)

@spec flatten_sortable_to_map(keyword() | list()) :: map()

Like flatten_filterable_to_map/1 but preserves operators for join fields.

Examples

iex> FatEcto.SharedHelper.flatten_sortable_to_map([id: "*", fat_rooms: [floor: "*", is_active: ["$ASC", "$DESC"]]])
%{"id" => "*", "floor" => "*", "is_active" => ["$ASC", "$DESC"]}

get_primary_keys(query)

@spec get_primary_keys(Ecto.Query.t()) :: [atom()] | nil

Retrieves the primary keys for a given query.

Parameters

  • query: The Ecto query.

Examples

iex> FatEcto.SharedHelper.get_primary_keys(from(u in User))
[:id]

implements_behaviour?(module, behaviour)

@spec implements_behaviour?(module(), module()) :: boolean()

Checks if a module implements the given behaviour by inspecting its __info__/1 metadata.

Parameters

  • module: The module to check.
  • behaviour: The behaviour to validate against.

Examples

iex> implements_behaviour?(MyApp.Repo, Ecto.Repo)
true

iex> implements_behaviour?(NotARepo, Ecto.Repo)
false

join_filter?(arg1)

@spec join_filter?({atom(), any()}) :: boolean()

Checks if a filterable entry is a join filter (nested keyword list).

A join filter is detected when the value is a keyword list (nested fields) rather than a list of operator strings.

Examples

iex> FatEcto.SharedHelper.join_filter?({:title, ["$ILIKE"]})
false

iex> FatEcto.SharedHelper.join_filter?({:classifications, [subject_id: ["$EQUAL"]]})
true

iex> FatEcto.SharedHelper.join_filter?({:name, "*"})
false

keyword_list_to_map(list)

@spec keyword_list_to_map(keyword() | map()) :: map()

Converts a keyword list to a map with string keys.

If the input is already a map or not a keyword list, returns it unchanged.

Parameters

  • list - A keyword list or map to convert

Examples

iex> FatEcto.SharedHelper.keyword_list_to_map([name: "John", age: 25])
%{"name" => "John", "age" => 25}

iex> FatEcto.SharedHelper.keyword_list_to_map(%{"name" => "John"})
%{"name" => "John"}

keyword_list_to_map_with_uppercase_operators(list)

@spec keyword_list_to_map_with_uppercase_operators(keyword() | map()) :: map()

Converts a keyword list to a map with string keys and normalizes operator values to uppercase.

This function is specifically designed for processing filterable options where operator values need to be normalized to uppercase format.

Parameters

  • list - A keyword list or map to convert

Examples

iex> FatEcto.SharedHelper.keyword_list_to_map_with_uppercase_operators([name: ["like"], age: ["gt"]])
%{"name" => ["LIKE"], "age" => ["GT"]}

parse_integer!(int_str)

@spec parse_integer!(any()) :: integer() | nil

Parses an integer from a string or returns the integer if already an integer.

Returns the parsed integer on success or nil on failure.

separate_filterables(filterable)

@spec separate_filterables(keyword() | any()) :: {keyword(), keyword()}

Separates filterable into direct filters and join filters.

Direct filters have a list of operator strings as values. Join filters have a nested keyword list as values.

Examples

iex> FatEcto.SharedHelper.separate_filterables([title: ["$ILIKE"], classifications: [subject_id: ["$EQUAL"]]])
{[title: ["$ILIKE"]], [classifications: [subject_id: ["$EQUAL"]]]}

iex> FatEcto.SharedHelper.separate_filterables([name: ["$EQUAL"], age: ["$GT"]])
{[name: ["$EQUAL"], age: ["$GT"]], []}

string_to_atom(already_atom)

@spec string_to_atom(String.t() | atom()) :: atom()

Converts a string to an atom.

Parameters

  • str: The string to convert.

Examples

iex> FatEcto.SharedHelper.string_to_atom("example")
:example

string_to_existing_atom(already_atom)

@spec string_to_existing_atom(String.t() | atom()) :: atom()

Converts a string to an existing atom.

Parameters

  • str: The string to convert.

Examples

iex> FatEcto.SharedHelper.string_to_existing_atom("example")
:example

to_date(date)

@spec to_date(String.t() | Date.t() | DateTime.t()) ::
  {:ok, Date.t()} | {:error, atom()}

Converts various date/datetime inputs to an Elixir Date struct.

Accepts:

  • String date (e.g., "2023-12-25")
  • String datetime (e.g., "2023-12-25T10:30:00Z", "2023-12-25 10:30:00")
  • Elixir Date struct
  • Elixir DateTime struct

Returns {:ok, date} on success or {:error, reason} on failure.

Examples

iex> FatEcto.SharedHelper.to_date("2023-12-25")
{:ok, ~D[2023-12-25]}

iex> FatEcto.SharedHelper.to_date("2023-12-25T10:30:00Z")
{:ok, ~D[2023-12-25]}

iex> FatEcto.SharedHelper.to_date(~D[2023-12-25])
{:ok, ~D[2023-12-25]}

iex> FatEcto.SharedHelper.to_date(~U[2023-12-25 10:30:00Z])
{:ok, ~D[2023-12-25]}

to_date!(input)

@spec to_date!(String.t() | Date.t() | DateTime.t()) :: Date.t()

Converts various date/datetime inputs to an Elixir Date struct.

Similar to to_date/1 but raises an exception on failure.

Examples

iex> FatEcto.SharedHelper.to_date!("2023-12-25")
~D[2023-12-25]

iex> FatEcto.SharedHelper.to_date!(~U[2023-12-25 10:30:00Z])
~D[2023-12-25]