# [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
```