跳至內容 跳至搜尋

Action Mailer 基礎

Action Mailer 讓你可以使用郵件模型和檢視從應用程式傳送電子郵件。

郵件模型

若要使用 Action Mailer,你需要建立一個郵件模型。

$ bin/rails generate mailer Notifier

產生的模型繼承自 ApplicationMailer,而 ApplicationMailer 又繼承自 ActionMailer::Base。郵件模型定義用於產生電子郵件訊息的方法。在這些方法中,你可以設定要在郵件檢視中使用的變數、郵件本身的選項(例如 :from 位址)和附件。

class ApplicationMailer < ActionMailer::Base
  default from: '[email protected]'
  layout 'mailer'
end

class NotifierMailer < ApplicationMailer
  default from: '[email protected]',
          return_path: '[email protected]'

  def welcome(recipient)
    @account = recipient
    mail(to: recipient.email_address_with_name,
         bcc: ["[email protected]", "Order Watcher <[email protected]>"])
  end
end

在郵件方法中,你可以存取下列方法

  • attachments[]= - 允許你以直覺的方式將附件新增到電子郵件中;attachments['filename.png'] = File.read('path/to/filename.png')

  • attachments.inline[]= - 允許你以與 attachments[]= 相同的方式將內嵌附件新增到電子郵件中

  • headers[]= - 允許你指定電子郵件中的任何標頭欄位,例如 headers['X-No-Spam'] = 'True'。請注意,多次宣告標頭會新增許多具有相同名稱的欄位。請參閱 headers 文件以取得更多資訊。

  • headers(hash) - 允許你指定電子郵件中的多個標頭,例如 headers({'X-No-Spam' => 'True', 'In-Reply-To' => '[email protected]'})

  • mail - 允許你指定要傳送的電子郵件。

傳遞給郵件方法的雜湊允許你指定 Mail::Message 會接受的任何標頭(任何有效的電子郵件標頭,包括選用欄位)。

如果未傳遞區塊給 mail 方法,它會檢查你的檢視並傳送所有與方法同名的檢視,因此上述動作會傳送 welcome.text.erb 檢視檔案,以及 welcome.html.erb 檢視檔案,並以 multipart/alternative 電子郵件傳送。

如果你只想明確呈現某些範本,請傳遞區塊

mail(to: user.email) do |format|
  format.text
  format.html
end

區塊語法在提供特定部分的資訊時也很有用

mail(to: user.email) do |format|
  format.text(content_transfer_encoding: "base64")
  format.html
end

甚至可以呈現特殊檢視

mail(to: user.email) do |format|
  format.text
  format.html { render "some_other_template" }
end

郵件檢視

就像 Action Controller,每個郵件類別都有對應的檢視目錄,類別中的每個方法都在其中尋找具有其名稱的範本。

若要定義要與郵件一起使用的範本,請建立一個 .erb 檔案,其名稱與郵件模型中的方法相同。例如,在上面定義的郵件中,app/views/notifier_mailer/welcome.text.erb 的範本將用於產生電子郵件。

在郵件模型的方法中定義的變數可以在其對應的檢視中作為實例變數存取。

預設情況下,電子郵件會以純文字傳送,因此我們模型範例的範本範例可能如下所示

Hi <%= @account.name %>,
Thanks for joining our service! Please check back often.

您甚至可以在這些檢視中使用 Action View 輔助程式。例如

You got a new note!
<%= truncate(@note.body, length: 25) %>

如果您需要在檢視中存取主旨、寄件者或收件者,您可以透過訊息物件來執行此操作

You got a new note from <%= message.from %>!
<%= truncate(@note.body, length: 25) %>

產生網址

可以使用 url_for 或命名路由在郵件檢視中產生網址。與 Action Pack 的控制器不同,郵件實例沒有任何關於傳入要求的內容,因此您需要提供產生網址所需的所有詳細資料。

當使用 url_for 時,您需要提供 :host:controller:action

