أساسيات Action Mailer في ريلز

من موسوعة حسوب
اذهب إلى التنقل اذهب إلى البحث

يوفّر لك هذا الدليل كل ما تحتاجه للبدء في إرسال واستقبال رسائل البريد الإلكتروني من وإلى تطبيقك، والعديد من عمليّات Action Mailer الداخليّة. كما يغطي كيفية اختبار مُرسلي بريدك (mailers).

ستتعلم بعد قراءة هذا الدليل:

  • كيفيّة إرسال واستقبال البريد الإلكتروني داخل تطبيق ريلز.
  • كيفيّة إنشاء وتعديل صنف Action Mailer والعرض المرتبط به.
  • كيفيّة إعداد Action Mailer لبيئتك.
  • كيفيّة اختبار أصنافك لـ Action Mailer.

مقدمة

يسمح لك Action Mailer بإرسال رسائل البريد الإلكتروني من تطبيقك باستخدام الأصناف mailer والعروض المرتبطة بها (views).

تعمل مُرسلات البريد بطريقة مشابهة جدًا لوحدات التحكّم. وهي ترث من ActionMailer::Base وتتوضع في app/mailers ولديها عروض مرتبطة تظهر في app/views.

إرسال البريد الإلكتروني

سيوفر هذا القسم دليلًا مُفصّلًا لإنشاء مُرسل بريد والعروض المرتبطة به.

خطوات إنشاء مرسل بريد إلكتروني

إنشاء المرسل

$ bin/rails generate mailer UserMailer
create  app/mailers/user_mailer.rb
create  app/mailers/application_mailer.rb
invoke  erb
create    app/views/user_mailer
create    app/views/layouts/mailer.text.erb
create    app/views/layouts/mailer.html.erb
invoke  test_unit
create    test/mailers/user_mailer_test.rb
create    test/mailers/previews/user_mailer_preview.rb
# app/mailers/application_mailer.rb
class ApplicationMailer < ActionMailer::Base
  default from: "from@example.com"
  layout 'mailer'
end
 
# app/mailers/user_mailer.rb
class UserMailer < ApplicationMailer
end

كما ترى، يمكنك توليد مُرسلات بريد مثلما تستخدم المولّدات الأخرى مع ريلز. تتشابه مُرسلات البريد من الناحية النظرية مع وحدات التحكّم، ولذلك نحصل على مُرسل بريد ومجلّد للعروض واختبار. إن لم ترغب في استخدام مولّد، يمكنك إنشاء ملفك الخاص داخل app/mailers لكن المهم أن تتأكّد أنّه يرث من ActionMailer::Base:

class MyMailer < ActionMailer::Base
end

تعديل المرسل

تشبه مُرسلات البريد الإلكتروني إلى حد بعيد وحدات تحكّم ريلز. لديها أيضا توابع تسمّى "إجراءات" (actions) وتستخدم العروض لهيكلة المحتوى. يُنشئ مرسلٌ رسالة للتسليم عبر البريد الإلكتروني عندما تُولّد وحدة التحكّم محتوى مثل HTML لإرساله مرة أخرى إلى العميل.

يحتوي app/mailers/user_mailer.rb على مُرسل بريد فارغ.

class UserMailer < ApplicationMailer
end

فلنضف تابعًا باسم welcome_email يرسل بريدًا إلكترونيًا إلى عنوان البريد الإلكتروني المسجل للمستخدم:

class UserMailer < ApplicationMailer
  default from: 'notifications@example.com'
 
  def welcome_email
    @user = params[:user]
    @url  = 'http://example.com/login'
    mail(to: @user.email, subject: 'Welcome to My Awesome Site')
  end
end

فيما يلي شرح سريع للعناصر المعروضة في التابع. للحصول على قائمة كاملة بجميع الخيارات المتاحة، يرجى إلقاء نظرة أدناه في قسم "قائمة خصائص Action Mailer الكاملة القابلة للتعيين من طرف المستخدم".

  • default: (كائن Hash) يمثِّل جدول Hash للقيم الافتراضية لأي بريد إلكتروني ترسله من هذا المُرسل. نُعيّن في هذه الحالة قيمة  الترويسة from: لجميع الرسائل في هذا الصنف. يمكن إعادة تعريف (override) هذا الحقل مع كل بريد إلكتروني على حدة.
  • mail: رسالة البريد الإلكتروني الفعليّة التي نُمرّر الترويستين to: و subject: داخلها.

تصبح أي متغيّرات نسخة نعرّفها في التابع متاحة للاستخدام في العرض مثل حالة وحدات التحكم تمامًا.

إنشاء عرض لـ Action Mailer

