Cinder.Filter.Helpers (Cinder v0.5.0)
View SourceHelper functions for building and validating custom filters.
This module provides common patterns and utilities that custom filter developers can use to simplify their implementations.
Usage
defmodule MyApp.Filters.CustomFilter do
use Cinder.Filter
import Cinder.Filter.Helpers
@impl true
def process(raw_value, column) do
with {:ok, trimmed} <- validate_string_input(raw_value),
{:ok, parsed} <- parse_custom_value(trimmed) do
build_filter(:my_filter, parsed, :equals)
else
_ -> nil
end
end
end
Summary
Functions
Builds a relationship-aware Ash query filter with embedded field support.
Builds a standard filter map.
Debug helper for filter development.
Extracts filter options with type safety.
Converts URL-safe format back to embedded field notation.
Converts field notation to human-readable labels.
Common empty value check for most filter types.
Parses field notation to determine field type and structure.
Converts embedded field notation to URL-safe format.
Validates and parses comma-separated values.
Validates date input in ISO 8601 format.
Validates embedded field syntax and returns appropriate error messages.
Validates that a module properly implements the Cinder.Filter behaviour.
Validates a filter structure has required fields.
Validates float input with optional min/max bounds.
Validates hex color input.
Validates integer input with optional min/max bounds.
Validates operator is in allowed list.
Validates and trims string input, returning error for empty strings.
Functions
Builds a relationship-aware Ash query filter with embedded field support.
Handles direct fields, relationship fields using dot notation, and embedded fields using bracket notation.
Examples
build_ash_filter(query, "name", "John", :equals)
build_ash_filter(query, "user.name", "John", :equals)
build_ash_filter(query, "profile[:first_name]", "John", :equals)
build_ash_filter(query, "settings[:address][:street]", "Main St", :contains)
Builds a standard filter map.
Examples
iex> build_filter(:my_filter, "value", :equals)
%{type: :my_filter, value: "value", operator: :equals}
iex> build_filter(:slider, 50, :less_than_or_equal, case_sensitive: false)
%{type: :slider, value: 50, operator: :less_than_or_equal, case_sensitive: false}
Debug helper for filter development.
Logs filter processing information when debug is enabled.
Examples
debug_filter("MyFilter", "processing input", %{input: "test"})
Extracts filter options with type safety.
Examples
iex> extract_option([min: 0, max: 100], :min, 50)
0
iex> extract_option([], :min, 50)
50
iex> extract_option(%{min: 0, max: 100}, :min, 50)
0
Converts URL-safe format back to embedded field notation.
Examples
iex> field_notation_from_url_safe("profile__first_name")
"profile[:first_name]"
iex> field_notation_from_url_safe("settings__address__street")
"settings[:address][:street]"
Converts field notation to human-readable labels.
Examples
iex> humanize_embedded_field("profile[:first_name]")
"Profile > First Name"
iex> humanize_embedded_field("user.profile[:first_name]")
"User > Profile > First Name"
Common empty value check for most filter types.
Examples
iex> is_empty_value?(nil)
true
iex> is_empty_value?("")
true
iex> is_empty_value?([])
true
iex> is_empty_value?(%{value: nil})
true
iex> is_empty_value?("test")
false
Parses field notation to determine field type and structure.
Examples
iex> parse_field_notation("username")
{:direct, "username"}
iex> parse_field_notation("user.name")
{:relationship, ["user"], "name"}
iex> parse_field_notation("profile[:first_name]")
{:embedded, "profile", "first_name"}
iex> parse_field_notation("settings[:address][:street]")
{:nested_embedded, "settings", ["address", "street"]}
Converts embedded field notation to URL-safe format.
Examples
iex> url_safe_field_notation("profile[:first_name]")
"profile__first_name"
iex> url_safe_field_notation("settings[:address][:street]")
"settings__address__street"
Validates and parses comma-separated values.
Examples
iex> validate_csv_input("a,b,c")
{:ok, ["a", "b", "c"]}
iex> validate_csv_input("a, b , c ", trim: true)
{:ok, ["a", "b", "c"]}
iex> validate_csv_input("", min_length: 1)
{:error, :empty}
Validates date input in ISO 8601 format.
Examples
iex> validate_date_input("2023-12-25")
{:ok, ~D[2023-12-25]}
iex> validate_date_input("invalid-date")
{:error, :invalid}
Validates embedded field syntax and returns appropriate error messages.
Examples
iex> validate_embedded_field_syntax("profile[:first_name]")
:ok
iex> validate_embedded_field_syntax("profile[invalid]")
{:error, "Invalid embedded field syntax: missing colon"}
Validates that a module properly implements the Cinder.Filter behaviour.
Examples
iex> validate_filter_implementation(MyApp.Filters.ValidFilter)
{:ok, "Filter implementation is valid"}
iex> validate_filter_implementation(InvalidModule)
{:error, ["Missing callback: render/4", "Missing callback: process/2"]}
Validates a filter structure has required fields.
Examples
iex> validate_filter_structure(%{type: :text, value: "test", operator: :equals})
{:ok, %{type: :text, value: "test", operator: :equals}}
iex> validate_filter_structure(%{type: :text, value: "test"})
{:error, :missing_operator}
Validates float input with optional min/max bounds.
Examples
iex> validate_float_input("42.5")
{:ok, 42.5}
iex> validate_float_input("42.5", min: 0.0, max: 100.0)
{:ok, 42.5}
iex> validate_float_input("150.0", max: 100.0)
{:error, :out_of_bounds}
iex> validate_float_input("abc")
{:error, :invalid}
Validates hex color input.
Examples
iex> validate_hex_color_input("#FF0000")
{:ok, "#ff0000"}
iex> validate_hex_color_input("#fff")
{:error, :invalid}
iex> validate_hex_color_input("red")
{:error, :invalid}
Validates integer input with optional min/max bounds.
Examples
iex> validate_integer_input("42")
{:ok, 42}
iex> validate_integer_input("42", min: 0, max: 100)
{:ok, 42}
iex> validate_integer_input("150", max: 100)
{:error, :out_of_bounds}
iex> validate_integer_input("abc")
{:error, :invalid}
Validates operator is in allowed list.
Examples
iex> validate_operator(:equals, [:equals, :contains])
{:ok, :equals}
iex> validate_operator(:invalid, [:equals, :contains])
{:error, :invalid_operator}
Validates and trims string input, returning error for empty strings.
Examples
iex> validate_string_input(" hello ")
{:ok, "hello"}
iex> validate_string_input("")
{:error, :empty}
iex> validate_string_input(nil)
{:error, :invalid}