Więcej na rubyonrails.pl: Start | Pobierz | Wdrożenie | Kod (en) | Screencasty | Dokumentacja | Ekosystem | Forum | IRC

Action Mailer Podstawy

Przewodnik ten powinien zapewnić Ci wszystko, czego potrzebujesz, aby rozpocząć wysyłanie i odbieranie wiadomości od/do aplikacji i zapoznać się z wieloma cechami wewnętrznymi Action Mailera. Pokazuje on również jak przetestować wysyłanie Twoich wiadomości.

1 Wstęp

Moduł Action Mailer umożliwia wysyłanie e-maili z Twojej aplikacji przy użyciu modelu programu pocztowego i jego widoków. Tak więc, w Rails, e-maile są wykorzystywane przez tworzenie modeli, które dziedziczą z ActionMailer::Base, które to istnieją obok innych modeli w folderze app/models. Modele te posiadają skojarzone widoki, które to są wyświetlane obok widoków kontrolera w folderze app/views.

2 Wysyłanie e-maili

W tej sekcji omówimy krok po kroku tworzenie mailera i jego widoków.

2.1 Wprowadzenie do Generowania Mailera

2.1.1 Tworzenie modułu Action Mailer
./script/generate mailer UserMailer exists app/models/ create app/views/user_mailer exists test/unit/ create test/fixtures/user_mailer create app/models/user_mailer.rb create test/unit/user_mailer_test.rb

Mamy więc takie elementy jak model, dodatki oraz testy.

2.1.2 Edycja Modelu

Plik app/models/user_mailer.rb zawiera pusty mailer:

class UserMailer < ActionMailer::Base end

Dodajmy metodę nazywaną welcome_email, która wyśle e-mail na zarejestrowany adres e-mail użytkownika:

class UserMailer < ActionMailer::Base def welcome_email(user) recipients user.email from "My Awesome Site Notifications <notifications@example.com>" subject "Welcome to My Awesome Site" sent_on Time.now body( {:user => user, :url => "http://example.com/login"}) end end

Oto krótkie wyjaśnienie opcji przedstawionych w poprzedniej metodzie. Aby uzyskać pełną listę dostępnych opcji, należy spojrzeć w dół, w pełną listę modułu Action Mailer (Complete List of Action Mailer) pokazującą ustawiane przez użytkownika atrybuty sekcji.

recipients e-mail odbiorcy. Może to być łańcuch znaków lub jeśli istnieje wielu odbiorców, tablica łańcuchów znaków
from z jakiego adresu pochodzi email
subject temat e-maila
sent_on znacznik czasu e-maila

Klucze hasha przekazywane do body stają się zmiennymi instancji w widoku. Zatem w naszym przykładzie, widok mailera będzie miał je dostępne jako zmienne instancji @user i @url.

2.1.3 Tworzenie widoku Mailera

Stwórz plik o nazwie welcome_email.text.html.erb w folderze app/views/user_mailer/. Będzie to szablon używany do wiadomości e-mail w formacie HTML:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html> <head> <meta content="text/html; charset=UTF-8" http-equiv="Content-Type" /> </head> <body> <h1>Welcome to example.com, <%=h @user.first_name %></h1> <p> You have successfully signed up to example.com, and your username is: <%= @user.login %>.<br/> To login to the site, just follow this link: <%=h @url %>. </p> <p>Thanks for joining and have a great day!</p> </body> </html>

Gdybyśmy chcieli wysłać tylko tekst wiadomości e-mail, plik byłby nazwany welcome_email.text.plain.erb.

Railsy ustawiają typ zawartości wiadomości e-mail jako ten używany w nazwie pliku.

2.1.4 Włączanie opcji wysyłającej e-maila z powiadomieniem, w momencie gdy użytkownik się zarejestruje

