コメントを即時更新するActionCableを用いた実装を紹介したいと思います。以下の流れで説明していきます。ブログの仕様上インデントが整っていないので、ご容赦ください。
1. チャンネルの作成
2. stream_formメソッドで関連づけ
3. broadcastを介する
4. comment_channel.jsの編集
5. Herokuでデプロイする場合
6. AWSでデプロイする場合
1. チャンネルの作成
まず即時更新機能を実現するサーバー側の仕組みであるChannelファイルを作成します。データの経路を設定したり、送られてきたデータを画面上に表示させたりします。
ターミナル
% rails g channel comment
2. stream_formメソッドで関連づけ
次にcomment_channel.rbを編集します。これはルーティングの機能を果たすファイルです。stream_formメソッドを用いることでサーバーとクライアントを関連づけます。
app/channels/comment_channel.rb
class CommentChannel < ApplicationCable::Channel
def subscribed
stream_from "comment_channel"
end
def unsubscribed
# Any cleanup needed when channel is unsubscribed
end
end
3. broadcastを介する
コメントの保存が成功した時に、broadcastを介してコメントが送信されるように記述します。僕が行ったこの実装では、ユーザーや商品を紐付けてユーザー名も送信されるようにしています。
app/controllers/comments_controller.rb
class CommentsController < ApplicationController
def new
@comments = Comment.all
@comment = Comment.new
end
def create
@comment = Comment.new(comment_params)
@user = @comment.user
if @comment.save
ActionCable.server.broadcast 'comment_channel', { content: @comment,
comment_user: @user }
end
end
private
def comment_params
params.require(:comment).permit(:text).merge(user_id: current_user.id,
item_id: params[:item_id])
end
end
4. comment_channel.jsの編集
受け取った情報はreceivedの引数のdataに入ります。このデータをhtmlに挿入できるように記述します。
app/javascript/channels/comment_channel.js
import consumer from "./consumer"
consumer.subscriptions.create("CommentChannel", {
connected() {
// Called when the subscription is ready for use on the server
},
disconnected() {
// Called when the subscription has been terminated by the server
},
received(data) {
const html = `
<p>
<strong> ${data.comment_user.nickname}:</strong>
${data.content.text}
</p>`;
const comments = document.getElementById('comments');
const newComment = document.getElementById('comment-text');
comments.insertAdjacentHTML('afterbegin', html);
}
});
これで即時コメントが表示される実装が完了です。
5. Herokuでデプロイする場合
adapterの指定をasyncに変更します。これは無料で導入が可能なアダプターです。
config/cable.yml
development:
adapter: async
test:
adapter: test
production:
adapter: async
#adapter: redis
ActionCableは指定されていないオリジンからのリクエストを受け付けない設定がされているため、設定を編集します。ブログ内に収めるためにやや記述が変更されています。
config/environments/production.rb
Rails.application.configure do
#省略
ActionCable.server.config.disable_request_forgery_protection = true
config.action_cable.url = "wss://[アプリ名].herokuapp.com/cable"
config.action_cable.allowed_request_origins =
['https://[アプリ名].herokuapp.com', 'http://[アプリ名].herokuapp.com']
#省略
end
これで後はgit push heroku masterをすれば実装完了です。
6. AWSでデプロイする場合
AWSでデプロイする場合もまずはHerokuの時と同じようにアダプターの指定をします。そしてproduction.rbの記述は以下のようになります。
config/environments/production.rb
Rails.application.configure do
#省略
ActionCable.server.config.disable_request_forgery_protection = true
config.action_cable.url = "ws://[Elastic IP]/cable"
config.action_cable.allowed_request_origins = ['http://[Elastic IP]']
次にNginxの設定ファイルであるrails.confを編集します。
ターミナル(EC2内)
sudo vim /etc/nginx/conf.d/rails.conf
/etc/nginx/conf.d/rails.conf
upstream app_server {
server unix:/var/www/アプリ名/shared/tmp/sockets/unicorn.sock;
}
server {
listen 80;
server_name Elastic IP;
root /var/www/アプリ名/current/public;
location ^~ /assets/ {
expires max;
add_header Cache-Control public;
root /var/www/アプリ名/current/public;
}
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://app_server;
}
#下記の記述を加える
location /cable {
proxy_pass http://app_server/cable;
proxy_http_version 1.1;
proxy_set_header Upgrade websocket;
proxy_set_header Connection Upgrade;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
error_page 500 502 503 504 /500.html;
}
保存が完了したら、以下のコマンドを実行します。
sudo systemctl reload nginx
sudo systemctl restart nginx
commitとpushを行った後bundle exec cap production deployを実行して実装が完了です。