如何从控制器或模型调用 ActionCable 方法 - "uninitialized constant QuizSession::App" 错误
How to call ActionCable method from controller or model - "uninitialized constant QuizSession::App" Error
我正在尝试用 Ruby 在 Rails 上与 2 个用户一起编写一个简单的测验应用程序,他们可以看到不同的观点:
- 测验主持人视图显示当前问题和一个 "next question" 按钮(他应该将其投影到观众的墙上)和
- 参与者视图显示了 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
我想知道的是:
- 如何解决这个错误?
- 是我没有正确建立socket连接的问题吗?
我已经阅读了 3 个 ActionCable 指南并观看了 2 个指南视频 - 因为它们中的大多数似乎都是关于聊天应用程序的(在我看来是一对一的连接),我现在完全不知道如何获得我的应用程序以我需要的一对多广播方式工作。
我是 Rails 的新手,如有任何指点,我们将不胜感激! :)
WebSockets 的设计使得无论您是在构建一对一、多对一、一对多还是多对多功能都无关紧要。原因是作为一个频道的发布者,你不知道谁订阅了它,所以无论是1个还是1000个用户,他们都会收到发布的消息。
至于为什么会出现错误,App
对象是一个 JavaScript 对象(请参阅您如何在 coffeescript 文件中访问它),因此您不能在 Ruby代码。
App
对象在您的 Ruby 代码(后端)中不可用,因为它应该是客户端(用户)与服务器(后端)通信的一种方法,这就是它在您的 coffeescript 代码中初始化的原因。
因此您需要通过将点击处理程序附加到您的按钮来调用 send_data 方法。假设在您的 html 中,您的按钮看起来像:
<button id="broadcast-button">Broadcast</button>
在您的客户端代码 (coffeescript) 中,您将执行以下操作:
$('#broadcast-button').on 'click', (e) ->
App.quiz_data.send_data()
所以当用户点击那个按钮时,send_data 方法将被调用。
As for why you are getting the error, well the App object is a JavaScript object (see how you are accessing it in your coffeescript files), so you cannot use it in Ruby code.
The App object is not made available in your Ruby code (backend)
because it is supposed to be a method for the client (user) to
communicate with the server (backend), which is why it is initialized
in your coffeescript code.
正如@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 通道中的方法更改为 class 方法:
#/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)
我正在尝试用 Ruby 在 Rails 上与 2 个用户一起编写一个简单的测验应用程序,他们可以看到不同的观点:
- 测验主持人视图显示当前问题和一个 "next question" 按钮(他应该将其投影到观众的墙上)和
- 参与者视图显示了 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:
中安装了 ActionCablemount ActionCable.server => '/cable'
5) 最后,我尝试通过在我的应用程序的其他地方调用 send_data 函数来广播数据:
def send_current_question
App.quiz_data.send_data <--- this is apparently where the error gets thrown
end
我想知道的是:
- 如何解决这个错误?
- 是我没有正确建立socket连接的问题吗?
我已经阅读了 3 个 ActionCable 指南并观看了 2 个指南视频 - 因为它们中的大多数似乎都是关于聊天应用程序的(在我看来是一对一的连接),我现在完全不知道如何获得我的应用程序以我需要的一对多广播方式工作。
我是 Rails 的新手,如有任何指点,我们将不胜感激! :)
WebSockets 的设计使得无论您是在构建一对一、多对一、一对多还是多对多功能都无关紧要。原因是作为一个频道的发布者,你不知道谁订阅了它,所以无论是1个还是1000个用户,他们都会收到发布的消息。
至于为什么会出现错误,App
对象是一个 JavaScript 对象(请参阅您如何在 coffeescript 文件中访问它),因此您不能在 Ruby代码。
App
对象在您的 Ruby 代码(后端)中不可用,因为它应该是客户端(用户)与服务器(后端)通信的一种方法,这就是它在您的 coffeescript 代码中初始化的原因。
因此您需要通过将点击处理程序附加到您的按钮来调用 send_data 方法。假设在您的 html 中,您的按钮看起来像:
<button id="broadcast-button">Broadcast</button>
在您的客户端代码 (coffeescript) 中,您将执行以下操作:
$('#broadcast-button').on 'click', (e) ->
App.quiz_data.send_data()
所以当用户点击那个按钮时,send_data 方法将被调用。
As for why you are getting the error, well the App object is a JavaScript object (see how you are accessing it in your coffeescript files), so you cannot use it in Ruby code.
The App object is not made available in your Ruby code (backend) because it is supposed to be a method for the client (user) to communicate with the server (backend), which is why it is initialized in your coffeescript code.
正如@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 通道中的方法更改为 class 方法:
#/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)