property values as maps closes #53 #56

Merged
moritz merged 5 commits from property_values into main 2025-05-29 15:34:24 +02:00
4 changed files with 43 additions and 18 deletions
Showing only changes of commit 859f5f4497 - Show all commits

34
lib/membership/email.ex Normal file
View file

@ -0,0 +1,34 @@
defmodule Mv.Membership.Email do
@constraints [
match: ~r/^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/,

For things like this, we can also integrate validations from ecto, with some duct-tape: By creating an ad-hoc ecto changeset and applying Ecto's validate_email function.

I don't think it's worth it to do this right now, but we should be aware of the opportunity to do this in the future, as Ecto's validations seem more battle-tested and feature-rich to me.

For things like this, we can also integrate [validations from ecto](https://hexdocs.pm/ecto_commons/EctoCommons.EmailValidator.html), with some duct-tape: By creating an ad-hoc ecto changeset and applying Ecto's `validate_email` function. I don't think it's worth it to do this right now, but we should be aware of the opportunity to do this in the future, as Ecto's validations seem more battle-tested and feature-rich to me.
trim?: true,
min_length: 5,
max_length: 254
]
use Ash.Type.NewType,
subtype_of: :string,
constraints: @constraints
@impl true
def cast_input(value, _) when is_binary(value) do
moritz marked this conversation as resolved Outdated

Why does this function get a binary value, is json stored as binary data in the DB? This is confusing to me because you use String functions with value below 🤔

Why does this function get a binary value, is json stored as binary data in the DB? This is confusing to me because you use String functions with `value` below 🤔
`value` is a string: https://hexdocs.pm/elixir/main/binaries-strings-and-charlists.html
value = if @constraints[:trim?], do: String.trim(value), else: value
cond do
@constraints[:min_length] && String.length(value) < @constraints[:min_length] ->
:error

Currently, this approach always shows an "is invalid" error message when validation fails. I think we should merge this as-is and move on as it's a minor detail, but in the future we could see if we can leverage ash's validations to provide more detailed error messages.

Currently, this approach always shows an "is invalid" error message when validation fails. I think we should merge this as-is and move on as it's a minor detail, but in the future we could see if we can leverage [ash's validations](https://hexdocs.pm/ash/validations.html) to provide more detailed error messages.
@constraints[:max_length] && String.length(value) > @constraints[:max_length] ->
:error
@constraints[:match] && !Regex.match?(@constraints[:match], value) ->
:error
true ->
{:ok, value}
end
end
@impl true
def cast_input(_, _), do: :error
end

View file

@ -23,7 +23,8 @@ defmodule Mv.Membership.Property do
boolean: [type: :boolean],
date: [type: :date],
integer: [type: :integer],
string: [type: :string]
string: [type: :string],
email: [type: Mv.Membership.Email]
]
]
end

View file

@ -19,7 +19,7 @@ defmodule Mv.Membership.PropertyType do
attribute :name, :string, allow_nil?: false, public?: true
attribute :value_type, :atom,
constraints: [one_of: [:string, :integer, :boolean, :date]],
constraints: [one_of: [:string, :integer, :boolean, :date, :email]],
allow_nil?: false,
description: "Definies the datatype `Property.value` is interpreted as"

View file

@ -2,13 +2,6 @@
#
# mix run priv/repo/seeds.exs
#
# Inside the script, you can read and write to any of your
# repositories directly:
#
# Mv.Repo.insert!(%Mv.SomeSchema{})
#
# We recommend using the bang functions (`insert!`, `update!`
# and so on) as they will fail if something goes wrong.
alias Mv.Membership
@ -43,18 +36,15 @@ for attrs <- [
},
%{
name: "Email",
value_type: :string,
value_type: :email,
description: "Email-Adresse des Mitglieds",
immutable: true,
required: true
}
] do
# upsert?: true sorgt dafür, dass bei bestehendem Namen kein Fehler,
# sondern ein Update (hier effektiv No-Op) ausgeführt wird
{:ok, _} =
Membership.create_property_type(
attrs,
upsert?: true,
upsert_identity: :unique_name
)
Membership.create_property_type!(
attrs,
upsert?: true,
upsert_identity: :unique_name
)
end