如何在 Rails 控制器中调用通道方法?
Posted
技术标签:
【中文标题】如何在 Rails 控制器中调用通道方法?【英文标题】:How can I call a channel method in a rails controller? 【发布时间】:2017-07-29 12:24:37 【问题描述】:我有一个订阅用户的 ActionCable 方法。如果启动了一个新的 convo,我也想为用户订阅新频道。我无法弄清楚在控制器中调用通道方法的正确语法。
更新:问题是消息在发送时附加到聊天框,但是当发送第一条消息时,尚未建立 websocket 连接,因此它看起来好像消息没有发送给用户(因为它没有被附加)。
频道/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 控制器中,创建方法,我尝试了几件事:
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 method
subscribe' for ActionCable:Module)`
ActionCable.msgs.subscribe(@convo.id)
错误:
NoMethodError (undefined method
msgs' for ActionCable:Module):`
App.msgs.subscribe(@convo.id)
错误:NameError (uninitialized constant ConvosController::App):
MsgsChannel.subscribe(@convo.id)
error:NoMethodError (undefined method
subscribe' for MsgsChannel:Class`
ActionCable.server.subscribe(@convo.id)
error:NoMethodError (undefined method
subscribe' for #):`
【问题讨论】:
是否尝试根据 id 为每个对话使用 websocket? @TallPaul 没错 【参考方案1】:因此,您不应在创建时为用户订阅控制器中的频道。它应该基于他们访问页面的时间。您应该通过添加 js/coffe 文件来更改用户的连接位置,以便根据连接的人为您执行此操作。当我学习时,一个很好的例子/教程是这个视频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 建立了连接,一切都很好。问题是,在用户会话的中间,用户打开一个与不同用户的新 convo,并发送一条消息。这个新的 convo 尚未订阅,因此用户看不到正确的操作(例如 - 附加到聊天框的 msg)。如何在创建方法中从 convos 控制器调用频道中的订阅方法?谢谢!【参考方案2】:从控制器中检索特定通道实例
Channel
实例与用户的Connection
相关联。以下将用于获取Channel
s 的散列以获取Connection
:
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
不存在,请按照the rails ActionCable overview 中第 3.1.1 节顶部的说明进行添加。)
然后,您可以根据您想要的任何标准获得Channel
。例如,您可以按类型搜索,在您的情况下为MsgsChannel
。 subs
中的 json 键也可以使用,但可能更复杂。
一旦你有了这个实例,你就可以调用任何你想要的方法(在你的例子中是MsgsChannel#subscribe
)。
对此方法的担忧
Channel
只是封装了服务器端工作,这些工作将被启动以响应来自应用程序客户端的消息。让服务器调用Channel
方法实际上相当于让服务器假装消费者发送了一条它没有发送的消息。
我建议不要这样做,而不是将您想要使用的任何逻辑从 Controller
和 Channel
放入共享位置的方法中。然后Controller
和Channel
可以根据需要直接调用该方法。
话虽如此,有一个痛苦的警告,那就是你需要Channel
来调用stream_from
。不幸的是,管理流是特定于Channel
s 的,尽管从根本上说它只是更新Server
的发布订阅信息。
我认为流管理实际上应该只需要适当的连接和 Server
及其 pubsub。如果是这种情况,您就不必担心从应用程序的服务器端调用 Channel
方法。
如果你得到一个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"
我自己没有尝试过,但它看起来应该可以工作。
【讨论】:
这非常有帮助!谢谢千钧!我在本地测试了您的第一个建议,它运行良好,但当我在生产中的虚拟 Heroku 应用程序上测试它时却没有用。有什么想法吗? @Amloelxer - 不工作是指subs
不包含您正在寻找的Channel
实例吗?以上是关于如何在 Rails 控制器中调用通道方法?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Ruby on Rails 的控制台中调用控制器/视图辅助方法?