-
Notifications
You must be signed in to change notification settings - Fork 43
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Infer interruption handler from a job's queue adapter
...and allow Iteration to be used with multiple job backends simultaneously Removed test_mark_job_worker_as_interrupted since it was testing stubs Co-authored-by: Justin Morris <[email protected]>
- Loading branch information
1 parent
a8422fa
commit 9248dda
Showing
11 changed files
with
145 additions
and
103 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
# frozen_string_literal: true | ||
|
||
module JobIteration | ||
module Integrations # @private | ||
LoadError = Class.new(StandardError) | ||
|
||
autoload :Sidekiq, "job-iteration/integrations/sidekiq" | ||
autoload :Resque, "job-iteration/integrations/resque" | ||
|
||
extend self | ||
|
||
def load(job) | ||
camelized_name = job.class.queue_adapter_name.camelize | ||
object = const_get(camelized_name) | ||
|
||
if object.respond_to?(:call) | ||
object | ||
else | ||
raise LoadError, "Integration for '#{camelized_name}' must respond to call" | ||
end | ||
rescue NameError | ||
raise LoadError, "Could not find integration for '#{camelized_name}'" | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,55 +1,84 @@ | ||
# frozen_string_literal: true | ||
|
||
require "test_helper" | ||
require "open3" | ||
|
||
class IntegrationsTest < ActiveSupport::TestCase | ||
test "will prevent loading two integrations" do | ||
with_env("ITERATION_DISABLE_AUTOCONFIGURE", nil) do | ||
rubby = <<~RUBBY | ||
require 'bundler/setup' | ||
require 'job-iteration' | ||
RUBBY | ||
_stdout, stderr, status = run_ruby(rubby) | ||
|
||
assert_equal false, status.success? | ||
assert_match(/resque integration has already been loaded, but sidekiq is also available/, stderr) | ||
|
||
class IntegrationsTest < IterationUnitTest | ||
class IterationJob < ActiveJob::Base | ||
include JobIteration::Iteration | ||
|
||
def build_enumerator(cursor:) | ||
enumerator_builder.build_once_enumerator(cursor: cursor) | ||
end | ||
|
||
def each_iteration(*) | ||
end | ||
end | ||
|
||
class ResqueJob < IterationJob | ||
self.queue_adapter = :resque | ||
end | ||
|
||
class SidekiqJob < IterationJob | ||
self.queue_adapter = :sidekiq | ||
end | ||
|
||
class StubOneAdapter | ||
class << self | ||
def enqueue(*); end | ||
def enqueue_at(*); end | ||
end | ||
end | ||
|
||
test "successfully loads one (resque) integration" do | ||
with_env("ITERATION_DISABLE_AUTOCONFIGURE", nil) do | ||
rubby = <<~RUBBY | ||
require 'bundler/setup' | ||
# Remove sidekiq, only resque will be left | ||
$LOAD_PATH.delete_if { |p| p =~ /sidekiq/ } | ||
require 'job-iteration' | ||
RUBBY | ||
_stdout, _stderr, status = run_ruby(rubby) | ||
class StubOneJob < IterationJob | ||
self.queue_adapter = StubOneAdapter | ||
end | ||
|
||
assert_equal true, status.success? | ||
module ::ActiveJob | ||
module QueueAdapters | ||
class StubTwoAdapter | ||
def enqueue(*); end | ||
def enqueue_at(*); end | ||
end | ||
end | ||
end | ||
|
||
private | ||
class StubTwoJob < IterationJob | ||
self.queue_adapter = :stub_two | ||
end | ||
|
||
test "loads multiple integrations" do | ||
resque_job = ResqueJob.new.serialize | ||
ActiveJob::Base.execute(resque_job) | ||
|
||
sidekiq_job = SidekiqJob.new.serialize | ||
ActiveJob::Base.execute(sidekiq_job) | ||
end | ||
|
||
def run_ruby(body) | ||
stdout, stderr, status = nil | ||
Tempfile.open do |f| | ||
f.write(body) | ||
f.close | ||
test "default interruption adapter loader raises for unknown Active Job queue adapter names" do | ||
error1 = assert_raises(JobIteration::Integrations::LoadError) do | ||
JobIteration::Integrations.load(StubOneJob.new) | ||
end | ||
assert_equal("Integration for 'Class' must respond to call", error1.message) | ||
|
||
command = "ruby #{f.path}" | ||
stdout, stderr, status = Open3.capture3(command) | ||
error2 = assert_raises(JobIteration::Integrations::LoadError) do | ||
JobIteration::Integrations.load(StubTwoJob.new) | ||
end | ||
[stdout, stderr, status] | ||
assert_equal("Could not find integration for 'StubTwo'", error2.message) | ||
end | ||
|
||
def with_env(variable, value) | ||
original = ENV[variable] | ||
ENV[variable] = value | ||
yield | ||
test "override default interruption adapter loader" do | ||
old_loader = JobIteration.interruption_adapter_loader | ||
JobIteration.interruption_adapter_loader = Module.new do | ||
module_function | ||
|
||
def load(_job) | ||
-> { false } | ||
end | ||
end | ||
|
||
stub_job = StubOneJob.new.serialize | ||
ActiveJob::Base.execute(stub_job) | ||
ensure | ||
ENV[variable] = original | ||
JobIteration.interruption_adapter_loader = old_loader | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters