- Notifications
You must be signed in to change notification settings - Fork334
Testable, composable, and adapter based Elixir email library for devs that love piping.
License
beam-community/bamboo
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
This README follows the main branch, which may not be the currently published version! Usethe docs for the published version of Bamboo.
Bamboo is now maintained by theBEAM Community!
Thank you tothoughtbot for creating and maintaining Bamboo for so long!
Flexible and easy to use email for Elixir.
- Built-in support for popular mail delivery services. Bamboo ships withadapters for several popular mail delivery services, including Mandrill,Mailgun, and SendGrid. It's also quite easy to writeyour own delivery adapter if your platform isn't yet supported.
- Deliver emails in the background. Most of the time you don't want or needto wait for the email to send. Bamboo makes it easy with
Mailer.deliver_later
. - A functional approach to mail delivery. Emails are created, manipulated,and sent using plain functions. This makes composition a breeze and fitsnaturally into your existing Elixir app.
- Unit test with ease. Bamboo separates email creation and email deliveryallowing you to test by asserting against email fields without the need forspecial functions.
- Dead-simple integration tests. Bamboo provides helper functions to makeintegration testing easy and robust.
- View sent emails during development. Bamboo provides a plug you can usein your router to view sent emails.
- Integrate with Phoenix out of the box. Use Phoenix views and layouts tomake rendering email easy.
See thedocs for the most up to date information.
We designed Bamboo to be simple and powerful. If you run intoanything thatis less than exceptional, or you just need some help, please open an issue.
To install Bamboo, add it to your list of dependencies inmix.exs
.
defdepsdo[{:bamboo,"~> 2.3.0"}]end
You may also use the latest code available from master instead of apublished version in hex:
defdepsdo[{:bamboo,github:"beam-community/bamboo"}]end
Once you've added Bamboo to your list, update your dependencies by running:
$ mix deps.get
If you are using Elixir < 1.4, also ensure Bamboo is started alongside yourapplication:
defapplicationdo[applications:[:bamboo]]end
Bamboo separates the tasks of email creation and email sending. To use Bamboo,you'll need to define one or more email modules (email creation), define amailer module (email sending), and provide some configuration.
To create emails, define an email module within your application.
# some/path/within/your/app/email.exdefmoduleMyApp.EmaildoimportBamboo.Emaildefwelcome_emaildonew_email(to:"john@example.com",from:"support@myapp.com",subject:"Welcome to the app.",html_body:"<strong>Thanks for joining!</strong>",text_body:"Thanks for joining!")endend
In addition to the keyword syntax above you can alsocompose emails using pipes.
To send emails, define a mailer module for your application thatuse
sBamboo's mailer.
# some/path/within/your/app/mailer.exdefmoduleMyApp.MailerdouseBamboo.Mailer,otp_app::my_append
Your configuration will need to know your OTP application, your mailer module,the adapter you are using, and any additional configuration required by theadapter itself.
# config/config.exsconfig:my_app,MyApp.Mailer,adapter:Bamboo.MandrillAdapter,api_key:"my_api_key"
Bamboo usesHackney for making requests.If you want to pass options to Hackney directly, such as controllingtimeouts, you can use thehackney_opts
key:
# config/config.exsconfig:my_app,MyApp.Mailer,adapter:Bamboo.MandrillAdapter,api_key:"my_api_key",hackney_opts:[recv_timeout::timer.minutes(1),connect_timeout::timer.minutes(1)]
Other adapter-specific configuration may be required. Be sure to check theadapter's docs.
Now that you have configured Bamboo and defined your modules, you can deliveremail in fitting places within your application.
defmoduleMyApp.SomeControllerPerhapsdodefsend_welcome_emaildoEmail.welcome_email()# Create your email|>Mailer.deliver_now!()# Send your emailendend
Your application is now set up to send email with Bamboo! 🎉
An adapter is a set of instructions for how to communicate with a specificemail delivery service. Bamboo ships with support forseveral popularservices, there are others made available by thecommunity, or you can use other services by writing a custom adapter.
To use an adapter, declare it in the configuration for your mailer:
# config/config.exsconfig:my_app,MyApp.Mailer,adapter:Bamboo.MandrillAdapter
Bamboo provides adapters for use in development and testing. To use theseadapters, declare them in the environment configuration.
The local adapterstores emails in memory that can be viewed duringdevelopment. Declare its use in your dev environment.
# config/dev.exsconfig:my_app,MyApp.Mailer,adapter:Bamboo.LocalAdapter
The test adapter sends emails to your running process allowing you to test maildelivery without emails being sent externally. Declare its use in your testenvironment.
# config/test.exsconfig:my_app,MyApp.Mailer,adapter:Bamboo.TestAdapter
You can create new adapters for any environment by implementing theBamboo.Adapter
behaviour.
Often times you don't want to send an email right away because it can blockprocess completion (e.g. a web request in Phoenix). Bamboo provides adeliver_later
function on your mailers to send emails in the background. Italso provides aBamboo.DeliverLaterStrategy
behaviour that you canimplement to tailor your background email sending.
By default,deliver_later
usesBamboo.TaskSupervisorStrategy
. Thisstrategy sends the email right away, but it does so in the background withoutlinking to the calling process, so errors in the mailer won't bring down yourapp.
You can also create custom strategies by implementing theBamboo.DeliverLaterStrategy
behaviour. For example, you could createstrategies for adding emails to a background processing queue such asexq ortoniq.
In addition to creating emails with keyword lists you, can use pipe syntax tocompose emails. This is particularly useful for providing defaults (e.g. fromaddress, default layout, etc.)
defmoduleMyApp.EmaildoimportBamboo.EmailimportBamboo.Phoenixdefwelcome_emaildobase_email()# Build your default email then customize for welcome|>to("foo@bar.com")|>subject("Welcome!!!")|>put_header("Reply-To","someone@example.com")|>html_body("<strong>Welcome</strong>")|>text_body("Welcome")enddefpbase_emaildonew_email()|>from("myapp@example.com")# Set a default from|>put_html_layout({MyApp.LayoutView,"email.html"})# Set default layout|>put_text_layout({MyApp.LayoutView,"email.text"})# Set default text layoutendend
The from, to, cc, and bcc addresses can be a string or a 2 element tuple. Whathappens if you try to send to a list ofMyApp.User
s? Transforming your datastructure each time you send an email would be a pain.
# This stinks. Do you want to do this every time you create a new email?users=foruser<-usersdo{user.name,user.email}endnew_email(to:users)
Bamboo alleviates this pain by providing theBamboo.Formatter
protocol. Byimplementing the protocol for your data structure once, you can pass thatstruct directly to Bamboo anywhere it expects an address. See theBamboo.Email
andBamboo.Formatter
docs for more information andexamples.
It's possible to configure per Mailer interceptors. Interceptors allow you tomodify or block emails on the fly.
# config/config.exsconfig:my_app,MyApp.Mailer,adapter:Bamboo.MandrillAdapter,interceptors:[MyApp.DenyListInterceptor]end
An interceptor must implement theBamboo.Interceptor
behaviour. To preventemail being sent, you can block it withBamboo.Email.block/1
.
# some/path/within/your/app/deny_list_interceptor.exdefmoduleMyApp.DenyListInterceptordo@behaviourBamboo.Interceptor@deny_list["bar@foo.com"]defcall(email)doifemail.toin@deny_listdoBamboo.Email.block(email)elseemailendendend
Phoenix is not required to use Bamboo. But if you want to use Phoenix's viewsand layouts to render emails, seebamboo_phoenix
andBamboo.Phoenix
.
Bamboo comes with a handy plug for viewing emails sent in development. Now youdon't have to look at the logs to get password resets, confirmation links, etc.Just open up the sent email viewer and click the link.
SeeBamboo.SentEmailViewerPlug
.
Here is what it looks like:
Mandrill offers extra features on top of regular SMTP email like tagging, mergevars, templates, and scheduling emails to send in the future. SeeBamboo.MandrillHelper
.
SendGrid offers extra features on top of regular SMTP email like transactionaltemplates with substitution tags. SeeBamboo.SendGridHelper
.
Bamboo comes with JSON support out of the box via theJason library. To useit, add:jason
to your dependencies:
{:jason,"~> 1.0"}
You can customize it to use another library via the:json_library
configuration:
config:bamboo,:json_library,SomeOtherLib
Bamboo separates email creation and email sending. Test email creation byasserting against the email struct created by your functions. For example,assuming your welcome email accepts a user recipient, provides the correct fromaddress, and provides specific text, you might test like this:
defmoduleMyApp.EmailTestdouseExUnit.Casetest"welcome email"douser={"Ralph","ralph@example.com"}email=MyApp.Email.welcome_email(user)assertemail.to==userassertemail.from=="welcome@myapp.com"assertemail.html_body=~"<p>Thanks for joining</p>"assertemail.text_body=~"Thanks for joining"endend
Test email sending in integration tests by using theBamboo.TestAdapter
along withBamboo.Test
. For example, assuming during the registrationprocess of your app an email is sent to the user welcoming them to theapplication, you might test this feature like this:
defmoduleMyApp.RegistrationTestdouseExUnit.CaseuseBamboo.Test# Remember to use the `Bamboo.TestAdapter` in your test configtest"after registering, the user gets a welcome email"douser=new_user()expected_email=MyApp.Email.welcome_email(user.email)MyApp.Registration.create(user)assert_delivered_emailexpected_emailenddefpnew_userdo# Build a user appropriate to your applicationendend
See the documentation forBamboo.Test
for more examples and additionalhelper functions.
Here is a list of adapters that either ship with Bamboo or have been madeavailable by the community. Feel free to open an issue or a PR if you'd like toadd a new adapter to the list.
Bamboo.LocalAdapter
- Ships with Bamboo. Stores email in memory. Great for local development.Bamboo.MailgunAdapter
- Ships with Bamboo. Thanks to@princemaple.Bamboo.MandrillAdapter
- Ships with Bamboo.Bamboo.SendGridAdapter
- Ships with Bamboo.Bamboo.TestAdapter
- Ships with Bamboo. Use in your test environment.Bamboo.CampaignMonitorAdapter
- Seejackmarchant/bamboo_campaign_monitor.Bamboo.ConfigAdapter
- SeeBinaryNoggin/bamboo_config_adapter declare config at runtime.Bamboo.FallbackAdapter
- Seeprosapient/bamboo_fallback. Allows using multiple adapters.Bamboo.GmailAdapter
- Seeparkerduckworth/bamboo_gmail.Bamboo.MailjetAdapter
- Seemoxide/bamboo_mailjet.Bamboo.PostmarkAdapter
- Seepablo-co/bamboo_postmark.Bamboo.SendcloudAdapter
- Seelinjunpop/bamboo_sendcloud.Bamboo.SesAdapter
- Seekalys/bamboo_ses.Bamboo.SMTPAdapter
- Seefewlinesco/bamboo_smtp.Bamboo.SparkPostAdapter
- Seeandrewtimberlake/bamboo_sparkpost.Bamboo.MailtrapSendingAdapter
,Bamboo.MailtrapSandboxAdapter
- Seerailsware/mailtrap-elixir
Before opening a pull request, please open an issue first.
Once we've decided how to move forward with a pull request:
$ git clone https://github.com/beam-community/bamboo.git$ cd bamboo$ mix deps.get$ mix test$ mix format
Once you've made your additions andmix test
passes, go ahead and open a PR!
We run the test suite as well as formatter checks on CI. Make sure you are usingthe Elixir version defined in the.tool-versions
file to have consistentformatting with what's being run on CI.
About
Testable, composable, and adapter based Elixir email library for devs that love piping.