メール機能の実装 ( ruby on rails )

# まえがき
**[DMM WEBCAMP Advent Calendar 2023](https://qiita.com/advent-calendar/2023/infratop)** 13日目記事です。

DWCメンター・卒業生が記事を投稿しておりますので、是非他の記事もご確認ください！

# 初めに
ここでは，rubyのフレームワークである，ruby on railsを使用して，メールを送信する機能を実装します．

# 1. メイラーの作成
```shell
$ rails g mailer GroupMailer
## 以下実行ログ
create app/mailers/group_mailer.rb           # mailerのコントローラー
create app/mailers/application_mailer.rb     # アプリケーションコントローラーと同じようなもの
invoke erb
create    app/views/group_mailer             # mailerのテンプレートのディレクトリ
create    app/views/layouts/mailer.text.erb  # textベースでのデフォルトレイアウト
create    app/views/layouts/mailer.html.erb  # htmlベースでのデフォルトレイアウト / application.html.erbと同じようなもの
invoke test_unit
create    test/mailers/group_mailer_test.rb
create    test/mailers/previews/group_mailer_preview.rb
```
このコマンドを実行することによって，コントローラーやレイアウトファイルが作成されます．

# 2. group_mailer.rb
このファイルは，メイラーのコントローラーのような役割のファイルで，ここに記述を追加することによって，メールの動作を決めることが出来ます．
また，同時に作成された`application_mailer.rb`は，application_controllerと同じような役割を持ち，全体の動作を定義できます．
```ruby:app/mailers/application_mailer.rb
class ApplicationMailer < ActionMailer::Base
    default from: "a@a"    # デフォルトで送る側のメールアドレスを設定
    layout 'mailer'        # レイアウトファイルの指定(この場合、mailer.html.erb or mailer.text.erbが呼び出される)
end
```
```ruby:app/mailers/group_mailer.rb
class GroupMailer < ApplicationMailer
end
```

## 2.2 メイラーの編集
メイラーはrailsのコントローラーと非常に似ており，メイラーにはコントローラーと同様に「アクション」があります．
このアクション内に，メールの処理の記述を書いていきます．
また，コントローラーとhtmlファイルの関係と同様に，アクションの処理実行後にアクション名と同名のレイアウトファイルが呼び出されます．
```ruby:app/mailers/group_mailer.rb
class GroupMailer < ApplicationMailer
    default from: 'notifications@example.com'    # このメイラーの送り元のデフォルトメールアドレスを設定

    def send_mail
        mail(                                    # メールの作成
          from: params[:owner_email],            # 送り元のメールアドレス
          to: params[:user_email],               # 宛先のメールアドレス 
          subject: params[:title]                # メールのタイトル 
        )
    end
end
```
mailメソッドのオプションは以下で説明します．

| オプション | 説明 |
|:-:|:-:|
| from  | 送信元メールアドレス  |
| subject  | メールの件名  |
| to  | メールの送信先アドレス  |
| cc  | CC（カーボンコピー）のメールアドレス  |
| bcc  | BCC（ブラインドカーボンコピー）のメールアドレス  |

## 2.3 メイラーのビューの作成
```erb:app/views/layouts/mailer.html.erb
<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <style>
      /* Email styles need to be inline */
    </style>
  </head>
  <body>
    <%= yield %>
  </body>
</html>
```
application.html.erbと同様に，それぞれのアクションに対応したerbファイルがyieldの部分に出力されます．
```erb:app/views/group_mailer/send_mail.html.erb
<br><br>
<%= params[:owner_name] %>様からイベントのお知らせです。
・内容
<%= params[:content] %>
```

## 2.4 メイラーの呼び出し
メイラーの準備は出来たため，実際にメイラーを呼び出し，メールを送信します．
メイラーの呼び出しは，それぞれのコントローラーで行うことが出来ます．
```ruby
class GroupMailsController < ApplicationController
    before_action :correct_owner 
    def new 
        @new_group_mail = GroupMail.new
        @group = Group.find(params[:group_id])
    end
    def create 
        @new_group_mail = GroupMail.new(str_params)     
        @new_group_mail.group_id = params[:group_id]
        @new_group_mail.save
        @group = Group.find(params[:group_id])
        @owner = @group.owner
        @group.users.each do |user|
######  メイラーの呼び出し  ###########################################################
        GroupMailer.with(
        title: @new_group_mail.title, content: @new_group_mail.content, 
        owner_email: @owner.email, owner_name: @owner.name,               
    　  user_email: user.email
       ).send_mail.deliver_later
######################################################################################
        end
        render :complete_mailed
    end

    private    
    def str_params 
        params.require(:group_mail).permit(:title, :content)
    end 
end
```
+ 今回は，GroupMailerというコントローラーでメイラーの実装をしているので，`GroupMailer.`という形で，メイラーのコントローラーを呼び出しています．
+ `.withメソッド`を使うことによって，parametersに値を送信することが出来ます．
この中には，ハッシュ型で値を定義することによって，GroupMailer側で`params[:~~~]`という風に記述することで，値を呼び出すことが出来ます．
例えば，`with(title: "メールのタイトル")`という風に定義すると，メイラーのコントローラー側で，`params[:title]`という風に使用できます．
+ `.send_mail`の部分で，GroupMailerコントローラーのsend_mailメソッドを呼び出しています．
+ `.deliver_laterメソッド`で，メールを後で送信することを定義

# 3. メールのプレビュー
ActionMailerにはプレビュー機能があり，レンダリング用のURLを開くことによって，送ったメールの内容を確認することが出来ます．
メールのプレビューを表示するには以下の設定が必要です．
```ruby:test/mailers/previews/group_mailer_preview.rb
class GroupMailerPreview < ActionMailer::Preview
  def send_mail
    GroupMailer.with(title: 'test', content: 'test',
                    owner_email: 'owner@owner', owner_name: 'test',
                    user_email: 'user@user').send_mail
  end
end
```
GroupMailerPreviewというメイラーのコントローラー名にPreviewという文字を追加クラスの中に，元のコントローラーと同じアクション名を定義し，その中でメールのメソッドを呼び出します．

ここで，`/rails/meilers/メイラーのコントローラー名`のURLに遷移することによって，プレビューを確認することが出来ます．
また，メイラー自身や，メイラーのレイアウトに変更を加えることによって，自動的に再読み込みし，レンダリングされるため，スタイル変更をすぐに確認することが出来ます．
加えて，`/rails/mailers`のURLにアクセスすることによって，利用可能なプレビューのリストを表示することが出来ます．
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1967037/eb24deb3-7553-3cf1-382b-96c2ba4ce100.png)

