Skip to content

Commit

Permalink
Add support for Phoenix views
Browse files Browse the repository at this point in the history
  • Loading branch information
alexgaribay committed Jul 4, 2017
1 parent 7c4ec57 commit d1e113c
Show file tree
Hide file tree
Showing 8 changed files with 194 additions and 16 deletions.
106 changes: 104 additions & 2 deletions lib/sendgrid/email.ex
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ defmodule SendGrid.Email do
custom_args: nil,
send_at: nil,
headers: nil,
attachments: nil
attachments: nil,
__phoenix_view__: nil


@type t :: %Email{to: nil | [recipient],
Expand All @@ -47,7 +48,8 @@ defmodule SendGrid.Email do
custom_args: nil | custom_args,
send_at: nil | integer,
headers: nil | [header],
attachments: nil | [attachment]}
attachments: nil | [attachment],
__phoenix_view__: nil | atom}

@type recipient :: %{ email: String.t, name: String.t | nil }
@type content :: %{ type: String.t, value: String.t }
Expand Down Expand Up @@ -340,4 +342,104 @@ defmodule SendGrid.Email do
defp add_address_to_list(list, email, name) when is_list(list) do
list ++ [address(email, name)]
end

@doc """
Sets the Phoenix View to use.
This will override the default Phoenix View if set in under the `:phoenix_view`
config value.
## Examples
Email.put_phoenix_view(email, MyApp.Web.EmailView)
"""
@spec put_phoenix_view(t, atom) :: t
def put_phoenix_view(%Email{} = email, module) when is_atom(module) do
%Email{email | __phoenix_view__: module}
end

@doc """
Renders the Phoenix template with the given assigns.
You can set the default Phoenix View to use for your templates by setting the `:phoenix_view` config value.
Additionally, you can set the view on a per email basis by calling `put_phoenix_view/2`.
## Explicit Template Extensions
You can provide a template name with an explicit extension such as `"some_template.html"` or
`"some_template.txt"`. This is set the content of the email respective to the content type of
the template rendered. For example, if you render an HTML template, the output of the rendering
will be the HTML content of the email.
## Implicit Template Extensions
You can omit a template's extension and attempt to have both a text template and HTML template
rendered. To have both types rendered, both templates must share the same base file name. For
example, if you have a template named `"some_template.txt"` and a template named `"some_template.html"`
and you call `put_phoenix_template(email, "some_template")`, both templates will be used and will
set the email content for both content types. The only caveat is *both files must exist*, otherwise you'll
have an exception raised.
## Examples
iex> Email.put_phoenix_template(email, "some_template.html")
%Email{content: [%{type: "text/html", value: ...}], ...}
iex> Email.put_phoenix_template(email, "some_template.txt", name: "John Doe")
%Email{content: [%{type: "text/plain", value: ...}], ...}
iex> Email.put_phoenix_template(email, "some_template", user: user)
%Email{content: [%{type: "text/plain", value: ...}, %{type: "text/html", value: ...}], ...}
"""
@spec put_phoenix_template(t, String.t, []) :: t
def put_phoenix_template(%Email{} = email, template_name, assigns \\ []) do
with true <- ensure_phoenix_loaded(),
view_mod <- phoenix_view_module(email) do
case Path.extname(template_name) do
".html" ->
render_html(email, view_mod, template_name, assigns)
".txt" ->
render_text(email, view_mod, template_name, assigns)
_ ->
email
|> render_html(view_mod, template_name <> ".html", assigns)
|> render_text(view_mod, template_name <> ".txt", assigns)
end
end
end

defp render_html(email, view_mod, template_name, assigns) do
html = Phoenix.View.render_to_string(view_mod, template_name, assigns)
put_html(email, html)
end

defp render_text(email, view_mod, template_name, assigns) do
text = Phoenix.View.render_to_string(view_mod, template_name, assigns)
put_text(email, text)
end

defp ensure_phoenix_loaded do
unless Code.ensure_loaded?(Phoenix) do
raise ArgumentError, "Attempted to call function that depends on Phoenix. " <>
"Make sure Phoenix is part of your dependencies"
end
true
end

defp phoenix_view_module(%Email{__phoenix_view__: nil}) do
mod = config(:phoenix_view)
unless mod do
raise ArgumentError, "Phoenix view is expected to be set or configured. " <>
"Ensure your config for :sendgrid includes a value for :phoenix_view or" <>
"explicity set the Phoenix view with `put_phoenix_view/2`."
end
mod
end
defp phoenix_view_module(%Email{__phoenix_view__: view_module}), do: view_module

