[RUBY] [Rails 6] Implementation of inquiry function using Action Mailer

Introduction

Creating a portfolio for job change activities. Since we have implemented the inquiry function from the user this time, we will describe it for memorandum and review.

environment

Ruby on Rails'6.0.0' Ruby'2.6.5'

① Routing description

config/routes.rb


resource :contacts, only: [:new, :create] do
 get "/thanks" => "contacts#thanks"
end

This time, after the user sends an inquiry, a page like "Thank you for your inquiry!" Is displayed.

② Model description

After creating a model with the rails g model contact command, describe the migration file and so on.

db/migrate/20201204073627_create_contacts.rb


class CreateContacts < ActiveRecord::Migration[6.0]
  def change
    create_table :contacts do |t|
      t.string :name, null: false
      t.string :email, null: false
      t.text :content, null: false
      t.timestamps
    end
  end
end

This time, I specified the name, the email address for reply, and the content of the inquiry. Then run the rails db: migrate command.

app/models/contact.rb


class Contact < ApplicationRecord
  validates :name, :email, :content, presence: true
end

Set validation so that your name, email address, and inquiry content cannot be saved if they are empty.

③ Action Mailer settings

This time, I would like to make the function so that when an inquiry is sent from a user, the content will be sent to the administrator by e-mail. So I used ActionMailer. First, run the rails g mailer Contact Mailer command. Then, the following file will be generated, so if you receive an inquiry, we will send an email to the administrator.

app/mailers/contact_mailer.rb


class ContactMailer < ApplicationMailer
  def contact_mail(contact)
    @contact = contact
    mail to: '(Administrator's email address)@gmail.com', subject: '(Email title)'
  end
end

Next, since a new view file is generated, we will describe the body of the email.

ruby:app/views/contact_mailer/contact_mail.html.erb


<p>User name:<%= @contact.name %></p>
<p>mail address:<%= @contact.email %></p>
<p>Content of inquiry:<%= @contact.content %></p>

④ Controller description

Now let's describe the controller.

app/controllers/contacts_controller.rb


class ContactsController < ApplicationController
  def new
    @contact = Contact.new
  end

  def create
    @contact = Contact.new(contact_params)
    if @contact.save
      ContactMailer.contact_mail(@contact).deliver
      redirect_to thanks_contacts_path
    else
      render :new
    end
  end

  def thanks
  end

  private

  def contact_params
    params.require(:contact).permit(:name, :email, :content)
  end
end

As a result, when the create action is called and the inquiry is saved successfully, the mail sending process can also be set to start.

⑤ description of view

I will describe the view file of the inquiry.

ruby:app/views/contacts/new.html.erb


<div class="container">
  <div class="row">
    <div class="offset-sm-2 col-sm-8 offset-sm-2">
    <%= form_with model: @contact, local: true do |f| %>
      <h5 class='form-header-text text-center'><i class="far fa-paper-plane fa-2x my-orange"></i>Contact Us</h5>

  <%= render 'layouts/error_messages', model: f.object %>
      <div class="form-group">
        <div class='form-text-wrap'>
          <label class="form-text" for="name">name</label>
          <span class="badge badge-danger">Mandatory</span>
        </div>
        <div class='input-name-wrap'>
          <%= f.text_field :name, class:"input-name", id:"name", placeholder:"Example)Taro Tanaka" %>
        </div>
      </div>

      <div class="form-group">
        <div class='form-text-wrap'>
          <label class="form-text" for="email">mail address</label>
          <span class="badge badge-danger">Mandatory</span>
        </div>
        <%= f.email_field :email, class:"input-default", id:"email", placeholder:"Either PC or mobile is acceptable", autofocus: true %>
      </div>

      <div class="form-group">
        <div class='form-text-wrap'>
          <label class="form-text" for="content">Contents</label>
          <span class="badge badge-danger">Mandatory</span>
        </div>
        <%= f.text_area :content, class:"article-input-default", id:"content", autofocus: true %>
      </div>
 
      <div class='contact-btn text-center'>
        <%= f.submit "Send" ,class:"btn btn-outline-danger w-50" %>
      </div>
    <% end %>
    </div>
  </div>
</div>

ruby:app/views/contacts/thanks.html.erb


<div class="container">
  <div class="row">
    <div class="offset-sm-2 col-sm-8 offset-sm-2">
      <h5 class='header-text text-center'><i class="far fa-smile fa-lg my-orange"></i>Thank you for your inquiry</h5>
      <%= link_to 'Go back to the top page', root_path %>
    </div>
  </div>
</div>

⑥ Gmail settings

Finally, I set up Gmail to be used with the address this time.

config/environments/development.rb


(Omission)
  config.action_mailer.perform_deliveries = true
  config.action_mailer.raise_delivery_errors = true
  config.action_mailer.delivery_method = :smtp
  config.action_mailer.smtp_settings = {
    address: 'smtp.gmail.com',
    domain: 'gmail.com',
    port: 587,
    user_name: '(Administrator email address)@gmail.com',
    password: ENV["GMAIL_KEY"],
    authentication: 'plain',
    enable_starttls_auto: true
  }
(Omission)

I think that other settings have already been described, so I described them in between. If the Gmail password goes up on GitHub, it will be a big deal, so set the environment variable. I think there are many ways to set it, but I used the vim command to set it in a ".zshrc" file.

Terminal


% vim ~/.zshrc

Open the file with the above command, put it in insert mode, and then write it.

.zshrc file


(Omitted)
export GMAIL_KEY = "Gmail password" 
(Omitted)

The setting is completed here!

⑦ Implementation of test code

As a bonus, I also implemented the test code using Rspec.

FactoryBot

spec/factories/contacts.rb


