4
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Rails 5とActionCableでチャットアプリ 初心者制作

Last updated at Posted at 2019-10-03

##環境
ruby '2.6.3'
'rails', '~> 5.2.3'

##参考
Rails 5 + ActionCableで作る!シンプルなチャットアプリ(DHH氏のデモ動画より)
Rails 5+ ActionCableで作る! シンプルなチャットアプリ ハマった所
Action Cableでリアルタイムチャットアプリの作成方法 (Rails 5.1.4にて)(その1) herokuで動かす!

#アプリケーション作成手順
##1.アプリ作成
chat_roomという名前のアプリを作成。

$ rails new chat_room
$ cd chat_room

Gemfile に追加。

gem 'jquery-rails'
gem 'jquery-turbolinks'
$ bundle install

application.jsファイルに追加

app/assets/javascripts/application.js
//= require jquery
//= require jquery_ujs

この順になれば良い

//
//= require rails-ujs
//= require activestorage
//= require jquery
//= require jquery_ujs
//= require turbolinks
//= require_tree .

##2.roomsコントローラー作成
showアクションをもつroomsコントローラーを作成。

$ rails g controller rooms show

get 'rooms/show'root to: 'rooms#show'に変更します。

config/routes.rb
Rails.application.routes.draw do
  root to: 'rooms#show'

  # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html

  # Serve websocket cable requests in-process
  # mount ActionCable.server => '/cable'
end

サーバーを起動

$ rails s

localhost:3000でアクセスできるようになった。

##3.Messageモデル作成
contentという属性を持つ、Messageクラスを作成。

$ rails g model message content:text
$ rails db:migrate

##4.controllerとview作成・編集
roomsのコントローラーのshowアクションを編集し、@messages変数に全ての投稿を代入する。

@messages = Message.allを追加。

app/controllers/rooms_controller.rb
class RoomsController < ApplicationController
  def show
    @messages = Message.all
  end
end

app/views/messagesviewsにmessagesというフォルダーを作成。
app/views/messages/_message.html.erbそのmessagesに_message.html.erbというファイルを作成。

controllerで代入した@messagesを表示。全て書き換える。

app/views/rooms/show.html.erb
<h1>Chat room</h1>

<div id="messages">
  <%= render @messages %>
</div>

投稿を1件だけ出力

app/views/messages/_message.html.erb
<div class="message">
  <p><%= message.content %></p>
</div>

Action Cableを有効にするため、mount ActionCable.server => '/cable'を追加。

config/routes.rb
Rails.application.routes.draw do
  root to: 'rooms#show'
  mount ActionCable.server => '/cable'
end

##5.Roomチャンネル作成
speakアクションを持つRoomチャンネルを作成。
app/channels/room_channel.rb(サーバーサイド用)
app/assets/javascripts/channels/room.coffee
(クライアントサイド用)
という2つのファイルが作られる。

$ rails g channel room speak

サーバーを起動

$ rails s

ブラウザーのコンソール(デベロッパーツールからconsoleを開く)にApp.room.speak()を入力し、trueが返ってくれば成功。
aa.PNG

##6.speakアクション
クライアントサイドのspeakアクションを定義する。サーバーサイドのspeakアクションを呼びだし、messageをパラメータとして渡す。
speak: -> @perform 'speak'
speak: (message) -> @perform 'speak', message: message
と書き換える。

app/assets/javascripts/channels/room.coffee
App.room = App.cable.subscriptions.create "RoomChannel",
 connected: ->

 disconnected: ->

 received: (data) ->


  speak: (message) ->
    @perform 'speak', message: message

サーバーサイドのspeakアクションを定義します。

def subscribed # stream_from "some_channel" endから#を削除し、some_channelroom_channelに書き換える。

def speak end
def speak(data) ActionCable.server.broadcast 'room_channel', message: data['message'] endに書き換える。

app/channels/room_channel.rb
class RoomChannel < ApplicationCable::Channel
  def subscribed
    stream_from "room_channel"
  end

  def unsubscribed
  end

  def speak(data)
    ActionCable.server.broadcast 'room_channel', message: data['message']
  end
