0

为这个问题命名非常困难,但本质上,我的 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 教程,以找到任何使用服务对象来接收消息的人,但一无所获。求救!

4

0 回答 0