أنشئ ملفًا يسمّى welcome_email.html.erb في ‎app/views/user_mailer/‎‎. سيُستخدم هذا القالب (template) للبريد الإلكتروني بتنسيق HTML:

<!DOCTYPE html>
<html>
  <head>
    <meta content='text/html; charset=UTF-8' http-equiv='Content-Type' />
  </head>
  <body>
    <h1>Welcome to example.com, <%= @user.name %></h1>
    <p>
      You have successfully signed up to example.com,
      your username is: <%= @user.login %>.<br>
    </p>
    <p>
      To login to the site, just follow this link: <%= @url %>.
    </p>
    <p>Thanks for joining and have a great day!</p>
  </body>
</html>

لننشئ أيضًا جزءًا نصيًا لهذا البريد الإلكتروني. لا يفضّل كل العملاء رسائل إلكترونيّة بتنسيق HTML، وبالتالي إرسال كليهما هو أفضل الممارسات. أنشئ إذًا ملفًّا يسمّى welcome_email.text.erb في app/views/user_mailer/‎ :

Welcome to example.com, <%= @user.name %>
===============================================
 
You have successfully signed up to example.com,
your username is: <%= @user.login %>.
 
To login to the site, just follow this link: <%= @url %>.
 
Thanks for joining and have a great day!

عند استدعاء التابع mail الآن، سيكتشف Action Mailer قالبين (النص و HTML) ويُنشئ بريدًا إلكترونيًّا multipart/alternative تلقائيًا.

استدعاء المرسل

مُرسلات البريد هي في الواقع مجرد طريقة أخرى لتصيير (rendering) العرض. بدلًا من تصيير عرض وإرساله عبر بروتوكول HTTP، يُرسلونه ببساطة عبر بروتوكولات البريد الإلكتروني. ونتيجة لهذا، من المنطقي أن تطلب وحدة تحكّمك من مرسل رسائل البريد بإرسال بريد إلكتروني عند إنشاء مستخدم بنجاح.

ضبط هذه العملية بسيط للغاية.

فلننشئ أولًا، سقالة بسيطة (أي scaffold) باسم User:

$ bin/rails generate scaffold user name email login
$ bin/rails db:migrate

لدينا الآن نموذج مُستخدم للتلاعب به. سنُعدّل فقط على الملف app/controllers/users_controller.rb بجعله يرشد UserMailer لإرسال بريد إلكتروني للمستخدم حديث الإنشاء عبر تعديل إجراء الإنشاء (create action) وإدخال نداء إلى UserMailer.with(user: @user).welcome_email مباشرةً بعد حفظ المستخدم بنجاح. يتكامل Action Mailer بشكل جيد مع Active Job، لذا تتمكن من إرسال رسائل إلكترونيّة خارج دورة الطلب-الاستجابة، بحيث لا يضطر المستخدم إلى الانتظار:

class UsersController < ApplicationController
  # POST /users
  # POST /users.json
  def create
    @user = User.new(params[:user])
 
    respond_to do |format|
      if @user.save
        # بإرسال بريد ترحيبي بعد حفظ UserMailer أخبر
        UserMailer.with(user: @user).welcome_email.deliver_later
 
        format.html { redirect_to(@user, notice: 'User was successfully created.') }
        format.json { render json: @user, status: :created, location: @user }
      else
        format.html { render action: 'new' }
        format.json { render json: @user.errors, status: :unprocessable_entity }
      end
    end
  end
end

ملاحظة: السلوك الافتراضي لـ Active Job هو تنفيذ المهام عبر المحوّل async:. لذا يمكنك استخدام deliver_later الآن لإرسال رسائل البريد الإلكتروني بشكل غير متزامن. يُشغّل محوّل Active Job الافتراضي المهام مع مجمع خيوط قيد المعالجة. وهو مناسب تمامًا لبيئات التطوير/الاختبار، نظرًا لأنَّه لا يتطلب أي بنية تحتيّة خارجيّة ولكنه غير ملائم للإنتاج بما أنّه يزيل الوظائف المعلّقة عند إعادة التشغيل. إن كنت في حاجة إلى خلفيّة دائمة (persistent backend)، فستحتاج لاستخدام محوّل Active Job (أي Active Job adapter) يحوي خلفيّة دائمة (Sidekiq ،Resque، ...إلخ). إن رغبت في إرسال بريد إلكتروني على الفور (من cronjob على سبيل المثال) فقط استدعي deliver_now:

class SendWeeklySummary
  def run
    User.find_each do |user|
      UserMailer.with(user: user).weekly_summary.deliver_now
    end
  end
end

أي زوج قيم مفتاح مُمرّر إلى with يصبح params لـ Action Mailer. لذلك، يجعل

