bamorim / typed_ecto_schema

A library to define Ecto schemas with typespecs without all the boilerplate code.
https://hexdocs.pm/typed_ecto_schema
Apache License 2.0
271 stars 18 forks source link

Support accessing individual field's type #39

Open mattste opened 1 year ago

mattste commented 1 year ago

I'm looking for a way to access an individual field's type.

Given the following schema:

defmodule OrganizationMembership do
  @primary_key false
  typed_schema "organizations_memberships" do
    belongs_to :organization, Organization
    belongs_to :user, User

    field(:role, Ecto.Enum, values: [:admin, :billing, :content_editor, :content_viewer])

    timestamps()
  end
end

I was hoping to find a way to do something like OrganizationMembership.role() so typespecs in other modules can use it. My current workaround is to manually define a role type and have the field use that. Perhaps there is already a way but it's not public surface area?

dvic commented 1 year ago

Unfortunately this is not supported as far as I know, for now the workaround is indeed to use

@role_values [:admin, :billing, :content_editor, :content_viewer]
@type role :: :admin | :billing | :content_editor | :content_viewer

defmodule OrganizationMembership do
  typed_schema "organizations_memberships" do

    field(:role, Ecto.Enum, values: @role_values) :: role()

  end
end

In my project I do use a macro to simplify this:

defmodule OrganizationMembership do
  import UnionType

  @role_values [:admin, :billing, :content_editor, :content_viewer]

  union_type(role :: @role_values)

  typed_schema "organizations_memberships" do

    field(:role, Ecto.Enum, values: @role_values) :: role()

  end
end

where

defmodule UnionType do
  defmacro union_type({:"::", _, [{name, _, _}, data]}) do
    quote bind_quoted: [data: data, name: name] do
      @type unquote(name)() :: unquote(UnionType.union_type_ast(data))
    end
  end

  def union_type_ast([item]), do: item
  def union_type_ast([head | tail]), do: {:|, [], [head, union_type_ast(tail)]}
end
bamorim commented 1 year ago

That could be a nice feature to have. Not sure the ergonomics, but it could be interesting to automatically generate types for Ecto.Enum types. That would need to be opt-in though, as it has a possibility of conflicting with other types or need to be "prefixed", which might not yield the nicest type names.