According to the DHCs Rails5 ActionCable chat example, I am going to create another example with dialogs and many messages there:
rails g model conversation class Conversation < ApplicationRecord has_many :messages end rails g model message content:text conversation:references
View / talk / show.html.erb
<h1>Conversation</h1> <div id="messages"> <%= render @messages %> </div> <form> <label>Say something:</label><br> <input type="text" data-behavior="conversation_speaker"> </form>
View / Posts / _message.html.erb
<div class="message"> <p><%= message.content %></p> </div>
My questions are how to write channel logic so that every message related to his conversation is written to the database:
First I recorded a conversation and a message on the console
Conversation.create Message.create(conversation_id: '1', content: 'hello')
after that I created Job
rails g job MessageBroadcast class MessageBroadcastJob < ApplicationJob queue_as :default render_message(message) def perform(data) message = Message.create! content: data ActionCable.server.broadcast 'conversation_channel', message: render_message(message) end private def render_message(message) ApplicationController.renderer.render(partial: 'messages/message', locals: { message: message }) end end
and channel
rails g channel conversation speak
assets / javascripts / channels / conversation.coffee
App.conversation = App.cable.subscriptions.create "ConversationChannel", 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) -> # Called when there incoming data on the websocket for this channel $('#messages').append data['message'] speak: -> @perform 'speak' $(document).on 'keypress', '[data-behavior~=conversation_speaker]', (event) -> if event.keyCode is 13 # return = send App.conversation.speak event.target.value event.target.value = "" event.preventDefault()
If I write:
Channels / conversation_channel.rb
class ConversationChannel < ApplicationCable::Channel def subscribed stream_from "conversation_channel" end def speak Message.create! content: data['message'] end end
I get
Started GET "/cable/" [WebSocket] for ::1 at 2016-04-22 00:22:13 +0200 Successfully upgraded to WebSocket (REQUEST_METHOD: GET, HTTP_CONNECTION: keep-a live, Upgrade, HTTP_UPGRADE: websocket) Started GET "/cable" for ::1 at 2016-04-22 00:22:13 +0200 Started GET "/cable/" [WebSocket] for ::1 at 2016-04-22 00:22:13 +0200 Successfully upgraded to WebSocket (REQUEST_METHOD: GET, HTTP_CONNECTION: keep-a live, Upgrade, HTTP_UPGRADE: websocket) ConversationChannel is transmitting the subscription confirmation ConversationChannel is streaming from conversation_channel ConversationChannel is transmitting the subscription confirmation ConversationChannel is streaming from conversation_channel
It looks fine, but if I type some text in the text box and hit return, I get:
Could not execute command from {"command"=>"message", "identifier"=>"{\"channel\":\"ConversationChannel\"}", "data"=>"{\"action\":\"speak\"}"}) [NameError - undefined local variable or method `data' for #<ConversationChannel:0x00000008ad3100>]: C:/Sites/ActionCable/app/channels/conversation_channel.rb:13: in `speak' | C:/Ruby22-x64/lib/ruby/gems/2.2.0/gems/actioncable-5.0.0.beta3/lib/action_cable/channel/base.rb:253: in `public_send' | C:/Ruby22-x64/lib/ruby/gems/2.2.0/gems/actioncable-5.0.0.beta3/lib/action_cable/channel/base.rb:253: in `dispatch_action' | C:/Ruby22-x64/lib/ruby/gems/2.2.0/gems/actioncable-5.0.0.beta3/lib/action_cable/channel/base.rb:163: in `perform_action' | C:/Ruby22-x64/lib/ruby/gems/2.2.0/gems/actioncable-5.0.0.beta3/lib/action_cable/connection/subscriptions.rb:49: in `perform_action'
Any ideas?