<%= url_for(host: "example.com", controller: "welcome", action: "greeting") %>

當使用命名路由時,您只需要提供 :host

<%= users_url(host: "example.com") %>

您應該使用 named_route_url 樣式(產生絕對網址),並避免使用 named_route_path 樣式(產生相對網址),因為閱讀郵件的客戶端將無法知道當前網址,因此無法決定相對路徑。

也可以透過在 config/application.rb 中將 :host 選項設定為組態選項,來設定所有郵件中將使用的預設主機。

config.action_mailer.default_url_options = { host: "example.com" }

您也可以在個別郵件中定義 default_url_options 方法,以覆寫每個郵件的這些預設設定。

預設情況下,當 config.force_ssltrue 時,為主機產生的網址將使用 HTTPS 協定。

傳送郵件

定義郵件動作和範本後,您可以傳送您的訊息或延後其建立和傳送的時間

NotifierMailer.welcome(User.first).deliver_now # sends the email
mail = NotifierMailer.welcome(User.first)      # => an ActionMailer::MessageDelivery object
mail.deliver_now                               # generates and sends the email now

ActionMailer::MessageDelivery 類別是包覆在委派器中的包裝器,它會呼叫您的方法來產生郵件。如果您想要直接存取委派器或 Mail::Message,您可以呼叫 ActionMailer::MessageDelivery 物件上的 message 方法。

NotifierMailer.welcome(User.first).message     # => a Mail::Message object

Action Mailer 與 Active Job 很好地整合,因此您可以在背景中產生並傳送電子郵件(範例:在要求回應循環之外,因此使用者不必等待)。

NotifierMailer.welcome(User.first).deliver_later # enqueue the email sending to Active Job

請注意,deliver_later 會從背景工作執行您的方法。

您永遠不會實例化您的郵件寄送器類別。相反地,您只要呼叫您在類別本身定義的方法即可。所有實例方法預期會傳回要傳送的訊息物件。

多部分電子郵件

多部分訊息也可以隱含地使用,因為 Action Mailer 會自動偵測並使用多部分範本,其中每個範本都以動作的名稱命名,後接內容類型。每個偵測到的範本都會新增到訊息中,作為一個獨立的部分。

例如,如果存在下列範本

  • signup_notification.text.erb

  • signup_notification.html.erb

  • signup_notification.xml.builder

  • signup_notification.yml.erb

每個範本都會被渲染並新增到訊息中,作為一個獨立的部分,並具有對應的內容類型。整個訊息的內容類型會自動設定為 multipart/alternative,這表示電子郵件包含同一電子郵件主體的多個不同表示。在動作中定義的相同實例變數會傳遞給所有電子郵件範本。

如果已將任何附件或部分新增到電子郵件,則不會執行隱含範本渲染。這表示您必須手動將每個部分新增到電子郵件,並將電子郵件的內容類型設定為 multipart/alternative

附件

在電子郵件中傳送附件很簡單

class NotifierMailer < ApplicationMailer
  def welcome(recipient)
    attachments['free_book.pdf'] = File.read('path/to/file.pdf')
    mail(to: recipient, subject: "New account information")
  end
end

如果在檢視目錄中有 welcome.text.erbwelcome.html.erb 範本,則會傳送一個完整的 multipart/mixed 電子郵件,其中包含兩個部分,第一個部分是包含文字和 HTML 電子郵件部分的 multipart/alternative,第二個部分是包含 file.pdf 檔案的 Base64 編碼副本的 application/pdf,檔案名稱為 free_book.pdf

如果您需要傳送沒有內容的附件,您需要為其建立一個空的檢視,或新增一個空的 body 參數,如下所示

class NotifierMailer < ApplicationMailer
  def welcome(recipient)
    attachments['free_book.pdf'] = File.read('path/to/file.pdf')
    mail(to: recipient, subject: "New account information", body: "")
  end
end

您也可以傳送包含 HTML 範本的附件,在這種情況下,您需要新增 body、附件和自訂內容類型,如下所示