defp config(key) do
Application.get_env(:sendgrid, key)
end
end
20 changes: 16 additions & 4 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ defmodule SendGrid.Mixfile do
version: "1.4.0",
elixir: "~> 1.4",
package: package(),
compilers: compilers(Mix.env),
description: description(),
source_url: project_url(),
homepage_url: project_url(),
elixirc_paths: elixirc_paths(Mix.env),
build_embedded: Mix.env == :prod,
start_permanent: Mix.env == :prod,
deps: deps()]
Expand All @@ -22,12 +24,22 @@ defmodule SendGrid.Mixfile do
]
end

# Use Phoenix compiler depending on environment.
defp compilers(:test), do: [:phoenix] ++ Mix.compilers()
defp compilers(_), do: Mix.compilers()

# Specifies which paths to compile per environment.
defp elixirc_paths(:test), do: ["lib", "test/support"]
defp elixirc_paths(_), do: ["lib"]

defp deps do
[
{:earmark, "~> 1.2", only: :dev},
{:ex_doc, "~> 0.16.2", only: :dev},
{:httpoison, "~> 0.11.0"},
{:poison, "~> 3.0"}
{:earmark, "~> 1.2", only: :dev},
{:ex_doc, "~> 0.16.2", only: :dev},
{:httpoison, "~> 0.11.0"},
{:poison, "~> 3.1", override: true},
{:phoenix, "~> 1.2", only: :test},
{:phoenix_html, "~> 2.9", only: :test}
]
end

Expand Down
7 changes: 7 additions & 0 deletions mix.lock
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
%{"certifi": {:hex, :certifi, "0.7.0", "861a57f3808f7eb0c2d1802afeaae0fa5de813b0df0979153cbafcd853ababaf", [:rebar3], []},
"earmark": {:hex, :earmark, "1.2.2", "f718159d6b65068e8daeef709ccddae5f7fdc770707d82e7d126f584cd925b74", [:mix], []},
"ex_doc": {:hex, :ex_doc, "0.16.2", "3b3e210ebcd85a7c76b4e73f85c5640c011d2a0b2f06dcdf5acdb2ae904e5084", [:mix], [{:earmark, "~> 1.1", [hex: :earmark, optional: false]}]},
"floki": {:hex, :floki, "0.17.2", "81b3a39d85f5cae39c8da16236ce152f7f8f50faf84b480ba53351d7e96ca6ca", [:mix], [{:mochiweb, "~> 2.15", [hex: :mochiweb, optional: false]}]},
"hackney": {:hex, :hackney, "1.6.5", "8c025ee397ac94a184b0743c73b33b96465e85f90a02e210e86df6cbafaa5065", [:rebar3], [{:certifi, "0.7.0", [hex: :certifi, optional: false]}, {:idna, "1.2.0", [hex: :idna, optional: false]}, {:metrics, "1.0.1", [hex: :metrics, optional: false]}, {:mimerl, "1.0.2", [hex: :mimerl, optional: false]}, {:ssl_verify_fun, "1.1.1", [hex: :ssl_verify_fun, optional: false]}]},
"httpoison": {:hex, :httpoison, "0.11.0", "b9240a9c44fc46fcd8618d17898859ba09a3c1b47210b74316c0ffef10735e76", [:mix], [{:hackney, "~> 1.6.3", [hex: :hackney, optional: false]}]},
"idna": {:hex, :idna, "1.2.0", "ac62ee99da068f43c50dc69acf700e03a62a348360126260e87f2b54eced86b2", [:rebar3], []},
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], []},
"mime": {:hex, :mime, "1.1.0", "01c1d6f4083d8aa5c7b8c246ade95139620ef8effb009edde934e0ec3b28090a", [:mix], []},
"mimerl": {:hex, :mimerl, "1.0.2", "993f9b0e084083405ed8252b99460c4f0563e41729ab42d9074fd5e52439be88", [:rebar3], []},
"mochiweb": {:hex, :mochiweb, "2.15.0", "e1daac474df07651e5d17cc1e642c4069c7850dc4508d3db7263a0651330aacc", [:rebar3], []},
"phoenix": {:hex, :phoenix, "1.2.4", "4172479b5e21806a5e4175b54820c239e0d4effb0b07912e631aa31213a05bae", [:mix], [{:cowboy, "~> 1.0", [hex: :cowboy, optional: true]}, {:phoenix_pubsub, "~> 1.0", [hex: :phoenix_pubsub, optional: false]}, {:plug, "~> 1.4 or ~> 1.3.3 or ~> 1.2.4 or ~> 1.1.8 or ~> 1.0.5", [hex: :plug, optional: false]}, {:poison, "~> 1.5 or ~> 2.0", [hex: :poison, optional: false]}]},
"phoenix_html": {:hex, :phoenix_html, "2.9.3", "1b5a2122cbf743aa242f54dced8a4f1cc778b8bd304f4b4c0043a6250c58e258", [:mix], [{:plug, "~> 1.0", [hex: :plug, optional: false]}]},
"phoenix_pubsub": {:hex, :phoenix_pubsub, "1.0.2", "bfa7fd52788b5eaa09cb51ff9fcad1d9edfeb68251add458523f839392f034c1", [:mix], []},
"plug": {:hex, :plug, "1.3.5", "7503bfcd7091df2a9761ef8cecea666d1f2cc454cbbaf0afa0b6e259203b7031", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1", [hex: :cowboy, optional: true]}, {:mime, "~> 1.0", [hex: :mime, optional: false]}]},
"poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], []},
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.1", "28a4d65b7f59893bc2c7de786dec1e1555bd742d336043fe644ae956c3497fbe", [:make, :rebar], []},
"ssl_verify_hostname": {:hex, :ssl_verify_hostname, "1.0.5"}}
71 changes: 61 additions & 10 deletions test/email_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -178,16 +178,16 @@ defmodule SendGrid.Email.Test do
assert email.send_at == time
end