Istnieją trzy sposoby osiągnięcia tego celu. Jednym z nich jest wysłanie e-maila od kontrolera, który wysyła wiadomości e-mail. Kolejnym jest umieszczenie w modelu użytkownika, e-maila zwrotnego w metodzie before_create. Natomiast ostatnim rozwiązaniem jest wykorzystanie obserwatora w modelu użytkownika. To czy korzystasz z drugiej, czy trzeciej metody zależy od Ciebie, jednak korzystanie z pierwszego rozwiązania nie jest zalecane. Nie dlatego, że jest ono złe, lecz dlatego, że drugie i trzecie rozwiązanie utrzymuje kontroler czysty i zachowuje wszystkie logiczne powiązania z modelem użytkownika. W ten sposób, niezależnie od sposobu tworzenia użytkownika (np. poprzez formularz na stronie www lub wywołania API) mamy zagwarantowane, że e-mail zostanie wysłany.

Zobaczmy jak pójdzie nam powiązanie go za pomocą obserwatora:

W pliku config/environment.rb:

Rails::Initializer.run do |config| # ... config.active_record.observers = :user_observer end

Możesz umieścić obserwatora w folderze app/models, gdzie zostanie on załadowany automatycznie przez Rails.

Teraz należy stworzyć plik o nazwie user_observer.rb w folderze app/models w zależności od miejsca gdzie go zapisałeś. Plik ten powinien wyglądać następująco:

class UserObserver < ActiveRecord::Observer def after_create(user) UserMailer.deliver_welcome_email(user) end end

Zauważ, jak nazywamy metodę deliver_welcome_email? W module Action Mailer wysyłamy e-maile za pomocą wywołania deliver_<method_name>. W module User Mailer, zdefiniowaliśmy metodę zwaną welcome_email, a więc dostarczamy wiadomości przez wywołanie metody deliver_welcome_email. W następnej sekcji przeanalizujemy jak Action Mailer jest w stanie to zrobić.

2.2 Action Mailer oraz metody dynamiczne deliver_<method_name>