class NotifierMailer < ApplicationMailer
  def welcome(recipient)
    attachments["free_book.pdf"] = File.read("path/to/file.pdf")
    mail(to: recipient,
         subject: "New account information",
         content_type: "text/html",
         body: "<html><body>Hello there</body></html>")
  end
end

內嵌附件

您也可以指定檔案應與其他 HTML 內嵌顯示。如果您想要顯示公司標誌或照片,這會很有用。

class NotifierMailer < ApplicationMailer
  def welcome(recipient)
    attachments.inline['photo.png'] = File.read('path/to/photo.png')
    mail(to: recipient, subject: "Here is what we look like")
  end
end

然後,若要在檢視中參照圖像,請建立一個 welcome.html.erb 檔案,並呼叫 image_tag,傳入要顯示的附件,然後呼叫附件上的 url 以取得圖像來源的相對內容 ID 路徑

<h1>Please Don't Cringe</h1>

<%= image_tag attachments['photo.png'].url -%>

由於我們使用 Action View 的 image_tag 方法,因此您可以傳入任何想要的其他選項

<h1>Please Don't Cringe</h1>

<%= image_tag attachments['photo.png'].url, alt: 'Our Photo', class: 'photo' -%>

觀察和攔截郵件

Action Mailer 提供掛鉤至 Mail 觀察者和攔截器方法。這些方法允許您註冊在郵件傳遞生命週期中會被呼叫的類別。

觀察者類別必須實作 :delivered_email(message) 方法,此方法會在郵件發送後針對每封已發送的郵件呼叫一次。

攔截器類別必須實作 :delivering_email(message) 方法,此方法會在郵件發送之前呼叫,讓您可以在郵件送達傳遞代理之前修改郵件。您的類別應直接對傳入的 Mail::Message 實例進行任何必要的修改。

預設雜湊

Action Mailer 為您的電子郵件提供一些智慧型預設值,這些預設值通常在類別定義內的預設方法中指定

class NotifierMailer < ApplicationMailer
  default sender: '[email protected]'
end

您可以傳入 Mail::Message 接受的任何標頭值。ActionMailer::Base 開箱即用時會設定下列內容

  • mime_version: "1.0"

  • charset: "UTF-8"

  • content_type: "text/plain"

  • parts_order: [ "text/plain", "text/enriched", "text/html" ]

parts_ordercharset 實際上並非有效的 Mail::Message 標頭欄位,但 Action Mailer 會適當地轉譯它們並設定正確的值。

由於您可以傳入任何標頭,因此您需要將標頭作為字串引用,或將其作為底線符號傳入,因此下列內容會運作

class NotifierMailer < ApplicationMailer
  default 'Content-Transfer-Encoding' => '7bit',
          content_description: 'This is a description'
end

最後,Action Mailer 也支援將 ProcLambda 物件傳入預設雜湊,因此您可以定義在訊息產生時評估的方法

class NotifierMailer < ApplicationMailer
  default 'X-Special-Header' => Proc.new { my_method }, to: -> { @inviter.email_address }

  private
    def my_method
      'some complex call'
    end
end

請注意,proc/lambda 會在郵件訊息產生的一開始就評估,因此如果您使用 proc 在預設雜湊中設定某個項目,然後在郵件程式方法中設定相同的項目,它會被郵件程式方法覆寫。

也可以透過 config/application.rb 中的 default_options= 組態設定這些預設選項,這些選項會用於所有郵件程式中

config.action_mailer.default_options = { from: "[email protected]" }

回呼

您可以使用 before_actionafter_action 指定回呼來設定您的訊息,並使用 before_deliverafter_deliver 來包裝傳遞程序。例如,當您想要為某個郵件程式類別所發送的所有訊息新增預設內嵌附件和記錄傳遞時

class NotifierMailer < ApplicationMailer
  before_action :add_inline_attachment!
  after_deliver :log_delivery

  def welcome
    mail
  end

  private
    def add_inline_attachment!
      attachments.inline["footer.jpg"] = File.read('/path/to/filename.jpg')
    end

    def log_delivery
      Rails.logger.info "Sent email with message id '#{message.message_id}' at #{Time.current}."
    end
