Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Embedded Schemas #2

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 17 additions & 8 deletions lib/ecto_dot/association.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,9 @@ defmodule EctoDot.Association do
defstruct [:name, :from, :to, :cardinality]

def from_ecto(mod) do
mod.__schema__(:associations)
|> Enum.map(fn assoc ->
mod.__schema__(:association, assoc)
end)
|> Enum.flat_map(fn
%Ecto.Association.Has{} = assoc ->
associations_and_embeds(mod)
|> Enum.flat_map(fn assoc ->
if is_struct(assoc, Ecto.Association.Has) || is_struct(assoc, Ecto.Embedded) do
[
%__MODULE__{
name: assoc.field,
Expand All @@ -17,12 +14,24 @@ defmodule EctoDot.Association do
cardinality: assoc.cardinality
}
]

_ ->
else
[]
end
end)
end

defp associations_and_embeds(mod) do
associations =
mod.__schema__(:associations)
|> Enum.map(& mod.__schema__(:association, &1))

embeds =
mod.__schema__(:embeds)
|> Enum.map(& mod.__schema__(:embed, &1))

associations ++ embeds
end

def to_dot(%__MODULE__{} = assoc, opts \\ []) do
indent = String.duplicate(" ", Keyword.get(opts, :indentation, 0))

Expand Down
6 changes: 5 additions & 1 deletion lib/ecto_dot/schema.ex
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,12 @@ defmodule EctoDot.Schema do
fields =
mod.__schema__(:fields)
|> Enum.map(fn field ->
%Field{name: field, type: mod.__schema__(:type, field)}
case mod.__schema__(:type, field) do
{:parameterized, _, _} -> nil
type -> %Field{name: field, type: type}
end
end)
|> Enum.reject(& &1 == nil)

%__MODULE__{mod: mod, name: Macro.to_string(mod), fields: fields}
end
Expand Down
2 changes: 1 addition & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ defmodule EctoDot.MixProject do
# Run "mix help deps" to learn about dependencies.
defp deps do
[
{:ecto, "~> 2.1"},
{:ecto, "~> 3.0"},
{:mix_test_watch, "~> 0.5", only: :dev, runtime: false}
]
end
Expand Down
9 changes: 5 additions & 4 deletions mix.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
%{
"decimal": {:hex, :decimal, "1.5.0", "b0433a36d0e2430e3d50291b1c65f53c37d56f83665b43d79963684865beab68", [], [], "hexpm"},
"ecto": {:hex, :ecto, "2.2.9", "031d55df9bb430cb118e6f3026a87408d9ce9638737bda3871e5d727a3594aae", [], [{:db_connection, "~> 1.1", [hex: :db_connection, repo: "hexpm", optional: true]}, {:decimal, "~> 1.2", [hex: :decimal, repo: "hexpm", optional: false]}, {:mariaex, "~> 0.8.0", [hex: :mariaex, repo: "hexpm", optional: true]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: true]}, {:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.13.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, repo: "hexpm", optional: true]}], "hexpm"},
"file_system": {:hex, :file_system, "0.2.4", "f0bdda195c0e46e987333e986452ec523aed21d784189144f647c43eaf307064", [], [], "hexpm"},
"mix_test_watch": {:hex, :mix_test_watch, "0.6.0", "5e206ed04860555a455de2983937efd3ce79f42bd8536fc6b900cc286f5bb830", [], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm"},
"decimal": {:hex, :decimal, "2.0.0", "a78296e617b0f5dd4c6caf57c714431347912ffb1d0842e998e9792b5642d697", [:mix], [], "hexpm", "34666e9c55dea81013e77d9d87370fe6cb6291d1ef32f46a1600230b1d44f577"},
"ecto": {:hex, :ecto, "3.6.1", "7bb317e3fd0179ad725069fd0fe8a28ebe48fec6282e964ea502e4deccb0bd0f", [: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", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "cbb3294a990447b19f0725488a749f8cf806374e0d9d0dffc45d61e7aeaf6553"},
"file_system": {:hex, :file_system, "0.2.4", "f0bdda195c0e46e987333e986452ec523aed21d784189144f647c43eaf307064", [:mix], [], "hexpm", "d3d5ee3a1d656cb1efa0d0446df2aeb230c55d0d5fa2ab2840082f7ace50d04a"},
"mix_test_watch": {:hex, :mix_test_watch, "0.6.0", "5e206ed04860555a455de2983937efd3ce79f42bd8536fc6b900cc286f5bb830", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm", "ea6f2a3766f18c2f53ca5b2d40b623ce2831c1646f36ff2b608607e20fc6c63c"},
"poolboy": {:hex, :poolboy, "1.5.1", "6b46163901cfd0a1b43d692657ed9d7e599853b3b21b95ae5ae0a777cf9b6ca8", [], [], "hexpm"},
"telemetry": {:hex, :telemetry, "0.4.3", "a06428a514bdbc63293cd9a6263aad00ddeb66f608163bdec7c8995784080818", [:rebar3], [], "hexpm", "eb72b8365ffda5bed68a620d1da88525e326cb82a75ee61354fc24b844768041"},
}
39 changes: 39 additions & 0 deletions test/ecto_dot/diagram_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,16 @@ defmodule EctoDot.DiagramTest do
assert EctoDot.diagram(User) |> Diagram.to_dot() == expected
end

test "only one schema with embedded schemas and no self associations" do
expected = ~s"""
digraph "Diagram" {
#{Schema.from_ecto(EmbeddedUser) |> Schema.to_dot()}
}
"""

assert EctoDot.diagram(EmbeddedUser) |> Diagram.to_dot() == expected
end

test "only one schema with self associations" do
expected = ~s"""
digraph "Diagram" {
Expand All @@ -28,6 +38,18 @@ defmodule EctoDot.DiagramTest do
assert EctoDot.diagram(Post) |> Diagram.to_dot() == expected
end

test "only one embedded schema with self associations" do
expected = ~s"""
digraph "Diagram" {
#{Schema.from_ecto(EmbeddedPost) |> Schema.to_dot()}

#{assoc_dot(EmbeddedPost, :related)}
}
"""

assert EctoDot.diagram(EmbeddedPost) |> Diagram.to_dot() == expected
end

test "many schemas" do
expected = ~s"""
digraph "Diagram" {
Expand All @@ -44,6 +66,23 @@ defmodule EctoDot.DiagramTest do

assert EctoDot.diagram([User, Post, Comment]) |> Diagram.to_dot() == expected
end

test "many embedded schemas" do
expected = ~s"""
digraph "Diagram" {
#{Schema.from_ecto(EmbeddedUser) |> Schema.to_dot()}
#{Schema.from_ecto(EmbeddedPost) |> Schema.to_dot()}
#{Schema.from_ecto(EmbeddedComment) |> Schema.to_dot()}

#{assoc_dot(EmbeddedUser, :posts)}
#{assoc_dot(EmbeddedUser, :comments)}
#{assoc_dot(EmbeddedPost, :comments)}
#{assoc_dot(EmbeddedPost, :related)}
}
"""

assert EctoDot.diagram([EmbeddedUser, EmbeddedPost, EmbeddedComment]) |> Diagram.to_dot() == expected
end
end

defp assoc_dot(mod, name) do
Expand Down
7 changes: 7 additions & 0 deletions test/ecto_dot/schema_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,11 @@ defmodule EctoDot.SchemaTest do

assert Schema.from_ecto(User) |> Schema.to_dot() == expected
end

test "end to end for embedded schemas" do
expected =
~s("EmbeddedUser" [shape="record", label="{EmbeddedUser|id: binary_id\\lfirst_name: string\\lsurname: string\\lemail: string\\l}"])

assert Schema.from_ecto(EmbeddedUser) |> Schema.to_dot() == expected
end
end
61 changes: 61 additions & 0 deletions test/ecto_dot_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -65,5 +65,66 @@ defmodule EctoDotTest do
%Association{name: :related, from: Post, to: Post, cardinality: :many}
]
end

test "one module with an embedded schema" do
diag = EctoDot.diagram(EmbeddedUser)

assert diag.name == "Diagram"
assert diag.schemas == [Schema.from_ecto(EmbeddedUser)]
assert diag.associations == []
end

test "one module with an embedded schema and self associations" do
diag = EctoDot.diagram(EmbeddedPost)

assert diag.name == "Diagram"
assert diag.schemas == [Schema.from_ecto(EmbeddedPost)]

assert diag.associations == [
%Association{name: :related, from: EmbeddedPost, to: EmbeddedPost, cardinality: :many}
]
end

test "many modules with embedded schemas and no self associations" do
diag = EctoDot.diagram([EmbeddedUser, EmbeddedComment])

assert diag.name == "Diagram"
assert diag.schemas == [Schema.from_ecto(EmbeddedUser), Schema.from_ecto(EmbeddedComment)]

assert diag.associations == [
%Association{name: :comments, from: EmbeddedUser, to: EmbeddedComment, cardinality: :many}
]
end

test "many modules with embedded schemas and self associations" do
diag = EctoDot.diagram([EmbeddedUser, EmbeddedPost])

assert diag.name == "Diagram"
assert diag.schemas == [Schema.from_ecto(EmbeddedUser), Schema.from_ecto(EmbeddedPost)]

assert diag.associations == [
%Association{name: :posts, from: EmbeddedUser, to: EmbeddedPost, cardinality: :many},
%Association{name: :related, from: EmbeddedPost, to: EmbeddedPost, cardinality: :many}
]
end

test "all modules with embedded schemas" do
diag = EctoDot.diagram([EmbeddedUser, EmbeddedPost, EmbeddedComment])

assert diag.name == "Diagram"

assert diag.schemas == [
Schema.from_ecto(EmbeddedUser),
Schema.from_ecto(EmbeddedPost),
Schema.from_ecto(EmbeddedComment)
]

assert diag.associations == [
%Association{name: :posts, from: EmbeddedUser, to: EmbeddedPost, cardinality: :many},
%Association{name: :comments, from: EmbeddedUser, to: EmbeddedComment, cardinality: :many},
%Association{name: :comments, from: EmbeddedPost, to: EmbeddedComment, cardinality: :many},
%Association{name: :related, from: EmbeddedPost, to: EmbeddedPost, cardinality: :many}
]
end
end
end
11 changes: 11 additions & 0 deletions test/support/embedded_comment.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
defmodule EmbeddedComment do
use Ecto.Schema

embedded_schema do
field(:title, :string)
field(:body, :string)

belongs_to(:author, EmbeddedUser)
belongs_to(:post, Post)
end
end
12 changes: 12 additions & 0 deletions test/support/embedded_post.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
defmodule EmbeddedPost do
use Ecto.Schema

embedded_schema do
field(:title, :string)
field(:body, :string)

embeds_many(:comments, EmbeddedComment)
embeds_many(:related, EmbeddedPost)
belongs_to(:author, EmbeddedUser)
end
end
12 changes: 12 additions & 0 deletions test/support/embedded_user.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
defmodule EmbeddedUser do
use Ecto.Schema

embedded_schema do
field(:first_name, :string)
field(:surname, :string)
field(:email, :string)

embeds_many(:posts, EmbeddedPost)
embeds_many(:comments, EmbeddedComment)
end
end