feat: infer belongs_to type via ecto introspection

This commit is contained in:
sloane 2024-11-13 08:35:47 -05:00
parent 5f45fe9306
commit ff769454e2
Signed by: sloanelybutsurely
SSH key fingerprint: SHA256:8SBnwhl+RY3oEyQxy1a9wByPzxWM0x+/Ejc+sIlY5qQ
13 changed files with 119 additions and 2 deletions

View file

@ -1,4 +1,6 @@
# Used by "mix format"
[
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
import_deps: [:ecto, :ecto_sql],
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"],
subdirectories: ["priv/*/migrations"]
]

3
config/config.exs Normal file
View file

@ -0,0 +1,3 @@
import Config
import_config "#{config_env()}.exs"

0
config/dev.exs Normal file
View file

0
config/prod.exs Normal file
View file

8
config/test.exs Normal file
View file

@ -0,0 +1,8 @@
import Config
config :typeid_elixir, ecto_repos: [TypeID.Repo]
config :typeid_elixir, TypeID.Repo,
database: "type_id_test",
hostname: "localhost",
pool: Ecto.Adapters.SQL.Sandbox

15
mix.exs
View file

@ -8,9 +8,11 @@ defmodule TypeID.MixProject do
app: :typeid_elixir,
version: @version,
elixir: "~> 1.11",
elixirc_paths: elixirc_paths(Mix.env()),
start_permanent: Mix.env() == :prod,
description: description(),
package: package(),
aliases: aliases(),
deps: deps(),
name: "TypeID Elixir",
source_url: "https://github.com/sloanelybutsurely/typeid-elixir",
@ -23,6 +25,9 @@ defmodule TypeID.MixProject do
[extra_applications: [:logger]]
end
defp elixirc_paths(:test), do: ["lib", "test/support"]
defp elixirc_paths(_), do: ["lib"]
defp description do
"A type-safe, K-sortable, globally unique identifier inspired by Stripe IDs"
end
@ -48,7 +53,15 @@ defmodule TypeID.MixProject do
{:phoenix, "~> 1.7", optional: true},
{:jason, "~> 1.4", optional: true},
{:ex_doc, "~> 0.27", only: :dev, runtime: false},
{:yaml_elixir, "~> 2.9", only: [:dev, :test], runtime: false}
{:yaml_elixir, "~> 2.9", only: [:dev, :test], runtime: false},
{:ecto_sql, "~> 3.10", only: [:dev, :test]},
{:postgrex, "~> 0.17", only: [:dev, :test]}
]
end
defp aliases do
[
test: ["ecto.create --quiet", "ecto.migrate --quiet", "test"]
]
end
end

View file

