Mix.install([:ecto])
defmodule ParamSchema do
defmodule ValidationError do
defstruct [:errors]
end
defmacro __using__(_opts) do
quote do
use Ecto.Schema
import Ecto.Changeset
@primary_key false
def create(params) do
ParamSchema.create(__MODULE__, params)
end
def validate(changeset) do
changeset
end
defoverridable validate: 1
end
end
def create(module, params) do
changeset =
struct(module)
|> Ecto.Changeset.cast(Map.new(params), module.__schema__(:fields))
|> module.validate()
if changeset.valid? do
{:ok, Ecto.Changeset.apply_changes(changeset)}
else
{:error, %ValidationError{errors: changeset.errors}}
end
end
end
Defining schemas will be like this then
defmodule CreateUserParams do
use ParamSchema
embedded_schema do
field(:name, :string)
field(:email, :string)
end
def validate(changeset) do
changeset
|> validate_required([:name, :email])
end
end
With valid params:
CreateUserParams.create(email: "[email protected]", name: "Alice")
With invalid params.
CreateUserParams.create(email: "[email protected]")
We could process the errors more if we wanna hide teh ecto internals