-
-
Notifications
You must be signed in to change notification settings - Fork 277
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Provide some configuration DSL for custom Strategies and Locks #383
Changes from 4 commits
5991f30
f6a45bb
f0d7cf9
e747b4e
bd07aa0
0582984
2e99f64
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
# frozen_string_literal: true | ||
|
||
module SidekiqUniqueJobs | ||
# Shared class for dealing with gem configuration | ||
# | ||
# @author Mauro Berlanda <mauro.berlanda@gmail.com> | ||
class Config < Concurrent::MutableStruct.new( | ||
:default_lock_timeout, | ||
:enabled, | ||
:unique_prefix, | ||
:logger, | ||
:locks, | ||
:strategies, | ||
) | ||
DEFAULT_LOCKS = { | ||
until_and_while_executing: SidekiqUniqueJobs::Lock::UntilAndWhileExecuting, | ||
until_executed: SidekiqUniqueJobs::Lock::UntilExecuted, | ||
until_executing: SidekiqUniqueJobs::Lock::UntilExecuting, | ||
until_expired: SidekiqUniqueJobs::Lock::UntilExpired, | ||
until_timeout: SidekiqUniqueJobs::Lock::UntilExpired, | ||
while_executing: SidekiqUniqueJobs::Lock::WhileExecuting, | ||
while_executing_reject: SidekiqUniqueJobs::Lock::WhileExecutingReject, | ||
}.freeze | ||
|
||
DEFAULT_STRATEGIES = { | ||
log: SidekiqUniqueJobs::OnConflict::Log, | ||
raise: SidekiqUniqueJobs::OnConflict::Raise, | ||
reject: SidekiqUniqueJobs::OnConflict::Reject, | ||
replace: SidekiqUniqueJobs::OnConflict::Replace, | ||
reschedule: SidekiqUniqueJobs::OnConflict::Reschedule, | ||
}.freeze | ||
|
||
class << self | ||
def default | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The only thing I immediately don't like is I like having the |
||
new( | ||
0, | ||
true, | ||
"uniquejobs", | ||
Sidekiq.logger, | ||
DEFAULT_LOCKS, | ||
DEFAULT_STRATEGIES, | ||
) | ||
end | ||
end | ||
|
||
def add_lock(name, klass) | ||
raise ArgumentError, "Lock #{name} already defined, please use another name" if locks.key?(name.to_sym) | ||
|
||
new_locks = locks.dup.merge(name.to_sym => klass).freeze | ||
self.locks = new_locks | ||
end | ||
|
||
def add_strategy(name, klass) | ||
raise ArgumentError, "strategy #{name} already defined, please use another name" if strategies.key?(name.to_sym) | ||
|
||
new_strategies = strategies.dup.merge(name.to_sym => klass).freeze | ||
self.strategies = new_strategies | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,17 +15,14 @@ module SidekiqUniqueJobs | |
# @author Mikael Henriksson <mikael@zoolutions.se> | ||
# | ||
module OnConflict | ||
STRATEGIES = { | ||
log: OnConflict::Log, | ||
raise: OnConflict::Raise, | ||
reject: OnConflict::Reject, | ||
replace: OnConflict::Replace, | ||
reschedule: OnConflict::Reschedule, | ||
}.freeze | ||
# A convenience method for using the configured strategies | ||
def self.strategies | ||
SidekiqUniqueJobs.strategies | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've used delegation a lot in many places but I honestly prefer a defined method and was planning on refactoring in this direction in some places 👍 |
||
end | ||
|
||
# returns OnConflict::NullStrategy when no other could be found | ||
def self.find_strategy(strategy) | ||
STRATEGIES.fetch(strategy.to_s.to_sym) { OnConflict::NullStrategy } | ||
strategies.fetch(strategy.to_s.to_sym) { OnConflict::NullStrategy } | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
# frozen_string_literal: true | ||
|
||
require "spec_helper" | ||
|
||
RSpec.describe SidekiqUniqueJobs::Config do | ||
describe ".locks" do | ||
let(:config) { described_class.default } | ||
|
||
context "when using default config" do | ||
it "falls back on default option" do | ||
expect(config.locks).to eq(SidekiqUniqueJobs::Config::DEFAULT_LOCKS) | ||
end | ||
end | ||
|
||
context "when trying to add an already existing lock" do | ||
it "raises an ArgumentError exception" do | ||
name = "while_executing" | ||
expect do | ||
config.add_lock name, Class | ||
end.to raise_exception(ArgumentError, /#{name} already defined/) | ||
end | ||
end | ||
|
||
context "when adding a new lock" do | ||
it "preserves it in the config instance" do | ||
name = "some_lock" | ||
klass = Class | ||
|
||
original_locks_id = config.locks.object_id | ||
config.add_lock name, klass | ||
|
||
expect(config.locks.frozen?).to be(true) | ||
expect(config.locks.keys).to include(:some_lock) | ||
expect(config.locks.fetch(:some_lock)).to eq(Class) | ||
expect(config.locks.object_id).not_to eq(original_locks_id) | ||
end | ||
|
||
it "accepts as many locks as you want" do | ||
CustomLock1 = Class.new | ||
CustomLock2 = Class.new | ||
|
||
config.add_lock :custom_lock1, CustomLock1 | ||
config.add_lock :custom_lock2, CustomLock2 | ||
|
||
expect(config.locks.frozen?).to be(true) | ||
expect(config.locks.keys).to include(:custom_lock1, :custom_lock2) | ||
expect(config.locks.fetch(:custom_lock1)).to eq(CustomLock1) | ||
expect(config.locks.fetch(:custom_lock2)).to eq(CustomLock2) | ||
end | ||
end | ||
end | ||
|
||
describe ".strategies" do | ||
let(:config) { described_class.default } | ||
|
||
context "when using default config" do | ||
it "falls back on default option" do | ||
expect(config.strategies).to eq(SidekiqUniqueJobs::Config::DEFAULT_STRATEGIES) | ||
end | ||
end | ||
|
||
context "when trying to add an already existing lock" do | ||
it "raises an ArgumentError exception" do | ||
name = "log" | ||
expect do | ||
config.add_strategy name, Class | ||
end.to raise_exception(ArgumentError, /#{name} already defined/) | ||
end | ||
end | ||
|
||
context "when adding a new strategy" do | ||
it "preserves it in the config instance" do | ||
name = "some_strategy" | ||
klass = Class | ||
|
||
original_strategies_id = config.strategies.object_id | ||
config.add_strategy name, klass | ||
|
||
expect(config.strategies.frozen?).to be(true) | ||
expect(config.strategies.keys).to include(:some_strategy) | ||
expect(config.strategies.fetch(:some_strategy)).to eq(Class) | ||
expect(config.strategies.object_id).not_to eq(original_strategies_id) | ||
end | ||
|
||
it "accepts as many strategies as you want" do | ||
CustomStrategy1 = Class.new | ||
CustomStrategy2 = Class.new | ||
|
||
config.add_strategy "custom_strategy1", CustomStrategy1 | ||
config.add_strategy :custom_strategy2, CustomStrategy2 | ||
|
||
expect(config.strategies.frozen?).to be(true) | ||
expect(config.strategies.keys).to include(:custom_strategy1, :custom_strategy2) | ||
expect(config.strategies.fetch(:custom_strategy1)).to eq(CustomStrategy1) | ||
expect(config.strategies.fetch(:custom_strategy2)).to eq(CustomStrategy2) | ||
end | ||
end | ||
end | ||
|
||
# Test backported from spec/unit/on_conflict_spec.rb | ||
describe "::DEFAULT_STRATEGIES" do | ||
subject { described_class::DEFAULT_STRATEGIES } | ||
|
||
let(:expected) do | ||
{ | ||
log: SidekiqUniqueJobs::OnConflict::Log, | ||
raise: SidekiqUniqueJobs::OnConflict::Raise, | ||
reject: SidekiqUniqueJobs::OnConflict::Reject, | ||
replace: SidekiqUniqueJobs::OnConflict::Replace, | ||
reschedule: SidekiqUniqueJobs::OnConflict::Reschedule, | ||
} | ||
end | ||
|
||
it { is_expected.to eq(expected) } | ||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the move to a separate file now that it turned complex again! 👍