Module: Goodmail

Extended by:
Configuration
Defined in:
lib/goodmail.rb,
lib/goodmail/email.rb,
lib/goodmail/error.rb,
lib/goodmail/layout.rb,
lib/goodmail/mailer.rb,
lib/goodmail/builder.rb,
lib/goodmail/version.rb,
lib/goodmail/dispatcher.rb,
lib/goodmail/configuration.rb

Overview

The main namespace for the Goodmail gem. Provides configuration and the primary .compose method.

Defined Under Namespace

Modules: Configuration, Dispatcher, Layout Classes: Builder, EmailParts, Error, Mailer

Constant Summary collapse

VERSION =
"0.3.0"

Constants included from Configuration

Configuration::DEFAULT_CONFIG, Configuration::REQUIRED_CONFIG_KEYS

Class Method Summary collapse

Methods included from Configuration

config, configure, reset_config!

Class Method Details

.compose(headers = {}, &block) ⇒ Mail::Message

Composes a Mail::Message object using the Goodmail DSL and layout.

This is the primary entry point for creating emails with Goodmail. The returned Mail::Message object can then have ‘.deliver_now` or `.deliver_later` called on it.

Examples:

mail = Goodmail.compose(to: '[email protected]', subject: 'Hello!') do
  text "This is the email body."
  button "Click Me", "https://example.com"
end

mail.deliver_now
# or
mail.deliver_later

Parameters:

  • headers (Hash) (defaults to: {})

    Mail headers (:to, :from, :subject, etc.) Also accepts :unsubscribe (true or String URL).

  • block (Proc)

    Block containing Goodmail DSL calls (text, button, etc.)

Returns:

  • (Mail::Message)

    The generated Mail object, ready for delivery.



44
45
46
47
# File 'lib/goodmail.rb', line 44

def self.compose(headers = {}, &block)
  # Delegate the actual building process to the Dispatcher
  Dispatcher.build_message(headers, &block)
end

.render(headers = {}, &dsl_block) ⇒ Goodmail::EmailParts

Renders the email content using the Goodmail DSL and returns HTML and text parts. This method does not send the email but prepares its content for sending.

Parameters:

  • headers (Hash) (defaults to: {})

    Mail headers. Expected to contain :subject. Can also contain :unsubscribe_url and :preheader to override defaults.

  • dsl_block (Proc)

    Block containing Goodmail DSL calls (text, button, etc.)

Returns:



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/goodmail/email.rb', line 16

def self.render(headers = {}, &dsl_block)
  # 1. Initialize the Builder and execute the DSL block
  builder = Goodmail::Builder.new
  builder.instance_eval(&dsl_block) if block_given?
  core_html_content = builder.html_output

  # 2. Determine unsubscribe_url and preheader
  #    These are removed from headers as they are Goodmail-specific, not standard mail headers.
  current_headers = headers.dup # Avoid modifying the original headers hash directly
  unsubscribe_url = current_headers.delete(:unsubscribe_url) || Goodmail.config.unsubscribe_url
  preheader = current_headers.delete(:preheader) || Goodmail.config.default_preheader || current_headers[:subject]

  # 3. Render the raw HTML body using the Layout
  #    The subject is passed for the <title> tag and potentially other uses in layout.
  #    Unsubscribe URL and preheader are passed for inclusion in the layout.
  raw_html_body = Goodmail::Layout.render(
    core_html_content,
    current_headers[:subject], # Use subject from (potentially modified) current_headers
    unsubscribe_url: unsubscribe_url,
    preheader: preheader
  )

  # 4. Use Premailer to inline CSS and generate plaintext
  premailer = Premailer.new(
    raw_html_body,
    with_html_string: true,
    adapter: :nokogiri,
    preserve_styles: false, # Force inlining and remove <style> block
    remove_ids: true,       # Remove IDs
    remove_comments: false  # Keep MSO conditional comments
  )

  final_inlined_html = premailer.to_inline_css
  generated_plain_text = premailer.to_plain_text

  # 5. Perform refined plaintext cleanup (ported from Goodmail::Mailer)
  # 5.1. Remove logo alt text line (if logo exists and has associated URL)
  if Goodmail.config.logo_url.present? && Goodmail.config.company_url.present? && Goodmail.config.company_name.present?
    company_name_escaped = Regexp.escape(Goodmail.config.company_name)
    company_url_escaped = Regexp.escape(Goodmail.config.company_url)
    # Regex to match the typical alt text pattern for a linked logo image
    logo_alt_pattern = /^\s*#{company_name_escaped}\s+Logo\s*\(.*?#{company_url_escaped}.*?\).*\n?/i
    generated_plain_text.gsub!(logo_alt_pattern, "")
  end

  # 5.2. Remove any remaining standalone URL lines (often from logo links or similar artifacts)
  # This targets lines that consist *only* of a URL.
  generated_plain_text.gsub!(/^\s*https?:\/\/\S+\s*$\n?/i, "")

  # 5.3. Compact excess blank lines (more than two consecutive newlines)
  generated_plain_text.gsub!(/\n{3,}/, "\n\n")

  # 6. Return the structured parts
  EmailParts.new(html: final_inlined_html, text: generated_plain_text.strip)
end