From 640afd05f3ce19e0495ebe58829f3537dc8663ac Mon Sep 17 00:00:00 2001 From: Phil Chen <06fahchen@gmail.com> Date: Thu, 11 Jul 2024 18:16:30 +0800 Subject: [PATCH 1/3] docs: enhance documentation for the `:module` option --- README.md | 69 +++++++++++++++++++++++++++++------- test/defmodule_like_test.exs | 52 +++++++++++++++++++++++++++ 2 files changed, 108 insertions(+), 13 deletions(-) create mode 100644 test/defmodule_like_test.exs diff --git a/README.md b/README.md index cc4a60d..1b5a4d6 100644 --- a/README.md +++ b/README.md @@ -131,7 +131,11 @@ defmodule User do end ``` -If you prefer to define a struct in a submodule, pass the `module` option. +If you prefer to define a struct in a submodule, you can use +the `module` option with `TypedStructor`. This allows you to +encapsulate the struct definition within a specific submodule context. + +Consider this example: ```elixir defmodule User do use TypedStructor @@ -144,27 +148,66 @@ defmodule User do end end ``` +When defining a struct in a submodule, the `typed_structor` block +functions similarly to a `defmodule` block. Therefore, +the previous example can be alternatively written as: +```elixir +defmodule User do + defmodule Profile do + use TypedStructor + + typed_structor do + field :id, pos_integer() + field :name, String.t() + field :age, non_neg_integer() + end + end +end +``` -You can define the type only without defining the struct, -it is useful when the struct is defined by another library(like `Ecto.Schema`). +Furthermore, the `typed_structor` block allows you to +define functions, derive protocols, and more, just +as you would within a `defmodule` block. Here's a example: ```elixir defmodule User do - use Ecto.Schema use TypedStructor - typed_structor define_struct: false do - field :id, pos_integer() - field :name, String.t() - field :age, non_neg_integer(), default: 0 - end + typed_structor module: Profile, define_struct: false do + @derive {Jason.Encoder, only: [:email]} + field :email, String.t() + + use Ecto.Schema + @primary_key false + + schema "users" do + Ecto.Schema.field(:email, :string) + end + + import Ecto.Changeset - schema "users" do - field :name, :string - field :age, :integer, default: 0 + def changeset(%__MODULE__{} = user, attrs) do + user + |> cast(attrs, [:email]) + |> validate_required([:email]) + end end end ``` - +Now, you can interact with these structures: +```elixir +iex> User.Profile.__struct__() +%User.Profile{__meta__: #Ecto.Schema.Metadata<:built, "users">, email: nil} +iex> Jason.encode!(%User.Profile{}) +"{\"email\":null}" +iex> User.Profile.changeset(%User.Profile{}, %{"email" => "my@email.com"}) +#Ecto.Changeset< + action: nil, + changes: %{email: "my@email.com"}, + errors: [], + data: #User.Profile<>, + valid?: true +> +``` ## Documentation To add a `@typedoc` to the struct type, just add the attribute in the typed_structor block: diff --git a/test/defmodule_like_test.exs b/test/defmodule_like_test.exs new file mode 100644 index 0000000..0b1b39f --- /dev/null +++ b/test/defmodule_like_test.exs @@ -0,0 +1,52 @@ +defmodule DefmoduleLikeTest do + use ExUnit.Case, async: true + + defmodule User do + use TypedStructor + + typed_structor module: Profile, define_struct: false do + @derive {Jason.Encoder, only: [:email]} + field :email, String.t() + + use Ecto.Schema + @primary_key false + + schema "users" do + Ecto.Schema.field(:email, :string) + end + + import Ecto.Changeset + + def changeset(%__MODULE__{} = user, attrs) do + user + |> cast(attrs, [:email]) + |> validate_required([:email]) + end + end + end + + test "works" do + assert %User.Profile{} === struct(User.Profile) + assert [:email] === User.Profile.__schema__(:fields) + end + + test "functions works" do + changset = User.Profile.changeset(%User.Profile{}, %{"email" => "my@email.com"}) + + assert match?( + %Ecto.Changeset{ + valid?: true, + changes: %{email: "my@email.com"} + }, + changset + ) + end + + test "deriving works" do + assert Jason.Encoder.DefmoduleLikeTest.User.Profile === + Jason.Encoder.impl_for(%User.Profile{}) + + user = %User.Profile{email: "my@email.com"} + assert ~s|{"email":"my@email.com"}| === Jason.encode!(user) + end +end From 23acc327192614a1a8032e438a22350bb2149126 Mon Sep 17 00:00:00 2001 From: Phil Chen <06fahchen@gmail.com> Date: Thu, 11 Jul 2024 18:16:30 +0800 Subject: [PATCH 2/3] docs: set source_ref for source link inference --- mix.exs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mix.exs b/mix.exs index e8ec8f9..73cb5ad 100644 --- a/mix.exs +++ b/mix.exs @@ -1,13 +1,14 @@ defmodule TypedStructor.MixProject do use Mix.Project + @version "0.4.1" @source_url "https://github.com/elixir-typed-structor/typed_structor" def project do [ app: :typed_structor, description: "TypedStructor is a library for defining structs with types effortlessly.", - version: "0.4.1", + version: @version, elixir: "~> 1.14", elixirc_paths: elixirc_paths(Mix.env()), start_permanent: Mix.env() == :prod, @@ -19,6 +20,7 @@ defmodule TypedStructor.MixProject do docs: [ main: "readme", source_url: @source_url, + source_ref: "v#{@version}", extra_section: "Guides", groups_for_extras: [ Guides: ~r<(guides/[^\/]+\.md)|(README.md)>, @@ -47,6 +49,7 @@ defmodule TypedStructor.MixProject do name: "typed_structor", licenses: ["MIT"], links: %{ + "Changelog" => "https://hexdocs.pm/typed_structor/changelog.html", "GitHub" => @source_url } ], From ef208e8884181bca9ddcb44fcdd42a0e6e3d21fb Mon Sep 17 00:00:00 2001 From: Phil Chen <06fahchen@gmail.com> Date: Thu, 11 Jul 2024 18:16:30 +0800 Subject: [PATCH 3/3] chore: release 0.4.2 --- CHANGELOG.md | 8 ++++++++ mix.exs | 2 +- mix.lock | 6 +++--- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d41330..39b92a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## [0.4.2](https://github.com/elixir-typed-structor/typed_structor/compare/v0.4.1...v0.4.2) (2024-07-11) + + +### Documentation + +* this release is a documentation update and does not contain any new features or bug fixes + + ## [0.4.1](https://github.com/elixir-typed-structor/typed_structor/compare/v0.4.0...v0.4.1) (2024-07-10) diff --git a/mix.exs b/mix.exs index 73cb5ad..555805f 100644 --- a/mix.exs +++ b/mix.exs @@ -1,7 +1,7 @@ defmodule TypedStructor.MixProject do use Mix.Project - @version "0.4.1" + @version "0.4.2" @source_url "https://github.com/elixir-typed-structor/typed_structor" def project do diff --git a/mix.lock b/mix.lock index 745bfd1..76ad4eb 100644 --- a/mix.lock +++ b/mix.lock @@ -3,12 +3,12 @@ "credo": {:hex, :credo, "1.7.7", "771445037228f763f9b2afd612b6aa2fd8e28432a95dbbc60d8e03ce71ba4446", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "8bc87496c9aaacdc3f90f01b7b0582467b69b4bd2441fe8aae3109d843cc2f2e"}, "decimal": {:hex, :decimal, "2.1.1", "5611dca5d4b2c3dd497dec8f68751f1f1a54755e8ed2a966c2633cf885973ad6", [:mix], [], "hexpm", "53cfe5f497ed0e7771ae1a475575603d77425099ba5faef9394932b35020ffcc"}, "dialyxir": {:hex, :dialyxir, "1.4.3", "edd0124f358f0b9e95bfe53a9fcf806d615d8f838e2202a9f430d59566b6b53b", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "bf2cfb75cd5c5006bec30141b131663299c661a864ec7fbbc72dfa557487a986"}, - "earmark_parser": {:hex, :earmark_parser, "1.4.39", "424642f8335b05bb9eb611aa1564c148a8ee35c9c8a8bba6e129d51a3e3c6769", [:mix], [], "hexpm", "06553a88d1f1846da9ef066b87b57c6f605552cfbe40d20bd8d59cc6bde41944"}, + "earmark_parser": {:hex, :earmark_parser, "1.4.41", "ab34711c9dc6212dda44fcd20ecb87ac3f3fce6f0ca2f28d4a00e4154f8cd599", [:mix], [], "hexpm", "a81a04c7e34b6617c2792e291b5a2e57ab316365c2644ddc553bb9ed863ebefa"}, "ecto": {:hex, :ecto, "3.11.2", "e1d26be989db350a633667c5cda9c3d115ae779b66da567c68c80cfb26a8c9ee", [:mix], [{:decimal, "~> 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", "3c38bca2c6f8d8023f2145326cc8a80100c3ffe4dcbd9842ff867f7fc6156c65"}, "erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"}, - "ex_doc": {:hex, :ex_doc, "0.34.1", "9751a0419bc15bc7580c73fde506b17b07f6402a1e5243be9e0f05a68c723368", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "d441f1a86a235f59088978eff870de2e815e290e44a8bd976fe5d64470a4c9d2"}, + "ex_doc": {:hex, :ex_doc, "0.34.2", "13eedf3844ccdce25cfd837b99bea9ad92c4e511233199440488d217c92571e8", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "5ce5f16b41208a50106afed3de6a2ed34f4acfd65715b82a0b84b49d995f95c1"}, "file_system": {:hex, :file_system, "1.0.0", "b689cc7dcee665f774de94b5a832e578bd7963c8e637ef940cd44327db7de2cd", [:mix], [], "hexpm", "6752092d66aec5a10e662aefeed8ddb9531d79db0bc145bb8c40325ca1d8536d"}, - "jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"}, + "jason": {:hex, :jason, "1.4.3", "d3f984eeb96fe53b85d20e0b049f03e57d075b5acda3ac8d465c969a2536c17b", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "9a90e868927f7c777689baa16d86f4d0e086d968db5c05d917ccff6d443e58a3"}, "makeup": {:hex, :makeup, "1.1.2", "9ba8837913bdf757787e71c1581c21f9d2455f4dd04cfca785c70bbfff1a76a3", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cce1566b81fbcbd21eca8ffe808f33b221f9eee2cbc7a1706fc3da9ff18e6cac"}, "makeup_diff": {:hex, :makeup_diff, "0.1.0", "5be352b6aa6f07fa6a236e3efd7ba689a03f28fb5d35b7a0fa0a1e4a64f6d8bb", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "186bad5bb433a8afeb16b01423950e440072284a4103034ca899180343b9b4ac"}, "makeup_elixir": {:hex, :makeup_elixir, "0.16.2", "627e84b8e8bf22e60a2579dad15067c755531fea049ae26ef1020cad58fe9578", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "41193978704763f6bbe6cc2758b84909e62984c7752b3784bd3c218bb341706b"},