end

Action Mailer 中的動作回呼是使用 AbstractController::Callbacks 實作的,因此您可以使用與在繼承自 ActionController::Base 的類別中使用回呼相同的方式來定義和設定回呼。

請注意,除非您有特定理由這樣做,否則您應該偏好於在 Action Mailer 類別中使用 before_action 而不是 after_action,以便正確地解析標頭。

救援錯誤

郵件程式方法中的 rescue 區塊無法救援發生在呈現之外的錯誤,例如背景工作中的記錄反序列化錯誤,或來自第三方郵件傳遞服務的錯誤。

若要救援在郵件處理的任何部分中發生的錯誤,請使用 rescue_from

class NotifierMailer < ApplicationMailer
  rescue_from ActiveJob::DeserializationError do
    # ...
  end

  rescue_from "SomeThirdPartyService::ApiError" do
    # ...
  end

  def notify(recipient)
    mail(to: recipient, subject: "Notification")
  end
end

預覽電子郵件

您可以透過將郵件程式預覽檔案新增到 ActionMailer::Base.preview_paths,以視覺方式預覽您的電子郵件範本。由於大多數電子郵件會對資料庫資料進行一些有趣的處理,因此您需要撰寫一些場景來載入具有虛假資料的訊息

class NotifierMailerPreview < ActionMailer::Preview
  def welcome
    NotifierMailer.welcome(User.first)
  end
end

方法必須傳回一個 Mail::Message 物件,該物件可以透過呼叫郵件程式方法而產生,而不需要額外的 deliver_now / deliver_later。郵件程式預覽目錄的位置可以使用 preview_paths 選項進行設定,其預設值為 test/mailers/previews

config.action_mailer.preview_paths << "#{Rails.root}/lib/mailer_previews"

在執行中的開發伺服器執行個體上,可以在 http://localhost:3000/rails/mailers 存取所有預覽的概觀。

Previews 也可以透過類似於傳遞的方式攔截,方法是註冊具有 previewing_email 方法的預覽攔截器

class CssInlineStyler
  def self.previewing_email(message)
    # inline CSS styles
  end
end

config.action_mailer.preview_interceptors :css_inline_styler

請注意,如果攔截器要在傳送和預覽電子郵件時執行,則需要使用 register_interceptorregister_preview_interceptor 註冊攔截器。

設定選項

