こんにちは
railsでインターンをしているものです。
今回はhpに関する依頼があったので実装していきます。
依頼
お問い合わせフォーム
- ユーザーがボタンを
連打
すると何重にもメールが届くのでそれを阻止してほしい -
非同期処理
を行なって、メール送信の動作を裏で行いユーザーには完了のページをすぐに移行できるようにする
一つずつ見ていきましょう
ボタン連打
を防ぐ
人ってうまくいかないと何回も試行錯誤しますよね?
お問い合わせフォームも然りです。
メールの送信に時間がかかるとその場面でページが止まってしまいます。
これは、プログラムが順番に処理を行うのでメール送信が完了するまで次のページに移行としないんです。(うまい)
後でもう少し詳しくやりますが、このせいでユーザーは送信できているか不安になり、何度
もボタンを連打
していきます。
PAKUTASO からの引用
そこで必要になるのが二重投稿防止の処理
しかし何とJSでちょこちょこっと描いてあげるだけでできるのです。
参考
以下を追加し
$(function () {
$("form").submit(function () {
var self = this;
$(":submit", self).prop("disabled", true);
setTimeout(function () { //一定時間でボタンをenabledにするようにした方が親切らしい。
$(":submit", self).prop("disabled", false);
}, 10000);
});
});
こちらを記述
これでボタンが一定時間立たないと押せなくなりました。
ちなみにsubmit自体にdisableを記述する方法などもありましたが、複数のsubmitがある際に適用できる場合を考えこちらを採用しました。
非同期処理
でメール送信の動作を裏で行う
先ほど記述した通り、プログラムには順番があり、それが完了しないと次の処理には移りません。
これを同期処理
と呼びます。
本来ならばそれが普通なのですが、メール送信や画像投稿など時間のかかる処理をする際にはユーザーが待たなくてはなりません。
もう連打はできないはずなのでこれではユーザーが何もすることができませんね。
そこで、非同期処理
を行なってメール送信の処理を裏に回して次の処理を先に行えるようにします。
非同期処理をもっと理解したい人はこちら
まずはrailsで非同期処理のリサーチ
Railsでは非同期処理を実装するのに便利なgemがいくつかあり、有名どころだと、
- sidekiq
- rescue
- delayed job
が挙げられていました。
ですが、僕は一目でどれにするか決めました。
sidekiq(恐らくsidekick(相棒))、君こそが僕のパートナーだ!!
素晴らしいネーミングセンスに一目惚れしました。こんなことってあるんですね。
いざ、実食
sidekiqにはredisがバックグラウンド(ジョブ管理)に必要なのでbrewでインストール
brew install redis
続いてsidekiqのgem登録
gem 'sidekiq'
bundle install
続いて、config/sidekiq.ymlに以下を記述
:concurrency: 25
:pidfile: ./tmp/pids/sidekiq.pid
:logfile: ./log/sidekiq.log
:queues:
- default
Active Jobがsidekiqを使う設定
config.active_job.queue_adapter = :sidekiq
Jobの作成
bundle exec rails g job My
MyJobクラスの実装
class MyJob < ActiveJob::Base
queue_as :default
def perform(@contact)
# 非同期で実行させたい処理を書く(今回はメール送信)
Mailer.post_email(@contact).deliver
end
end
ContactMailerクラスの実装
class Mailer < ApplicationMailer
def post_email(contacts)
@contacts = contacts
mail to: 'info@hoge.com',
bcc: 'hoge@hoge.com',
subject: "[HP]お問い合わせ"
end
end
コントローラー作成
rails generate controller contact
メールを送信するアクション実装
class ContactController < ApplicationController
def run
@contact = Contact.new(contact_params) #contactクラスの実装は割愛します
# 即時実行する場合
MyJob.perform_later(@contact)
# 時間を置いて実行する場合
MyJob.set(wait: 5.second).perform_later(@contact) #5秒後実行
MyJob.set(wait: 10.second).perform_later(@contact) #10秒後実行
render :nothing => true
end
end
ルーティング
post 'contact/run'
お疲れ様です。ここまでで一旦実装は完了です。
続いて
redisの起動
redis-server
bundle exec sidekiq
また別のターミナルにて、Railsを起動
bundle exec rails s
ログを監視
tail -f log/development.log
ジョブを実行
curl http://localhost:3000/start/run -X POST -d ''
お疲れ様でした。
これでかっこよくユーザーの行動を眠らせることができましたね!!