Action Mailerをつかいユーザー登録を行なった際にメール送信するようにする
Action Mailerとは
Action Mailerを使うと、アプリケーションのメイラークラスやビューでメールを送信することができます。メイラーの動作はコントローラときわめて似通っています。
引用: Action Mailer の基礎 - Railsガイド
メールを送信する際によく使われていて、パスワードリセット、お問い合わせ、ユーザー登録などなど様々な用途で使用されていて、誰もが利用したことがあると思います。 また、Rakeタスクを組み合わせると設定したタイミングでメールを自動送信することも可能です。
今回は、ユーザーの新規登録をした際に通知メールが届く機能を実装します。
実装手順
簡易的なアプリを使って説明していきます。
まずは簡易的なアプリをscaffold
で作成します。
まずはrails new
したあとにscaffold
でuserテーブルに今回の実装に必要なカラムを追加します。
rails generate scaffold user name email login
以下のコマンドを実行し適用させます。
rails db:migrate
これでアプリの設定は終わりです!
メイラー生成
メールを送信するにはメイラー(コントローラーと似ている)とビューを作成する必要があります。
これからメイラーとビューを作成していきます。
ターミナルで以下のコマンドを実行します。
rails g mailer UserMailer
下記のログがでているはずです!
Running via Spring preloader in process 28545 create app/mailers/user_mailer.rb invoke erb create app/views/user_mailer invoke test_unit create test/mailers/user_mailer_test.rb create test/mailers/previews/user_mailer_preview.rb
ちなみにscaffold
でアプリを作成した場合にはapp/mailers/application_mailer.rb
が自動で生成されています。
scaffold
は便利ですね。
これでメイラー生成は終了です!
ビューを作成する
先程作成したapp/views/user_mailer
ディレクトリの中にビューを作成してきます。
app/views/user_mailer
ディレクトリ下のファイルがレイアウトファイルのapp/views/layouts/application.html.erb
に読み込まれます。
また、 scaffold
でアプリを作成した際にはレイアウトファイルも自動生成されています!
以下のファイルを作成してください
# app/views/user_mailer/welcome_email.html.erb <!DOCTYPE html> <html> <head> <meta content='text/html; charset=UTF-8' http-equiv='Content-Type' /> </head> <body> <h1><%= @user.name %>様、example.comへようこそ。</h1> <p> example.comへのサインアップが成功しました。 your username is: <%= @user.login %>.<br> </p> <p> このサイトにログインするには、<%= @url %>をクリックしてください。 </p> <p>ご入会ありがとうございます。どうぞお楽しみくださいませ。</p> </body> </html>
# app/views/user_mailer/welcome_email.text.erb <%= @user.name %>様、example.comへようこそ。 =============================================== example.comへのサインアップが成功しました。ユーザー名は「<%= @user.login %>」です。 このサイトにログインするには、<%= @url %>をクリックしてください。 本サイトにユーザー登録いただきありがとうございます。
html形式とtext形式のファイルを作成する理由は、顧客によってフォーマットを使い分けるためです。 これでビューの作成は終了です!
メイラー編集
app/mailers/user_mailer.rb
に空のメイラーがあるので、メールを送信するためのメソッドを定義していきます。
# output_app/app/mailers/user_mailer.rb class UserMailer < ApplicationMailer default from: 'notifications@example.com' def welcome_email @user = params[:user] @url = 'http://example.com/login' mail(to: @user.email, subject: '私の素敵なサイトへようこそ') end end
上記のコードを解説していきます。
default from:
はメールの送信元名です。
メールにはユーザーのname、login、emailを表示したいので、メソッドの中にインスタンス変数を作成しておきます。
ここで定義したwelcome_emailメソッドをビューやRakeタスクで実行することでメール送信のタイミングを使い分けています。
mail
メソッドで使われているsubjectはメールの件名になります。
controllerを編集する
ユーザーが新規登録された際に登録したメールアドレスに登録完了メールが送信されるようにコードを追記していきます。
scaffold
で生成されたoutput_app/app/controllers/users_controller.rb
のcreateメソッドを編集します。
class UsersController < ApplicationController def create @user = User.new(user_params) respond_to do |format| if @user.save # 保存後にUserMailerを使ってwelcomeメールを送信 UserMailer.with(user: @user).welcome_email.deliver_later format.html { redirect_to(@user, notice: 'ユーザーが正常に作成されました。') } format.json { render json: @user, status: :created, location: @user } else format.html { render action: 'new' } format.json { render json: @user.errors, status: :unprocessable_entity } end end end
実装確認
それでは実際にユーザーを作成しメールが送られてくるか確認します。
letter_opener_web
を使いメールを受信します。
上記についても記事を書いたのでどうぞ
letter_opener_web実装方法 - Mekun’s blog
ユーザー新規登録
letter_opener_web
上記の通りユーザー新規登録時に入力した情報をもとにメールが届いていました。実装完了です!
letter_opener_web実装方法
letter_opener_web
letter_opener_webを導入すると、送信されたメールをwebサイトで確認することができます。
ログイン時のパスワードリセットなどなど、RailsのMailerと組み合わせて使われるケースが多いです。
実装方法
Gemfile
Gemfileに設定を行っていきます
# Gemfile group :development do gem 'letter_opener_web' end
ターミナルで下記コマンドを実行しgemを読み込みます。
bundle install
Gemfile.lockを見てみるとletter_opener_webが追加されています。
# Gemfile.lock launchy (2.5.0) addressable (~> 2.7) letter_opener (1.7.0) launchy (~> 2.2) letter_opener_web (1.4.0) actionmailer (>= 3.2) letter_opener (~> 1.0) railties (>= 3.2) DEPENDENCIES letter_opener_web
config/routes.rb
config/routes.rbにも設定を記述していきます。
# config/routes.rb Rails.application.routes.draw do root to: 'welcome#index' # (略) # letter opener の為に、一番最後に記述する mount LetterOpenerWeb::Engine, at: '/letter_opener' if Rails.env.development? end
config/environments/development.rb
config/environments/development.rbにも設定を記述していきます。
# config/environments/development.rb # letter opener の為に以下を追加 config.action_mailer.default_url_options = { host: 'localhost:3000' } config.action_mailer.perform_caching = true config.action_mailer.delivery_method = :letter_opener_web config.action_mailer.perform_deliveries = true
使い方
ターミナルで下記コマンドを実行しサーバーを起動します。
rails s
ブラウザのURL欄にlocalhost:3000/letter_opener/
と入力します。
すると下記のようにletter_opener_webが立ち上がるので実装完了です!
参考にしたサイト
https://github.com/fgrehm/letter_opener_web
Ruby on Rails の 開発環境で 簡単にメール送信機能の確認ができる letter_opener_web の使い方
has_many_attachedを使いUnpermitted parameter が発生した
Active Storageを使用して、複数のファイルを1つのレコードに添付する実装を行っていてエラーに遭遇したのでメモしておきます。
最初に結論
複数のファイルを1つのレコードに添付する場合は、permitメソッドを下記のように使う必要がありました。
params.permit(キー: [])
詳しく解説していきます。
エラーが発生した状況
# エラー内容 Unpermitted parameter: :main_images
# app/models/site.rb has_many_attached :main_images
# edit.html.slim = f.input :main_images, as: :file, input_html: {multiple: true}, hint: 'JPEG/PNG (1200x400)' - if @site.main_images.attached? .main_images_box - @site.main_images.each do |main_image| .main_image = image_tag main_image.variant(resize:'300x100').processed = link_to '削除', admin_site_attachment_path(main_image.id), method: :delete, class: 'btn btn-danger'
# app/controllers/sites_controller.rb def site_params params.require(:site).permit(:name, :subtitle, :description, :favicon, :og_image, :main_images) end
経緯
複数の画像を一度に登録できるレコードを作成するのが目的です。
そこでActive Storage
のhas_many_attached
を使い、レコードとファイルの間に1対多の関係を設定しました。
viewも記述し、Webブラウザで複数の画像を登録してみたら上記に載せたUnpermitted parameter: :main_images
というエラーが発生しました。
いつもなら_params
メソッドのpermitの中身が抜けているorタイポ、この二択だったのですが、今回はどちらも当てはまりませんでした。
どのようにエラーを解決したか
困ったときのRailsガイド!を調べてみると載っていました。
引用: Active Storage の概要 - Railsガイド
def message_params params.require(:message).permit(:title, :content, images: []) end
images: []
この記述だけ見ても基礎がなっていない自分には理解できなかったので、permitメソッドに載っているのかな?と思いpermitメソッドについて調べてみました。
わかりやすく解説している記事が見つかったので引用させていただきました。
【Rails】 | Pikawaka - ピカ1わかりやすいプログラミング用語サイト
バリューに複数の値が入るときは配列となってパラメーターに入ります。
例えばform_forやform_withでフォームを作成する際、collection_check_boxesで複数の値を保存するときなどに使います。
今回実装した、複数枚の画像を表示する箇所のHTMLを検証ツールを使い調べてみました。
<input multiple="multiple" class="file optional" type="file" name="site[main_images][]" id="site_main_images">
上記のようにnameのバリューが配列となっていることがわかりました。
今回の学び
- バリューに複数の値が入るときは配列となってパラメーターに入るので、
params.permit(キー: [])
のような形で記述する。 - フォームヘルバーを使った場合はHTMLを見てみると理解が深まる。
- 実装したい機能がある場合は公式リファレンスを読み込む。
ポリモーフィック関連付けとは
動機
Railsで記事に埋め込み機能を実装する際に、ポリモーフィック関連付けが出てきたので、理解を深めるためにも備忘録としてメモを書いていきます。
以下のモデル構成で説明していきます。
# article_block.rb class ArticleBlock < ApplicationRecord belongs_to :article belongs_to :blockable, polymorphic: true, dependent: :destroy # ... end
# sentence.rb class Sentence < ApplicationRecord has_one :article_block, as: :blockable, dependent: :destroy # ... end
# medium.rb class Medium < ApplicationRecord has_one :article_block, as: :blockable, dependent: :destroy # ... end
# embed.rb class Embed < ApplicationRecord has_one :article_block, as: :blockable, dependent: :destroy # ... end
# article.rb class Article < ApplicationRecord has_many :article_blocks, -> { order(:level) }, inverse_of: :article has_many :sentences, through: :article_blocks, source: :blockable, source_type: 'Sentence' has_many :media, through: :article_blocks, source: :blockable, source_type: 'Medium' has_many :embeds, through: :article_blocks, source: :blockable, source_type: 'Embed' # ... end
ポリモーフィックとはなにか?
ポリモーフィック関連付けを使うと、ある1つのモデルが他の複数のモデルに属していることを、1つの関連付けだけで表現できます。
:引用 Active Record の関連付け - Railsガイド
上記のコードを例にすると、ArticleBlock
モデルは、複数のモデルSentence
, Medium
, Embed
に属していることを1つの関連付けのblockable
だけで表現できています。
ポリモーフィックってなんなの?という人におすすめの記事です。
Railsのポリモーフィック関連とはなんなのか - Qiita
この記事を読んで、「ダックタイピング」、「インターフェース」という言葉がでてきてあまり理解できなかったので、調べようと思いました。
ポリモーフィック関連付けの方法
説明
今回のモデル構成で説明してみます。
- belongs_to宣言に:polymorphicオプションを指定すると、ポリモーフィック関連になる
下記のArticleBlock
クラスでポリモーフィック関連を利用するために、belongs_to :blockable
にpolymorphic
オプションが指定されています。
# article_block.rb class ArticleBlock < ApplicationRecord belongs_to :article belongs_to :blockable, polymorphic: true, dependent: :destroy # ... end
asオプションとは?
:asオプションを設定すると、ポリモーフィック関連付けを指定できます。
:引用 Active Record の関連付け - Railsガイド
ポリモーフィック関連付けの利点
1つのモデルが他の複数のモデルに属していることを、一つの関連付けだけで表現できることです。 両者のコードを比べるとポリモーフィック関連付けありの方がコードが見やすくなっているのがわかると思います。
# article_block.rb # ポリモーフィック関連付けなし class ArticleBlock < ApplicationRecord belongs_to :article belongs_to :sentence belongs_to :medium belongs_to :embed # ... end
# article_block.rb # ポリモーフィック関連付けあり class ArticleBlock < ApplicationRecord belongs_to :article belongs_to :blockable, polymorphic: true, dependent: :destroy # ... end
参考にしたサイト
Active Record の関連付け - Railsガイド
Railsのポリモーフィック関連とはなんなのか - Qiita
ActiveModelとは
ActiveModelとは
ActiveModelとはActiveRecodeモデルクラスの便利な機能を普通のクラスで実現することをサポートしてくれるモジュール郡です。
例えば一括代入、バリデーション、コールバックが例です。
「データベースに直接紐付かないけれども、フォームの入力を検証して何らかの操作を行う。」ようなモデルを作るために便利に使えます。
上記のような概念をFormObjectといいます。
ActiveModelは多くのサブモジュールから構成されています。
その中でもよく使われるActiveModel:Model
を例に説明していきます。
ActiveModel:Modelとは
ActiveModel::Model
をincludeすると、以下のような機能を使えるようになります。
- モデル名の調査
- 変換
- 翻訳
- バリデーション
これら上記の機能が使える理由は
ActiveModel:Model
をincludeすれば、以下のモジュールが一度に利用できるようになるからです。
- ActiveModel::AttributeAssignment
- ActiveModel::Naming
- ActiveModel::Conversion
- ActiveModel::Translation
- ActiveModel::Validations
モジュールの詳細はこちらに
まとめ
ActiveModelとはActiveRecordを継承しないクラスでもActiveRecordと同じような便利メソッドが使えるようになる
ActiveModelは多くのサブモジュールから構成されている。
ActiveModelはFormObjectという概念で使われることが多い
ActiveModelとは
ActiveModelとは
ActiveModelとはActiveRecodeモデルクラスの便利な機能を普通のクラスで実現することをサポートしてくれるモジュール郡です。
例えば一括代入、バリデーション、コールバックが例です。
「データベースに直接紐付かないけれども、フォームの入力を検証して何らかの操作を行う。」ようなモデルを作るために便利に使えます。
上記のような概念をFormObjectといいます。
ActiveModelは多くのサブモジュールから構成されています。
その中でもよく使われるActiveModel:Model
を例に説明していきます。
ActiveModel:Modelとは
ActiveModel::Model
をincludeすると、以下のような機能を使えるようになります。
- モデル名の調査
- 変換
- 翻訳
- バリデーション
これら上記の機能が使える理由は
ActiveModel:Model
をincludeすれば、以下のモジュールが一度に利用できるようになるからです。
- ActiveModel::AttributeAssignment
- ActiveModel::Naming
- ActiveModel::Conversion
- ActiveModel::Translation
- ActiveModel::Validations
モジュールの詳細はこちらに
まとめ
ActiveModelとはActiveRecordを継承しないクラスでもActiveRecordと同じような便利メソッドが使えるようになる
ActiveModelは多くのサブモジュールから構成されている。
ActiveModelはFormObjectという概念で使われることが多い
find_eachメソッドとは
find_each
メソッドは、分割してレコードを取得して1件ずつ処理します。
メモリの消費量を抑えることができるため、
大量のレコードをループ処理する際に使われます。
デフォルトでは、レコードを1000件ごとに取得して処理を実行します。
構文
find_eachメソッドは、モデルのクラスに対して呼び出すことができるメソッドです。
モデル.find_each([オプション]) do |f| 処理内容 end