這些選項是在類別層級上指定的,例如 ActionMailer::Base.raise_delivery_errors = true

  • default_options - 您可以根據上述部分在類別層級以及類別本身中傳入此選項。

  • logger - 若可用,則記錄器用於產生郵件執行資訊。可設定為 nil 以不記錄。相容於 Ruby 自有的 Logger 和 Log4r 記錄器。

  • smtp_settings - 允許對 :smtp 傳送方法進行詳細設定

    • :address - 允許您使用遠端郵件伺服器。只需將其從預設的「localhost」設定變更即可。

    • :port - 您的郵件伺服器若未執行於埠 25,則可變更。

    • :domain - 若您需要指定 HELO 網域,則可在此處執行。

    • :user_name - 若您的郵件伺服器需要驗證,請在此設定中設定使用者名稱。

    • :password - 若您的郵件伺服器需要驗證,請在此設定中設定密碼。

    • :authentication - 若您的郵件伺服器需要驗證,則您需要在此處指定驗證類型。這是一個符號,且為 :plain(將以 Base64 編碼傳送密碼)、:login(將以 Base64 編碼傳送密碼)或 :cram_md5(結合挑戰/回應機制以交換資訊和加密訊息 Digest 5 演算法以雜湊重要資訊)之一

    • :enable_starttls - 連線至您的 SMTP 伺服器時使用 STARTTLS,且若不支援則會失敗。預設為 false。需要 Mail 範例程式碼的 2.7 以上版本。

    • :enable_starttls_auto - 偵測您的 SMTP 伺服器中是否啟用 STARTTLS,並開始使用它。預設為 true

    • :openssl_verify_mode - 使用 TLS 時,您可以設定 OpenSSL 如何檢查憑證。若您需要驗證自簽署和/或萬用字元憑證,這非常有用。您可以使用 OpenSSL 驗證常數的名稱('none''peer')或直接使用常數(OpenSSL::SSL::VERIFY_NONEOpenSSL::SSL::VERIFY_PEER)。

    • :ssl/:tls 啟用 SMTP 連線以使用 SMTP/TLS(SMTPS:透過直接 TLS 連線的 SMTP)

    • :open_timeout 嘗試開啟連線時等待的秒數。

    • :read_timeout 讀取 (read(2)) 呼叫逾時前的等待秒數。

  • sendmail_settings - 允許您覆寫 :sendmail 傳送方法的選項。

    • :location - sendmail 可執行檔的位置。預設為 /usr/sbin/sendmail

    • :arguments - 命令列引數。預設為 %w[ -i ],其中 -f sender@address 會在傳送訊息前自動新增。

  • file_settings - 允許您覆寫 :file 傳送方法的選項。

    • :location - 將寫入電子郵件的目錄。預設為應用程式 tmp/mails

  • raise_delivery_errors - 如果電子郵件無法傳送,是否應產生錯誤。

  • delivery_method - 定義傳送方式。可能的數值為 :smtp (預設值)、:sendmail:test:file。或者,您可以提供自訂傳送方式物件,例如 MyOwnDeliveryMethodClass。請參閱 Mail 範例文件,了解自訂傳送代理程式需要實作的介面。

  • perform_deliveries - 決定當您在電子郵件訊息或 Action Mailer 方法上呼叫 .deliver 時,是否實際從 Action Mailer 傳送電子郵件。預設為開啟,但可以關閉以協助功能測試。

  • deliveries - 保留透過 Action Mailer 傳送的所有電子郵件陣列,其中 delivery_method :test。最適合單元和功能測試。

  • delivery_job - 與 deliver_later 一起使用的作業類別。郵件程式可以設定此設定項以使用自訂傳送作業。預設為 ActionMailer::MailDeliveryJob

  • deliver_later_queue_name - deliver_later 使用的佇列名稱,其中 delivery_job 為預設值。郵件程式可以設定此設定項以使用自訂佇列名稱。

命名空間
方法
A
C
D
E
H
M
N
R
S
U
包含的模組

常數

PROTECTED_IVARS = AbstractController::Rendering::DEFAULT_PROTECTED_INSTANCE_VARIABLES + [:@_action_has_layout]
 

屬性

[W] 郵件程式名稱

允許設定目前郵件發送器的名稱。

類別公共方法

controller_path()

別名為:mailer_name

default(value = nil)

透過應用程式設定來設定預設值

config.action_mailer.default(from: "[email protected]")

別名為 ::default_options=

也別名為:default_options=
# File actionmailer/lib/action_mailer/base.rb, line 582
def default(value = nil)
  self.default_params = default_params.merge(value).freeze if value
  default_params
end

default_options=(value = nil)

允許透過應用程式設定來設定預設值

config.action_mailer.default_options = { from: "[email protected]" }
別名為:default

email_address_with_name(address, name)

傳回格式為「Name <[email protected]>」的電子郵件。

如果名稱是空白字串,則只傳回位址。

# File actionmailer/lib/action_mailer/base.rb, line 607
def email_address_with_name(address, name)
  Mail::Address.new.tap do |builder|
    builder.address = address
    builder.display_name = name.presence
  end.to_s
end

mailer_name()

傳回目前郵件發送器的名稱。此方法也用作檢視查詢的路徑。如果這是匿名郵件發送器,則此方法將傳回 anonymous