test "email" do
assert :ok ==
Email.build()
|> Email.add_to(@email)
|> Email.put_from(@email)
|> Email.put_subject("Test")
|> Email.put_text("123")
|> Email.put_html("<p>123</p>")
|> SendGrid.Mailer.send()
end
# test "email" do
# assert :ok ==
# Email.build()
# |> Email.add_to(@email)
# |> Email.put_from(@email)
# |> Email.put_subject("Test")
# |> Email.put_text("123")
# |> Email.put_html("<p>123</p>")
# |> SendGrid.Mailer.send()
# end

describe "add_attachemnt/2" do
test "adds a single attachemnt" do
Expand All @@ -208,4 +208,55 @@ defmodule SendGrid.Email.Test do
assert Enum.count(email.attachments) == 2
end
end

defmodule EmailView do
use Phoenix.View, root: "test/support/templates", namespace: SendGrid.Email.Test
end

test "put_phoenix_view/2" do
result =
Email.build()
|> Email.put_phoenix_view(SendGrid.Email.Test.EmailView)

assert %Email{__phoenix_view__: SendGrid.Email.Test.EmailView} = result
end

describe "put_phoenix_template/2" do
test "renders templates with explicit extensions" do
# HTML
result =
Email.build()
|> Email.put_phoenix_view(SendGrid.Email.Test.EmailView)
|> Email.put_phoenix_template("test.html", test: "awesome")
assert %Email{content: [%{type: "text/html", value: "<p>awesome</p>"}]} = result

# Text
result =
Email.build()
|> Email.put_phoenix_view(SendGrid.Email.Test.EmailView)
|> Email.put_phoenix_template("test.txt", test: "awesome")
assert %Email{content: [%{type: "text/plain", value: "awesome"}]} = result
end

test "renders templates with implicit extensions" do
result =
Email.build()
|> Email.put_phoenix_template("test", test: "awesome")

assert %Email{content: [%{type: "text/plain", value: "awesome"}, %{type: "text/html", value: "<p>awesome</p>"}]} = result
end

test "raises when a template doesn't exist for implicit extensions" do
assert_raise Phoenix.Template.UndefinedError, fn ->
Email.put_phoenix_template(Email.build(), "test2")
end
end

test "renders using the configured phoenix view" do
result =
Email.build()
|> Email.put_phoenix_template("test.txt", test: "awesome")
assert %Email{content: [%{type: "text/plain", value: "awesome"}]} = result
end
end
end
4 changes: 4 additions & 0 deletions test/support/email_view.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
defmodule SendGrid.EmailView do
use Phoenix.View, root: "test/support/templates",
namespace: SendGrid
end
1 change: 1 addition & 0 deletions test/support/templates/email/test.html.eex
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<p><%= @test %></p>
1 change: 1 addition & 0 deletions test/support/templates/email/test.txt.eex
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<%= @test %>
Empty file.

0 comments on commit d1e113c

Please sign in to comment.