defmodule Apostle.Mail do defstruct email: nil, from: nil, headers: %{}, layout_id: nil, name: nil, reply_to: nil, template_id: nil, data: %{}, attachments: %{} @moduledoc """ Functions for working with individual emails. """ @doc """ Generates an `Apostle.Mail` struct based on the supplied options. Options: * `:email` - the destination email address * `:from` - the from address * `:name` - not sure. ask @snikch * `:reply_to` - the reply to address * `:layout_id` - the "slug" of your template layout should you not want to use the default * `:template_id` - the "slug" of the template to send * `:cc` - contents of the `CC` header * `:bcc` - contents of the `BCC` header * `:headers` - a `Map` containing additional mail headers to send * `:attachments` - a `List` of paths on the filesystem to files you wish to attach * _any_other_key_ - set as template variable values (aka `data`). """ def init opts do {%Apostle.Mail{}, opts} |> parse_value(:email) |> parse_value(:from) |> parse_value(:name) |> parse_value(:reply_to) |> parse_value(:layout_id) |> parse_value(:template_id) |> parse_header(:cc) |> parse_header(:bcc) |> parse_attachments |> parse_data end def set_value %Apostle.Mail{}=mail, _key, nil do mail end @doc """ Set a value on the mail struct. """ def set_value %Apostle.Mail{}=mail, key, value do Map.put mail, key, value end def set_header %Apostle.Mail{}=mail, _name, nil do mail end @doc """ Set an email header. """ def set_header %Apostle.Mail{}=mail, name, value do headers = Map.put mail.headers, name, value Map.put mail, :headers, headers end def set_data %Apostle.Mail{}=mail, _key, nil do mail end @doc """ Set a key and value to the mail's data (ie template variables) """ def set_data %Apostle.Mail{}=mail, key, value do data = Map.put mail.data, key, value Map.put mail, :data, data end @doc """ Add a file attachment, using a path on the filesystem. """ def add_attachment(%Apostle.Mail{}=mail, path) when is_binary(path) do file_name = Path.basename path {:ok, content} = File.read path add_attachment mail, file_name, content end @doc """ Add a file attachment by passing in a file name and content. """ def add_attachment %Apostle.Mail{}=mail, file_name, content do attachments = Map.put mail.attachments, file_name, content Map.put mail, :attachments, attachments end # Move values directly from the options map into the mail struct defp parse_value {%Apostle.Mail{}=mail, opts}, key do value = Dict.get opts, key opts = Dict.delete opts, key mail = set_value mail, key, value {mail, opts} end # Move header values from the options mail into the mail's header map. defp parse_header {%Apostle.Mail{}=mail, opts}, key do value = Dict.get opts, key opts = Dict.delete opts, key mail = set_header mail, key, value {mail, opts} end defp parse_attachments {%Apostle.Mail{}=mail, opts} do attachments = Dict.get opts, :attachments, [] mail = Enum.reduce(attachments, mail, fn ({file_name, content}, mail) -> add_attachment mail, file_name, content (path, mail) -> add_attachment mail, path end) opts = Dict.delete opts, :attachments {mail, opts} end # Merge all remaining options into the mail's data map. defp parse_data {%Apostle.Mail{}=mail, opts} do Enum.reduce opts, mail, fn({k,v},r)-> set_data r, k, v end end end