lib/sidekiq_unique_jobs/lock_args.rb
# frozen_string_literal: true
module SidekiqUniqueJobs
# Handles uniqueness of sidekiq arguments
#
# @author Mikael Henriksson <mikael@mhenrixon.com>
class LockArgs
include SidekiqUniqueJobs::Logging
include SidekiqUniqueJobs::SidekiqWorkerMethods
include SidekiqUniqueJobs::JSON
# Convenience method for returning a digest
# @param [Hash] item a Sidekiq job hash
# @return [String] a unique digest
def self.call(item)
new(item).lock_args
end
# The sidekiq job hash
# @return [Hash] the Sidekiq job hash
attr_reader :item
#
# @!attribute [r] args
# @return [Array<Objet>] the arguments passed to `perform_async`
attr_reader :args
# @param [Hash] item a Sidekiq job hash
def initialize(item)
@item = item
@args = item[ARGS]
self.job_class = item[CLASS]
end
# The unique arguments to use for creating a lock
# @return [Array] the arguments filters by the {#filtered_args} method if {#lock_args_enabled?}
def lock_args
@lock_args ||= filtered_args || []
end
# Checks if the worker class has enabled lock_args
# @return [true, false]
def lock_args_enabled?
# return false unless lock_args_method_valid?
lock_args_method
end
# Validate that the lock_args_method is acceptable
# @return [true, false]
def lock_args_method_valid?
[NilClass, TrueClass, FalseClass].none? { |klass| lock_args_method.is_a?(klass) }
end
# Checks if the worker class has disabled lock_args
# @return [true, false]
def lock_args_disabled?
!lock_args_method
end
# Filters unique arguments by proc or symbol
# @return [Array] {#filter_by_proc} when {#lock_args_method} is a Proc
# @return [Array] {#filter_by_symbol} when {#lock_args_method} is a Symbol
# @return [Array] args unfiltered when neither of the above
def filtered_args
return args if lock_args_disabled?
json_args = Normalizer.jsonify(args)
case lock_args_method
when Proc
filter_by_proc(json_args)
when Symbol
filter_by_symbol(json_args)
end
end
# Filters unique arguments by proc configured in the sidekiq worker
# @param [Array] args the arguments passed to the sidekiq worker
# @return [Array] with the filtered arguments
def filter_by_proc(args)
lock_args_method.call(args)
end
# Filters unique arguments by method configured in the sidekiq worker
# @param [Array] args the arguments passed to the sidekiq worker
# @return [Array] unfiltered unless {#job_method_defined?}
# @return [Array] with the filtered arguments
def filter_by_symbol(args)
return args unless job_method_defined?(lock_args_method)
job_class.send(lock_args_method, args)
rescue ArgumentError
raise SidekiqUniqueJobs::InvalidUniqueArguments,
given: args,
job_class: job_class,
lock_args_method: lock_args_method
end
# The method to use for filtering unique arguments
def lock_args_method
@lock_args_method ||= job_options.slice(LOCK_ARGS_METHOD, UNIQUE_ARGS_METHOD).values.first
@lock_args_method ||= :lock_args if job_method_defined?(:lock_args)
@lock_args_method ||= :unique_args if job_method_defined?(:unique_args)
@lock_args_method ||= default_lock_args_method
end
# The global worker options defined in Sidekiq directly
def default_lock_args_method
default_job_options[LOCK_ARGS_METHOD] ||
default_job_options[UNIQUE_ARGS_METHOD]
end
#
# The globally default worker options configured from Sidekiq
#
#
# @return [Hash<String, Object>]
#
def default_job_options
@default_job_options ||= if Sidekiq.respond_to?(:default_job_options)
Sidekiq.default_job_options.stringify_keys
else
Sidekiq.default_worker_options.stringify_keys
end
end
end
end