# [Rails4][mailer]用內建mailer發送郵件:使用hostinger ###### tags: `Rails4`,`mailer` >ruby: 2.2.6 >rails: 4.2.8 > 參考資料: https://railsbook.tw/chapters/20-send-email.html https://railsbook.tw/chapters/21-background-job.html https://stackoverflow.com/questions/48970619/rails-delayed-job-is-using-a-different-time-zone https://ihower.tw/rails/actionmailer.html >本文假設已有hostinger的帳號並有開通相關smtp設定 >hostinger是一個國外知名的網路主機供應商 >主頁: >https://hpanel.hostinger.com/ >啟用smtp: >https://www.hostinger.com/how-to/how-can-i-enable-smtp-authentication ## 新建專案 ```c! $ rails new rails_mailer -d postgresql $ cd rails_mailer $ rake db:setup $ bundle $ rails s ``` ## mailer ### 1.新增Contact_mailer ```c! $ rails g mailer Contact ``` ### 2.編輯application_mailer app/mailers/application_mailer.rb ```ruby class ApplicationMailer < ActionMailer::Base default from: "urmailaccounts@urdomain" #<--編輯此行 layout 'mailer' end ``` ### 3.編輯contact.rb app/mailers/contact.rb ```ruby class Contact < ApplicationMailer def say_hello(address="clouderstorage@gmail.com") mail to: address,subject:"這是一封測試郵件" end end ``` >Rails 4.2版的mailer用cmd產生時,似乎不會在檔名後面加上_mailer > ### 4.新增say_hello.html.erb app/views/contact/say_hello.html.erb ```ruby <h2>這是一封測試信</h2> ``` ## 修改發信設定 參考: https://ihower.tw/rails/actionmailer.html ### 1.修改development.rb config/environments/development.rb ```ruby! Rails.application.configure do #... config.action_mailer.delivery_method = :smtp config.action_mailer.default_url_options = { host: "http://localhost:3000" } config.action_mailer.smtp_settings = config_for(:email).symbolize_keys end ``` ### 2.新增email.yml config/email.yml ```yaml= development: address: "smtp.hostinger.com" port: 587 authentication: "plain" user_name: "ur_mail_account@ur_domain" password: "ur password" enable_starttls_auto: true openssl_verify_mode: 'none' ``` >*上面檔案是hostinger的設定,各家mail dsmtp設定不同 ## 開啟log顯示(optional) config/environments/development.rb ```ruby! Rails.application.configure do #... # Don't care if the mailer can't send. config.action_mailer.raise_delivery_errors = true #<--把這個改為true ``` ## 修改config/routes ```ruby root 'home#index' ``` ## home_controller ### 1.新增home_controller ```terminal $ rails g controller home ``` ### 2.修改home_controller app/controllers/home_controller.rb ```ruby= class HomeController < ApplicationController def index end #POST home/send_mail def send_mail Contact.say_hello(params[:email]).deliver_now flash[:notice]="email has been sent to #{params[:email]}" redirect_to root_path end end ``` ## 新增index.html.erb app/views/home/index.html.erb ```htmlmixed= <h2>home index</h2> <% if flash[:notice] %> <p><%=flash[:notice]%></p> <% end %> <%=form_tag("/home/send_mail") do%> <div>email:</div> <%= text_field_tag :email, "", class: 'form-control' %> <%= submit_tag "send email"%> <% end %> ``` >1.沒有要寫入資料庫也沒有用到model,使用form_tag直接傳值即可, >不使用form_for >2.input欄位是輸入收件人之用,按下send email後,收件人會收到測試信件 到此,執行rails s,應可正常發送信件 因發送信件需要時間, 實務上有時會將發信的工作在系統較不忙碌時再於背景執行 要在背景執行有兩種做法,delayed_job跟sidekiq,擇一使用即可 ## delayed_job ### 安裝 delayed_job_active_record ```c! $ bundle add 'delayed_job_active_record' $ rails g delayed_job:active_record $ rake db:migrate ``` ### 修改application.rb https://stackoverflow.com/questions/48970619/rails-delayed-job-is-using-a-different-time-zone https://railsbook.tw/chapters/21-background-job.html config/application.rb ```ruby! require File.expand_path('../boot', __FILE__) require 'rails/all' # Require the gems listed in Gemfile, including any gems # you've limited to :test, :development, or :production. Bundler.require(*Rails.groups) module RailsMailer class Application < Rails::Application ###加上以下內容 config.active_job.queue_adapter = :delayed_job config.active_record.default_timezone = :local #<-加上這句以避免時區問題 ###加上以上內容 #... config.active_record.raise_in_transactional_callbacks = true end end ``` ### 設定jobs app/jobs/send_test_email_job.rb ```ruby class SendTestEmailJob < ActiveJob::Base queue_as :default def perform(email) # Do something later Contact.say_hello(email).deliver_now end end ``` ### 修改home_controller ```ruby! class HomeController < ApplicationController def index end #POST home/send_mail def send_mail SendTestEmailJob.perform_later(params[:email]) #<-修改此行 flash[:notice]="email has been sent to #{params[:email]}" redirect_to root_path end end ``` ### 自訂路徑設定 https://stackoverflow.com/questions/20396944/rails-custom-delayed-job-uninitialized-constant config/application.rb ```ruby! config.autoload_paths += %W( #{config.root}/ur/path/to/jobs ) ``` ### 執行server https://stackoverflow.com/questions/28229923/invoke-rake-jobswork-automatically-after-running-rails-s-in-console ```terminal rails server & rake jobs:work ``` > 必須加上rake jobs:work 才會在背景執行工作 --- ## sidekiq https://ihower.tw/rails/background-process.html https://www.botreetechnologies.com/blog/migrate-from-delayed-job-to-sidekiq https://hostpresto.com/community/tutorials/how-to-install-and-configure-redis-on-ubuntu-14-04/ sidekiq與delayed_jobs類似,只是它使用redis做為暫存資料庫. 使用redis不需帳號密碼 (有些檔案與之前的相同未更動,故略過) ### 安裝redis ```c! $ sudo apt-get update $ sudo apt-get -y install redis-server ``` ### 確認redis已經在運行 ```c! $ sudo service redis-server status ``` ### 安裝sidekiq gem ```c! bundle add sidekiq ``` config/application.rb ```ruby= require File.expand_path('../boot', __FILE__) require 'rails/all' Bundler.require(*Rails.groups) module RailsMailer class Application < Rails::Application ###加上以下內容 config.active_job.queue_adapter = :sidekiq #<-改成使用sidekiq config.active_record.default_timezone = :local #<-加上這句以避免時區問題 ###加上以上內容 #... config.active_record.raise_in_transactional_callbacks = true end end ``` ### 設定worker app/workers/email_sender.rb ```ruby class EmailSender include Sidekiq::Worker def perform(email) # Do something later Contact.say_hello(email).deliver_now end end ``` ### 自訂路徑設定 https://stackoverflow.com/questions/20396944/rails-custom-delayed-job-uninitialized-constant config/application.rb ```ruby! config.autoload_paths += %W( #{config.root}/ur/path/to/workers ) ``` ### 修改home_controller ```ruby! class HomeController < ApplicationController def index end #POST home/send_mail def send_mail # send email using sidekiq EmailSender.perform_async(emails,title,content) #<-修改此行 flash[:notice]="email has been sent to #{params[:email]}" redirect_to root_path end end ``` ## 執行server 先執行sidekiq ```c! $ sidekiq ``` 再開新terminal執行rails server ```c! $ rails s ```