Skip to content

Commit 1d35408

Browse files
committed
Accept params in Schemecto.new/2
1 parent 59995c2 commit 1d35408

5 files changed

Lines changed: 93 additions & 99 deletions

File tree

README.md

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,19 +30,17 @@ defmodule Example do
3030
%{name: :city, type: :string},
3131
%{name: :zip, type: :string}
3232
],
33-
with: &Example.validate_address/2
33+
with: &Example.validate_address/1
3434
)}
3535
]
3636

37-
Schemecto.new(fields)
38-
|> Ecto.Changeset.cast(params, [:name, :email, :address])
37+
Schemecto.new(fields, params)
3938
|> Ecto.Changeset.validate_required([:name, :email])
4039
|> Ecto.Changeset.validate_format(:email, ~r/@/)
4140
end
4241

43-
def validate_address(changeset, params) do
42+
def validate_address(changeset) do
4443
changeset
45-
|> Ecto.Changeset.cast(params, [:street, :city, :zip])
4644
|> Ecto.Changeset.validate_required([:street, :city])
4745
|> Ecto.Changeset.validate_length(:zip, is: 5)
4846
end

lib/schemecto.ex

Lines changed: 29 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ defmodule Schemecto do
66
@doc """
77
Creates a new schemaless changeset with the given field definitions.
88
9+
If parameters are given, they are cast into the changeset according
10+
to fields. Parameters are a keyword list, a map of string or atom keys,
11+
or nil.
12+
913
## Parameters
1014
1115
* `fields` - List of field definitions. Each field is a map with:
@@ -23,12 +27,19 @@ defmodule Schemecto do
2327
%{name: :age, type: :integer, default: 0, description: "Age in years"}
2428
]
2529
26-
changeset = Schemecto.new(fields)
27-
changeset = Ecto.Changeset.cast(changeset, %{name: "John", age: 30}, [:name, :age])
30+
params = %{"name" => "John", "age" => 30}
31+
changeset = Schemecto.new(fields, params)
2832
2933
"""
30-
def new(fields) when is_list(fields) do
31-
build_changeset(fields)
34+
def new(fields, params \\ %{})
35+
when is_list(fields) and (is_list(params) or is_map(params) or params == nil) do
36+
changeset = build_changeset(fields)
37+
38+
if params == [] or params == %{} or params == nil do
39+
changeset
40+
else
41+
Ecto.Changeset.cast(changeset, params, Map.keys(changeset.types))
42+
end
3243
end
3344

3445
@doc """
@@ -38,14 +49,13 @@ defmodule Schemecto do
3849
3950
* `fields` - List of field definitions for the nested changeset
4051
* `opts` - Keyword list of options:
41-
* `:with` - A 2-arity function that receives a changeset and params,
42-
and returns a validated changeset (required)
52+
* `:with` - A 1-arity function that receives a changeset
53+
with the parameters already cast into them (if any) (required)
4354
4455
## Examples
4556
46-
def validate_address(changeset, params) do
57+
def validate_address(changeset) do
4758
changeset
48-
|> Ecto.Changeset.cast(params, [:street, :city, :zip])
4959
|> Ecto.Changeset.validate_required([:street, :city])
5060
end
5161
@@ -58,21 +68,19 @@ defmodule Schemecto do
5868
%{name: :city, type: :string},
5969
%{name: :zip, type: :string}
6070
],
61-
with: &validate_address/2
71+
with: &validate_address/1
6272
)}
6373
]
6474
65-
changeset =
66-
Schemecto.new(fields)
67-
|> Ecto.Changeset.cast(params, [:name, :email, :address])
75+
changeset = Schemecto.new(fields, params)
6876
6977
"""
7078
def one(fields, opts) when is_list(fields) and is_list(opts) do
7179
function = Keyword.fetch!(opts, :with)
7280

73-
if not is_function(function, 2) do
81+
if not is_function(function, 1) do
7482
raise ArgumentError,
75-
"expected :with option to be a 2-arity function, got: #{inspect(function)}"
83+
"expected :with option to be a 1-arity function, got: #{inspect(function)}"
7684
end
7785

