Skip to content

Commit c0f51e8

Browse files
authored
Ecto.Enum.cast_value/3 (#4596)
1 parent a1bbc0c commit c0f51e8

File tree

2 files changed

+83
-11
lines changed

2 files changed

+83
-11
lines changed

lib/ecto/enum.ex

Lines changed: 63 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,53 @@ defmodule Ecto.Enum do
279279
|> Keyword.values()
280280
end
281281

282+
@doc """
283+
Casts a value from the given `schema` and `field`.
284+
285+
## Examples
286+
287+
Assuming this schema:
288+
289+
defmodule MySchema do
290+
use Ecto.Schema
291+
292+
schema "my_schema" do
293+
field :my_string_enum, Ecto.Enum, values: [:foo, :bar, :baz]
294+
field :my_integer_enum, Ecto.Enum, values: [foo: 1, bar: 2, baz: 5]
295+
end
296+
end
297+
298+
Then:
299+
300+
Ecto.Enum.cast_value(MySchema, :my_string_enum, "foo")
301+
#=> {:ok, :foo}
302+
303+
Ecto.Enum.cast_value(MySchema, :my_string_enum, :foo)
304+
#=> {:ok, :foo}
305+
306+
Ecto.Enum.cast_value(MySchema, :my_string_enum, "qux")
307+
#=> :error
308+
309+
Ecto.Enum.cast_value(MySchema, :my_integer_enum, 1)
310+
#=> {:ok, :foo}
311+
312+
Ecto.Enum.cast_value(MySchema, :my_integer_enum, :foo)
313+
#=> {:ok, :foo}
314+
315+
Ecto.Enum.cast_value(MySchema, :my_integer_enum, 6)
316+
#=> :error
317+
318+
`schema_or_types` can also be a types map. See `mappings/2` for more information.
319+
"""
320+
@spec cast_value(module | map, atom, binary | atom | integer) :: {:ok, atom} | :error
321+
def cast_value(schema_or_types, field, value) do
322+
params = get_params(schema_or_types, field)
323+
case cast(value, params) do
324+
{:ok, casted_value} -> {:ok, casted_value}
325+
{:error, _reason} -> :error
326+
end
327+
end
328+
282329
@doc """
283330
Returns the mappings between values and dumped values.
284331
@@ -317,25 +364,30 @@ defmodule Ecto.Enum do
317364
318365
"""
319366
@spec mappings(module | map, atom) :: keyword(String.t() | integer())
320-
def mappings(schema_or_types, field)
321-
322-
def mappings(types, field) when is_map(types) do
323-
case types do
324-
%{^field => {:parameterized, {Ecto.Enum, %{mappings: mappings}}}} -> mappings
325-
%{^field => {_, {:parameterized, {Ecto.Enum, %{mappings: mappings}}}}} -> mappings
326-
%{^field => _} -> raise ArgumentError, "#{field} is not an Ecto.Enum field"
327-
%{} -> raise ArgumentError, "#{field} does not exist"
328-
end
367+
def mappings(schema_or_types, field) do
368+
get_params(schema_or_types, field)
369+
|> Map.fetch!(:mappings)
329370
end
330371

331-
def mappings(schema, field) when is_atom(schema) do
372+
defp get_params(schema_or_types, field)
373+
374+
defp get_params(schema, field) when is_atom(schema) do
332375
try do
333376
schema.__changeset__()
334377
rescue
335378
_ in UndefinedFunctionError ->
336379
raise ArgumentError, "#{inspect(schema)} is not an Ecto schema or types map"
337380
else
338-
%{} = types -> mappings(types, field)
381+
%{} = types -> get_params(types, field)
382+
end
383+
end
384+
385+
defp get_params(types, field) when is_map(types) do
386+
case types do
387+
%{^field => {:parameterized, {Ecto.Enum, params}}} -> params
388+
%{^field => {_, {:parameterized, {Ecto.Enum, params}}}} -> params
389+
%{^field => _} -> raise ArgumentError, "#{field} is not an Ecto.Enum field"
390+
%{} -> raise ArgumentError, "#{field} does not exist"
339391
end
340392
end
341393
end

test/ecto/enum_test.exs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -562,6 +562,26 @@ defmodule Ecto.EnumTest do
562562
end
563563
end
564564

565+
describe "cast_value/3" do
566+
test "returns correct values" do
567+
assert Ecto.Enum.cast_value(EnumSchema, :my_enum, "foo") == {:ok, :foo}
568+
assert Ecto.Enum.cast_value(EnumSchema, :my_enum, :foo) == {:ok, :foo}
569+
assert Ecto.Enum.cast_value(EnumSchema, :my_enum, "qux") == :error
570+
assert Ecto.Enum.cast_value(EnumSchema, :my_enum, :qux) == :error
571+
572+
assert Ecto.Enum.cast_value(EnumSchema, :my_integer_enum, 1) == {:ok, :foo}
573+
assert Ecto.Enum.cast_value(EnumSchema, :my_integer_enum, :foo) == {:ok, :foo}
574+
assert Ecto.Enum.cast_value(EnumSchema, :my_integer_enum, 6) == :error
575+
assert Ecto.Enum.cast_value(EnumSchema, :my_integer_enum, "qux") == :error
576+
assert Ecto.Enum.cast_value(EnumSchema, :my_integer_enum, :qux) == :error
577+
578+
assert Ecto.Enum.cast_value(EnumSchema, :my_string_enum, "fooo") == {:ok, :foo}
579+
assert Ecto.Enum.cast_value(EnumSchema, :my_string_enum, :foo) == {:ok, :foo}
580+
assert Ecto.Enum.cast_value(EnumSchema, :my_string_enum, "quux") == :error
581+
assert Ecto.Enum.cast_value(EnumSchema, :my_string_enum, :qux) == :error
582+
end
583+
end
584+
565585
describe "mappings/2" do
566586
test "returns correct values" do
567587
assert Ecto.Enum.mappings(EnumSchema, :my_enum) == [foo: "foo", bar: "bar", baz: "baz"]

0 commit comments

Comments
 (0)