Skip to content

Commit

Permalink
feat: Support Many To Many Association (#9)
Browse files Browse the repository at this point in the history
* feat: Support Many To Many Association

* feat: put nil outside the association type
  • Loading branch information
bamorim authored Feb 11, 2021
1 parent 5ab8343 commit 3b90512
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 47 deletions.
34 changes: 18 additions & 16 deletions lib/typed_ecto_schema/ecto_type_mapper.ex
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
defmodule TypedEctoSchema.EctoTypeMapper do
@moduledoc false

alias Ecto.Association.NotLoaded

@schema_many_function_name [:embeds_many, :has_many]
@schema_many_function_name [:embeds_many, :has_many, :many_to_many]

@schema_assoc_function_name [
:has_many,
:has_one,
:belongs_to
:belongs_to,
:many_to_many
]

@module_for_ecto_type %{
Expand Down Expand Up @@ -42,8 +41,8 @@ defmodule TypedEctoSchema.EctoTypeMapper do
def type_for(ecto_type, function_name, nullable_default, opts) do
ecto_type
|> base_type_for(opts)
|> wrap_in_list_if_many(function_name)
|> add_not_loaded_if_assoc(function_name)
|> wrap_embeds_many(function_name)
|> wrap_assoc_type(function_name)
|> add_nil_if_nullable(field_is_nullable?(nullable_default, function_name, opts))
end

Expand Down Expand Up @@ -137,23 +136,26 @@ defmodule TypedEctoSchema.EctoTypeMapper do
any()
end
end
@spec wrap_in_list_if_many(Macro.t(), function_name()) :: Macro.t()
defp wrap_in_list_if_many(type, function_name)
when function_name in @schema_many_function_name do

@spec wrap_assoc_type(Macro.t(), function_name()) :: Macro.t()
defp wrap_assoc_type(type, function_name) when function_name in @schema_assoc_function_name do
quote do
list(unquote(type))
unquote(Ecto.Schema).unquote(function_name)(unquote(type))
end
end

defp wrap_in_list_if_many(type, _), do: type
defp wrap_assoc_type(type, _function_name) do
type
end

@spec add_not_loaded_if_assoc(Macro.t(), function_name()) :: Macro.t()
defp add_not_loaded_if_assoc(type, function_name)
when function_name in @schema_assoc_function_name do
quote(do: unquote(type) | unquote(NotLoaded).t())
@spec wrap_embeds_many(Macro.t(), function_name()) :: Macro.t()
defp wrap_embeds_many(type, :embeds_many) do
quote do
list(unquote(type))
end
end

defp add_not_loaded_if_assoc(type, _), do: type
defp wrap_embeds_many(type, _), do: type

@spec add_nil_if_nullable(Macro.t(), nullable :: boolean) :: Macro.t()
defp add_nil_if_nullable(type, false), do: type
Expand Down
3 changes: 2 additions & 1 deletion lib/typed_ecto_schema/syntax_sugar.ex
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ defmodule TypedEctoSchema.SyntaxSugar do
:embeds_many,
:has_one,
:has_many,
:belongs_to
:belongs_to,
:many_to_many
]

@embeds_function_names [:embeds_one, :embeds_many]
Expand Down
1 change: 1 addition & 0 deletions lib/typed_ecto_schema/type_builder.ex
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ defmodule TypedEctoSchema.TypeBuilder do
| :has_one
| :has_many
| :belongs_to
| :many_to_many

@typep schema_option ::
{:null, boolean()}
Expand Down
54 changes: 24 additions & 30 deletions test/typed_ecto_schema_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,14 @@ defmodule TypedEctoSchemaTest do
end
end

defmodule ManyToMany do
use TypedEctoSchema

typed_schema "many_to_many" do
field(:int, :integer)
end
end

{:module, _name, bytecode, _exports} =
defmodule TestStruct do
use TypedEctoSchema
Expand All @@ -57,6 +65,7 @@ defmodule TypedEctoSchemaTest do
has_one(:has_one, HasOne)
has_many(:has_many, HasMany)
belongs_to(:belongs_to, BelongsTo)
many_to_many(:many_to_many, ManyToMany, join_through: "join_table")
timestamps()
end

Expand Down Expand Up @@ -218,6 +227,7 @@ defmodule TypedEctoSchemaTest do
has_one(:has_one, HasOne)
has_many(:has_many, HasMany)
belongs_to(:belongs_to, BelongsTo)
many_to_many(:many_to_many, ManyToMany, join_through: "join_table")
timestamps()
end

Expand All @@ -236,12 +246,11 @@ defmodule TypedEctoSchemaTest do
enum_type_required: :foo1 | :foo2 | :foo3,
embed: Embedded.t() | nil,
embeds: list(Embedded.t()),
has_one: (HasOne.t() | unquote(NotLoaded).t()) | nil,
has_many: list(HasMany.t()) | unquote(NotLoaded).t(),
belongs_to:
(BelongsTo.t() | unquote(NotLoaded).t())
| nil,
has_one: unquote(Ecto.Schema).has_one(unquote(HasOne).t()) | nil,
has_many: unquote(Ecto.Schema).has_many(unquote(HasMany).t()),
belongs_to: unquote(Ecto.Schema).belongs_to(unquote(BelongsTo).t()) | nil,
belongs_to_id: integer() | nil,
many_to_many: unquote(Ecto.Schema).many_to_many(unquote(ManyToMany).t()),
inserted_at: unquote(NaiveDateTime).t() | nil,
updated_at: unquote(NaiveDateTime).t() | nil
}
Expand Down Expand Up @@ -303,14 +312,11 @@ defmodule TypedEctoSchemaTest do
enum_type_required: :foo1 | :foo2 | :foo3,
embed: unquote(Embedded).t() | nil,
embeds: list(unquote(Embedded).t()),
has_one:
(unquote(HasOne).t() | unquote(NotLoaded).t())
| nil,
has_many: list(unquote(HasMany).t()) | unquote(NotLoaded).t(),
belongs_to:
(unquote(BelongsTo).t() | unquote(NotLoaded).t())
| nil,
has_one: unquote(Ecto.Schema).has_one(unquote(HasOne).t()) | nil,
has_many: unquote(Ecto.Schema).has_many(unquote(HasMany).t()),
belongs_to: unquote(Ecto.Schema).belongs_to(unquote(BelongsTo).t()) | nil,
belongs_to_id: integer() | nil,
many_to_many: unquote(Ecto.Schema).many_to_many(unquote(ManyToMany).t()),
inserted_at: unquote(NaiveDateTime).t() | nil,
updated_at: unquote(NaiveDateTime).t() | nil
]
Expand All @@ -329,12 +335,8 @@ defmodule TypedEctoSchemaTest do
normal: integer(),
enforced: integer(),
overriden: integer() | nil,
has_one:
(unquote(HasOne).t() | unquote(NotLoaded).t())
| nil,
belongs_to:
(unquote(BelongsTo).t() | unquote(NotLoaded).t())
| nil,
has_one: unquote(Ecto.Schema).has_one(unquote(HasOne).t()) | nil,
belongs_to: unquote(Ecto.Schema).belongs_to(unquote(BelongsTo).t()) | nil,
belongs_to_id: integer()
]
end
Expand All @@ -348,21 +350,13 @@ defmodule TypedEctoSchemaTest do
quote do
[
__meta__: unquote(Metadata).t(),
normal:
(unquote(BelongsTo).t() | unquote(NotLoaded).t())
| nil,
normal: unquote(Ecto.Schema).belongs_to(unquote(BelongsTo).t()) | nil,
normal_id: integer() | nil,
with_custom_fk:
(unquote(BelongsTo).t() | unquote(NotLoaded).t())
| nil,
with_custom_fk: unquote(Ecto.Schema).belongs_to(unquote(BelongsTo).t()) | nil,
custom_fk: integer() | nil,
custom_type:
(unquote(BelongsTo).t() | unquote(NotLoaded).t())
| nil,
custom_type: unquote(Ecto.Schema).belongs_to(unquote(BelongsTo).t()) | nil,
custom_type_id: binary() | nil,
no_define:
(unquote(BelongsTo).t() | unquote(NotLoaded).t())
| nil
no_define: unquote(Ecto.Schema).belongs_to(unquote(BelongsTo).t()) | nil
]
end

Expand Down

0 comments on commit 3b90512

Please sign in to comment.