Skip to content

Commit 4d3c5ee

Browse files
committed
Improve error message on custom preload, closes #4554
1 parent c0203db commit 4d3c5ee

1 file changed

Lines changed: 55 additions & 41 deletions

File tree

lib/ecto/query/builder/preload.ex

Lines changed: 55 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -44,61 +44,65 @@ defmodule Ecto.Query.Builder.Preload do
4444
** (Ecto.Query.CompileError) cannot preload join association `:bar` with binding `c` because parent preload is not a join association
4545
4646
"""
47-
@spec escape(Macro.t, Keyword.t) :: {[Macro.t], [Macro.t]}
47+
@spec escape(Macro.t(), Keyword.t()) :: {[Macro.t()], [Macro.t()]}
4848
def escape(preloads, vars) do
4949
{preloads, assocs} = escape(preloads, :both, [], [], vars)
5050
{Enum.reverse(preloads), Enum.reverse(assocs)}
5151
end
5252

5353
defp escape(atom, _mode, preloads, assocs, _vars) when is_atom(atom) do
54-
{[atom|preloads], assocs}
54+
{[atom | preloads], assocs}
5555
end
5656

5757
defp escape(list, mode, preloads, assocs, vars) when is_list(list) do
58-
Enum.reduce list, {preloads, assocs}, fn item, acc ->
58+
Enum.reduce(list, {preloads, assocs}, fn item, acc ->
5959
escape_each(item, mode, acc, vars)
60-
end
60+
end)
6161
end
6262

6363
defp escape({:^, _, [inner]}, _mode, preloads, assocs, _vars) do
64-
{[inner|preloads], assocs}
64+
{[inner | preloads], assocs}
6565
end
6666

6767
defp escape(other, _mode, _preloads, _assocs, _vars) do
68-
Builder.error! "`#{Macro.to_string other}` is not a valid preload expression. " <>
69-
"preload expects an atom, a list of atoms or a keyword list with " <>
70-
"more preloads as values. Use ^ on the outermost preload to interpolate a value"
68+
Builder.error!(
69+
"`#{Macro.to_string(other)}` is not a valid preload expression. " <>
70+
"preload expects an atom, a list of atoms or a keyword list with " <>
71+
"more preloads as values. Use ^ on the outermost preload to interpolate a value"
72+
)
7173
end
7274

7375
defp escape_each({key, {:^, _, [inner]}}, _mode, {preloads, assocs}, _vars) do
7476
key = escape_key(key)
75-
{[{key, inner}|preloads], assocs}
77+
{[{key, inner} | preloads], assocs}
7678
end
7779

78-
defp escape_each({key, {var, _, context}}, mode, {preloads, assocs}, vars) when is_atom(context) do
80+
defp escape_each({key, {var, _, context}}, mode, {preloads, assocs}, vars)
81+
when is_atom(context) do
7982
assert_assoc!(mode, key, var)
8083
key = escape_key(key)
8184
idx = Builder.find_var!(var, vars)
82-
{preloads, [{key, {idx, []}}|assocs]}
85+
{preloads, [{key, {idx, []}} | assocs]}
8386
end
8487

85-
defp escape_each({key, {{var, _, context}, list}}, mode, {preloads, assocs}, vars) when is_atom(context) do
88+
defp escape_each({key, {{var, _, context}, list}}, mode, {preloads, assocs}, vars)
89+
when is_atom(context) do
8690
assert_assoc!(mode, key, var)
8791
key = escape_key(key)
8892
idx = Builder.find_var!(var, vars)
8993
{inner_preloads, inner_assocs} = escape(list, :assoc, [], [], vars)
90-
assocs = [{key, {idx, Enum.reverse(inner_assocs)}}|assocs]
94+
assocs = [{key, {idx, Enum.reverse(inner_assocs)}} | assocs]
9195

9296
case inner_preloads do
9397
[] -> {preloads, assocs}
94-
_ -> {[{key, Enum.reverse(inner_preloads)}|preloads], assocs}
98+
_ -> {[{key, Enum.reverse(inner_preloads)} | preloads], assocs}
9599
end
96100
end
97101

98102
defp escape_each({key, list}, _mode, {preloads, assocs}, vars) do
99103
key = escape_key(key)
100104
{inner_preloads, []} = escape(list, :preload, [], [], vars)
101-
{[{key, Enum.reverse(inner_preloads)}|preloads], assocs}
105+
{[{key, Enum.reverse(inner_preloads)} | preloads], assocs}
102106
end
103107

104108
defp escape_each(other, mode, {preloads, assocs}, vars) do
@@ -114,17 +118,18 @@ defmodule Ecto.Query.Builder.Preload do
114118
end
115119

116120
defp escape_key(other) do
117-
Builder.error! "malformed key in preload `#{Macro.to_string(other)}` in query expression"
121+
Builder.error!("malformed key in preload `#{Macro.to_string(other)}` in query expression")
118122
end
119123

120124
@doc """
121125
Called at runtime to check dynamic preload keys.
122126
"""
123127
def key!(key) when is_atom(key),
124128
do: key
129+
125130
def key!(key) do
126131
raise ArgumentError,
127-
"expected key in preload to be an atom, got: `#{inspect key}`"
132+
"expected key in preload to be an atom, got: `#{inspect(key)}`"
128133
end
129134

130135
@doc """
@@ -134,7 +139,7 @@ defmodule Ecto.Query.Builder.Preload do
134139
If possible, it does all calculations at compile time to avoid
135140
runtime work.
136141
"""
137-
@spec build(Macro.t, [Macro.t], Macro.t, Macro.Env.t) :: Macro.t
142+
@spec build(Macro.t(), [Macro.t()], Macro.t(), Macro.Env.t()) :: Macro.t()
138143
def build(query, _binding, {:^, _, [expr]}, _env) do
139144
quote do
140145
Ecto.Query.Builder.Preload.preload!(unquote(query), unquote(expr))
@@ -150,10 +155,11 @@ defmodule Ecto.Query.Builder.Preload do
150155
@doc """
151156
The callback applied by `build/4` to build the query.
152157
"""
153-
@spec apply(Ecto.Queryable.t, term, term) :: Ecto.Query.t
158+
@spec apply(Ecto.Queryable.t(), term, term) :: Ecto.Query.t()
154159
def apply(%Ecto.Query{preloads: p, assocs: a} = query, preloads, assocs) do
155160
%{query | preloads: p ++ preloads, assocs: a ++ assocs}
156161
end
162+
157163
def apply(query, preloads, assocs) do
158164
apply(Ecto.Queryable.to_query(query), preloads, assocs)
159165
end
@@ -205,7 +211,7 @@ defmodule Ecto.Query.Builder.Preload do
205211
end
206212

207213
defp expand(atom, _query, _mode, preloads, assocs) when is_atom(atom) do
208-
{[atom|preloads], assocs}
214+
{[atom | preloads], assocs}
209215
end
210216

211217
defp expand(list, query, mode, preloads, assocs) when is_list(list) do
@@ -219,39 +225,44 @@ defmodule Ecto.Query.Builder.Preload do
219225

220226
defp expand(other, _query, _mode, _preloads, _assocs) do
221227
raise ArgumentError,
222-
"`#{inspect(other)}` is not a valid preload expression, " <>
223-
"expected an atom or a list."
228+
"`#{inspect(other)}` is not a valid preload expression, " <>
229+
"expected an atom or a list."
224230
end
225231

226232
defp expand_each(atom, _query, _mode, {preloads, assocs}) when is_atom(atom) do
227-
{[atom|preloads], assocs}
233+
{[atom | preloads], assocs}
228234
end
229235

230236
defp expand_each({key, atom}, _query, _mode, {preloads, assocs}) when is_atom(atom) do
231237
assert_key!(key)
232238

233-
{[{key, atom}|preloads], assocs}
239+
{[{key, atom} | preloads], assocs}
234240
end
235241

236242
defp expand_each({key, %Ecto.Query.DynamicExpr{} = dynamic}, query, mode, {preloads, assocs}) do
237243
assert_key!(key)
238244
assert_assoc!(mode, key)
239245

240246
idx = expand_dynamic(dynamic, query)
241-
{preloads, [{key, {idx, []}}|assocs]}
247+
{preloads, [{key, {idx, []}} | assocs]}
242248
end
243249

244-
defp expand_each({key, {%Ecto.Query.DynamicExpr{} = dynamic, inner}}, query, mode, {preloads, assocs}) do
250+
defp expand_each(
251+
{key, {%Ecto.Query.DynamicExpr{} = dynamic, inner}},
252+
query,
253+
mode,
254+
{preloads, assocs}
255+
) do
245256
assert_key!(key)
246257
assert_assoc!(mode, key)
247258

248259
idx = expand_dynamic(dynamic, query)
249260
{inner_preloads, inner_assocs} = expand(inner, query, :assoc, [], [])
250-
assocs = [{key, {idx, inner_assocs}}|assocs]
261+
assocs = [{key, {idx, inner_assocs}} | assocs]
251262

252263
case inner_preloads do
253264
[] -> {preloads, assocs}
254-
_ -> {[{key, inner_preloads}|preloads], assocs}
265+
_ -> {[{key, inner_preloads} | preloads], assocs}
255266
end
256267
end
257268

@@ -260,21 +271,21 @@ defmodule Ecto.Query.Builder.Preload do
260271
assert_query_or_fun!(query_or_fun, key)
261272

262273
{inner_preloads, []} = expand(inner, query, :preload, [], [])
263-
{[{key, {query_or_fun, inner_preloads}}|preloads], assocs}
274+
{[{key, {query_or_fun, inner_preloads}} | preloads], assocs}
264275
end
265276

266277
defp expand_each({key, list}, query, _mode, {preloads, assocs}) when is_list(list) do
267278
assert_key!(key)
268279

269280
{inner_preloads, []} = expand(list, query, :preload, [], [])
270-
{[{key, inner_preloads}|preloads], assocs}
281+
{[{key, inner_preloads} | preloads], assocs}
271282
end
272283

273284
defp expand_each({key, query_or_fun}, _query, _mode, {preloads, assocs}) do
274285
assert_key!(key)
275286
assert_query_or_fun!(query_or_fun, key)
276287

277-
{[{key, query_or_fun}|preloads], assocs}
288+
{[{key, query_or_fun} | preloads], assocs}
278289
end
279290

280291
defp expand_each(other, query, mode, {preloads, assocs}) do
@@ -288,35 +299,38 @@ defmodule Ecto.Query.Builder.Preload do
288299

289300
_ ->
290301
raise ArgumentError,
291-
"invalid dynamic in preload: `#{inspect(dynamic)}`. " <>
292-
"Dynamic expressions in preload must evaluate to a single binding, as in: " <>
293-
"`dynamic([comments: c], c)`"
302+
"invalid dynamic in preload: `#{inspect(dynamic)}`. " <>
303+
"Dynamic expressions in preload must evaluate to a single binding, as in: " <>
304+
"`dynamic([comments: c], c)`"
294305
end
295306
end
296307

297308
defp assert_key!(key), do: key!(key) && :ok
298309

299310
defp assert_query_or_fun!(%Ecto.Query{}, _key), do: :ok
300311
defp assert_query_or_fun!(fun, _key) when is_function(fun, 1), do: :ok
312+
301313
defp assert_query_or_fun!(other, key) do
302314
raise ArgumentError,
303-
"invalid preload for key `#{inspect(key)}`: #{inspect(other)}. " <>
304-
"Preloads can be a query, a function (arity 1), or a dynamic that " <>
305-
"evaluates to a single binding."
315+
"invalid preload for key `#{inspect(key)}`: #{inspect(other)}. " <>
316+
"Preloads can be a query, a function expecting one or two arguments, " <>
317+
"or a dynamic that evaluates to a single binding"
306318
end
307319

308320
defp assert_assoc!(mode, _atom) when mode in [:both, :assoc], do: :ok
321+
309322
defp assert_assoc!(_mode, atom) do
310323
raise ArgumentError,
311-
"cannot preload join association `#{inspect(atom)}` " <>
312-
"because parent preload is not a join association"
324+
"cannot preload join association `#{inspect(atom)}` " <>
325+
"because parent preload is not a join association"
313326
end
314327

315328
defp assert_assoc!(mode, _atom, _var) when mode in [:both, :assoc], do: :ok
329+
316330
defp assert_assoc!(_mode, atom, var) do
317331
Builder.error!(
318-
"cannot preload join association `#{Macro.to_string atom}` with binding `#{var}` " <>
319-
"because parent preload is not a join association"
332+
"cannot preload join association `#{Macro.to_string(atom)}` with binding `#{var}` " <>
333+
"because parent preload is not a join association"
320334
)
321335
end
322336
end

0 commit comments

Comments
 (0)