with(user: @user, account: @user.account)‎ المعاملين params[:user]‎ و params[:account]‎ متوفّرين في Action Mailer.

ويعيد التابع welcome_email كائنًا من النوع ActionMailer::MessageDelivery والذي يمكن إخباره لاحقًا بالإرسال الأن عبر deliver_now أو الإرسال لاحقًا عبر deliver_later لإرسال نفسه. الكائن ActionMailer::MessageDelivery هو مجرد غلاف (wrapper) حول Mail::Message. إن رغبت في فحص أو تغيير أو فعل بأي شيء آخر مع الكائن Mail::Message، تستطيع الوصول إليه عبر التابع message في الكائن ActionMailer::MessageDelivery.

التشفير التلقائي لقيم الترويسة

يعالج Action Mailer الترميز التلقائي لأحرف متعددة البايت داخل الترويسات وجسم الرسالة.

لأمثلة أكثر تعقيدًا مثل تعريف مجموعات أحرف بديلة أو ترميز النص ذاتيًّا أولًا، الرجاء الرجوع إلى المكتبة Mail.

قائمة توابع Action Mailer الكاملة

تحتاج ثلاثة توابع فقط لأي رسالة بريد إلكتروني:

  • headers - يحدّد أي ترويسة بريد تريدها. تستطيع تمرير جدول Hash بأسماء حقول وأزواج القيم أو يمكنك استدعاءه بالشكل headers[:field_name]‎ = 'value'‎.
  • attachments - يسمح لك بإضافة مرفقات إلى بريدك الإلكتروني مثل attachments['file-name.jpg'] = File.read('file-name.jpg')‎.
  • mail - يرسل البريد الإلكتروني الفعلي ذاته. تستطيع تمرير ترويسة كجدول Hash إلى التابع mail كمعاملة، ثم يُنشئ التابع  بريدًا إلكترونيَّا، إما كنص عادي أو نص متعدّد الأجزاء، اعتمادًا على قوالب البريد الإلكتروني التي عرّفتها.

إضافة المرفقات

يُسهّل Action Mailer إضافة المرفقات.

  • مرّر اسم الملف والمحتوى وسيخمّن Action Mailer وجوهرة البريد (Mail gem) تلقائيًا النوع MIME، ويضبط التشفير وينشئ المُرفق.
attachments['filename.jpg'] = File.read('/path/to/filename.jpg')

عندما ينفَّذ التابع mail، سيُرسل بريد إلكتروني متعدد الأجزاء مع مُرفق، مُضمّن بشكل مناسب مع كون المستوى الأعلى multipart/mixed والجزء الأول عبارة عن multipart/alternative يحتوي على النص العادي ورسائل بريد إلكتروني بتنسيق HTML.

ملاحظة: سيُشفّر البريد المُرفق تلقائيا بتشفير Base64. إن أردت شيئًا مختلفًا شفّر مُحتواك ومرّره المحتوى المشفّر وترميزه داخل جدول Hash إلى التابع attachments.

  • مرّر اسم الملف وحدد الترويسة والمحتوى وسيستخدم Action Mailer و mail الإعدادات التي تمررها.
encoded_content = SpecialEncode(File.read('/path/to/filename.jpg'))
attachments['filename.jpg'] = {
  mime_type: 'application/gzip',
  encoding: 'SpecialEncoding',
  content: encoded_content
}

ملاحظة: يفترض البريد أنَّ محتواك مُشفّر مسبقًا ولا يحاول تشفيره عبر Base64.

إنشاء مرفقات مضمّنة (Making Inline Attachments)

يجعل Action Mailer 3.0 المُرفقات المضمّنة - والتي تطلّبت الكثير من التلاعب في الإصدارات قبل 3.0 - بالبساطة والسهولة كما يجدر بها أن تكون.

  • أولًا لتأمر mail بتحويل مُرفق إلى مُرفق مضمّن (Inline Attachment)، عليك فقط استدعاء inline. مع تابع المرفقات داخل مُرسلك (Mailer):
def welcome
  attachments.inline['image.jpg'] = File.read('/path/to/image.jpg')
end
  • ثم تستطيع في عرضك أن تشير (reference) إلى attachments كجدول Hash وتحدد المُرفق الذي تريد إظهاره، وتستدعي url عليه ثم تمرر النتيجة إلى التابع image_tag:
<p>Hello there, this is our image</p>
 
<%= image_tag attachments['image.jpg'].url %>
  • نظرًا لأن هذا استدعاء اعتيادي إلى image_tag، تستطيع تمرير جدول Hash من الخيارات بعد عنوان URL المُرفق كما هو الحال مع أي صورة أخرى:
<p>Hello there, this is our image</p>
 
