Skip to content

Commit

Permalink
make default embeds_one field nilable, and non-nilable when required
Browse files Browse the repository at this point in the history
a try of bitwalker#11
  • Loading branch information
fishtreesugar authored and bitwalker committed Oct 25, 2022
1 parent 2b6c643 commit 43df55a
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 3 deletions.
15 changes: 12 additions & 3 deletions lib/typespec.ex
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ defmodule Strukt.Typespec do
if required? or not is_nil(default_value) do
type_name
else
{:|, [], [type_name, nil]}
nilable(type_name)
end

{name, type_spec}
Expand All @@ -70,8 +70,15 @@ defmodule Strukt.Typespec do
embeds
|> Enum.map(fn name -> {name, Map.fetch!(info, name)} end)
|> Enum.map(fn
{name, %{type: :embeds_one, value_type: type}} ->
{name, compose_call(type, :t, [])}
{name, %{type: :embeds_one, value_type: type} = meta} ->
required? = Map.get(meta, :required) == true
type_name = compose_call(type, :t, [])

if required? do
{name, type_name}
else
{name, nilable(type_name)}
end

{name, %{type: :embeds_many, value_type: type}} ->
{name, List.wrap(compose_call(type, :t, []))}
Expand All @@ -98,6 +105,8 @@ defmodule Strukt.Typespec do
defp compose_call({:__aliases__, _, _} = module, function, args) when is_list(args),
do: {{:., [], [module, function]}, [], args}

defp nilable(type_name), do: {:|, [], [type_name, nil]}

defp type_to_type_name(:id), do: primitive(:non_neg_integer)
defp type_to_type_name(:binary_id), do: primitive(:binary)
defp type_to_type_name(:integer), do: primitive(:integer)
Expand Down
24 changes: 24 additions & 0 deletions test/strukt_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,30 @@ defmodule Strukt.Test do
Fixtures.TypeSpec.expected_type_spec_ast_str()
end

test "embeds_one nilable type" do
require Fixtures.EmbedsOneTypeSpec

assert inspect(
Strukt.Typespec.generate(%Strukt.Typespec{
caller: Strukt.Test.Fixtures.EmbedsOneTypeSpec,
fields: [],
info: %{
required_embeds_one: %{
type: :embeds_one,
value_type: :Aux,
required: true
},
optional_embeds_one: %{
type: :embeds_one,
value_type: :Aux
}
},
embeds: [:required_embeds_one, :optional_embeds_one]
})
) ==
Fixtures.EmbedsOneTypeSpec.expected_type_spec_ast_str()
end

defp changeset_errors(%Ecto.Changeset{} = cs) do
cs
|> Ecto.Changeset.traverse_errors(fn {msg, opts} ->
Expand Down
31 changes: 31 additions & 0 deletions test/support/defstruct_fixtures.ex
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
defmodule Aux do
@moduledoc """
auxiliary module for testing
"""
use Strukt

defstruct do
field(:aux, :string)
end
end

defmodule Strukt.Test.Fixtures do
use Strukt

Expand Down Expand Up @@ -41,6 +52,26 @@ defmodule Strukt.Test.Fixtures do
end
end

defmodule EmbedsOneTypeSpec do
use Strukt

@primary_key false
defstruct do
embeds_one(:required_embeds_one, Aux, required: true)
embeds_one(:optional_embeds_one, Aux)
end

defmacro expected_type_spec_ast_str do
quote context: __MODULE__ do
@type t :: %__MODULE__{
required_embeds_one: Aux.t(),
optional_embeds_one: Aux.t() | nil
}
end
|> inspect()
end
end

defmodule CustomFields do
@moduledoc "This module represents the params keys are not snake case"

Expand Down

0 comments on commit 43df55a

Please sign in to comment.