目的
- Rails6で作成したアプリからGmailのメールサーバを利用してメールを送信する方法をまとめる。
前提条件
- Googleのアカウントを持っていること。
- Googleの二段階認証の設定がなされていること。
- Googleのアプリケーション用パスワードを取得していること。
- 正常に動作しているRails6のアプリがあること。
- テストメールを送信できるアドレス(メールを送信しても迷惑にならないメールアドレス)を知っていること。
- 二段階認証、アプリケーション用パスワードを取得しているアカウントのメールアドレスでもOK(自分にテストメールを送信して確認する)
作業期待値
- とりあえず難しい処理は無しにしてRails6アプリケーションからのメールが送信できるようにする。
- Rails6のアプリケーションから何かのトリガーを用いてGmailのメールサーバからテストメールを送信する。
- 継承やその他詳しい話はせずとにかくメールを送信できるようにする。
- メーラーの詳しい話や処理方法は下記の本のメールについての記載を確認することをオススメする。(アフィリエイトリンクでは無いので安心してください。)
作業の前にちょっと聞いてほしいこと
- 筆者はこのメールの実装に非常に時間がかかった。
- データベースの内容をメールに添付しようとしたり、宛先を複数指定したりしたためミスが多くあった。
- 余計なことは考えずとにかくテストメールを送信できることを最優先に作業をした方が良い気がする。
作業概要
- メーラーの作成
- 送信アドレス設定
- メールサーバの設定
- 送信トリガーの設定
- テスト送信
作業詳細
-
メーラーの作成
-
下記コマンドを実行してメーラーを作成する。(notice greetingはクラス名とメソット名であるため任意のものでも構わない)
$ cd アプリ名フォルダ $ rails g mailer notice greeting
-
先のコマンドを実行するとメーラーが作成される。メール送信に必要なファイル群が作成されると思っていただきたい。
-
-
送信アドレス設定
-
下記に存在する送信先を指定するファイル
notice_mailer.rb
をエディタで開く- アプリ名フォルダ/app/mailers
- notice_mailer.rb
- アプリ名フォルダ/app/mailers
-
ファイル
notice_mailer.rb
の下記の部分を下記のように修正してテストメールを送信する先のアドレスを設定する。class NoticeMailer < ApplicationMailer # Subject can be set in your I18n file at config/locales/en.yml # with the following lookup: # # en.notice_mailer.greeting.subject # def greeting @greeting = "Hi" mail to: "to@example.org" end end
↓修正
class NoticeMailer < ApplicationMailer # Subject can be set in your I18n file at config/locales/en.yml # with the following lookup: # # en.notice_mailer.greeting.subject # def greeting @greeting = "Hi" mail to: "テストメール送信先アドレス" end end
-
-
メールサーバの設定
-
下記に存在する送信先を指定するファイル
development.rb
をエディタで開く- アプリ名フォルダ/config/environments
- development.rb
- アプリ名フォルダ/config/environments
-
ファイル
development.rb
を下記のように修正してテストメールを送信するメールサーバを設定する。Rails.application.configure do # Settings specified here will take precedence over those in config/application.rb. # In the development environment your application's code is reloaded on # every request. This slows down response time but is perfect for development # since you don't have to restart the web server when you make code changes. config.cache_classes = false # Do not eager load code on boot. config.eager_load = false # Show full error reports. config.consider_all_requests_local = true # Enable/disable caching. By default caching is disabled. # Run rails dev:cache to toggle caching. if Rails.root.join('tmp', 'caching-dev.txt').exist? config.action_controller.perform_caching = true config.action_controller.enable_fragment_cache_logging = true config.cache_store = :memory_store config.public_file_server.headers = { 'Cache-Control' => "public, max-age=#{2.days.to_i}" } else config.action_controller.perform_caching = false config.cache_store = :null_store end # Store uploaded files on the local file system (see config/storage.yml for options). config.active_storage.service = :local # Don't care if the mailer can't send. config.action_mailer.raise_delivery_errors = false config.action_mailer.perform_caching = false # Print deprecation notices to the Rails logger. config.active_support.deprecation = :log # Raise an error on page load if there are pending migrations. config.active_record.migration_error = :page_load # Highlight code that triggered database queries in logs. config.active_record.verbose_query_logs = true # Debug mode disables concatenation and preprocessing of assets. # This option may cause significant delays in view rendering with a large # number of complex assets. config.assets.debug = true # Suppress logger output for asset requests. config.assets.quiet = true # Raises error for missing translations. # config.action_view.raise_on_missing_translations = true # Use an evented file watcher to asynchronously detect changes in source code, # routes, locales, etc. This feature depends on the listen gem. config.file_watcher = ActiveSupport::EventedFileUpdateChecker end
↓修正
Rails.application.configure do # Settings specified here will take precedence over those in config/application.rb. # In the development environment your application's code is reloaded on # every request. This slows down response time but is perfect for development # since you don't have to restart the web server when you make code changes. config.cache_classes = false # Do not eager load code on boot. config.eager_load = false # Show full error reports. config.consider_all_requests_local = true # Enable/disable caching. By default caching is disabled. # Run rails dev:cache to toggle caching. if Rails.root.join('tmp', 'caching-dev.txt').exist? config.action_controller.perform_caching = true config.action_controller.enable_fragment_cache_logging = true config.cache_store = :memory_store config.public_file_server.headers = { 'Cache-Control' => "public, max-age=#{2.days.to_i}" } else config.action_controller.perform_caching = false config.cache_store = :null_store end # Store uploaded files on the local file system (see config/storage.yml for options). config.active_storage.service = :local # Don't care if the mailer can't send. # config.action_mailer.perform_caching = false config.action_mailer.raise_delivery_errors = true config.action_mailer.delivery_method = :smtp config.action_mailer.smtp_settings = { :enable_starttls_auto => true, :address => 'smtp.gmail.com', :port => '587', :domain => 'smtp.gmail.com', :authentication => 'plain', :user_name => '二段階認証設定、アプリケーションパスワードを取得したアカウントのメールアドレス', :password => 'アプリケーションパスワード(表示された時は4文字で区切られていたがスペース入れず連続して記載)' } # Print deprecation notices to the Rails logger. config.active_support.deprecation = :log # Raise an error on page load if there are pending migrations. config.active_record.migration_error = :page_load # Highlight code that triggered database queries in logs. config.active_record.verbose_query_logs = true # Debug mode disables concatenation and preprocessing of assets. # This option may cause significant delays in view rendering with a large # number of complex assets. config.assets.debug = true # Suppress logger output for asset requests. config.assets.quiet = true # Raises error for missing translations. # config.action_view.raise_on_missing_translations = true # Use an evented file watcher to asynchronously detect changes in source code, # routes, locales, etc. This feature depends on the listen gem. config.file_watcher = ActiveSupport::EventedFileUpdateChecker end
-
-
送信トリガーの設定(ここからは各個人のプロダクトにより若干方法が異なるが、単純に送信のきっかけを作っているだけなので難しく考えないでほしい)
-
送信のトリガーとなる処理(以降、メールトリガーコードと呼ぶ)を任意のコントローラファイルに記載する。
-
任意のコントローラのアクション内に下記メールトリガーコードを記載する。
- ※メーラーの作成時にクラス名、メソット名を独自の物にした人は若干異なるので注意
- ※難しく考えず、下記の処理を記載してコントローラ内で実行してあげればメールが送信されると考えると記載箇所の応用が効きやすいかもしれない
# メーラークラス名.メール送信メソット名.送信メソット名 NoticeMailer.greeting.deliver_now
-
前述の記載を任意のアクション内に記載するが、オススメは任意ページを表示するアクションに記載することである。
-
筆者は
http://localhost:3000/posts/index/:id
にアクセスした時にテストメールが送られるように設定した。 -
筆者のメールトリガーコードの記載例を下記に記載する。
-
post_controller.rb
ファイルのindex
アクション内に下記を記載
class PostsController < ApplicationController def index @posts = Post.where(user_id: @current_user.id) end
↓メールトリガーコードを記載
class PostsController < ApplicationController def index NoticeMailer.greeting.deliver_now @posts = Post.where(user_id: @current_user.id) end
-
-
-
テスト送信
-
下記コマンドを実行してアプリケーションを起動する。
$ cd アプリ名フォルダ $ rails s
-
アプリケーション内でメールトリガーコードを記載したコントローラのアクションが実行されるようにページ遷移する。
-
筆者の場合
http://localhost:3000/posts/index/:id
にアクセスするとメールトリガーコードが実行されるため上記画面を表示した。
-
-
メール確認
Net::SMTPAuthenticationErrorのエラー
- メールトリガーコードを記載したコントローラのアクションを実行したところエラー
Net::SMTPAuthenticationError
が発生した。 - これは二段階認証を使用してGmailのをメールサーバを使用した場合にアプリケーションパスワードが一致していない時に出るエラーらしい。
- 筆者のエラーメッセージ: 534-5.7.9 Application-specific password required. Learn more at
- ファイル
development.rb
の二段階認証を行なったアカウントのメールアドレス、パスワードが間違えていないかもう一度確認しよう。 - 正常に設定されているなら、railsアプリを終了し10分~20分ほど放置してから再トライしてみよう。
- サーバとのやりとりに時間がかかっており、ファイル
development.rb
でパスワード設定直後だと接続がうまくいかない恐れがある。(筆者談、真意は不明)
エラーは出てないがメールが来ない
- ファイル
notice_mailer.rb
で指定している送信先のメールアドレスを入力していないかを確認してみよう。
付録
-
筆者の環境でメール作成の実績がある各ファイルの内容を下記に記載する。
-
パスワードはダミーを入力する。
-
今回説明に出てきていないメーラー作成コマンドで作成されたそのほかのファイルの記載状況もまとめる。
-
notice_mailer.rb
class NoticeMailer < ApplicationMailer
# Subject can be set in your I18n file at config/locales/en.yml
# with the following lookup:
#
# en.notice_mailer.greeting.subject
#
def greeting
@greeting = "Hi"
mail to: "shun.okawa@gmail.com",
cc: "miriwo.rails@gmail.com"
end
end
- development.rb
Rails.application.configure do
# Settings specified here will take precedence over those in config/application.rb.
# In the development environment your application's code is reloaded on
# every request. This slows down response time but is perfect for development
# since you don't have to restart the web server when you make code changes.
config.cache_classes = false
# Do not eager load code on boot.
config.eager_load = false
# Show full error reports.
config.consider_all_requests_local = true
# Enable/disable caching. By default caching is disabled.
# Run rails dev:cache to toggle caching.
if Rails.root.join('tmp', 'caching-dev.txt').exist?
config.action_controller.perform_caching = true
config.action_controller.enable_fragment_cache_logging = true
config.cache_store = :memory_store
config.public_file_server.headers = {
'Cache-Control' => "public, max-age=#{2.days.to_i}"
}
else
config.action_controller.perform_caching = false
config.cache_store = :null_store
end
# Store uploaded files on the local file system (see config/storage.yml for options).
config.active_storage.service = :local
# Don't care if the mailer can't send.
# config.action_mailer.perform_caching = false
config.action_mailer.raise_delivery_errors = true
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
:enable_starttls_auto => true,
:address => 'smtp.gmail.com',
:port => '587',
:domain => 'smtp.gmail.com',
:authentication => 'plain',
:user_name => 'miriwo.rails@gmail.com',
:password => 'smppyvjbfzhweyxx'
}
# Print deprecation notices to the Rails logger.
config.active_support.deprecation = :log
# Raise an error on page load if there are pending migrations.
config.active_record.migration_error = :page_load
# Highlight code that triggered database queries in logs.
config.active_record.verbose_query_logs = true
# Debug mode disables concatenation and preprocessing of assets.
# This option may cause significant delays in view rendering with a large
# number of complex assets.
config.assets.debug = true
# Suppress logger output for asset requests.
config.assets.quiet = true
# Raises error for missing translations.
# config.action_view.raise_on_missing_translations = true
# Use an evented file watcher to asynchronously detect changes in source code,
# routes, locales, etc. This feature depends on the listen gem.
config.file_watcher = ActiveSupport::EventedFileUpdateChecker
end
- posts_controller.rb
class PostsController < ApplicationController
def index
NoticeMailer.greeting.deliver_now
@posts = Post.where(user_id: @current_user.id)
end
def show
@post = Post.find_by(id: params[:id])
end
def new
@post = Post.new
end
def create
@post = Post.new(content: params[:content], study_time: params[:'study_time'], today_study_time: params[:'study_time'], hash_tag: params[:hash_tag], user_id: @current_user.id)
if @post.save
# flash[:notice] = "保存完了"
redirect_to("/posts/#{@post.id}")
else
# flash[:notice] = "保存失敗"
render ("posts/new")
end
end
def edit_form
@post = Post.find_by(id: params[:id])
end
def edit
@post = Post.find_by(id: params[:id])
@post.content = params[:content]
@post.hash_tag = params[:hash_tag]
@post.save
redirect_to("/posts/#{@post.id}")
end
def achievement
@post = Post.find_by(id: params[:id])
end
def destroy
@post = Post.find_by(id: params[:id])
@post.destroy
redirect_to("/posts/index/#{@post.user_id}")
end
def update
@post = Post.find_by(id: params[:id])
# 本当は下記見たいにしたいけどエラー出る。
# 原因は@post.study_timeが数値でparams[:study_time]が文字列になってしまっているから
@post.today_study_time = params[:study_time].to_f
@post.save
@post.study_time += @post.today_study_time
@post.save
redirect_to("/posts/#{@post.id}")
end
def tweet_content
@post = Post.find_by(id: params[:id])
end
end
-
アプリ名/app/mailers
のapplication_mailer.rb
class ApplicationMailer < ActionMailer::Base
default from: 'from@example.com'
layout 'mailer'
end
-
アプリ名/app/views/notice_mailer
のgreeting.html.erb
<h1>Notice#greeting</h1>
<p>
<%= @greeting %>, find me in app/views/notice_mailer/greeting.html.erb
</p>
-
アプリ名/app/views/notice_mailer
のgreeting.text.erb
Notice#greeting
<%= @greeting %>, find me in app/views/notice_mailer/greeting.text.erb
-
アプリ名/test/mailers
のnotice_mailer_test.rb
require 'test_helper'
class NoticeMailerTest < ActionMailer::TestCase
test "greeting" do
mail = NoticeMailer.greeting
assert_equal "Greeting", mail.subject
assert_equal ["to@example.org"], mail.to
assert_equal ["from@example.com"], mail.from
assert_match "Hi", mail.body.encoded
end
end
-
アプリ名/test/mailers/previews
のnotice_mailer_preview.rb
# Preview all emails at http://localhost:3000/rails/mailers/notice_mailer
class NoticeMailerPreview < ActionMailer::Preview
# Preview this email at http://localhost:3000/rails/mailers/notice_mailer/greeting
def greeting
NoticeMailer.greeting
end
end
- Gemfile
source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
ruby '2.5.0'
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '~> 6.0.0'
# Use sqlite3 as the database for Active Record
# gem 'sqlite3', '~> 1.4'
# Use Puma as the app server
gem 'puma', '~> 3.12'
# Use SCSS for stylesheets
gem 'sass-rails', '~> 5'
# Transpile app-like JavaScript. Read more: https://github.com/rails/webpacker
gem 'webpacker', '~> 4.0'
# Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks
gem 'turbolinks', '~> 5'
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
gem 'jbuilder', '~> 2.7'
# Use Redis adapter to run Action Cable in production
# gem 'redis', '~> 4.0'
# Use Active Model has_secure_password
gem 'bcrypt', '~> 3.1.7'
# Use Active Storage variant
# gem 'image_processing', '~> 1.2'
# Reduces boot times through caching; required in config/boot.rb
gem 'bootsnap', '>= 1.4.2', require: false
group :development, :test do
# Call 'byebug' anywhere in the code to stop execution and get a debugger console
gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
end
group :development do
# Access an interactive console on exception pages or by calling 'console' anywhere in the code.
gem 'web-console', '>= 3.3.0'
gem 'listen', '>= 3.0.5', '< 3.2'
# Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
gem 'spring'
gem 'spring-watcher-listen', '~> 2.0.0'
end
group :test do
# Adds support for Capybara system testing and selenium driver
gem 'capybara', '>= 2.15'
gem 'selenium-webdriver'
# Easy installation and use of web drivers to run system tests with browsers
gem 'webdrivers'
end
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
gem 'bootstrap', '~> 4.1.1'
gem 'jquery-rails'
gem 'mysql2'
#gem 'bcrypt'