Giter Site home page Giter Site logo

thoughtbot / bamboo Goto Github PK

View Code? Open in Web Editor NEW
1.9K 25.0 339.0 794 KB

Testable, composable, and adapter based Elixir email library for devs that love piping.

Home Page: https://hex.pm/packages/bamboo

License: MIT License

Elixir 95.49% HTML 4.38% Shell 0.13%
elixir email elixir-phoenix bamboo

bamboo's People

Contributors

aellispierce avatar bartoszgorka avatar bgentry avatar bratsche avatar brian-penguin avatar dependabot-support avatar dependabot[bot] avatar drapergeek avatar germsvel avatar jbernardo95 avatar jerodsanto avatar jonrowe avatar jsteiner avatar kalys avatar lancejjohnson avatar leaexplores avatar linjunpop avatar maartenvanvliet avatar maymillerricci avatar mtarnovan avatar paulcsmith avatar princemaple avatar randycoulman avatar rawkode avatar ruudk avatar smdern avatar stevedomin avatar tomtaylor avatar tsubery avatar ymtszw avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

bamboo's Issues

Add ability to add attachments

This is probably going to come a bit later since I have never needed to send an attachment in an email, it's always a link back to the app. I know this is useful to people though so it should be added.

assert_delivered_email with options

Usually assert_delivered_email(Email.welcome_email(user) will work fine, but there are cases when it won't

I think I should add assert_delivered_email(subject: "Something") in case you can't/don't want to assert against a whole email.

This would be helpful if parts of the email have state that would make it difficult to test the whole email. For example, if a token is generated in the text/html_body from Phoenix.Token, then the body would be different every time since the token would change. So it might be better to assert just a few of the most important fields.

Improve folder naming

@scrogson suggested improving the folder names for clarity

I would create lib/bamboo/adapters
move mandrill_adapter.ex to lib/bamboo/adapters/mandrill.ex
same for the test_adapter.ex
make lib/bamboo/adapters/mandrill/email.ex
and lib/bamboo/adapters/test/mailbox.ex
and update the module names accordingly

Should deliver and deliver_async noop if all recipients are blank?

While using this in one of our applications we had to do something like this

users = Repo.all(User)

if length(users) > 0 do
  Emails.welcome_email(users) |> Mailer.deliver_async
end

Maybe deliver_async should throw a warning? Maybe an option to allow empty_recipients?

deliver_async(allow_empty_recipients: true)

Maybe this could be done at the mailer level via an option?

defmodule MyApp.Mailer do
  use Bamboo.Mailer, otp_app: :my_app

  @allow_empty_recipients true
end

Fix options with assert_delivered_email

It should normalize the email addresses, and should only match against the keys that are in the options. For asserting just what you pass in, do something like this

defmacro assert_delivered_email(email_params) do
    # Normalize the address as well
    quote do
      import ExUnit.Assertions
      assert_received({:delivered_email, unquote(email_params)})
    end
  end

Rename Formatter.format_recipient and pass type of email

Instead have Bamboo.EmailAddress.format It should also have a second argument that is passed a keyword list (or maybe a map) that contains what type of address it is (from, cc, bcc, to)

That way you can do something like

defimpl Bamboo.EmailAddress, for: MyApp.User do
  # Add the app's name when sending the email from a user
  def format(user, %{type: :from}) do
    %{name: "#{user.name} (NameOfApp)", address: user.address}
  end

  def format(user, _opts) do
    %{name: user.name, address: user.address
  end
end

Don't parse all config opts at compile time

Instead grab the adapter and other info (API key, etc.) at runtime. This makes it easier to change during tests, or while using in iex. That way you can switch to a TestAdapter or LocalAdapter long enough to send some tests emails and then switch it back if you wanted to. Also would clean up the Bamboo tests

EDIT: I'm not totally sure about this. Probably going to hold off on it for a bit

Remove use Mandrill.Email

It only imports two modules and most people won't use this that much (usually will have just a couple email modules). So I think it's better to just let people import the modules they need.

This is almost done

Make TestMailbox.one wait

If you use deliver_async TestMailbox.one will often fail because the email hasn't quite been sent yet. The Mailbox should wait a bit. Maybe using receive

Add Bamboo.Test

This will

  • alias Bamboo.SentEmail
  • import Bamboo.Formatter.format_recipient
  • Add a setup block that calls SentEmail.reset

Raise if there is no html or text unless you explicitly set them both to empty

This makes it easier to find out when you accidentally forget to render the body, but still allows you to leave it empty if you really want to.

The reason that you might want to set empty html and text bodies is that some adapters may be used to render emails from another service. For example, you can use Mandrill to render responsive templates that are rendered on their server, so you would not set a text or html body

Don't use Task.async in deliver_async

Task.async is linking the task process with the parent process so if the latter dies first it will kill the former.

For example, with Phoenix it would mean that a HTTP request could finish before we actually had a chance to send the email.

Elixir 1.2 introduced Task.async_no_link which could be used here.

Happy to submit a PR if you think that's the way to go.

More explicit phoenix template rendering

Right now view rendering uses Phoenix.View.render_existing which is ok except that it can make it easy to think you're sending an email with both formats, but in reality you had a typo in a template name and you only send a text or html version. Instead I think this should be more explicit so you get more immediate and helpful feedback.

Right now you do: render("template_name") which will render a text or html template if it exists

Instead I think it should be render("template_name.html") and render("template_name.text") if you have just one type of template to send.

If you want to send text and html then you would do render(:template_name) and it will render both and raise if either one is missing.

Use more reliable async task

I think Task.Supervisor.start_child should do the trick. Task.start_link links to the caller process and if the caller process dies, so does the background task. This is probably not preferred.

Change nil on %Email{from} to %UnknowMailbox{}

nil doesn't really mean much so it can be hard to figure out where it's coming from and what it means. Instead the default should be a struct like %UnknownMailbox{} or %UnsetMailbox{}. Something along those lines

Change deliver to deliver_now for delivering right away

I think this makes the library a bit easier to use. That way deliver uses the strategy you want and if you want to deliver right away then you use deliver_sync. This essentially flips how the current mailer works.

Still not 100% sold on the name though. Function name ideas:

  • deliver_now
  • deliver_sync
  • deliver_blocking (though it's not really blocking so maybe a bad name)

Make sure task worker is added

I wonder if there should be some way to test that the task worker is added so you don't deploy to prod and find out that you forgot to add the Bamboo.TaskSupervisor to your supervisor.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.