@ -1,8 +1,10 @@
%{
"castore": {:hex, :castore, "1.0.3", "7130ba6d24c8424014194676d608cb989f62ef8039efd50ff4b3f33286d06db8", [:mix], [], "hexpm", "680ab01ef5d15b161ed6a95449fac5c6b8f60055677a8e79acf01b27baa4390b"},
"db_connection": {:hex, :db_connection, "2.7.0", "b99faa9291bb09892c7da373bb82cba59aefa9b36300f6145c5f201c7adf48ec", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "dcf08f31b2701f857dfc787fbad78223d61a32204f217f15e881dd93e4bdd3ff"},
"decimal": {:hex, :decimal, "2.1.1", "5611dca5d4b2c3dd497dec8f68751f1f1a54755e8ed2a966c2633cf885973ad6", [:mix], [], "hexpm", "53cfe5f497ed0e7771ae1a475575603d77425099ba5faef9394932b35020ffcc"},
"earmark_parser": {:hex, :earmark_parser, "1.4.32", "fa739a0ecfa34493de19426681b23f6814573faee95dfd4b4aafe15a7b5b32c6", [:mix], [], "hexpm", "b8b0dd77d60373e77a3d7e8afa598f325e49e8663a51bcc2b88ef41838cca755"},
"ecto": {:hex, :ecto, "3.10.2", "6b887160281a61aa16843e47735b8a266caa437f80588c3ab80a8a960e6abe37", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "6a895778f0d7648a4b34b486af59a1c8009041fbdf2b17f1ac215eb829c60235"},
"ecto_sql": {:hex, :ecto_sql, "3.10.2", "6b98b46534b5c2f8b8b5f03f126e75e2a73c64f3c071149d32987a5378b0fdbd", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.10.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16.0 or ~> 0.17.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "68c018debca57cb9235e3889affdaec7a10616a4e3a80c99fa1d01fdafaa9007"},
"ex_doc": {:hex, :ex_doc, "0.29.4", "6257ecbb20c7396b1fe5accd55b7b0d23f44b6aa18017b415cb4c2b91d997729", [:mix], [{:earmark_parser, "~> 1.4.31", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "2c6699a737ae46cb61e4ed012af931b57b699643b24dabe2400a8168414bc4f5"},
"jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"},
"makeup": {:hex, :makeup, "1.1.0", "6b67c8bc2882a6b6a445859952a602afc1a41c2e08379ca057c0f525366fc3ca", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "0a45ed501f4a8897f580eabf99a2e5234ea3e75a4373c8a52824f6e873be57a6"},
@ -16,6 +18,7 @@
"phoenix_template": {:hex, :phoenix_template, "1.0.1", "85f79e3ad1b0180abb43f9725973e3b8c2c3354a87245f91431eec60553ed3ef", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "157dc078f6226334c91cb32c1865bf3911686f8bcd6bcff86736f6253e6993ee"},
"plug": {:hex, :plug, "1.14.2", "cff7d4ec45b4ae176a227acd94a7ab536d9b37b942c8e8fa6dfc0fff98ff4d80", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "842fc50187e13cf4ac3b253d47d9474ed6c296a8732752835ce4a86acdf68d13"},
"plug_crypto": {:hex, :plug_crypto, "1.2.5", "918772575e48e81e455818229bf719d4ab4181fcbf7f85b68a35620f78d89ced", [:mix], [], "hexpm", "26549a1d6345e2172eb1c233866756ae44a9609bd33ee6f99147ab3fd87fd842"},
"postgrex": {:hex, :postgrex, "0.17.5", "0483d054938a8dc069b21bdd636bf56c487404c241ce6c319c1f43588246b281", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "50b8b11afbb2c4095a3ba675b4f055c416d0f3d7de6633a595fc131a828a67eb"},
"telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"},
"websock": {:hex, :websock, "0.5.2", "b3c08511d8d79ed2c2f589ff430bd1fe799bb389686dafce86d28801783d8351", [:mix], [], "hexpm", "925f5de22fca6813dfa980fb62fd542ec43a2d1a1f83d2caec907483fe66ff05"},
"websock_adapter": {:hex, :websock_adapter, "0.5.3", "4908718e42e4a548fc20e00e70848620a92f11f7a6add8cf0886c4232267498d", [:mix], [{:bandit, ">= 0.6.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "cbe5b814c1f86b6ea002b52dd99f345aeecf1a1a6964e209d208fb404d930d3d"},

View file

@ -0,0 +1,4 @@
[
import_deps: [:ecto_sql],
inputs: ["*.exs"]
]

View file

@ -0,0 +1,18 @@
defmodule TypeID.Repo.Migrations.CreateTestTables do
use Ecto.Migration
def change do
create table(:posts, primary_key: false) do
add :id, :uuid, primary_key: true
add :content, :text
end
create table(:comments, primary_key: false) do
add :id, :uuid, primary_key: true
add :post_id, references(:posts, type: :uuid, on_delete: :delete_all)
add :content, :text
end
create index(:comments, [:post_id], name: :comments_post_id_index)
end
end

21
test/support/data_case.ex Normal file
View file

@ -0,0 +1,21 @@
defmodule TypeID.DataCase do
use ExUnit.CaseTemplate
alias Ecto.Adapters.SQL.Sandbox
using do
quote do
import TypeID.DataCase
end
end
setup tags do
TypeID.DataCase.setup_sandbox(tags)
:ok
end
def setup_sandbox(tags) do
pid = Sandbox.start_owner!(TypeID.Repo, shared: not tags[:async])
on_exit(fn -> Sandbox.stop_owner(pid) end)
end
end

5
test/support/repo.ex Normal file
View file

@ -0,0 +1,5 @@
defmodule TypeID.Repo do
use Ecto.Repo,
otp_app: :typeid_elixir,
adapter: Ecto.Adapters.Postgres
end

View file

@ -1 +1,2 @@
TypeID.Repo.start_link()
ExUnit.start()

View file

@ -0,0 +1,39 @@
defmodule TypeID.EctoTest do
use TypeID.DataCase, async: true
describe "belongs_to relationships" do
defmodule Post do
use Ecto.Schema
@primary_key {:id, TypeID, autogenerate: true, prefix: "post", type: :uuid}
@foreign_key_type TypeID
schema "posts" do
field :content
has_many :comments, TypeID.EctoTest.Comment
end
end
defmodule Comment do
use Ecto.Schema
@primary_key {:id, TypeID, autogenerate: true, prefix: "post", type: :uuid}
@foreign_key_type TypeID
schema "comments" do
field :content
belongs_to :post, TypeID.EctoTest.Post
end
end
test "infers the database type for a belongs_to relationship" do
post_changeset = Ecto.Changeset.change(%Post{}, %{content: "Hello, World!"})
assert {:ok, post} = TypeID.Repo.insert(post_changeset)
comment_changeset =
post
|> Ecto.build_assoc(:comments)
|> Ecto.Changeset.change(%{content: "Great post!"})
assert {:ok, comment} = TypeID.Repo.insert(comment_changeset)
end
end
end