end

サーバーからデータを受け取ったときの動きを定義します。

receivedのところにalert data['message']を追加

app/assets/javascripts/channels/room.coffee
App.room = App.cable.subscriptions.create "RoomChannel",
  connected: ->
    
  disconnected: ->
    
  received: (data) ->
    alert data['message']

speak: (message) ->
    @perform 'speak', message: message

サーバーを再起動します。

$ rails s

ブラウザのコンソールから App.room.speak('Hello world') を入力し、trueが返ってくれば成功。
bb.PNG

ブラウザ内のフォームからデータを送信できるようにする。

フォーム`


Say something:

`を追加。
app/views/rooms/show.html.erb
<h1>Chat room</h1>

<div id="messages">
  <%= render @messages %>
</div>

<form>
  <label>Say something:</label><br>
  <input type="text" data-behavior="room_speaker">
</form>

テキストボックスのkeypressイベントを定義します。
リターンキーが押されたときに、Roomチャンネルのspeakアクションを実行。

app/assets/javascripts/channels/room.coffee
App.room = App.cable.subscriptions.create "RoomChannel",
  # (省略)

$(document).on 'keypress', '[data-behavior~=room_speaker]', (event) ->
  if event.keyCode is 13 # return = send
    App.room.speak event.target.value
    event.target.value = ''
    event.preventDefault()

ブラウザをリロードすると、テキストボックスが現れる。
cc.PNG

##データの保存とブロードキャスト処理の改善
このままではmessageが保存されないので、データベースに保存。
ActionCable.server.broadcast 'room_channel', message: data['message']Message.create! content: data['message']に書き換える。

app/channels/room_channel.rb
class RoomChannel < ApplicationCable::Channel
  def subscribed
    stream_from "some_channel"
  end

  def unsubscribed
    
  end

  def speak(data)
    Message.create! content: data['message']
  end
end

Messageモデルのコールバックを定義し、データが作成されたら非同期でブロードキャスト処理を実行する。

トランザクションをコミットしたあとでブロードキャストしないと、他のクライアントからデータが見えない恐れがある。

app/models/message.rb
class Message < ApplicationRecord
  after_create_commit { MessageBroadcastJob.perform_later self }
end

非同期でブロードキャストするためのMessageBroadcastジョブを作成

$ rails g job MessageBroadcast

def perform(*args) # Do something later end
def perform(message) ActionCable.server.broadcast 'room_channel', message: render_message(message) end private def render_message(message) ApplicationController.renderer.render(partial: 'messages/message', locals: { message: message }) endに書き換える。

app/jobs/message_broadcast_job.rb
class MessageBroadcastJob < ApplicationJob
  queue_as :default

  def perform(message)
    ActionCable.server.broadcast 'room_channel', message: render_message(message)
  end

  private
    def render_message(message)
      ApplicationController.renderer.render(partial: 'messages/message', locals: { message: message })
    end
end

##サーバーからデータを受け取ったらブラウザ内の表示を書き換える

サーバーからデータを受け取ったら、ブラウザ内の表示を更新する。
入力したものが更新なしで画面に反映される。同時にデータベースに登録される。

received: (data) -> alert data['message']
received: (data) -> $('#messages').append data['message']に書き換える。

app/assets/javascripts/channels/room.coffee
App.room = App.cable.subscriptions.create "RoomChannel",
  connected: ->
    
  disconnected: ->
    
  received: (data) ->
     $('#messages').append data['message']
    
  speak: (message) ->
    @perform 'speak', message: message

$(document).on 'keypress', '[data-behavior~=room_speaker]', (event) ->
  if event.keyCode is 13 # return = send
    App.room.speak event.target.value
    event.target.value = ''
    event.preventDefault()

ブラウザをリロードし、文字列を入力。

リターンキーを押すと、ブラウザの表示が更新。

これでチャットアプリの完成。

4
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?