<%= image_tag attachments['image.jpg'].url, alt: 'My Photo', class: 'photos' %>

إرسال بريد إلكتروني إلى عدة مستلمين

من الممكن إرسال بريد إلى مستلم واحد أو أكثر في بريد إلكتروني واحد (مثلًا إعلام كافة المسؤولين بتسجيل جديد) عن طريق تعيين قائمة بعناوين البريد إلى المفتاح to:. يمكن أن تكون قائمة عناوين البريد مصفوفة من عناوين البريد أو سلسلة واحدة مع عناوين مفصولة بفواصل.

class AdminMailer < ApplicationMailer
  default to: -> { Admin.pluck(:email) },
          from: 'notification@example.com'
 
  def new_registration(user)
    @user = user
    mail(subject: "New User Signup: #{@user.email}")
  end
end

يمكن استخدام نفس التنسيق لوضع نسخة كربونية (:Cc) ونسخة كربونية مخفية (:Bcc) باستخدام المفتاحين cc: و bcc: على التوالي.

إرسال البريد الإلكتروني بالاسم

قد ترغب في بعض الأحيان في إظهار اسم الشخص بدلًا عن مجرّد عنوان بريده الإلكتروني عندما يستلم البريد الإلكتروني الذي أرسلته إليه. وطريقة عمل ذلك هي بتنسيق عنوان البريد الإلكتروني بالشكل ‎"Full Name" <email>‎.

