From d6e0c542ec658a56375fb54fd2b3d9893442cafd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20Ara=C3=BAjo?= <44007026+laraujo7@users.noreply.github.com> Date: Tue, 10 Dec 2024 20:44:21 +0100 Subject: [PATCH] feat: add defaults to schemas (#120) --- lib/goal.ex | 18 +++++++++++++++--- test/goal_test.exs | 32 ++++++++++++++++++++++++++++++++ test/test_helper.exs | 2 ++ 3 files changed, 49 insertions(+), 3 deletions(-) diff --git a/lib/goal.ex b/lib/goal.ex index 79df54e..a8e9033 100644 --- a/lib/goal.ex +++ b/lib/goal.ex @@ -459,7 +459,9 @@ defmodule Goal do Returns the validated parameters or an error changeset. """ @spec validate(Ecto.Changeset.t()) :: {:ok, map()} | {:error, Ecto.Changeset.t()} - def validate(%Changeset{valid?: true, changes: changes}), do: {:ok, changes} + def validate(%Changeset{valid?: true} = changeset), + do: {:ok, Changeset.apply_changes(changeset)} + def validate(%Changeset{valid?: false} = changeset), do: {:error, changeset} @doc """ @@ -660,7 +662,7 @@ defmodule Goal do |> build_changeset(params) |> Map.put(:action, :validate) |> case do - %Changeset{valid?: true, changes: changes} -> {:ok, changes} + %Changeset{valid?: true} = changes -> {:ok, Changeset.apply_changes(changes)} %Changeset{valid?: false} = changeset -> {:error, changeset} end end @@ -680,8 +682,9 @@ defmodule Goal do @spec build_changeset(schema(), params()) :: Changeset.t() def build_changeset(schema, params) do types = get_types(schema) + defaults = build_defaults(schema) - {%{}, types} + {defaults, types} |> Changeset.cast(params, Map.keys(types), force_changes: true) |> validate_required_fields(schema) |> validate_basic_fields(schema) @@ -793,6 +796,15 @@ defmodule Goal do end) end + defp build_defaults(schema) do + Enum.reduce(schema, %{}, fn {field, rules}, acc -> + case Keyword.get(rules, :default) do + nil -> acc + default -> Map.put_new(acc, field, default) + end + end) + end + defp validate_required_fields(%Changeset{} = changeset, schema) do required_fields = Enum.reduce(schema, [], fn {field, rules}, acc -> diff --git a/test/goal_test.exs b/test/goal_test.exs index 33cb461..df222fd 100644 --- a/test/goal_test.exs +++ b/test/goal_test.exs @@ -70,6 +70,11 @@ defmodule GoalTest do optional(:status, :enum, values: @statuses) end + defparams :defaults do + required(:status, :enum, values: @statuses, default: :pending) + optional(:name, :string, default: "The one who shall not be named") + end + describe "__using__/1" do test "schema/0" do assert schema() == %{id: [type: :integer, required: true]} @@ -292,6 +297,14 @@ defmodule GoalTest do fields2: %{"hello" => "world"}, fields4: [%{"bye" => "world"}] }} + + assert validate(:defaults, %{}) == { + :ok, + %{status: :pending, name: "The one who shall not be named"} + } + + assert validate(:defaults, %{status: :done, name: "Voldemort"}) == + {:ok, %{status: :done, name: "Voldemort"}} end end @@ -344,6 +357,12 @@ defmodule GoalTest do assert Goal.validate_params(schema, params, opts) == {:ok, %{}} end + + test "includes default value when one is not provider" do + schema = %{name: [type: :string, default: "Harry Potter"]} + + assert Goal.validate_params(schema, %{}) == {:ok, %{name: "Harry Potter"}} + end end describe "build_changeset/2" do @@ -948,6 +967,19 @@ defmodule GoalTest do assert errors_on(changeset_4) == %{status: ["is invalid"]} end + test "default" do + schema = %{ + status: [type: :enum, values: [:active, :inactive], default: :active, required: true], + name: [type: :string] + } + + data_5 = %{} + + changeset_5 = Goal.build_changeset(schema, data_5) + + assert data_on(changeset_5) == %{status: :active} + end + test "enum, string-based" do schema = %{status: [type: :enum, values: ["active", "inactive"]]} diff --git a/test/test_helper.exs b/test/test_helper.exs index 4d7db22..57a8bbc 100644 --- a/test/test_helper.exs +++ b/test/test_helper.exs @@ -10,6 +10,8 @@ defmodule Goal.Helpers do end) end) end + + def data_on(%Ecto.Changeset{data: data}), do: data end defmodule Goal.DemoSchema do