# 4.Gmailを用いてメールを送信
```ruby:config/environments/development.rb
config.action_mailer.raise_delivery_errors = true
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
    address:                'smtp.gmail.com',
    port:                   587,
    domain:                 'example.com', 
    user_name:              'ユーザー名', 
    password:               'パスワード', 
    authentication:         'login',
    enable_starttls_auto:   true
}
```
+ `raise_delivery_errors = true`は，メールの送信に失敗したときにエラーを出すかどうかを決める．
エラーを出したい場合は，`true`を設定する．
+ `delivery_method = :smtp`は，メールの送信方法を決める．
+ `smtp_settings`は，smtpは詳細設定をする．

| 設定項目 | 意味 |
|:-:|:-:|
| address  | SMTPサーバーのホスト名  |
| port  | SMTPサーバーのポート番号  |
| domain  | HELOドメイン  |
| user_name  | メール送信に使用するGmailアカウント  |
| password  | メール送信に使用するGmailパスワード<br>※googleのパスワードではないため，詳細は以下に示す．|
| authentication  | 認証方法  |
| enable_starttls_auto  | メールの送信にTLS認証を使用するか  |

:::note warn
補足
1. user_nameはご自身のgoogleアカウントのメールアドレスを入力してください．
2. passwordはgoogleアカウントのパスワードではありません．<br>passwordについての説明は下の「passwordの設定について」でします．
:::

:::note alert
注意
gitでファイルを管理している場合，絶対にpasswordの値をプッシュしないようにしましょう．
ignoreで管理する，もしくはpasswordの部分のみ削除してからプッシュしてください．
:::

## passwordの設定について
gmailのパスワードの設定については，googleのアカウントに2段階認証の設定がされているのか，されていないのかの2パターンで分かれる．

### 2段階認証の設定方法
2段階認証設定方法は
[こちら](https://support.google.com/accounts/answer/185839?hl=ja&co=GENIE.Platform%3DAndroid)
のサイトを参考に設定してください．

### 2段階認証が設定されていない場合
https://www.google.com/settings/security/lesssecureapps

2段階認証が設定されていない場合，こちらのリンク先から，試行を許可することが出来る．

### 2段階認証が設定されている場合
https://myaccount.google.com/apppasswords

2段階認証が設定されている場合，こちらのリンク先からアプリケーションパスワードを発行し，development.rbのpasswordの部分に入力する．