def welcome_email
  @user = params[:user]
  email_with_name = %("#{@user.name}" <#{@user.email}>)
  mail(to: email_with_name, subject: 'Welcome to My Awesome Site')
end

عروض المرسل (Mailer Views)

تُوجد عروض المُرسل (Mailer) في المُجلّد app/views/name_of_mailer_class. يعرف الصنف عرض مرسل البريد المحدّد تلقائيًّا لأنَّ اسمه مماثل لتابع المُرسل. في المثال الوارد أعلاه، سيكون عرض مُرسل بريدنا للتابع welcome_email هو pp/views/user_mailer/welcome_email.html.erb بالنسبة للنسخة HTML و welcome_email.text.erb لنسخة النص العادي.

لتغيير عرض المُرسل الافتراضي لإجرائك (action)، نفّذ ما يلي:

class UserMailer < ApplicationMailer
  default from: 'notifications@example.com'
 
  def welcome_email
    @user = params[:user]
    @url  = 'http://example.com/login'
    mail(to: @user.email,
         subject: 'Welcome to My Awesome Site',
         template_path: 'notifications',
         template_name: 'another')
  end
end

سيبحث في هذه الحالة عن قوالب في app/views/notifications بالاسم another. تستطيع أيضًا تحديد مصفوفة مسارات لـ template_path وسيُبحث بها حسب الترتيب. إن أردت مزيدًا من المرونة، تستطيع أيضًا تمرير كتلة (block) وتصيير (render) قوالب محددّة أو حتى تصيير على نفس السطر أو تصيير نص دون استخدام ملف قالب:

class UserMailer < ApplicationMailer
  default from: 'notifications@example.com'
 
  def welcome_email
    @user = params[:user]
    @url  = 'http://example.com/login'
    mail(to: @user.email,
         subject: 'Welcome to My Awesome Site') do |format|
      format.html { render 'another_template' }
      format.text { render plain: 'Render text' }
    end
  end
end

سيُصيّر هذا القالب "another_template.html.erb" لجزء HTML ويستخدم النص المُصيّر للجزء النصّي.

أمر التصيير (render) هو نفسه المُستخدم داخل وحدة التحكم (Action Controller) كي تستطيع استخدام جميع الخيارات نفسها مثل text: و inline: ...إلخ.

التخزين المؤقت لعرض المرسل

تستطيع تخزين الأجزاء مؤقّتًّا (fragment caching) في عروض المُرسل مثلما نفعل مع عروض التطبيق باستخدام التابع cache.

<% cache do %>
  <%= @company.name %>
<% end %>

ولاستخدام هذه الخاصيّة، تحتاج لإعداد تطبيقك بالشكل التالي:

config.action_mailer.perform_caching = true

يُدعم التخزين المؤقت للأجزاء أيضًا في رسائل البريد متعدّدة الأجزاء. اقرأ المزيد حول التخزين المؤقت في دليل التخزين المؤقت في ريلز.

تخطيطات Action Mailer

يملك مُرسل البريد تخطيطات تمامًا مثل عروض وحدات التحكّم. يجب أن يكون اسم التخطيط نفس اسم مُرسلك مثل user_mailer.html.erb و user_mailer.text.erb كي يُتعرّف عليه تلقائيًا كتخطيط من طرف مُرسلك.

استدعي التابع layout كي تستخدم ملفًّا مختلفًا في مُرسلك:

class UserMailer < ApplicationMailer
  layout 'awesome' # use awesome.(html|text).erb as the layout
end

استخدم yield لتصيير العرض داخل التخطيط مثل طرق العرض لوحدات التحكّم تمامًا. تستطيع أيضًا تمرير الخيار 'layout: 'layout_name لاستدعاء التصيير داخل بُنية التنسيق لتحديد مخططات مختلفة للتنسيقات المختلفة:

class UserMailer < ApplicationMailer
  def welcome_email
    mail(to: params[:user].email) do |format|
      format.html { render layout: 'my_layout' }
      format.text
    end
  end
end

سيُعرض جزء HTML باستخدام الملف my_layout.html.erb وجزء النص مع ملف user_mailer.text.erb المعتاد في حالة وجوده.

معاينة رسائل البريد الإلكتروني (Previewing Emails)

توفر معاينات Action Mailer طريقة لمعرفة كيفيّة ظهور الرسائل الإلكترونية من خلال زيارة عنوان URL خاص يعرضها. يجب تسمية صنف معاينة UserMailer في المثال أعلاه UserMailerPreview ووضعه في test/mailers/previews/user_mailer_preview.rb. لمشاهدة معاينة welcome_email، عرّف استخدام تابع له نفس الاسم واستدعي UserMailer.welcome_email:

class UserMailerPreview < ActionMailer::Preview
  def welcome_email
    UserMailer.with(user: User.first).welcome_email
  end
end

ثم ستُصبح المعاينة متاحة في http://localhost:3000/rails/mailers/user_mailer/welcome_email.

سيُعاد التحميل تلقائيًا إن غيّرت شيئًا ما في app/views/user_mailer/welcome_email.html.erb أو المُرسل نفسه حتى تتمكن من رؤية التنسيق الجديد على الفور. تتوفّر أيضًا قائمة بالمعاينات في http://localhost:3000/rails/mailers.

توجد أصناف المعاينة هذه في test/mailers/previews. يمكن إعدادها باستخدام الخيار preview_path. إن أردت مثلًا تغييرها إلى lib/mailer_previews تستطيع إعداد في config/application.rb:

config.action_mailer.preview_path = "#{Rails.root}/lib/mailer_previews"

توليد العناوين URL في عروض Action Mailer

لا تحتوي نسخة البريد على أي سياق حول الطلب الوارد عكس وحدات التحكّم، لذا عليك تقديم المعامل host: بنفسك.

نظرًا لأنه عادةً ما يكون host: متسقًا عبر التطبيق، تستطيع إعداده على العموم (globally) في config/application.rb:

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

لا تستطيع استخدام أي من المساعدين path_* داخل البريد الإلكتروني بسبب هذا السلوك. سوف تحتاج بدلًا من ذلك إلى استخدام المساعد url_*. مثلًا بدل استخدام:

<%= link_to 'welcome', welcome_path %>

ستحتاج لاستخدام:

<%= link_to 'welcome', welcome_url %>

ستعمل الروابط الآن في رسائلك الإلكترونية باستخدام العنوان URL الكامل.

إنشاء عناوين URL مع url_for

ينشئ url_for عنوان URL كامل افتراضيًا في القوالب.

إن لم تضبط الخيار host: على المستوى العام، تأكّد من تمريره إلى url_for.

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

إنشاء عناوين URL باستخدام المسارات المسماة

لا يمتلك عملاء البريد الإلكتروني أي سياق ويب وبالتالي لا تحتوي المسارات على عنوان URL أساسي لتكوين عناوين ويب كاملة. لذلك يجب دائمًا استخدام المتغيّر "url_" لمساعدي المسارات الموجهة المُسمّاة.

إن لم تضبط الخيار host: على المستوى العام، فتأكّد من تمريره إلى المساعد url.

<%= user_url(@user, host: 'example.com') %>

ملاحظة: تتطلب الروابط غير GET استخدام rails-ujs أو jQuery UJS ولن تعمل في قوالب المُرسل (mailer templates). سيؤدي ذلك إلى طلبات GET العادية.

إضافة الصور في عروض Action Mailer

لا تحتوي نسخة البريد على أي سياق حول الطلب الوارد عكس وحدات التحكّم، لذا عليك تقديم المعامل asset_host: بنفسك.

نظرًا لكون asset_host: عادةً متسقًا عبر التطبيق، تستطيع إعداده بشكل عام (globally) في config/application.rb:

config.action_mailer.asset_host = 'http://example.com'

تستطيع الآن عرض صورة داخل رسالتك الإلكترونيّة.

<%= image_tag 'image.jpg' %>

إرسال رسائل متعددة الأجزاء

سيُرسل Action Mailer رسائل إلكترونيّة متعددة الأجزاء تلقائيًا إن كان لديك قوالب مختلفة لنفس الإجراء (action). لذلك، سيُسرسل Action Mailer تلقائيًا رسالة إلكترونيّة متعدّدة الأجزاء مع نسختين نصيّة و HTML مُعدّتين كجزئين مختلفين بالنسبة لمثالنا UserMailer إن كان الملف welcome_email.text.erb والملف welcome_email.html.erb موجودين في app/views/user_mailer.

يُحدّد parts_order: ترتيب الأجزاء التي تُدخَل داخل التابع ActionMailer::Base.default.

إرسال البريد الإلكتروني مع خيارات التسليم الديناميكي (Dynamic Delivery Options)

إن رغبت في إعادة تعريف (override) خيارات التسليم الافتراضيّة (مثل بيانات اعتماد SMTP) أثناء تسليم البريد الإلكتروني، تستطيع القيام بذلك باستخدام delivery_method_options في الإجراء mailer.

class UserMailer < ApplicationMailer
  def welcome_email
    @user = params[:user]
    @url  = user_url(@user)
    delivery_options = { user_name: params[:company].smtp_user,
                         password: params[:company].smtp_password,
                         address: params[:company].smtp_host }
    mail(to: @user.email,
         subject: "Please see the Terms and Conditions attached",
         delivery_method_options: delivery_options)
  end
end

إرسال رسائل البريد الإلكتروني دون تصيير القالب

قد توجد حالات تريد فيها تخطّي خطوة تصيير القالب وتزويد نص البريد الإلكتروني كسلسلة نصيّة. يمكنك تحقيق ذلك باستخدام الخيار body:. لا تنسَ في مثل هذه الحالات إضافة الخيار content_type:. وإلّا سوف يفترض ريلز انّ نوعه text/plain افتراضيّا.

class UserMailer < ApplicationMailer
  def welcome_email
    mail(to: params[:user].email,
         body: params[:email_body],
         content_type: "text/html",
         subject: "Already rendered!")
  end
end

استلام رسائل البريد الإلكتروني

يمكن أن يكون تلقي رسائل البريد الإلكتروني وتحليلها باستخدام Action Mailer معقّدًا نوعًا ما. عليك إعداد نظامك لتوجيه رسائل البريد إلى تطبيقك بطريقة ما، قبل وصول بريد إلكتروني إلى تطبيقك ريلز، والذي يحتاج للاستماع كي يتحقّق. لذلك لتلقي رسائل البريد الإلكتروني في تطبيق ريلز ستحتاج إلى:

  • تعريف استخدام التابع receive في مُرسل بريدك.
  • إعداد خادم بريدك الإلكتروني لإعادة توجيه الرسائل من العنوان (أو العناوين) الذي تريد استلام رسائل البريد الإلكتروني إلى ‎/path/to/app/bin/rails runner 'UserMailer.receive(STDIN.read)'‎ .

يُحلّل Action Mailer البريد الإلكتروني الوارد الخام بمجرّد تعريف التابع المُسمّى receive جاعلًا منه كائن بريد إلكتروني، ثم يفك ترميزه، وينشئ نسخة مُرسل بريد جديدة، ويُمرّر كائن البريد الإلكتروني إلى تابع نُسخة المُرسل receive. إليك مثال على ذلك:

class UserMailer < ApplicationMailer
  def receive(email)
    page = Page.find_by(address: email.to.first)
    page.emails.create(
      subject: email.subject,
      body: email.body
    )
 
    if email.has_attachments?
      email.attachments.each do |attachment|
        page.attachments.create({
          file: attachment,
          description: email.subject
        })
      end
    end
  end
end

ردود نداء Action Mailer

يتيح لك Action Mailer تحديد before_action و after_action و around_action.

  • يمكن تحديد الفلاتر ببُنية أو رمز لتابع في صنف مرسل البريد بشكل شبيه بوحدات التحكّم.
  • تستطيع استخدام before_action لتعبئة كائن البريد افتراضيًا أو delivery_method_options أو إدراج ترويسات ومرفقات افتراضية.
class InvitationsMailer < ApplicationMailer
  before_action { @inviter, @invitee = params[:inviter], params[:invitee] }
  before_action { @account = params[:inviter].account }
 
  default to:       -> { @invitee.email_address },
          from:     -> { common_address(@inviter) },
          reply_to: -> { @inviter.email_address_with_name }
 
  def account_invitation
    mail subject: "#{@inviter.name} invited you to their Basecamp (#{@account.name})"
  end
 
  def project_invitation
    @project    = params[:project]
    @summarizer = ProjectInvitationSummarizer.new(@project.bucket)
 
    mail subject: "#{@inviter.name.familiar} added you to a project in Basecamp (#{@account.name})"
  end
end
  • تستطيع استخدام after_action لتنفيذ إعداد مشابه كإجراء سابق before_action ولكن باستخدام متغيّرات نسخة (instance variables) مضبوطة بإجراء مُرسل بريدك (mailer action).
class UserMailer < ApplicationMailer
  before_action { @business, @user = params[:business], params[:user] }
 
  after_action :set_delivery_options,
               :prevent_delivery_to_guests,
               :set_business_headers
 
  def feedback_message
  end
 
  def campaign_message
  end
 
  private
 
    def set_delivery_options
      # mail لديك الوصول إلى النسخة,
      # @business و @user ومتغيرات النسخة
      if @business && @business.has_smtp_settings?
        mail.delivery_method.settings.merge!(@business.smtp_settings)
      end
    end
 
    def prevent_delivery_to_guests
      if @user && @user.guest?
        mail.perform_deliveries = false
      end
    end
 
    def set_business_headers
      if @business
        headers["X-SMTPAPI-CATEGORY"] = @business.code
      end
    end
end
  • تقوم مرشحات Action Mailer بإحباط المعالجة الإضافيّة إن ضبط جسم الرسالة إلى قيمة غير معدومة (non-nil).

استخدام مساعدي Action Mailer

يرث Action Mailer الآن من AbstractController، لذلك لديك حق الوصول إلى نفس المُساعدات العاديّة كما تفعل عادةً مع إجراء التحكم (Action Controller).

ضبط Action Mailer

من الأفضل وضع خيارات الضبط التالية في أحد ملفات البيئة (environment.rb، أو production.rb، ...إلخ):

الإعداد الوصف
logger يولّد معلومات حول الدورة البريديّة (mailing run) إن توفّرت. يمكن ضبطه إلى القيمة nil كي لا يُسجّل أي شيء. يتوافق مع كل من مُسجّل روبي Logger والمسجّلات Log4r.
smtp_settings يسمح بالإعداد المُفصّل لتابع التسليم smtp:
  • address:‎ - يسمح لك باستخدام خادم بريد عن بعد. كل ما عليك هو تغييره عن إعداده "localhost" الافتراضي.
  • port:‎ - تستطيع تغييره في حال عدم عمل خادم بريدك على المنفذ 25.
  • domain:‎ - تستطيع إن إحتجت لتحديد نطاق HELO فعل ذلك هنا.
  • user_name:‎ - إن تطلّب خادم بريدك الاستيثاق، عيّن اسم مستخدم في هذا الإعداد.
  • password:‎ - إن تطلّب خادم بريدك الاستيثاق عيّن كلمة المرور في هذا الإعداد.
  • authentication:‎ - إن تطلّب خادم بريدك الاستيثاق، ستحتاج إلى تحديد نوعه في هذا الإعداد. هذا رمزٌ وواحدٌ من plain:‎ (يرسل كلمة المرور بشكل واضح) و login:‎ (سيرسل كلمة المرور المشفّرة على Base64) أو cram_md5:‎ (يجمع آلية تحدّي/استجابة لتبادل المعلومات وخوارزميّة Message Digest 5 المُشفّرة لتجزئة المعلومات المهمّة)
  • enable_starttls_auto:‎ - يكتشف إن فُعّل STARTTLS في خادمك SMTP ويبدأ في استخدامه. القيمة الافتراضية له هي true.
  • openssl_verify_mode:‎ - عند استخدام TLS، تستطيع ضبط كيفيّة تحقّق OpenSSL من الشهادة. يكون هذا مفيدًا حقًا إن احتجت للتحقّق من صحّة شهادة ذاتيّة التوقيع و/أو بطاقة بدل (wildcard certificate). تستطيع استخدام اسم OpenSSL للتحقق من الثابت ('none' أو 'peer') أو مباشرة من الثابت ( OpenSSL::SSL::VERIFY_NONE أو OpenSSL::SSL::VERIFY_PEER).
sendmail_settings يسمح لك بإعادة تعريف خيارات تابع التسليم sendmail:‎.
  • location:‎ - موقع ملف sendmail التنفيذي (executable). القيمة الافتراضية هي ‎/usr/sbin/sendmail .
  • arguments:‎ - مُتغيّرات سطر الأوامر التي ستُمرّر إلى sendmail. القيمة الافتراضية هي ‎-i.
raise_delivery_errors يحدِّد إن وجب رفع الأخطاء أم لا إن لم يُسلّم البريد الإلكتروني. يعمل هذا الإعداد فقط إن أُعدّ خادم البريد الإلكتروني الخارجي للتسليم الفوري.
delivery_method يُعرّف تابع تسليم. القيم المحتملة هي:
  • smtp:‎: (الافتراضية) يمكن إعداده باستخدام config.action_mailer.smtp_settings.
  • sendmail:‎: يمكن إعداده باستخدام config.action_mailer.sendmail_settings.
  • file:‎: حفظ رسائل البريد الإلكتروني بملفّات. يمكن إعداده باستخدام config.action_mailer.file_settings.
  • test:‎: حفظ رسائل البريد الإلكتروني في ActionMailer::Base.deliveries.

راجع توثيقات API لمزيد من المعلومات.

perform_deliveries يحدّد إن نُفّذت عمليات التسليم فعلًا عند استدعاء التابع deliver على رسالة البريد. ذلك هو الوضع افتراضيًّا ولكن يمكن إيقاف تشغيله للمساعدة في الاختبار الوظيفي.
deliveries يحافظ على مصفوفة من جميع الرسائل المرسلة عبر Action Mailer مع delivery_method :test. مفيدة خاصّة بلاختبار الوحدوي والوظيفي.
default_options تسمح لك بتعيين القيم الافتراضية لخيارات التابع mail (مثل from:‎ و reply_to:‎ وما إلى ذلك).

للحصول على معلومات كاملة عن الإعدادات المحتملة، راجع توثيق إعداد Action Mailer في دليل ضبط تطبيقات ريلز.

مثال عن إعداد Action Mailer

مثال على ذلك هو إضافة ما يلي بملفك config/environments/$RAILS_ENV.rb المناسب:

config.action_mailer.delivery_method = :sendmail
# :القيم الافتراضية هي
# config.action_mailer.sendmail_settings = {
#   location: '/usr/sbin/sendmail',
#   arguments: '-i'
# }
config.action_mailer.perform_deliveries = true
config.action_mailer.raise_delivery_errors = true
config.action_mailer.default_options = {from: 'no-reply@example.com'}

إعداد Action Mailer مع Gmail

بما أن Action Mailer يستخدم الآن جوهرة البريد، تصبح العمليّة ببساطة إضافة ما يلي إلى ملفك config/environments/$RAILS_ENV.rb:

config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
  address:              'smtp.gmail.com',
  port:                 587,
  domain:               'example.com',
  user_name:            '<username>',
  password:             '<password>',
  authentication:       'plain',
  enable_starttls_auto: true }

