如何在Rails控制器中调用通道方法?

10

我有一个ActionCable的方法,用于订阅用户。如果开始了新的对话,我也想要订阅用户到新的频道。我无法弄清楚在控制器中调用通道方法的正确语法。

更新:问题在于当发送消息时将消息附加到聊天框中,但当发送第一条消息时,websocket连接尚未建立,因此对用户来说看起来好像消息未发送(因为消息没有附加)。

channel/msgs_channel.rb

class MsgsChannel < ApplicationCable::Channel  
  #This function subscribes the user to all existing convos
  def subscribed
    @convos = Convo.where("sender_id = ? OR recipient_id = ?", current_user, current_user)
    @convos.each do |convo|
        stream_from "msg_channel_#{convo.id}"
    end
  end

  #This is a new function I wrote to subscribe the user to a new convo that is started during their session.
  def subscribe(convo_id)
      stream_from "msg_channel_#{convo_id}"
  end
end

在我的「convos_controller」的「create」方法中,我尝试了几件事情:

convos_controller.rb

def create
  @convo = Convo.create!({sender_id: @sender_id, recipient_id: @recipient_id})
  ActionCable.server.subscribe(@convo.id)
end

ActionCable.subscribe(@convo.id)

错误信息: NoMethodError (undefined methodsubscribe' for ActionCable:Module)`


ActionCable.msgs.subscribe(@convo.id)

错误信息: NoMethodError (undefined methodmsgs' for ActionCable:Module):`


  App.msgs.subscribe(@convo.id)

错误:NameError(未初始化的常量ConvosController :: App):


MsgsChannel.subscribe(@convo.id)

错误:NoMethodError(undefined method subscribe' for MsgsChannel:Class`


ActionCable.server.subscribe(@convo.id)

错误:NoMethodError(undefined method subscribe' for#):`


尝试根据ID为每个会话使用WebSocket? - Tall Paul
@TallPaul 那是正确的。 - gwalshington
2个回答

3

从控制器中检索特定的通道实例

Channel 实例与用户的 Connection 相关联。以下代码可以获取 ConnectionChannel 哈希值:

conn = ActionCable.server.connections.first { |c| c.current_user == user_id }

# subs is a hash where the keys are json identifiers and the values are Channels
subs = conn.subscriptions.instance_variable_get("@subscriptions")

(如果Connection#current_user不存在,则按照Rails ActionCable概述 3.1.1节顶部的说明添加它。)
然后,您可以根据任何标准获取一个基于Channel的实例。例如,您可以按类型搜索,在您的情况下可能是MsgsChannelsubs中的json键也可以使用,但可能更加复杂。
一旦您拥有此实例,您就可以调用其上的任何方法(在您的情况下为MsgsChannel#subscribe)。 对此方法的关注 Channel只是封装了将在应用程序客户端的消息触发后启动的服务器端工作。让服务器调用Channel方法实际上等同于让服务器假装消费者发送了未发送的消息。
我建议不要这样做,而是将您想要从ControllerChannel使用的任何逻辑放入共享位置的方法中。然后,ControllerChannel可以根据需要直接调用此方法。
话虽如此,这种方法有一个痛苦的警告,即您需要Channel调用stream_from。不幸的是,即使在根本上只是更新Server的pubsub信息,管理流也是特定于Channel的。
我认为流管理实际上应该需要适当的连接和具有其pubsub的Server。如果是这种情况,您就不需要担心从应用程序的服务器端调用Channel方法。
如果您获得了Channel(您也可以使用此答案顶部的subs),则可以更直接有效地运行stream_for并在其上调用stream_for
conn = ActionCable.server.connections.first { |c| c.current_user == user_id }
MsgsChannel.new(conn, "made up id").stream_from "your_stream_name"

我自己没有尝试过,但看起来应该可以工作。


这真是太有帮助了!谢谢你,Chiune!我在本地测试了你的第一个建议,结果完美无缺,但是当我在我的Heroku应用上进行生产环境测试时却不起作用。你有什么想法为什么会这样呢? - Amloelxer
@Amloelxer - 你所说的“不起作用”是指subs中不包含你正在寻找的Channel实例吗? - Chiune Sugihara

1
所以,在控制器上创建时,您不应该订阅用户到频道。它应该基于他们访问页面的时间。您应该添加一个js/coffee文件来更改用户连接的位置,根据谁已连接来执行此操作。我学习时的一个很好的示例/教程是这个视频 here
视频中遗漏的是如何连接到个人对话。因此,我为此苦苦挣扎,并找到了一种从url获取对话id的方法,可能不是最好的方法,但它起作用了。希望这能帮到你。
conversation_id = parseInt(document.URL.match(new RegExp("conversations/"+ "(.*)"))[1]) # gets the conversation id from the url of the page

App.conversation = App.cable.subscriptions.create { channel: "ConversationChannel", conversation_id: conversation_id },
  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) ->
    $('#messages').append data['message']

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

嗨,感谢您的帮助。我已经为convo_ids设置了连接,一切都很顺利。问题是,在用户会话的中途,用户与另一个用户打开了一个新的对话,并发送了一条消息。这个新的对话还没有订阅,所以用户看不到正确的操作(例如-消息附加到聊天框)。我该如何在convos控制器的create方法中调用通道中的subscribe方法?谢谢! - gwalshington

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接