Rails5 ActionCable 通过对话聊天
Rails5 ActionCable Chat with Conversations
根据 DHHs Rails5 ActionCable 聊天示例,我将创建一个包含对话和许多消息的进一步示例:
rails g model conversation
class Conversation < ApplicationRecord
has_many :messages
end
rails g model message content:text conversation:references
view/conversations/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/messages/_message.html.erb
<div class="message">
<p><%= message.content %></p>
</div>
我的问题是如何编写一个通道逻辑,将与其对话相关的每条消息都写入数据库:
首先我在控制台上录制了一段对话和一条消息
Conversation.create
Message.create(conversation_id: '1', content: 'hello')
之后我创建了一个工作
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
和一个频道
rails g channel conversation speak
assets/javascripts/channels/conversation.咖啡
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's 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()
如果我写:
channels/conversation_channel.rb
class ConversationChannel < ApplicationCable::Channel
def subscribed
stream_from "conversation_channel"
end
def speak
Message.create! content: data['message']
end
end
我明白了
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
看起来不错,但如果我在文本字段中输入一些文本并点击 return,我会得到:
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'
有什么想法吗?
从客户端到服务器端,您必须(首先)让您的 speak
函数接受一个 message
参数并将该消息作为 [=30] 发送到服务器=]对象。
speak: (message) ->
@perform 'speak', message: message
其次,您必须在 channels/conversation_channel.rb 处定义 speak
函数接收的参数。因此,您必须将其重新定义为:
def speak(data)
Message.create! content: data['message']
end
现在您的 speak
方法正在接收一个 data
参数,它是一个 JSON 和一个 message
属性,其中包含发送到服务器。正在记录到数据库中,但没有回复订阅该频道的人。
因此我们必须通知他们将上面的方法重新定义为:
def speak(data)
Message.create! content: data['message']
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
现在它应该可以工作了。您将在后台执行的操作由您决定 ;)
根据 DHHs Rails5 ActionCable 聊天示例,我将创建一个包含对话和许多消息的进一步示例:
rails g model conversation
class Conversation < ApplicationRecord
has_many :messages
end
rails g model message content:text conversation:references
view/conversations/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/messages/_message.html.erb
<div class="message">
<p><%= message.content %></p>
</div>
我的问题是如何编写一个通道逻辑,将与其对话相关的每条消息都写入数据库:
首先我在控制台上录制了一段对话和一条消息
Conversation.create
Message.create(conversation_id: '1', content: 'hello')
之后我创建了一个工作
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
和一个频道
rails g channel conversation speak
assets/javascripts/channels/conversation.咖啡
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's 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()
如果我写:
channels/conversation_channel.rb
class ConversationChannel < ApplicationCable::Channel
def subscribed
stream_from "conversation_channel"
end
def speak
Message.create! content: data['message']
end
end
我明白了
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
看起来不错,但如果我在文本字段中输入一些文本并点击 return,我会得到:
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'
有什么想法吗?
从客户端到服务器端,您必须(首先)让您的 speak
函数接受一个 message
参数并将该消息作为 [=30] 发送到服务器=]对象。
speak: (message) ->
@perform 'speak', message: message
其次,您必须在 channels/conversation_channel.rb 处定义 speak
函数接收的参数。因此,您必须将其重新定义为:
def speak(data)
Message.create! content: data['message']
end
现在您的 speak
方法正在接收一个 data
参数,它是一个 JSON 和一个 message
属性,其中包含发送到服务器。正在记录到数据库中,但没有回复订阅该频道的人。
因此我们必须通知他们将上面的方法重新定义为:
def speak(data)
Message.create! content: data['message']
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
现在它应该可以工作了。您将在后台执行的操作由您决定 ;)