也別名為:controller_path
# File actionmailer/lib/action_mailer/base.rb, line 570
def mailer_name
  @mailer_name ||= anonymous? ? "anonymous" : name.underscore
end

new()

# File actionmailer/lib/action_mailer/base.rb, line 644
def initialize
  super()
  @_mail_was_called = false
  @_message = Mail.new
end

register_interceptor(interceptor)

註冊一個攔截器,它會在郵件發送之前被呼叫。可以傳入攔截器作為類別、字串或符號。如果傳入字串或符號,它將被駝峰化並常數化。

# File actionmailer/lib/action_mailer/base.rb, line 547
def register_interceptor(interceptor)
  Mail.register_interceptor(observer_class_for(interceptor))
end

register_interceptors(*interceptors)

註冊一個或多個攔截器,這些攔截器會在發送郵件之前被呼叫。

# File actionmailer/lib/action_mailer/base.rb, line 521
def register_interceptors(*interceptors)
  interceptors.flatten.compact.each { |interceptor| register_interceptor(interceptor) }
end

register_observer(observer)

註冊一個觀察者,當郵件傳送時會通知該觀察者。可以將類別、字串或符號傳遞為觀察者。如果傳遞的是字串或符號,它將會被駝峰化並常數化。

# File actionmailer/lib/action_mailer/base.rb, line 533
def register_observer(observer)
  Mail.register_observer(observer_class_for(observer))
end

register_observers(*observers)

註冊一個或多個觀察者,當郵件傳送時會通知這些觀察者。

# File actionmailer/lib/action_mailer/base.rb, line 511
def register_observers(*observers)
  observers.flatten.compact.each { |observer| register_observer(observer) }
end

unregister_interceptor(interceptor)

取消註冊先前註冊的攔截器。可以將類別、字串或符號傳遞為攔截器。如果傳遞的是字串或符號,它將會被駝峰化並常數化。

# File actionmailer/lib/action_mailer/base.rb, line 554
def unregister_interceptor(interceptor)
  Mail.unregister_interceptor(observer_class_for(interceptor))
end

unregister_interceptors(*interceptors)

取消註冊一個或多個先前註冊的攔截器。

# File actionmailer/lib/action_mailer/base.rb, line 526
def unregister_interceptors(*interceptors)
  interceptors.flatten.compact.each { |interceptor| unregister_interceptor(interceptor) }
end

unregister_observer(觀察者)

取消註冊先前註冊的觀察者。可以將類別、字串或符號傳遞為觀察者。如果傳遞字串或符號,它將會變成駝峰式大小寫並轉換為常數。

# File actionmailer/lib/action_mailer/base.rb, line 540
def unregister_observer(observer)
  Mail.unregister_observer(observer_class_for(observer))
end

unregister_observers(*觀察者)

取消註冊一個或多個先前註冊的觀察者。

# File actionmailer/lib/action_mailer/base.rb, line 516
def unregister_observers(*observers)
  observers.flatten.compact.each { |observer| unregister_observer(observer) }
end

類別私有方法

supports_path?()

電子郵件不支援相對路徑連結。

# File actionmailer/lib/action_mailer/base.rb, line 943
def self.supports_path? # :doc:
  false
end

執行個體公開方法

attachments()

允許您將附件新增到電子郵件,如下所示

mail.attachments['filename.jpg'] = File.read('/path/to/filename.jpg')

如果您這樣做,則 Mail 將取得檔案名稱並找出 MIME 類型。它還會設定 Content-TypeContent-DispositionContent-Transfer-Encoding,並以 Base64 編碼附件的內容。

如果您願意,也可以透過傳遞雜湊而不是字串來指定覆寫。

mail.attachments['filename.jpg'] = {mime_type: 'application/gzip',
                                    content: File.read('/path/to/filename.jpg')}

如果您想使用 Base64 以外的編碼,則需要傳遞編碼類型以及預編碼的內容,因為 Mail 不知道如何解碼資料

