Hi,
I have a JSON view module for a Criterion
schema that handles optional preloaded associations. Here’s my current implementation:
defmodule CockpitWeb.CriterionJSON do
alias Cockpit.Certification.Criterion
alias CockpitWeb.{ProgramJSON, VariantJSON, ValueJSON, RequirementJSON}
def data(%Criterion{} = criterion) do
%{
id: criterion.id,
code: criterion.code,
# ... other base fields ...
program_id: criterion.program_id,
variant_id: criterion.variant_id
}
|> maybe_add_program(criterion)
|> maybe_add_variant(criterion)
|> maybe_add_value(criterion)
|> maybe_add_requirement(criterion)
end
defp maybe_add_program(data, criterion) do
if Ecto.assoc_loaded?(criterion.program) do
Map.put(data, :program, ProgramJSON.data(criterion.program))
else
data
end
end
...
# Similar pattern for variant, value, and requirement
defp maybe_add_value(data, criterion) do
if Ecto.assoc_loaded?(criterion.values) do
data
|> Map.drop([:values])
|> Map.put(:value, extract_value(criterion.values))
else
data
end
end
defp extract_value(values) do
case values do
[value | _] -> ValueJSON.data(value)
_ -> nil
end
end
end
My current approach works but feels verbose for a few reasons:
- I have multiple
maybe_add_xxx
functions that follow the same pattern - Some associations (like
values
andrequirements
) come back as lists fromleft_join
even though I know they only contain one item at most - Each association requires both a check for
assoc_loaded?
and handling the data transformation
Is there a more idiomatic or concise way to handle this pattern in Elixir? I’d especially appreciate suggestions for:
- Reducing the repetitive
maybe_add_xxx
functions - Handling single-item associations that come back as lists
- Maintaining flexibility for different preloading scenarios