7886
Ecto.ParameterizedType.init(Schemecto.One, %{
@@ -93,9 +101,8 @@ defmodule Schemecto do
93101
94102
## Examples
95103
96-
def validate_tag(changeset, params) do
104+
def validate_tag(changeset) do
97105
changeset
98-
|> Ecto.Changeset.cast(params, [:name, :color])
99106
|> Ecto.Changeset.validate_required([:name])
100107
end
101108
@@ -106,21 +113,19 @@ defmodule Schemecto do
106113
%{name: :name, type: :string},
107114
%{name: :color, type: :string}
108115
],
109-
with: &validate_tag/2
116+
with: &validate_tag/1
110117
)}
111118
]
112119
113-
changeset =
114-
Schemecto.new(fields)
115-
|> Ecto.Changeset.cast(params, [:title, :tags])
120+
changeset = Schemecto.new(fields, params)
116121
117122
"""
118123
def many(fields, opts) when is_list(fields) and is_list(opts) do
119124
function = Keyword.fetch!(opts, :with)
120125

121-
if not is_function(function, 2) do
126+
if not is_function(function, 1) do
122127
raise ArgumentError,
123-
"expected :with option to be a 2-arity function, got: #{inspect(function)}"
128+
"expected :with option to be a 1-arity function, got: #{inspect(function)}"
124129
end
125130

126131
Ecto.ParameterizedType.init(Schemecto.Many, %{
@@ -250,18 +255,13 @@ defmodule Schemecto do
250255
end
251256

252257
defp type_to_json_schema({:parameterized, {Schemecto.One, %{changeset: changeset, with: fun}}}) do
253-
changeset
254-
|> fun.(%{})
255-
|> to_json_schema()
258+
changeset |> fun.() |> to_json_schema()
256259
end
257260

258261
defp type_to_json_schema({:parameterized, {Schemecto.Many, %{changeset: changeset, with: fun}}}) do
259262
%{
260263
"type" => "array",
261-
"items" =>
262-
changeset
263-
|> fun.(%{})
264-
|> to_json_schema()
264+
"items" => changeset |> fun.() |> to_json_schema()
265265
}
266266
end
267267

lib/schemecto/many.ex

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,19 +21,25 @@ defmodule Schemecto.Many do
2121
def cast(nil, _params), do: {:ok, nil}
2222

2323
def cast(values, %{changeset: changeset, with: fun}) when is_list(values) do
24-
# Validate each value in the list and collect results
24+
keys = Map.keys(changeset.types)
25+
2526
{valid_values, all_errors} =
2627
values
2728
|> Enum.with_index()
2829
|> Enum.reduce({[], []}, fn {value, index}, {valid_acc, error_acc} ->
2930
if is_map(value) do
30-
# Call the validation function with the changeset and the input value
31-
case fun.(changeset, value) do
31+
changeset =
32+
if value == %{} do
33+
changeset
34+
else
35+
Ecto.Changeset.cast(changeset, value, keys)
36+
end
37+
38+
case fun.(changeset) do
3239
%Ecto.Changeset{valid?: true} = cs ->
3340
{[Ecto.Changeset.apply_changes(cs) | valid_acc], error_acc}
3441

3542
%Ecto.Changeset{valid?: false, errors: errors} ->
36-
# Add index information to the errors
3743
indexed_errors = [{index, errors} | error_acc]
3844
{valid_acc, indexed_errors}
3945
end

lib/schemecto/one.ex

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,14 @@ defmodule Schemecto.One do
1414
def cast(nil, _params), do: {:ok, nil}
1515

1616
def cast(value, %{changeset: changeset, with: fun}) when is_map(value) do
17-
# Call the validation function with the changeset and the input value
18-
case fun.(changeset, value) do
17+
changeset =
18+
if value == %{} do
19+
changeset
20+
else
21+
Ecto.Changeset.cast(changeset, value, Map.keys(changeset.types))
22+
end
23+
24+
case fun.(changeset) do
1925
%Ecto.Changeset{valid?: true} = cs ->
2026
{:ok, Ecto.Changeset.apply_changes(cs)}
2127

0 commit comments

Comments
 (0)