refactor: Rename Property/PropertyType to CustomFieldValue/CustomField
All checks were successful
continuous-integration/drone/push Build is passing

Complete refactoring of resources, database tables, code references, tests, and documentation for improved naming consistency.
This commit is contained in:
Moritz 2025-11-13 17:58:12 +01:00
parent 47f18e9ef3
commit 8400e727a7
Signed by: moritz
GPG key ID: 1020A035E5DD0824
31 changed files with 1002 additions and 647 deletions

View file

@ -1,18 +1,18 @@
defmodule Mv.Membership.PropertyType do
defmodule Mv.Membership.CustomField do
@moduledoc """
Ash resource defining the schema for custom member properties.
Ash resource defining the schema for custom member fields.
## Overview
PropertyTypes define the "schema" for custom fields in the membership system.
Each PropertyType specifies the name, data type, and behavior of a custom field
that can be attached to members via Property resources.
CustomFields define the "schema" for custom fields in the membership system.
Each CustomField specifies the name, data type, and behavior of a custom field
that can be attached to members via CustomFieldValue resources.
## Attributes
- `name` - Unique identifier for the property (e.g., "phone_mobile", "birthday")
- `name` - Unique identifier for the custom field (e.g., "phone_mobile", "birthday")
- `value_type` - Data type constraint (`:string`, `:integer`, `:boolean`, `:date`, `:email`)
- `description` - Optional human-readable description
- `immutable` - If true, property values cannot be changed after creation
- `required` - If true, all members must have this property (future feature)
- `immutable` - If true, custom field values cannot be changed after creation
- `required` - If true, all members must have this custom field (future feature)
## Supported Value Types
- `:string` - Text data (unlimited length)
@ -22,22 +22,22 @@ defmodule Mv.Membership.PropertyType do
- `:email` - Validated email addresses
## Relationships
- `has_many :properties` - All property values of this type
- `has_many :custom_field_values` - All custom field values of this type
## Constraints
- Name must be unique across all property types
- Cannot delete a property type that has existing property values (RESTRICT)
- Name must be unique across all custom fields
- Cannot delete a custom field that has existing custom field values (RESTRICT)
## Examples
# Create a new property type
PropertyType.create!(%{
# Create a new custom field
CustomField.create!(%{
name: "phone_mobile",
value_type: :string,
description: "Mobile phone number"
})
# Create a required property type
PropertyType.create!(%{
# Create a required custom field
CustomField.create!(%{
name: "emergency_contact",
value_type: :string,
required: true
@ -48,7 +48,7 @@ defmodule Mv.Membership.PropertyType do
data_layer: AshPostgres.DataLayer
postgres do
table "property_types"
table "custom_fields"
repo Mv.Repo
end
@ -65,7 +65,7 @@ defmodule Mv.Membership.PropertyType do
attribute :value_type, :atom,
constraints: [one_of: [:string, :integer, :boolean, :date, :email]],
allow_nil?: false,
description: "Defines the datatype `Property.value` is interpreted as"
description: "Defines the datatype `CustomFieldValue.value` is interpreted as"
attribute :description, :string, allow_nil?: true, public?: true
@ -79,7 +79,7 @@ defmodule Mv.Membership.PropertyType do
end
relationships do
has_many :properties, Mv.Membership.Property
has_many :custom_field_values, Mv.Membership.CustomFieldValue
end
identities do

View file

@ -1,11 +1,11 @@
defmodule Mv.Membership.Property do
defmodule Mv.Membership.CustomFieldValue do
@moduledoc """
Ash resource representing a custom property value for a member.
Ash resource representing a custom field value for a member.
## Overview
Properties implement the Entity-Attribute-Value (EAV) pattern, allowing
dynamic custom fields to be attached to members. Each property links a
member to a property type and stores the actual value.
CustomFieldValues implement the Entity-Attribute-Value (EAV) pattern, allowing
dynamic custom fields to be attached to members. Each custom field value links a
member to a custom field and stores the actual value.
## Value Storage
Values are stored using Ash's union type with JSONB storage format:
@ -24,19 +24,19 @@ defmodule Mv.Membership.Property do
- `:email` - Validated email addresses (custom type)
## Relationships
- `belongs_to :member` - The member this property belongs to (CASCADE delete)
- `belongs_to :property_type` - The property type definition
- `belongs_to :member` - The member this custom field value belongs to (CASCADE delete)
- `belongs_to :custom_field` - The custom field definition
## Constraints
- Each member can have only one property per property type (unique composite index)
- Properties are deleted when the associated member is deleted (CASCADE)
- Each member can have only one custom field value per custom field (unique composite index)
- Custom field values are deleted when the associated member is deleted (CASCADE)
"""
use Ash.Resource,
domain: Mv.Membership,
data_layer: AshPostgres.DataLayer
postgres do
table "properties"
table "custom_field_values"
repo Mv.Repo
references do
@ -46,7 +46,7 @@ defmodule Mv.Membership.Property do
actions do
defaults [:create, :read, :update, :destroy]
default_accept [:value, :member_id, :property_type_id]
default_accept [:value, :member_id, :custom_field_id]
end
attributes do
@ -68,16 +68,16 @@ defmodule Mv.Membership.Property do
relationships do
belongs_to :member, Mv.Membership.Member
belongs_to :property_type, Mv.Membership.PropertyType
belongs_to :custom_field, Mv.Membership.CustomField
end
calculations do
calculate :value_to_string, :string, expr(value[:value] <> "")
end
# Ensure a member can only have one property per property type
# For example: A member can have only one "email" property, one "phone" property, etc.
# Ensure a member can only have one custom field value per custom field
# For example: A member can have only one "phone" custom field value, one "email" custom field value, etc.
identities do
identity :unique_property_per_member, [:member_id, :property_type_id]
identity :unique_custom_field_per_member, [:member_id, :custom_field_id]
end
end

View file

@ -4,7 +4,7 @@ defmodule Mv.Membership.Email do
## Overview
This type extends `:string` with email-specific validation constraints.
It ensures that email values stored in Property resources are valid email
It ensures that email values stored in CustomFieldValue resources are valid email
addresses according to a standard regex pattern.
## Validation Rules
@ -14,12 +14,12 @@ defmodule Mv.Membership.Email do
- Automatic trimming of leading/trailing whitespace
## Usage
This type is used in the Property union type for properties with
`value_type: :email` in PropertyType definitions.
This type is used in the CustomFieldValue union type for custom fields with
`value_type: :email` in CustomField definitions.
## Example
# In a property type definition
PropertyType.create!(%{
# In a custom field definition
CustomField.create!(%{
name: "work_email",
value_type: :email
})

View file

@ -7,7 +7,7 @@ defmodule Mv.Membership.Member do
can have:
- Personal information (name, email, phone, address)
- Optional link to a User account (1:1 relationship)
- Dynamic custom properties via PropertyType system
- Dynamic custom field values via CustomField system
- Full-text searchable profile
## Email Synchronization
@ -16,7 +16,7 @@ defmodule Mv.Membership.Member do
See `Mv.EmailSync` for details.
## Relationships
- `has_many :properties` - Dynamic custom fields
- `has_many :custom_field_values` - Dynamic custom fields
- `has_one :user` - Optional authentication account link
## Validations
@ -48,8 +48,8 @@ defmodule Mv.Membership.Member do
create :create_member do
primary? true
# Properties can be created along with member
argument :properties, {:array, :map}
# Custom field values can be created along with member
argument :custom_field_values, {:array, :map}
# Allow user to be passed as argument for relationship management
# user_id is NOT in accept list to prevent direct foreign key manipulation
argument :user, :map, allow_nil?: true
@ -70,7 +70,7 @@ defmodule Mv.Membership.Member do
:postal_code
]
change manage_relationship(:properties, type: :create)
change manage_relationship(:custom_field_values, type: :create)
# Manage the user relationship during member creation
change manage_relationship(:user, :user,
@ -95,8 +95,8 @@ defmodule Mv.Membership.Member do
primary? true
# Required because custom validation function cannot be done atomically
require_atomic? false
# Properties can be updated or created along with member
argument :properties, {:array, :map}
# Custom field values can be updated or created along with member
argument :custom_field_values, {:array, :map}
# Allow user to be passed as argument for relationship management
# user_id is NOT in accept list to prevent direct foreign key manipulation
argument :user, :map, allow_nil?: true
@ -117,7 +117,7 @@ defmodule Mv.Membership.Member do
:postal_code
]
change manage_relationship(:properties, on_match: :update, on_no_match: :create)
change manage_relationship(:custom_field_values, on_match: :update, on_no_match: :create)
# Manage the user relationship during member update
change manage_relationship(:user, :user,
@ -349,7 +349,7 @@ defmodule Mv.Membership.Member do
end
relationships do
has_many :properties, Mv.Membership.Property
has_many :custom_field_values, Mv.Membership.CustomFieldValue
# 1:1 relationship - Member can optionally have one User
# This references the User's member_id attribute
# The relationship is optional (allow_nil? true by default)

View file

@ -3,15 +3,15 @@ defmodule Mv.Membership do
Ash Domain for membership management.
## Resources
- `Member` - Club members with personal information and custom properties
- `Property` - Dynamic custom field values attached to members
- `PropertyType` - Schema definitions for custom properties
- `Member` - Club members with personal information and custom field values
- `CustomFieldValue` - Dynamic custom field values attached to members
- `CustomField` - Schema definitions for custom fields
## Public API
The domain exposes these main actions:
- Member CRUD: `create_member/1`, `list_members/0`, `update_member/2`, `destroy_member/1`
- Property management: `create_property/1`, `list_property/0`, etc.
- PropertyType management: `create_property_type/1`, `list_property_types/0`, etc.
- Custom field value management: `create_custom_field_value/1`, `list_custom_field_values/0`, etc.
- Custom field management: `create_custom_field/1`, `list_custom_fields/0`, etc.
## Admin Interface
The domain is configured with AshAdmin for management UI.
@ -31,18 +31,18 @@ defmodule Mv.Membership do
define :destroy_member, action: :destroy
end
resource Mv.Membership.Property do
define :create_property, action: :create
define :list_property, action: :read
define :update_property, action: :update
define :destroy_property, action: :destroy
resource Mv.Membership.CustomFieldValue do
define :create_custom_field_value, action: :create
define :list_custom_field_values, action: :read
define :update_custom_field_value, action: :update
define :destroy_custom_field_value, action: :destroy
end
resource Mv.Membership.PropertyType do
define :create_property_type, action: :create
define :list_property_types, action: :read
define :update_property_type, action: :update
define :destroy_property_type, action: :destroy
resource Mv.Membership.CustomField do
define :create_custom_field, action: :create
define :list_custom_fields, action: :read
define :update_custom_field, action: :update
define :destroy_custom_field, action: :destroy
end
end
end