-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathecto_require_associations.ex
58 lines (47 loc) · 1.63 KB
/
ecto_require_associations.ex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
defmodule EctoRequireAssociations do
@moduledoc """
Documentation for `RequireAssociations`.
"""
alias RequireAssociations.Associations
def ensure!(records, association_names) when is_list(records) do
Associations.paths(association_names)
|> Enum.filter(fn association_name_path ->
association_names_not_loaded?(records, association_name_path)
end)
|> case do
[] ->
:ok
[path] ->
raise ArgumentError, "Expected association to be set: #{path_to_string(path)}"
paths ->
paths_string =
paths
|> Enum.map(&path_to_string/1)
|> Enum.join(", ")
raise ArgumentError, "Expected associations to be set: #{paths_string}"
end
end
def ensure!(record, association_names), do: ensure!([record], association_names)
defp path_to_string(path) do
"`#{Enum.join(path, ".")}`"
end
defp association_names_not_loaded?(records, [association_name]) do
!Enum.all?(records, & assoc_loaded?(&1, association_name))
end
defp association_names_not_loaded?(records, [association_name | rest]) do
records
|> Enum.map(& Map.get(&1, association_name))
|> List.flatten()
|> association_names_not_loaded?(rest)
end
defp assoc_loaded?(%Ecto.Association.NotLoaded{}, _), do: true
defp assoc_loaded?(%struct_mod{} = record, association_name) do
if Map.has_key?(struct(struct_mod), association_name) do
record
|> Map.get(association_name)
|> Ecto.assoc_loaded?()
else
raise ArgumentError, "Association `#{association_name}` is not defined for the `#{Macro.to_string(struct_mod)}` struct"
end
end
end