ملاحظة: بدءًا من 15 تموز 2014، عمدت Google إلى زيادة إجراءاتها الأمنيّة، إذ تحظر الآن محاولات التطبيقات التي تراها أقل أمانًا. تستطيع تغيير إعدادات حسابك Gmail هنا للسماح بالمحاولات. إن كانت المصادقة الثنائية (‎2-factor authentication) مُفعّلة بحسابك Gmail، فستحتاج لتعيين كلمة مرور تطبيق واستخدامها بدلًا من كلمة مرورك العاديّة. أو تستطيع بدلًا من ذلك استخدام ESP آخر لإرسال البريد الإلكتروني عن طريق استبدال "smtp.gmail.com" أعلاه بعنوان موفّرك.

اختبار مرسل البريد (Mailer Testing)

تستطيع إيجاد تعليمات مفصّلة حول كيفيّة اختبار مُرسلي بريدك في دليل الاختبار.

اعتراض رسائل البريد الإلكتروني

هناك حالات تحتاج فيها لتعديل بريد إلكتروني قبل تسليمه. لحسن الحظ يوفّر Action Mailer خطافًا لاعتراض كل بريد إلكتروني. تستطيع تسجيل مُعترض لإجراء تعديلات على رسائل البريد قبل تسليمها لوكلاء التوصيل مباشرة.

class SandboxEmailInterceptor
  def self.delivering_email(message)
    message.to = ['sandbox@example.com']
  end
end

يجب تسجيل المُعترض في إطار عمل Action Mailer قبل أن يتمكن من القيام بواجبه. يمكنك ذلك في ملف تهيئة (initializer) من config/initializers/sandbox_email_interceptor.rb:

if Rails.env.staging?
  ActionMailer::Base.register_interceptor(SandboxEmailInterceptor)
end

ملاحظة: يستخدم المثال أعلاه بيئة مخصصّة تسمى "منصة الإطلاق" (staging) لاختبار خادم شبيه بخادم الإنتاج ولكن لأغراض الاختبار. تستطيع قراءة القسم "إنشاء بيئات ريلز" في دليل ضبط تطبيقات ريلز لمزيد من المعلومات حول بيئات ريلز المخصصّة.

مصادر