file_content = SpecialEncode(File.read('/path/to/filename.jpg'))
mail.attachments['filename.jpg'] = {mime_type: 'application/gzip',
                                    encoding: 'SpecialEncoding',
                                    content: file_content }

您也可以搜尋特定附件

# By Filename
mail.attachments['filename.jpg']   # => Mail::Part object or nil

# or by index
mail.attachments[0]                # => Mail::Part (first attachment)
# File actionmailer/lib/action_mailer/base.rb, line 761
def attachments
  if @_mail_was_called
    LateAttachmentsProxy.new(@_message.attachments)
  else
    @_message.attachments
  end
end

email_address_with_name(地址,名稱)

傳回格式為「Name <[email protected]>」的電子郵件。

如果名稱是空白字串,則只傳回位址。

# File actionmailer/lib/action_mailer/base.rb, line 685
def email_address_with_name(address, name)
  self.class.email_address_with_name(address, name)
end

headers(args = nil)

允許您傳遞隨機且不尋常的標頭給新的 Mail::Message 物件,它會將這些標頭新增到自己身上。

headers['X-Special-Domain-Specific-Header'] = "SecretValue"

您也可以將雜湊傳遞到標題欄位名稱和值的標題,然後將其設定在 Mail::Message 物件上

headers 'X-Special-Domain-Specific-Header' => "SecretValue",
        'In-Reply-To' => incoming.message_id

產生的 Mail::Message 會在其標題中包含以下內容

X-Special-Domain-Specific-Header: SecretValue

關於取代已定義標題的注意事項

  • 主旨

  • 寄件者

  • 發件者

  • 收件者

  • 副本收件者

  • 密件副本收件者

  • 回覆至

  • 原始日期

  • 訊息識別碼

  • 參考

欄位在電子郵件標題中只能出現一次,而其他欄位(例如 X-Anything)可以出現多次。

如果您要取代任何已存在的標題,請先將其設定為 nil 以重設值,否則會為同一個標題新增另一個欄位。

# File actionmailer/lib/action_mailer/base.rb, line 723
def headers(args = nil)
  if args
    @_message.headers(args)
  else
    @_message
  end
end

mail(headers = {}, &block)

建立訊息和呈現電子郵件範本的主要方法。有兩種呼叫此方法的方式,使用區塊或不使用區塊。

它接受標題雜湊。這個雜湊讓您能夠在電子郵件訊息中指定最常用的標題,這些標題是

  • :subject - 訊息的主旨,如果省略這個,Action Mailer 會要求 Rails I18n 類別在 [mailer_scope, action_name] 範圍內翻譯 :subject,或者如果這個不存在,會翻譯 action_name 的人性化版本

  • :to - 訊息的收件者,可以是地址字串或地址陣列。

  • :from - 訊息的寄件者

  • :cc - 您希望在這個電子郵件中抄送的人,可以是地址字串或地址陣列。

  • :bcc - 您希望在這個電子郵件中密件抄送的人,可以是地址字串或地址陣列。

  • :reply_to - 將電子郵件的 回覆至 標題設定為誰。

  • :date - 寄送電子郵件的日期。

你可以使用 ::default 類別方法,為上述任何標頭設定預設值(:date 除外)

class Notifier < ActionMailer::Base
  default from: '[email protected]',
          bcc: '[email protected]',
          reply_to: '[email protected]'
end

如果你需要上述未列出的其他標頭,你可以將它們傳遞為標頭雜湊的一部分,或使用 headers['name'] = value 方法。

:return_path 指定為標頭時,該值將用作 Mail 訊息的「信封寄件人」地址。當你想要將傳遞通知寄送到與 :from 中不同的地址時,設定此值很有用。Mail 實際上會優先使用 :return_path,其次是 :sender,最後才是 :from 欄位,作為「信封寄件人」的值。

如果你沒有將區塊傳遞給 mail 方法,它將使用預設的郵件名稱和呼叫它的方法名稱,在檢視路徑中找到所有範本,然後它將為這些範本中的每個範本建立部分,對正確的內容類型和順序進行有根據的猜測,並傳回一個完全準備好的 Mail::Message,準備好呼叫 :deliver 來傳送。

