为这个问题命名非常困难,但本质上,我的 Rails 应用程序中有一个服务对象,它为 Resume Processing 创建了一个流程。我正在尝试使用 ActionCable 将我的前端与后端连接起来。我这样做的当前方法是在我的控制器中实例化我的服务对象:
def create_from_resume
...
ResumeParseService.new(@candidate, current_user)
end
然后我的服务开始向我的前端广播以打开相应的模式:
服务:
class ResumeParseService
attr_reader :user
attr_reader :employee
attr_reader :candidate
def initialize(candidate, user)
@user = user
@employee = user.employee
@candidate = candidate
@progress = 0
--> broadcast_begin
end
def begin_from_parse_modal
broadcast_progress(10)
parsed_resume = get_a_resume_while_hiding_implementation_details
broadcast_progress(rand(40..60))
...
broadcast_progress(100 - @progress)
...
end
private
def broadcast_begin
ResumeParseChannel.broadcast_and_set_service(self, user, {
event_name: 'transition_screen',
props: {
to: 'parse',
},
})
end
def broadcast_progress(addition)
@progress += addition
ResumeParseChannel.broadcast_to(user, {
event_name: 'progress',
props: {
progress: @progress,
},
})
end
def broadcast_transition_screen(screen_name, body = nil)
ResumeParseChannel.broadcast_to(user, {
event_name: 'transition_screen',
props: {
to: screen_name,
data: body,
},
})
end
end
导轨通道:
# frozen_string_literal: true
class ResumeParseChannel < ApplicationCable::Channel
def subscribed
stream_for(current_user)
end
def unsubscribed
# Any cleanup needed when channel is unsubscribed
end
def self.broadcast_and_set_service(service, *args)
@service = service
broadcast_to *args
end
def screen_transitioned(data)
case data['screen_name']
when 'parse'
pp @service
@service.begin_from_parse_modal
else
# type code here
end
end
private
def current_user
if (current_user = env["warden"].user)
current_user
else
reject_unauthorized_connection
end
end
end
然后由我的频道负责。稍后,我的频道会发回一个“进度更新”,让我的服务知道模式打开成功: JS Channel:
consumer.subscriptions.create(
{ channel: "ResumeParseChannel" },
{
connected() {
document.addEventListener("resume-parse:screen_transitioned", event =>
--> this.perform("screen_transitioned", event.detail)
);
},
}
);
现在,我的问题是,一旦该消息被发送回我的(ruby)通道,我想不出一种方法让它找到我现有的服务对象实例并使用它。如您所见,我尝试在第一次广播时使用服务对象实例在频道上设置实例 var,但这(以及其他一百万件事)不起作用。一旦我得到带有'parse'的screen_name的'screen_transitioned',我需要调用#begin_from_parse_modal。理想情况下,我想尽可能地分离广播逻辑和解析逻辑。
我知道频道的实例可以被认为是实际的订阅,但我只是不明白一个系统的最佳实践是什么,我可以发送一个“做这个”消息,然后在我得到后做某事“完成”消息。
如果我在解释和/或代码方面遗漏了什么,请告诉我。也请随时让我知道下次我提出问题时是否应该做一些不同的事情!这是我第一次在 stackoverflow 上提问,但这是我第十亿次寻找答案 :)
编辑:我仍然对这种看似常见的情况感到目瞪口呆。仅仅让通道充当服务对象可能是最佳实践吗?如果是这样,我们将如何在其上存储状态?我能想到的以任何形式工作的唯一可能方法是在每个 WS 消息中发送完整状态。或者至少是每个处于状态的记录的 id,然后在每条消息上查找每条记录。这似乎过于复杂和昂贵。我搜索了其他问题,甚至是 ActionCable 教程,以找到任何使用服务对象来接收消息的人,但一无所获。求救!