LoginSignup
3
2

More than 3 years have passed since last update.

Action Cableの設定でつまずいたこと

Last updated at Posted at 2020-01-24

行っていること

以下の記事や動画を参考にチャット機能を作成しています。
Rails 5 + ActionCableで作る!シンプルなチャットアプリ(DHH氏のデモ動画より)
0から手を動かして作るRailsチャットアプリ【チュートリアル】

リアルタイムでのチャット機能を作成する際につまずいたことを、備忘録も兼ねて共有します。
ご指摘やアドバイスがありましたらコメントよろしくお願いします。

やりたいこと

自分(current_user)と他の人で表示方法を変える
スクリーンショット 2020-01-24 17.56.47.png

チャンネルの作成

$ rails g channel chatroom speak
    create  app/channels/chatroom_channel.rb
    create  app/assets/javascripts/channels/chatroom.coffee

つまずいたこと

chatroom_channel.rbから_post.html.erbをrenderで呼び出そうとすると、[ActionView::Template::Error - undefined method 'id' for nil:NilClass] と言われる。→channelからrenderした際にcurrent_userの値を取れていないため

コード一覧

routes.rb
Rails.application.routes.draw do
  root to: 'toppages#index'
  # (省略)

  resources :chatrooms do
    member do
      get :users
    end
  end

  mount ActionCable.server => '/cable'
end
posts/_post.html.erb
<% unless post.user_id == current_user.id %>
  <p style="color:blue;"><%= post.content %></p>
<% else %>
  <p style="color:red;"><%= post.content %></p>
<% end %>
chatroom_channel.rb
class ChatroomChannel < ApplicationCable::Channel
  def subscribed
    stream_from "chatroom_channel"
  end

  def unsubscribed
    # Any cleanup needed when channel is unsubscribed
  end

  def speak(data)
    message = Post.create! content: data['message'], user_id: data['user_id'], chatroom_id: data['room_id']
    template = ApplicationController.renderer.render(partial: 'posts/post', locals: { post: message })
    ActionCable.server.broadcast 'chatroom_channel', message: template
  end
end
chatroom.coffee
# クライアントサイドの処理を受け持つチャンネ
App.chatroom = App.cable.subscriptions.create "ChatroomChannel",
  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) ->
    $('#posts').append data['message']


    # Called when there's incoming data on the websocket for this channel

  speak: (content, data_user, data_room) ->
    @perform 'speak', message: content, user_id: data_user, room_id: data_room


document.addEventListener 'DOMContentLoaded', ->
  input = document.getElementById('chat_input') 
  data_user = input.getAttribute("data_user")
  data_room = input.getAttribute("data_room")
  button = document.getElementById('chat_button')
  button.addEventListener 'click', ->
    content = input.value
    App.chatroom.speak(content, data_user, data_room)
    input.value = ''
    return
  return

原因

channelからは sessionを使うことが出来ないそうです。
(sessions_helperでcurrent_userを定義している。)

解決方法

channelからcookieは使うことができるそうなので、cookieからユーザー情報を取ってきて、current_user変数に代入。
主にこちらの記事を参考にしました。

cookieの情報からcurrent_userの作成

channels/application_cable/connection.rb
module ApplicationCable
  class Connection < ActionCable::Connection::Base
    identified_by :current_user

    def connect
      self.current_user = find_verified_user
    end

    protected

    def find_verified_user
      if verified_user = User.find_by(id: session['user_id'])
        verified_user
      else
        reject_unauthorized_connection
      end
    end

    def session
      cookies.encrypted[Rails.application.config.session_options[:key]]
    end
  end
end
chatroom_channel.rb
# (省略)

  def speak(data)
    message = Post.create! content: data['message'], user_id: data['user_id'], chatroom_id: data['room_id']
    template = ApplicationController.renderer.render(partial: 'posts/post', locals: { post: message, current_user: current_user }) # current_user変数にconnection.rbで取得したcurrent_userを設定
    ActionCable.server.broadcast 'chatroom_channel', message: template
  end

関連リンク

DOMContentLoadedからturbolinks:loadへ変更
Action Cableへのidの受け渡し

参考リンク

Rails5のActionCableで簡易チャットの作成 ~モデルに応じたチャンネルを聴講する方法~

3
2
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
3
2