例如

class Notifier < ActionMailer::Base
  default from: '[email protected]'

  def welcome
    mail(to: '[email protected]')
  end
end

將在「app/views/notifier」中尋找所有名稱為「welcome」的範本。如果沒有 welcome 範本,它將引發 ActionView::MissingTemplate 錯誤。

但是,這些可以自訂

mail(template_path: 'notifications', template_name: 'another')

現在它將在「app/views/notifications」中尋找所有名稱為「another」的範本。

如果你傳遞一個區塊,你可以呈現你選擇的特定範本

mail(to: '[email protected]') do |format|
  format.text
  format.html
end

你甚至可以直接呈現純文字,而不使用範本

mail(to: '[email protected]') do |format|
  format.text { render plain: "Hello Mikel!" }
  format.html { render html: "<h1>Hello Mikel!</h1>".html_safe }
end

這將呈現一個具有 text/plaintext/html 部分的 multipart/alternative 電子郵件。

區塊語法還允許你自訂部分標頭(如果需要)

mail(to: '[email protected]') do |format|
  format.text(content_transfer_encoding: "base64")
  format.html
end
# File actionmailer/lib/action_mailer/base.rb, line 870
def mail(headers = {}, &block)
  return message if @_mail_was_called && headers.blank? && !block

  # At the beginning, do not consider class default for content_type
  content_type = headers[:content_type]

  headers = apply_defaults(headers)

  # Apply charset at the beginning so all fields are properly quoted
  message.charset = charset = headers[:charset]

  # Set configure delivery behavior
  wrap_delivery_behavior!(headers[:delivery_method], headers[:delivery_method_options])

  assign_headers_to_message(message, headers)

  # Render the templates and blocks
  responses = collect_responses(headers, &block)
  @_mail_was_called = true

  create_parts_from_responses(message, responses)
  wrap_inline_attachments(message)

  # Set up content type, reapply charset and handle parts order
  message.content_type = set_content_type(message, content_type, headers[:content_type])
  message.charset      = charset

  if message.multipart?
    message.body.set_sort_order(headers[:parts_order])
    message.body.sort_parts!
  end

  message
end

mailer_name()

傳回郵件物件的名稱。

# File actionmailer/lib/action_mailer/base.rb, line 678
def mailer_name
  self.class.mailer_name
end

執行個體私有方法

default_i18n_subject(interpolations = {})

使用 Rails I18n 類別在 [mailer_scope, action_name] 範圍下翻譯 subject。如果在指定的範圍下找不到 subject 的翻譯,它會預設為 action_name 的人性化版本。如果主旨有內插,您可以透過 interpolations 參數傳遞。

# File actionmailer/lib/action_mailer/base.rb, line 937
def default_i18n_subject(interpolations = {}) # :doc:
  mailer_scope = self.class.mailer_name.tr("/", ".")
  I18n.t(:subject, **interpolations.merge(scope: [mailer_scope, action_name], default: action_name.humanize))
end

set_content_type(m, user_content_type, class_default)

mail 使用來設定訊息的內容類型。

它會使用指定的 user_content_type,或是在郵件訊息有任何附件時使用 multipart。如果附件是內嵌的,內容類型會是「multipart/related」,否則為「multipart/mixed」。

如果沒有透過標頭傳入任何內容類型,而且沒有任何附件,或訊息是 multipart,則會使用預設的內容類型。

# File actionmailer/lib/action_mailer/base.rb, line 915
def set_content_type(m, user_content_type, class_default) # :doc:
  params = m.content_type_parameters || {}
  case
  when user_content_type.present?
    user_content_type
  when m.has_attachments?
    if m.attachments.all?(&:inline?)
      ["multipart", "related", params]
    else
      ["multipart", "mixed", params]
    end
  when m.multipart?
    ["multipart", "alternative", params]
  else
    m.content_type || class_default
  end
end