Как я могу вызвать метод канала в контроллере рельсов?

У меня есть метод ActionCable, который подписывается на пользователя. Если запускается новый конво, я также хочу подписать пользователя на новый канал. Я не могу понять правильный синтаксис для вызова метода канала в контроллере.

ОБНОВЛЕНИЕ: проблема заключается в том, что сообщения добавляются в окно чата при отправке, но когда отправляется первое сообщение, соединение с веб-сокетом еще не установлено, и поэтому для пользователя оно выглядит так, как будто сообщение не было отправлено (потому что оно не дополняется).

канал/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 methodsubscribe' для ActionCable:Module)`


ActionCable.msgs.subscribe(@convo.id)

ошибка: NoMethodError (undefined methodmsgs для ActionCable:Module):`


  App.msgs.subscribe(@convo.id)

ошибка:NameError (uninitialized constant ConvosController::App):


MsgsChannel.subscribe(@convo.id)

ошибка: NoMethodError (undefined methodsubscribe' для MsgsChannel: Class`


ActionCable.server.subscribe(@convo.id)

error:NoMethodError (undefined methodsubscribe' для #):`


person gwalshington    schedule 08.03.2017    source источник
comment
Пытаетесь использовать веб-сокет для каждого разговора на основе идентификатора?   -  person Tall Paul    schedule 09.03.2017
comment
@TallPaul Это правильно   -  person gwalshington    schedule 09.03.2017


Ответы (2)


Получение определенного экземпляра канала из контроллера

Экземпляры Channel связаны с Connection пользователя. Следующее будет работать, чтобы получить хэш Channels для 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 не существует, следуйте инструкциям в верхней части раздела 3.1.1 из обзор Rails ActionCable, чтобы добавить его.)

Затем вы можете получить Channel на основе любых критериев, которые вы хотите. Например, вы можете искать по типу, который в вашем случае будет MsgsChannel. Можно также использовать ключ json в subs, но это может быть сложнее.

Получив этот экземпляр, вы можете вызвать для него любой метод (в вашем случае MsgsChannel#subscribe).

Опасения по поводу этого подхода

Channel просто инкапсулирует работу на стороне сервера, которая будет запускаться в ответ на сообщения с клиентской стороны приложения. Вызов сервером метода Channel фактически означает, что сервер делает вид, что потребитель отправил сообщение, которое он не отправлял.

Я бы рекомендовал вместо этого помещать любую логику, которую вы хотите использовать как из Controller, так и из Channel, в метод в общем месте. Затем Controller и Channel могут напрямую вызывать этот метод по мере необходимости.

При этом есть одно болезненное предостережение, которое заключается в том, что вам нужно Channel для вызова stream_from. К сожалению, управление потоками специфично для Channels, хотя по сути это просто обновление информации pubsub для Server.

Я думаю, что управление потоком действительно должно требовать соответствующего соединения и Server с его pubsub. Если бы это было так, вам не нужно было бы беспокоиться о вызове Channel методов со стороны сервера приложения.

Вы сможете эффективно запускать stream_for более непосредственно, если вы получите Channel, любое Channel (вы также можете использовать subs из верхней части этого ответа) и вызовете stream_for на нем.

conn = ActionCable.server.connections.first { |c| c.current_user == user_id }
MsgsChannel.new(conn, "made up id").stream_from "your_stream_name"

Я не пробовал это сам, но похоже, что это должно работать.

person Chiune Sugihara    schedule 13.04.2017
comment
Это было очень полезно! Спасибо Чиуне! Я протестировал ваше первое предложение локально, и оно сработало безупречно, но не сработало, когда я тестировал его на своем фиктивном приложении Heroku в производстве. Есть мысли, почему? - person Amloelxer; 10.05.2017
comment
@Amloelxer. Под неработающим вы подразумеваете, что subs не содержит экземпляр Channel, который вы ищете? - person Chiune Sugihara; 11.05.2017

Таким образом, вы не должны подписывать пользователя на канал в контроллере при создании. Это должно быть основано на том, когда они посещают страницу. Вы должны изменить, где пользователи подключаются, добавив файл js/coffe, чтобы сделать это за вас в зависимости от того, кто подключен. Хорошим примером/учебником для этого, когда я учился, было это видео здесь.

В видео не упоминается, как подключиться к индивидуальному разговору. Поэтому я боролся с этим и нашел способ получить идентификатор разговора из URL-адреса, возможно, не лучший способ, но он работает. Надеюсь это поможет

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
person Tall Paul    schedule 09.03.2017
comment
Привет, спасибо за вашу помощь. Я уже настроил соединение для convo_ids и все отлично работает. Вопрос в том, что в середине сеанса пользователя пользователь открывает НОВУЮ беседу с другим пользователем и отправляет сообщение. Этот новый конво еще не подписан, поэтому пользователь не видит правильных действий (например, добавление сообщения в окно чата). Как я могу вызвать метод подписки в канале, из контроллера convos, в методе создания? Спасибо! - person gwalshington; 09.03.2017