# Bamboo
Flexible and easy to use email for Elixir.
* Adapter based so it can be used with Mandrill, SMTP, or whatever else you want. Comes with a Mandrill adapter out of the box.
* Easy to format recipients. You can do `new_email(to: Repo.one(User))` and Bamboo can format the user automatically.
* Works out of the box with Phoenix. Use views and layouts to make rendering email easy.
* Very composable. Emails are just a Bamboo.Email struct and be manipulated with plain functions.
* Easy to unit test. Because delivery is separated from email creation, no special functions are needed, just assert against fields on the email.
* Easy to test delivery in integration tests. As little repeated code as possible.
See the module docs for the most up to date information.
## Usage
Bamboo breaks email creation and email sending in to two separate modules.
```elixir
# In your config/config.exs file
config :my_app, MyApp.Mailer,
adapter: Bamboo.MandrillAdapter,
api_key: "my_api_key"
# Somewhere in your application
defmodule MyApp.Mailer do
use Bamboo.Mailer, otp_app: :my_app
end
# Define your emails
defmodule MyApp.Emails do
import Bamboo.Email
def welcome_email do
new_mail(
to: "foo@example.com",
from: "me@example.com",
subject: "Welcome!!!",
html_body: "<strong>Welcome</strong>",
text_body: "welcome"
)
end
end
# In a controller or some other module
defmodule MyApp.Foo do
alias MyApp.Emails
alias MyApp.Mailer
def register_user do
# Create a user and whatever else is needed
# Emails are not delivered until you explicitly deliver them.
Emails.welcome_email |> Mailer.deliver
end
end
```
## Composing with pipes. Use for default from address, default layouts, etc.
```elixir
defmodule MyApp.Emails do
# Adds a `render` function for rending emails with a Phoenix view
use Bamboo.Phoenix, view: MyApp.EmailView
import Bamboo.MandrillEmails
def welcome_email do
base_email
# Emails addresses can be a string
|> to("foo@bar.com")
# or a 2 item tuple
|> bcc({"John Smith", "john@gmail.com"})
# or you can set up a custom protocol that handles different types of structs.
|> cc(author_from_db())
|> subject("Welcome!!!")
# Imported by Bamboo.MandrillEmails
|> tag("welcome-email")
|> put_header("Reply-To", "somewhere@example.com")
# Uses the view from `use Bamboo.Phoenix, view: View` to render the `welcome_email.html.eex`
# and `welcome_email.text.eex` templates with the passed in assigns.
# Use a string to render a specific template, e.g. `welcome_email.html.eex`
|> render(:welcome_email, author: author)
end
defp author_from_db do
User |> Repo.one
end
defp base_email do
# Set a default from, default headers, etc.
mail(from: "myapp@example.com")
end
end
defimpl Bamboo.Formatter, for: User do
# Used by to, bcc, cc and from
def format_email_address(user, _opts) do
fullname = "#{user.first_name} #{user.last_name}"
{fullname, user.email}
end
end
```
## In development (coming soonish)
You can see the sent emails by forwarding a route to the `Bamboo.Preview`
module. You can see all the emails sent. It will live update with new emails
sent.
```elixir
# In your Phoenix router
forward "/delivered_emails", Bamboo.SentEmailController
# In your browser
localhost:4000/delivered_emails
```
## Testing
You can use the `Bamboo.TestAdapter` to make testing your emails a piece of cake.
See documentation for `Bamboo.Test` for more examples.
```elixir
# Use the Bamboo.TestAdapter in your config/test.exs file
config :my_app, MyApp.Mailer,
adapter: Bamboo.TestAdapter
# Unit testing requires no special functions
defmodule MyApp.EmailsTest do
use ExUnit.Case
alias MyApp.Emails
test "welcome email" do
user = %User{...}
email = Emails.welcome_email(user)
assert email.to == "someone@foo.com"
assert email.subject == "This is your welcome email"
assert email.html_body =~ "Welcome to the app!"
end
end
# Integration tests
defmodule MyApp.RegistrationControllerTest do
use ExUnit.Case
use Bamboo.Test
alias MyApp.Emails
test "registers user and sends welcome email" do
...post to registration controller
newly_created_user = Repo.first(User)
assert_delivered_email Emails.welcome_email(newly_created_user)
end
end
```
## Installation
To use the latest from master.
1. Add bamboo to your list of dependencies in `mix.exs`:
def deps do
[{:bamboo, github: "paulcsmith/bamboo"}]
end
2. Ensure bamboo is started before your application:
def application do
[applications: [:bamboo]]
end
3. Add the the Bamboo.TaskSupervior as a child to your supervisor
```elixir
# Usually in lib/my_app_name/my_app_name.ex
def start(_type, _args) do
import Supervisor.Spec
children = [
# Add the supervisor that handles deliver_later calls
Bamboo.TaskSupervisorStrategy.child_spec
]
# This part is usually already there.
opts = [strategy: :one_for_one, name: MyApp.Supervisor]
Supervisor.start_link(children, opts)
end
```