Unified field selection processor using type-driven recursive dispatch.
This module mirrors the architecture of ValueFormatter, using the same
{type, constraints} pattern for type-driven dispatch. Each type is
self-describing - no separate classification step is needed.
Design Principle
The key insight is that field selection and value formatting are parallel operations - both traverse composite types recursively based on type information. By using the same dispatch pattern, we achieve consistency and simplicity.
Type Categories
| Category | Detection | Handler |
|---|---|---|
| Ash Resource | Ash.Resource.Info.resource?(type) | select_resource_fields/3 |
| TypedStruct/NewType/CustomType | typescript_field_names/0 callback | select_typed_struct_fields/3 |
| Typed Map/Struct | Has fields constraints | select_typed_map_fields/3 |
| Tuple | Ash.Type.Tuple | select_tuple_fields/3 |
| Union | Ash.Type.Union | select_union_fields/3 |
| Array | {:array, inner_type} | Recurse with inner type |
| Primitive | Default | Validate no fields requested |
Summary
Functions
Converts an action to its type specification.
Processes requested fields for a given resource and action.
Main recursive dispatch function for field selection.
Selects fields from an Ash resource.
Selects fields from a tuple type using named fields.
Selects fields from a typed map (Ash.Type.Map/Keyword with field constraints).
Selects fields from a TypedStruct or NewType with typescript_field_names callback.
Selects fields from a union type.
Types
Functions
@spec action_to_type_spec(module(), Ash.Resource.Actions.action()) :: {atom() | tuple(), keyword()}
Converts an action to its type specification.
Returns {type, constraints} tuple representing the action's return type.
@spec process(module(), atom(), list()) :: {:ok, select_result()} | {:error, term()}
Processes requested fields for a given resource and action.
Returns {:ok, {select_fields, load_fields, extraction_template}} or {:error, error}.
Parameters
resource- The Ash resource moduleaction_name- The action name (atom)requested_fields- List of field selections (atoms, strings, or maps)
Examples
iex> process(MyApp.Todo, :read, [:id, :title, %{user: [:id, :name]}])
{:ok, {[:id, :title], [{:user, [:id, :name]}], [:id, :title, {:user, [:id, :name]}]}}
Main recursive dispatch function for field selection.
Mirrors ValueFormatter.format/5 - uses the same type detection and dispatch pattern.
Each type category has its own handler that may recurse back into this function.
Selects fields from an Ash resource.
Handles attributes, calculations, relationships, and aggregates.
Selects fields from a tuple type using named fields.
Tuples in Ash have named positions (like :latitude, :longitude) and the template stores both the field_name and its index for result processing. When no fields are requested, all fields are returned.
Selects fields from a typed map (Ash.Type.Map/Keyword with field constraints).
The error_type parameter allows distinguishing between different type categories for better error messages.
Selects fields from a TypedStruct or NewType with typescript_field_names callback.
Selects fields from a union type.
Supports:
- Simple member selection: [:member_name]
- Member with nested fields: [%{member_name: fields}]
- Multiple members in a single map: %{member1: fields1, member2: fields2}