FactoryBot.define do
  factory :contact do
    name { Faker::Name.name }
    email { Faker::Internet.email }
    content { Faker::Lorem.sentence }
  end
end
Unit test code

spec/models/contact_spec.rb


require 'rails_helper'

RSpec.describe Contact, type: :model do
  before do
    @contact = FactoryBot.build(:contact)
  end

  describe 'Send an inquiry' do
    context 'If you can send an inquiry' do
      it 'You can post if all the elements are present' do
        expect(@contact).to be_valid
      end
    end
    context 'If you cannot send your inquiry' do
      it 'Cannot be sent if name is empty' do
        @contact.name = nil
        @contact.valid?
        expect(@contact.errors.full_messages).to include('Please enter your name')
      end
      it 'Email cannot be sent empty' do
        @contact.email = nil
        @contact.valid?
        expect(@contact.errors.full_messages).to include('Please enter your e-mail address')
      end
      it 'Cannot send if content is empty' do
        @contact.content = nil
        @contact.valid?
        expect(@contact.errors.full_messages).to include('Please enter your inquiry')
      end
    end
  end
end
Integration test code

spec/system/contacts_spec.rb


require 'rails_helper'

RSpec.describe 'Send inquiry', type: :system do
  before do
    @contact = FactoryBot.build(:contact)
  end

  context 'When you can send an inquiry' do
    it 'If you enter the correct information, you can send an inquiry' do
      visit root_path
      expect(page).to have_content('Contact Us')
      visit new_contacts_path
      fill_in 'name', with: @contact.name
      fill_in 'mail address', with: @contact.email
      fill_in 'Contents', with: @contact.content
      expect do
        find('input[name="commit"]').click
      end.to change { Contact.count }.by(1)
      expect(current_path).to eq thanks_contacts_path
      click_link 'Go back to the top page'
      expect(current_path).to eq root_path
    end
  end

  context 'When you cannot send an inquiry' do
    it 'Inquiries cannot be sent without entering the correct information' do
      visit root_path
      expect(page).to have_content('Contact Us')
      visit new_contacts_path
      fill_in 'name', with: ''
      fill_in 'mail address', with: ''
      fill_in 'Contents', with: ''
      expect do
        find('input[name="commit"]').click
      end.to change { Contact.count }.by(0)
      expect(current_path).to eq contacts_path
    end
  end
end

At the end

https://qiita.com/mmdrdr/items/9c5dd4ca886f034fb0ef https://qiita.com/hirotakasasaki/items/ec2ca5c611ed69b5e85e

I referred to the above article. Thank you very much.

I'd like to test sending emails, so I'll check it out! !! Please point out any mistakes.

Recommended Posts

[Rails 6] Implementation of inquiry function using Action Mailer
[Rails] Implementation of search function using gem's ransack
[Rails] Implementation of image enlargement function using lightbox2
[Rails 6] Implementation of search function
[Rails] Implementation of category function
[Rails] Implementation of tutorial function
[Rails] Implementation of like function
[Rails] Implementation of multi-layer category function using ancestry "Preparation"
[Rails] Implementation of multi-layer category function using ancestry "seed"
[Rails] Implementation of CSV import function
[Rails] Asynchronous implementation of like function
[Rails] Implementation of image preview function
[Rails] About implementation of like function
[Rails] Implementation of user withdrawal function
[Rails] Implementation of CSV export function
[Rails] Implementation of multi-layer category function using ancestry "Editing form"
[Rails] Implementation of multi-layer category function using ancestry "Creation form"
[Rails] Implementation of tagging function using intermediate table (without Gem)
Implementation of user authentication function using devise (2)
Implementation of user authentication function using devise (1)
Rails [For beginners] Implementation of comment function
[Rails 6] Implementation of SNS (Twitter) sharing function
Implementation of user authentication function using devise (3)
[Vue.js] Implementation of menu function Implementation version rails6
[Ruby on rails] Implementation of like function
[Vue.js] Implementation of menu function Vue.js introduction rails6
[Rails] Implementation of new registration function in wizard format using devise
[Rails] Implementation of coupon function (with automatic deletion function using batch processing)
[Rails] Implementation of tag function using acts-as-taggable-on and tag input completion function using tag-it
Implementation of Ruby on Rails login function (Session)
Fastest rails mail devise Welcome mail sending function implementation action mailer unnecessary Easyest
Rails Action Text implementation
Implementation of search function
[Rails] I will explain the implementation procedure of the follow function using form_with.
[Rails] Implementation of retweet function in SNS application
[Rails] Implementation of batch processing using whenever (gem)
Rails search function implementation
Implementation of pagination function
[Rails] Implementation of PV number ranking using impressionist
[Rails] Implementation of image slide show using Bootstrap 3
[Note] Summary of rails login function using devise ①
[Rails] Set validation for the search function using Rakuten API (from the implementation of Rakuten API)
[Rails] Implementation of drag and drop function (with effect)
[Rails] Test of star evaluation function using Raty [Rspec]
Implementation of Ruby on Rails login function (devise edition)
[Ruby on Rails] Implementation of tagging function/tag filtering function
[Rails] Implementation of SNS authentication (Twitter, Facebook, Google) function
Rails implementation of ajax removal
Rails fuzzy search function implementation
Search function using [rails] ransack
Implementation of sequential search function
Implementation of like function (Ajax)
Implementation of image preview function
Implementation of category pull-down function
Login function implementation with rails
Ajax bookmark function using Rails
[Rails 6] Pagination function implementation (kaminari)
[Rails] Implementation of automatic address input using jpostal and jp_prefecture
Rails sorting function implementation (displayed in order of number of like)
Chat function that is updated immediately (implementation of Action Cable)
[Rails] Implementation of user logic deletion