如何从控制器或模型调用ActionCable方法 - “未初始化的常量QuizSession :: App”错误

时间:2017-05-16 20:33:42

标签: ruby-on-rails ruby actioncable

我正在尝试使用带有2个用户的Ruby on Rails编写一个简单的测验应用程序,他们可以看到不同的视图:

  • 测验主持人视图显示当前问题和“下一个问题” - 按钮(他应该为观众投射到墙上)和
  • 参与者视图显示4个按钮,其中包含与当前问题相对应的答案选项(因此受众可以通过智能手机参与测验)。

我正在尝试使用ActionCable将这4个答案按钮广播到我的频道,但是当我尝试调用我在频道中定义的方法时,我收到错误"uninitialized constant QuizSession::App"

这些是我为启用ActionCable而采取的步骤:

1)我在/app/channels/quiz_data_channel.rb中生成了一个新频道:

class QuizDataChannel < ApplicationCable::Channel
  def subscribed
    stream_from "quiz_data"
  end

  def unsubscribed
    # Any cleanup needed when channel is unsubscribed
  end

  def send_data
    Services::QuizDataCreation.new(user: current_user).create
  end
end

# /app/assets/javascripts/channels/quiz_data.coffee:
App.quiz_data = App.cable.subscriptions.create "QuizDataChannel",
  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
  $('answer-buttons').append(data.partial)

  send_data: ->
    @perform 'send_data'

2)然后我在app / models / services / quiz_data_creation.rb中创建了一项新服务:

module Services
  class QuizDataCreation

    def initialize(user)
      self.user = user
    end

    def create
      create_quiz_data
      broadcast_creation
    end

    private
    attr_accessor :user, :answers

    def create_quiz_data  #not sure if this will work
      @quiz_session = user.quiz_session
      self.answers = @quiz_session.quiz.questions[@quiz_session.current_question_index].answers
    end

    def broadcast_creation
      QuizDataBroadcastJob.perform_now(answers)
    end
  end
end

3)然后我在/app/jobs/quiz_data_broadcast_job.rb上创建了一份新工作:

class QuizDataBroadcastJob < ApplicationJob
  queue_as :default

  def perform(answers)
    ActionCable.server.broadcast('quiz_data', data: render_answer_options(answers))
  end

  private
  def render_answer_options(answers)
    answers.each do |answer|
      ApplicationController.render(
          #render student/quiz_question page and render as many answer_option partials as needed
          partial: 'pages/student/answer_option',
          locals: {answer: answer}
      )
    end
  end
end

4)我在我的routes.rb中安装了ActionCable:

mount ActionCable.server => '/cable'

5)最后我试图通过在我的应用程序的其他地方调用send_data函数来广播数据:

def send_current_question
    App.quiz_data.send_data <--- this is apparently where the error gets thrown
end

我想知道的是:

  • 如何解决此错误?
  • 问题是我没有正确建立套接字连接吗?

我已经阅读了3个ActionCable指南,并观看了2个指南视频 - 因为它们中的大多数似乎是关于聊天应用程序(在我看来是一对一连接)我现在对于如何将我的应用程序设置为以我需要的一对多广播方式工作。

我是Rails的新手,所以任何指针都会受到赞赏! :)

2 个答案:

答案 0 :(得分:0)

WebSockets的设计使您无论是构建一对一,多对一,一对多还是多对多功能都无关紧要。原因是作为渠道的发布者,您不知道谁订阅了它,因此无论是1个还是1000个用户,他们都将收到已发布的消息。

至于你收到错误的原因,App对象是一个JavaScript对象(看你在coffeescript文件中如何访问它),所以你不能在Ruby代码中使用它。

您的Ruby代码(后端)中没有提供App对象,因为它应该是客户端(用户)与服务器(后端)通信的方法,这就是它被初始化的原因在你的coffeescript代码中。

因此,您需要通过将单击处理程序附加到按钮来调用send_data方法。让我们在你的HTML中说你的按钮看起来像:

<button id="broadcast-button">Broadcast</button>

在您的客户端代码(coffeescript)中,您将执行以下操作:

$('#broadcast-button').on 'click', (e) ->
  App.quiz_data.send_data()

因此,当用户点击该按钮时,将调用send_data方法。

答案 1 :(得分:0)

  

至于你收到错误的原因,App对象是一个JavaScript对象(请参阅如何在coffeescript文件中访问它),因此你不能在Ruby代码中使用它。

     

您的Ruby代码(后端)中没有提供App对象   因为它应该是客户端(用户)的方法   与服务器(后端)通信,这就是它被初始化的原因   在你的coffeescript代码中。

正如@Laith Azer所指出的,不可能从控制器或模型调用CoffeeScript方法(因为它代表前端),但我的所有CoffeeScript方法send_data确实是的,是在后端调用它的对应物:

#/app/assets/javascripts/channels/quiz_data.coffee:
App.quiz_data = App.cable.subscriptions.create "QuizDataChannel",
  ...
  send_data: ->
    @perform 'send_data'

那么可能,就是直接调用这个后端方法:

#In some controller or model:
QuizDataChannel.send_data(current_user)

要实现这一点,我们需要将ActionCable通道中的方法更改为类方法:

#/app/channels/quiz_data_channel.rb:
class QuizDataChannel < ApplicationCable::Channel
  def self.send_data(current_user) #self. makes it a class method
    new_quiz_html = Services::QuizDataCreation.new(user: current_user).create
    #change "some_room" to the room name you want to send the page on
    ActionCable.server.broadcast("some_room", html: new_quiz_html) 
  end
end

剩下的就是接收这个html页面并显示它:

#/app/assets/javascripts/channels/quiz_data.coffee:
App.quiz_data = App.cable.subscriptions.create "QuizDataChannel",
  ...
  send_data: ->
    @perform 'send_data'

  received: (data) ->
    # This will replace your body with the page html string:
    $('body').html(data.html)