Skip to content

Commit

Permalink
No longer requires recompilation of previews to update the Gallery (r…
Browse files Browse the repository at this point in the history
…emoteoss#2)

* Evaluates preview details during runtime

* Documents static gallery generation

* Better specs

* Replaces previews/0 and groups/0 by get/0

Co-authored-by: Vitor Cavalcanti <[email protected]>
Co-authored-by: Lucas Mazza <[email protected]>
  • Loading branch information
3 people authored May 17, 2022
1 parent 7320c25 commit ce52be4
Show file tree
Hide file tree
Showing 7 changed files with 99 additions and 49 deletions.
15 changes: 14 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,22 @@ You can see Swoosh.Gallery in action with the Phoenix app included on `./sample`

1. Run `mix do deps.get, phx.server`
2. Go to `http://localhost:4000/dev/emails`


### Static gallery

You can also generate static HTML files for you Gallery. This is useful when you want to expose the gallery without the need of a server.

```bash
mix swoosh.gallery.html --gallery Sample.Gallery --path="./_build/gallery"

open _build/gallery/index.html_
```


## Contributing

1. Download the project.
2. Run `mix do deps.get, tailwind.install`
3. Make some changes.
4. If you need add new tailwind styles, run `mix tailwind default`.
4. If you need add new tailwind styles, run `mix tailwind default`.
7 changes: 4 additions & 3 deletions lib/mix/tasks/swoosh.gallery.html.ex
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ defmodule Mix.Tasks.Swoosh.Gallery.Html do
|> Module.concat()
|> Code.ensure_compiled!()
|> tap(fn mod ->
unless function_exported?(mod, :previews, 0) do
unless function_exported?(mod, :get, 0) do
Mix.raise("""
The module #{inspect(mod)} is not a valid gallery. Make sure it uses Swoosh.Gallery:
Expand All @@ -72,13 +72,14 @@ defmodule Mix.Tasks.Swoosh.Gallery.Html do
""")
end
end)
|> tap(&ensure_required_functions!(&1.previews))
|> then(& &1.get())
|> tap(&ensure_required_functions!(&1))
else
Mix.raise("No gallery available. Please pass a gallery with the --gallery option")
end
end

defp ensure_required_functions!(previews) when is_list(previews) do
defp ensure_required_functions!(%{previews: previews}) when is_list(previews) do
Enum.each(previews, fn %{email_mfa: {module, _fun, _args}} ->
ensure_required_functions!(module)
end)
Expand Down
54 changes: 42 additions & 12 deletions lib/swoosh/gallery.ex
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ defmodule Swoosh.Gallery do
@group_path nil

def init(opts) do
Keyword.put(opts, :gallery, __MODULE__)
Keyword.put(opts, :gallery, __MODULE__.get())
end

def call(conn, opts) do
Expand All @@ -106,9 +106,10 @@ defmodule Swoosh.Gallery do
defmacro __before_compile__(_env) do
quote do
@doc false
def previews, do: @previews

def groups, do: @groups
def get do
previews = eval_details(@previews)
%{previews: previews, groups: @groups}
end
end
end

Expand All @@ -131,14 +132,14 @@ defmodule Swoosh.Gallery do
defmacro preview(path, module) do
path = validate_path(path)
module = Macro.expand(module, __ENV__)
preview_details = Macro.escape(eval_preview_details(module))
validate_preview_details!(module)

quote do
@previews %{
group: @group_path,
path: build_preview_path(@group_path, unquote(path)),
email_mfa: {unquote(module), :preview, []},
preview_details: unquote(preview_details)
details_mfa: {unquote(module), :preview_details, []}
}
end
end
Expand Down Expand Up @@ -187,18 +188,47 @@ defmodule Swoosh.Gallery do
end
end

# Evaluates a preview. It loads the results of email_mfa into the email property.
# Evaluates a preview. It loads the results of email_mfa and details_mfa into the email
# and preview_details properties respectively.
@doc false
@spec eval_preview(%{:email_mfa => {module(), atom(), list()}}) :: map()
@spec eval_preview(%{
:email_mfa => {module(), atom(), list()},
:details_mfa => {module(), atom(), list()}
}) :: map()
def eval_preview(%{email: _email} = preview), do: preview

def eval_preview(%{email_mfa: {module, fun, opts}} = preview) do
def eval_preview(preview) do
preview
|> eval_email()
|> eval_details()
end

defp eval_email(%{email_mfa: {module, fun, opts}} = preview) do
Map.put(preview, :email, apply(module, fun, opts))
end

# Evaluates preview details. It loads the results of details_mfa into the
# preview_details property.
@doc false
@spec eval_details(
%{:details_mfa => {module(), atom(), list()}}
| list(%{:details_mfa => {module(), atom(), list()}})
) :: map()
def eval_details(%{preview_details: _details} = preview), do: preview

def eval_details(%{details_mfa: {module, fun, opts}} = preview) do
Map.put(preview, :preview_details, validate_preview_details!(module, fun, opts))
end

def eval_details(previews) when is_list(previews) do
Enum.map(previews, fn %{details_mfa: _mfa} = preview ->
eval_details(preview)
end)
end

# Evaluates a preview and reads the attachment at a given index position.
@doc false
@spec read_email_attachment_at(%{email_mfa: {atom, atom, list}}, integer()) ::
@spec read_email_attachment_at(%{email_mfa: {module, atom, list}}, integer()) ::
{:error, :invalid_attachment | :not_found}
| {:ok, %{content_type: String.t(), data: any}}
def read_email_attachment_at(preview, index) do
Expand All @@ -223,9 +253,9 @@ defmodule Swoosh.Gallery do
end
end

defp eval_preview_details(module) do
defp validate_preview_details!(module, fun \\ :preview_details, opts \\ []) do
module
|> apply(:preview_details, [])
|> apply(fun, opts)
|> Keyword.validate!([:title, :description, tags: []])
|> Map.new()
|> tap(&ensure_title!/1)
Expand Down
2 changes: 1 addition & 1 deletion lib/swoosh/gallery/plug.ex
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
defmodule Swoosh.Gallery.Plug do
@moduledoc """
Plug to mount a `Swoosh.Gallery` into an existing Phoenix/Plug application. Gallery themselves
will forward `init/1` and `call/2` to this dmoule, so users don't need to use it directly.
will forward `init/1` and `call/2` to this module, so users don't need to use it directly.
## Examples
Expand Down
12 changes: 6 additions & 6 deletions test/mix/tasks/swoosh.gallery.html_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@ defmodule Mix.Tasks.Swoosh.Gallery.HtmlTest do
assert File.dir?(tmp_dir)

assert sorted_ls!(tmp_dir) == [
"auth.reset_password",
"auth.reset_password.html",
"index.html",
"reset_password",
"reset_password.html",
"welcome",
"welcome.html"
]

assert sorted_ls!("#{tmp_dir}/reset_password") == ["preview.html"]
assert sorted_ls!("#{tmp_dir}/auth.reset_password") == ["preview.html"]
assert sorted_ls!("#{tmp_dir}/welcome") == ["attachments", "preview.html"]
assert sorted_ls!("#{tmp_dir}/welcome/attachments") == ["0"]
assert sorted_ls!("#{tmp_dir}/welcome/attachments/0") == ["my_file.txt"]
Expand All @@ -44,7 +44,7 @@ defmodule Mix.Tasks.Swoosh.Gallery.HtmlTest do
test "has links to the previews", %{tmp_dir: tmp_dir} do
run_task(Support.Gallery, tmp_dir)
contents = File.read!("#{tmp_dir}/index.html")
assert contents =~ "a href=\"./reset_password.html\""
assert contents =~ "a href=\"./auth.reset_password.html\""
assert contents =~ "a href=\"./welcome.html\""
end

Expand All @@ -58,7 +58,7 @@ defmodule Mix.Tasks.Swoosh.Gallery.HtmlTest do

test "accessing a preview shows the email as text", %{tmp_dir: tmp_dir} do
run_task(Support.Gallery, tmp_dir)
contents = File.read!("#{tmp_dir}/reset_password.html")
contents = File.read!("#{tmp_dir}/auth.reset_password.html")
assert contents =~ "Reset Password"
assert contents =~ "Sends instructions on how to reset password"
assert contents =~ "passwords: yes"
Expand All @@ -67,7 +67,7 @@ defmodule Mix.Tasks.Swoosh.Gallery.HtmlTest do

test "accessing a preview.html shows the email as html", %{tmp_dir: tmp_dir} do
run_task(Support.Gallery, tmp_dir)
contents = File.read!("#{tmp_dir}/reset_password/preview.html")
contents = File.read!("#{tmp_dir}/auth.reset_password/preview.html")

assert contents ==
"Please, reset your password <a href=\"http://reset.pw\">here</a>."
Expand Down
5 changes: 4 additions & 1 deletion test/support/gallery.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,8 @@ defmodule Support.Gallery do
use Swoosh.Gallery

preview("/welcome", Support.Emails.WelcomeEmail)
preview("/reset_password", Support.Emails.ResetPasswordEmail)

group "/auth", title: "Auth" do
preview("/reset_password", Support.Emails.ResetPasswordEmail)
end
end
53 changes: 28 additions & 25 deletions test/swoosh/gallery_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -8,30 +8,33 @@ defmodule Swoosh.GalleryTest do

describe "previews/0" do
test "returns the list of previews" do
assert previews = Support.Gallery.previews()
assert previews = Support.Gallery.get()

assert [
%{
preview_details: %{
description: "Sends instructions on how to reset password",
tags: [passwords: "yes"],
title: "Reset Password"
assert %{
previews: [
%{
preview_details: %{
description: "Sends instructions on how to reset password",
tags: [passwords: "yes"],
title: "Reset Password"
},
path: "auth.reset_password",
email_mfa: {Support.Emails.ResetPasswordEmail, :preview, []},
group: "auth"
},
path: "reset_password",
email_mfa: {Support.Emails.ResetPasswordEmail, :preview, []},
group: nil
},
%{
preview_details: %{
description: "Sends a warm welcome to the user",
tags: [attachments: "yes"],
title: "Welcome"
},
path: "welcome",
email_mfa: {Support.Emails.WelcomeEmail, :preview, []},
group: nil
}
] == previews
%{
preview_details: %{
description: "Sends a warm welcome to the user",
tags: [attachments: "yes"],
title: "Welcome"
},
path: "welcome",
email_mfa: {Support.Emails.WelcomeEmail, :preview, []},
group: nil
}
],
groups: [%{path: "auth", title: "Auth"}]
} = previews
end
end

Expand All @@ -46,7 +49,7 @@ defmodule Swoosh.GalleryTest do
test "has links to the previews" do
response = Router.call(conn(:get, "/gallery"), [])
assert response.status == 200
assert response.resp_body =~ "a href=\"/gallery/reset_password\""
assert response.resp_body =~ "a href=\"/gallery/auth.reset_password\""
assert response.resp_body =~ "a href=\"/gallery/welcome\""
end

Expand All @@ -59,7 +62,7 @@ defmodule Swoosh.GalleryTest do
end

test "accessing a preview shows the email as text" do
response = Router.call(conn(:get, "/gallery/reset_password"), [])
response = Router.call(conn(:get, "/gallery/auth.reset_password"), [])
assert response.status == 200
assert response.resp_body =~ "Reset Password"
assert response.resp_body =~ "Sends instructions on how to reset password"
Expand All @@ -68,7 +71,7 @@ defmodule Swoosh.GalleryTest do
end

test "accessing a preview.html shows the email as html" do
response = Router.call(conn(:get, "/gallery/reset_password/preview.html"), [])
response = Router.call(conn(:get, "/gallery/auth.reset_password/preview.html"), [])
assert response.status == 200

assert response.resp_body ==
Expand Down

0 comments on commit ce52be4

Please sign in to comment.