ActiveJob:如果没有完整的工作类,如何进行简单的操作?

时间:2015-06-21 08:53:46

标签: ruby-on-rails ruby ruby-on-rails-4 delayed-job rails-activejob

使用delayed_job,我能够执行这样的简单操作:

@foo.delay.increment!(:myfield)

是否有可能对Rails的新ActiveJob做同样的事情?(没有创建一大堆执行这些小操作的作业类)

3 个答案:

答案 0 :(得分:1)

据我所知,目前尚不支持此功能。您可以使用接受模型或实例的自定义代理作业,要执行的方法以及参数列表轻松模拟此功能。

但是,为了代码测试和可维护性,这种快捷方式不是一个好方法。它更有效(即使您需要编写更多代码),以便为您要排队的所有内容提供特定的工作。它迫使您更多地考虑应用程序的设计。

答案 1 :(得分:1)

ActiveJob 仅仅是各种后台作业处理器之上的抽象,因此许多功能取决于您实际使用的提供商。但我会尽量不依赖任何后端。

通常,职位提供者由持久性机制运行者组成。卸载作业时,以某种方式将其写入持久性机制,然后其中一个运行程序检索并运行它。所以问题是:你能否以一种格式表达你的工作数据,与你需要的任何行动兼容?

这将是棘手的。

然后让我们定义什么是作业定义。例如,它可以是单个方法调用。假设有这样的语法:

Model.find(42).delay.foo(1, 2)

我们可以使用以下格式:

{
  class: 'Model',
  id: '42', # whatever
  method: 'foo',
  args: [
    1, 2
  ]
}

现在我们如何从给定的调用构建这样的哈希并将其排入作业队列?

首先,如图所示,我们需要定义一个具有method_missing的类来捕获被调用的方法名称:

class JobMacro
  attr_accessor :data
  def initialize(record = nil)
    self.data = {}
    if record.present?
      self.data[:class] = record.class.to_s
      self.data[:id]    = record.id
    end
  end
  def method_missing(action, *args)
    self.data[:method] = action.to_s
    self.data[:args] = args
    GenericJob.perform_later(data)
  end
end

作业本身必须像这样重建那个表达式:

data[:class].constantize.find(data[:id]).public_send(data[:method], *data[:args])

当然,您必须在模型上定义delay宏。最好将它分解为一个模块,因为定义非常通用:

def delay
  JobMacro.new(self)
end

确实有一些限制:

  • 仅支持在持久性ActiveRecord模型上运行作业。一个工作需要一种方法来重建被调用方来调用方法,我选择了最可能的方法。如果需要,您也可以使用编组,但我认为这是不可靠的:在作业执行时,未编组的对象可能无效。 “GlobalID”也是如此。
  • 它使用Ruby的反射。对于许多问题来说,这是一个诱人的解决方案,但它并不快,在安全性方面有点冒险。所以谨慎使用这种方法。
  • 只有一个方法调用。没有过滤器(你可以用ruby2ruby gem做到这一点)。依靠作业提供程序正确地序列化参数,如果失败,请使用您自己的代码来帮助它。例如,que在内部使用JSON,因此无论在JSON中工作,都适用于que。例如,符号没有。

一开始,事情会以惊人的方式破裂 因此,请务必在开始之前设置调试工具。

这方面的一个例子是Sidekiq's backward (Delayed::Job) compatibility extension for ActiveRecord

答案 2 :(得分:0)

我写了一个可以帮助你https://github.com/cristianbica/activejob-perform_later的宝石。但请注意,我相信在您的代码中使用可能在工作程序中执行的方法是灾难的完美配方并未得到谨慎处理:)