W jaki więc sposób Action Mailer rozumie wywołanie deliver_welcome_email ? Jeśli przeczytasz dokumentację (http://api.rubyonrails.org/files/vendor/rails/actionmailer/README.html), to w sekcji “Sending Emails” znajdziesz wszelkie potrzebne informacje:

Twoje metody dostarczania instancji są zazwyczaj automatycznie opakowane w metodach klas, zaczynających się od słowa deliver_, po którym następuje nazwa metody mailera, którą chcesz wykonać.

Zatem, jak dokładnie to działa?

Patrząc na źródło ActionMailer::Base znajdziesz:

def method_missing(method_symbol, *parameters)#:nodoc: case method_symbol.id2name when /^create_([_a-z]\w*)/ then new($1, *parameters).mail when /^deliver_([_a-z]\w*)/ then new($1, *parameters).deliver! when "new" then nil else super end end

Tak więc, jeśli nazwa metody rozpoczyna się od deliver_ po dowolnej kombinacji małych liter lub podkreślenia, to metoda method_missing wywołuje metodę new na Twojej klasie mailera (w naszym przykładzie powyżej jest to UserMailer), wysyłając kombinację małych liter lub podkreślenia wraz z parametrami. Obiekt jest następnie przesyłany metodą deliver!, która to… go dostarcza.

2.3 Pełna lista atrybutów ustawialnych przez użytkownika modułu Action Mailer

bcc Adresy BCC adresów e-mail, występują albo jako ciąg znaków (jeden adres) lub tablica łańcuchów znaków (do wielu adresatów)
body Część body adresu e-mail. Jest to albo tablica asocjacyjna (w tym przypadku określa ona zmienne przekazywane do szablonu podczas renderingu) lub ciąg znaków, w którym to przypadku określa on aktualną zawartość części body wiadomości e-mail
cc Adresy CC dla poczty elektronicznej, reprezentowane jako ciąg znaków (jeden adres) lub jako tablica łańcuchów znaków (do wielu adresatów)
charset Charset dla wiadomości e-mail. Ustawienia domyśle default_charset określone dla ActionMailer::Base.
content_type Typ zawartości (content type) wiadomości email. Domyślnie ustawiony jako “text/plain”, ale nazwa pliku może go określać
from Z jakiego adresu e-mail nadano wiadomość
reply_to Adres, na który bezpośrednio będzie przesłana odpowiedź (jeśli jest on inny niż adres “from”)
headers Dodatkowe nagłówki, które będą dodane do wiadomości e-mail
implicit_parts_order Kolejność, w jakiej części powinny być posortowane na podstawie typu zawartości (content type). Domyślna wartość to default_implicit_parts_order
mime_version Domyślna wersja mime to “1.0”, ale może zostać podana w razie wyraźnej potrzeby
recipient Odbiorca wiadomości e-mail, reprezentowany jako ciąg znaków( gdy jest to jeden adres) lub jako tablica łańcuchów znaków (w przypadku wielu adresów)
sent_on Znacznik czasu, w którym wiadomość została wysłana. Jeśli nie został on ustawiony, nagłówek zostanie ustalony przez dostarczyciela
subject Temat wiadomości e-mail
template Szablon możliwy do użycia. Jest to “podstawowa” nazwa szablonu, bez rozszerzenia lub katalogu, może być użyta do wielu metod mailera tego samego szablonu
2.3.1 Making Inline Attachments

Inline attachments are now possible in ActionMailer. While previously in the pre 3.0 version of Rails, you could do inline attachments, it involved a lot of hacking and determination to pull it off.

ActionMailer now makes inline attachments as trivial as they should be.

  • Firstly, to tell Mail to turn an attachment into an inline attachment, you just call #inline on the attachments method within your Mailer:
def welcome attachments.inline['image.jpg'] = File.read('/path/to/image.jpg') end
  • Then in your view, you can just reference attachments[] as a hash and specify which attachment you want to show, calling url on it and then passing the result into the image_tag method:
<p>Hello there, this is our image</p> <%= image_tag attachments['image.jpg'].url %>
  • As this is a standard call to image_tag you can pass in an options hash after the attachment url as you could for any other image:
<p>Hello there, this is our image</p> <%= image_tag attachments['image.jpg'].url, :alt => 'My Photo', :class => 'photos' %>

2.4 Widoki Mailera

Widoki Mailera znajdują się w katalogu app/views/name_of_mailer_class. Konkretny widok mailera jest znany klasie, ponieważ jego nazwa jest taka sama jak metoda mailera. Na przykład, w naszym przykładzie u góry, nasz widok mailera dla metody welcome_email znajduje się w pliku app/views/user_mailer/welcome_email.text.html.erb dla wersji HTML oraz dla wersji tekstowej w welcome_email.text.plain.erb.

Aby zmienić domyślny widok mailera powinieneś zrobić coś takiego:

class UserMailer < ActionMailer::Base def welcome_email(user) recipients user.email from "My Awesome Site Notifications<notifications@example.com>" subject "Welcome to My Awesome Site" sent_on Time.now body( {:user => user, :url => "http://example.com/login"}) content_type "text/html" # use some_other_template.text.(html|plain).erb instead template "some_other_template" end end

2.5 Layouty Action Mailera

Podobnie jak w widokach kontrolera możemy posiadać layouty mailera. Nazwa layoutu musi kończyć się końcówką “_mailer”, aby być automatycznie rozpoznawalną przez mailera jako layout. Tak więc w naszym przykładzie UserMailer, musimy odwołać się do naszego layoutu user_mailer.text.(html|plain).erb. Aby korzystać z innego pliku, użyj:

class UserMailer < ActionMailer::Base layout 'awesome' # use awesome.text.(html|plain).erb as the layout end

Podobnie jak w przypadku kontrolera widoku, należy użyć metody yield, aby wyrenderować widok wewnątrz layoutu.

2.6 Generowanie adresów URL w widokach Action Mailer

Adresy URL mogą być generowane przy użyciu metody url_for lub nazwanej ścieżki. W przeciwieństwie do kontrolerów, zapytanie mailera nie ma żadnego związku z przychodzącym żądaniem, dlatego musimy podać takie dane jak :host, :controller, i :action:.

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

Przy użyciu nazwanych ścieżek potrzebujemy dodać tylko :host:

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

Klienci e-mail nie mają żadnego związku z adresami internetowymi, dlatego też ścieżki nie mają bazowego adresu URL do utworzenia kompletnych adresów internetowych. Tak więc podczas korzystania z ścieżek sens ma tylko wariant “_url”.

Jest także możliwość ustawienia domyślnego hosta, który będzie stosowany we wszystkich podmiotach wysyłających poprzez użycie opcji :host w hashu ActionMailer::Base.default_url_options tak jak poniżej:

ActionMailer::Base.default_url_options[:host] = "example.com"

Możemy to również ustawić jako opcję w konfiguracji config/environment.rb:

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

W przypadku domyślnego ustawienia :host dla wysyłanych wiadomości należy dodać :only_path => false do url_for. W przeciwnym razie nasze ustawienia nie zostaną uwzględnione.

2.7 Wysyłanie wieloczęściowych wiadomości e-mail

Action Mailer automatycznie wyśle wieloczęściowe wiadomości e-mail, jeśli wykorzystasz różne szablony dla tych samych akcji. Tak więc dla naszego przykładu UserMailer, jeśli pliki welcome_email.text.plain.erb i welcome_email.text.html.erb znajdują się w katalogu app/views/user_mailer, to Action Mailer automatycznie wyśle wieloczęściową wiadomość e-mail jako wersję HTML i wersję tekstową w dwóch różnych częściach.

Aby jawnie określić wieloczęściowe wiadomości, możesz zrobić coś takiego:

class UserMailer < ActionMailer::Base def welcome_email(user) recipients user.email_address subject "New account information" from "system@example.com" content_type "multipart/alternative" part :content_type => "text/html", :body => "<p>html content, can also be the name of an action that you call<p>" part "text/plain" do |p| p.body = "text content, can also be the name of an action that you call" end end end

2.8 Wysyłanie e-maili z załącznikami

Załączniki mogą być dodawane za pomocą metody attachment :

class UserMailer < ActionMailer::Base def welcome_email(user) recipients user.email_address subject "New account information" from "system@example.com" content_type "multipart/alternative" attachment :content_type => "image/jpeg", :body => File.read("an-image.jpg") attachment "application/pdf" do |a| a.body = generate_your_pdf_here() end end end

2.9 Wysyłanie wieloczęściowych wiadomości e-mail z załącznikami

Po zastosowaniu metody attachment moduł Action Mailer nie będzie automatycznie używać odpowiedniego szablonu na podstawie nazwy pliku, ani nie będzie prawidłowo porządkował alternatywnych części. Aby to zrobić, musimy zadeklarować, których szablonów używamy do każdego typu zawartości poprzez zastosowanie metody part. W dodatku musimy zadeklarować je w odpowiedniej kolejności.

W poniższym przykładzie, byłyby dwa pliki szablonów, welcome_email_html.erb oraz welcome_email_plain.erb w folderze app/views/user_mailer. Część text/plain musi być wymieniona jako pierwsza oraz w pełni kompatybilna z klientem e-mail. Jeśli text/plain znajduje się na liście po text/html, niektórzy klienci mogą wyświetlać wiadomość zarówno w wersji HTML jak i jako zwykły tekst wiadomości e-mail. Każdy alternatywny tekst musi być zamknięty w części multipart/alternative. Nie przypisuj całej zawartości wiadomości content_type do części multipart/alternative. Warto również nadmienić, iż niektóre rodzaje klientów e-mail mogą ignorować wyświetlanie załączników w formacie PDF.

class UserMailer < ActionMailer::Base def welcome_email(user) recipients user.email_address subject "New account information" from "system@example.com" part "multipart/alternative" do |pt| pt.part "text/plain" do |p| p.body = render_message("welcome_email_plain", :message => "text content") end pt.part "text/html" do |p| p.body = render_message("welcome_email_html", :message => "<h1>HTML content</h1>") end end attachment :content_type => "image/jpeg", :body => File.read("an-image.jpg") attachment "application/pdf" do |a| a.body = generate_your_pdf_here() end end end

3 Odbieranie wiadomości e-mail

Odbieranie i przetwarzanie e-maili z modułu Action Mailer może być dość skomplikowanym i trudnym przedsięwzięciem. Nim e-mail dociera do aplikacji Rails, musimy skonfigurować system, aby w jakiś sposób wiadomości były przekazywane do aplikacji. Tak więc, aby otrzymywać wiadomości w aplikacji Rails, musimy:

1. Zaimplementować metodę receive w mailerze.

2. Skonfigurować nasz serwer pocztowy do przekazywania wiadomości e-mail z adresu(ów), które chcesz aby aplikacja otrzymywała /path/to/app/script/runner 'UserMailer.receive(STDIN.read)'.

Gdy metoda receive jest już zdefiniowana w każdym mailerze, moduł Action Mailer przetworzy surową wiadomość na obiekt e-mail, zdekoduje go, utworzy nowy mailer i e-mail oraz przekaże obiekt do instancji metody receive naszego mailera.

Oto przykład:

class UserMailer < ActionMailer::Base def receive(email) page = Page.find_by_address(email.to.first) page.emails.create( :subject => email.subject, :body => email.body ) if email.has_attachments? for attachment in email.attachments page.attachments.create({ :file => attachment, :description => email.subject }) end end end end

4 Korzystanie z helperów modułu Action Mailer

Action Mailer ma 4 dostępne klasy metod pomocniczych:

add_template_helper(helper_module) Tworzy wszystkie (instancje) metody w module pomocny dla dostępnych szablonów wykonywanych przez ten kontroler
helper(*args, &block) Deklarowanie helpera: helper :foo wymaga ‘foo_helper’ i zawiera FooHelper w szablonie klasy. Helper { def foo() “#{bar} is the very best” end } tworzy blok w szablonie klasy, dodając metodę foo. helper(:three, BlindHelper) { def mice() ‘mice’ end } tworzy natomiast wszystkie trzy.
helper_method Deklarowanie metody kontrolera jako helpera. Na przykład, helper_method: link_to def link_to (nazwa, opcje) … end tworzy link_to metody kontrolera dostępnego w widoku.
helper_attr Deklarowanie atrybutów kontrolera jako helpera. Na przykład, helper_attr :name attr_accessor :name sprawia, że :name i name w metodzie kontrolera umożliwia dostęp do widoku. Jest to wygodny wrapper dla helper_method.

5 Action Mailer – konfiguracja

Następujące opcje konfiguracyjne są najlepiej wykonane w jednym z plików środowiskowych (environment.rb, production.rb, itp. ..)

template_root Określa bazę szablonów odniesienia, z których będą one pochodzić
logger Logger służy do generowania informacji o uruchomionym mailingu jeśli wiadomości są dostępne. Może on być ustawiony na zero bez logowania. Jest kompatybilny z obydwoma loggerami własnymi Ruby jak i z loggerami Log4r.
smtp_settings Umożliwia szczegółową konfigurację dla metody :smtp delivery method: :address – metoda ta pozwala korzystać ze zdalnego serwera poczty. Wystarczy zmienić domyślne ustawienie “localhost”. :port – W przypadku, gdy twój serwer nie działa na porcie 25 możesz go zmienić. :domain – Jeśli musisz określić domenę HELO, możesz zrobić to tutaj. :user_name – Jeśli serwer poczty wymaga uwierzytelniania należy ustawić jego nazwę w tym ustawieniu. :password – Jeśli serwer poczty wymaga uwierzytelniania należy ustawić hasło w tym ustawieniu. :authentication – Jeśli serwer poczty wymaga uwierzytelniani należy określić typ uwierzytelniania tutaj. Jest to jeden z symboli :plain, :login lub :cram_md5.
sendmail_settings Pozwala na nadpisanie opcji dla metody dostawy :sendmail. :location – lokacja wykonywania sendmail. Domyślnie w folderze /usr/sbin/sendmail. :arguments – argumenty wiersza poleceń. Domyślnie ustawione z -i -t.
raise_delivery_errors Ustawienie decydujące o tym, czy ilość błędów powinna być zwiększona, jeżeli e-mail nie zostanie dostarczony.
delivery_method Określa metody dostawy. Możliwe wartości to :smtp (domyślnie), :sendmail oraz :test.
perform_deliveries Określa, czy metody deliver_* są rzeczywiście realizowane. Domyślnie są one włączone, ale można je wyłączyć w celu testowania funkcjonalnego.
deliveries Trzymają tablicę wszystkich e-maili wysyłanych za pośrednictwem modułu Action Mailer z delivery_method :test. Najbardziej przydatne dla jednego elementu i testowania funkcjonalnego.
default_charset Domyślne ustawienie kodowania dla części body oraz odkodowania tematu wiadomości e-mail. Domyślnie jest to kodowanie UTF-8. Możemy również wybrać inne kodowanie.
default_content_type Domyślny typ zawartości używany dla głównej części wiadomości. Domyślnie jest on ustawiony na “text/plain”. Można również wybrać inny typ zawartości z wnętrza metody content_type.
default_mime_version Domyślna wersja mime wiadomości. Domyślnie ustawiona na 1.0. Można również wybrać inną wartość z wnętrza metody mime_version.
default_implicit_parts_order Gdy wiadomość zbudowana została w sposób domyślny, bezwarunkowy (np. różnorodne części są złożone z szablonów które określają typ zawartości w ich nazwach) zmienna ta kontroluje to jak ułożone zostaną te części. Domyślnie ustawione zostaną w takiej kolejności: [“text/html”, “text/enriched”, “text/plain”]. Elementy, które pojawiają się jako pierwsze w tablicy, posiadają wyższy priorytet w kliencie poczty e-mail i pojawiają się na końcu w komunikacie zakodowanym mime. Można również wybrać inny układ z wnętrza metody implicit_parts_order.

5.1 Przykład konfiguracji Action Mailera

Przykładem może być:

ActionMailer::Base.delivery_method = :sendmail ActionMailer::Base.sendmail_settings = { :location => '/usr/sbin/sendmail', :arguments => '-i -t' } ActionMailer::Base.perform_deliveries = true ActionMailer::Base.raise_delivery_errors = true ActionMailer::Base.default_charset = "iso-8859-1"

5.2 Action Mailer – konfiguracja Gmail

Instrukcja skopiowana z tego wpisu na blogu utworzonego przez Roberta Zottera.

Najpierw należy zainstalować plugin action_mailer_tls. Następnie wszystko co musisz zrobić, to skonfigurować moduł Action Mailer w następujący sposób:

ActionMailer::Base.smtp_settings = { :address => "smtp.gmail.com", :port => 587, :domain => "domain.com", :user_name => "user@domain.com", :password => "password", :authentication => :plain }

5.3 Skonfiguruj moduł Action Mailer, aby rozpoznać szablony Haml

W pliku config/environment.rb, należy dodać następujący wiersz:

ActionMailer::Base.register_template_extension('haml')

6 Testowanie Mailera

Domyślnie Action Mailer nie wysyła e-maili w środowisku testowym. Są one po prostu dodawane do tablicy ActionMailer::Base.deliveries.

Testowanie mailera obejmuje zazwyczaj dwie rzeczy: Jedna, to informacja, że mail został w kolejce, a druga, że email jest poprawny. Mając to na uwadze, możemy przetestować nasz przykład z góry, tak jak poniżej:

class UserMailerTest < ActionMailer::TestCase def test_welcome_email user = users(:some_user_in_your_fixtures) # Send the email, then test that it got queued email = UserMailer.deliver_welcome_email(user) assert !ActionMailer::Base.deliveries.empty? # Test the body of the sent email contains what we expect it to assert_equal [user.email], email.to assert_equal "Welcome to My Awesome Site", email.subject assert_match /Welcome to example.com, #{user.first_name}/, email.body end end

W teście mamy wysłać e-maila i zapisać zwrócony obiekt w zmiennej email. Po czym możemy upewnić się, że wiadomość została wysłana (pierwsze potwierdzenie), następnie stwierdzamy, czy e-mail rzeczywiście zawiera to, czego się spodziewaliśmy.

7 Changelog

Lighthouse ticket