Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 70 additions & 53 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
name: Main

on: [push,pull_request,workflow_dispatch]
on:
push:
branches: [main]
pull_request:
workflow_dispatch:

jobs:
build:
Expand All @@ -20,12 +24,12 @@ jobs:
strategy:
matrix:
ruby:
- 2.5.9
- 2.6.10
- 2.7.7
- 3.0.5
- 3.1.3
- 3.2.0
- 2.7.8
- 3.0.7
- 3.1.7
- 3.2.9
- 3.3.9
- 3.4.7
gemfile:
- gemfiles/activejob_4.2.x.gemfile
- gemfiles/activejob_5.2.x.gemfile
Expand All @@ -35,78 +39,91 @@ jobs:
- gemfiles/activejob_7.1.x.gemfile
- gemfiles/activejob_7.2.x.gemfile
- gemfiles/activejob_8.0.x.gemfile
- gemfiles/activejob_8.1.x.gemfile
- gemfiles/sidekiq_4.x.gemfile
- gemfiles/sidekiq_5.x.gemfile
- gemfiles/sidekiq_6.x.gemfile
- gemfiles/sidekiq_7.x.gemfile
exclude:
- ruby: 2.5.9
gemfile: gemfiles/activejob_7.0.x.gemfile
- ruby: 2.5.9
gemfile: gemfiles/activejob_7.1.x.gemfile
- ruby: 2.5.9
gemfile: gemfiles/activejob_7.2.x.gemfile
- ruby: 2.5.9
gemfile: gemfiles/activejob_8.0.x.gemfile
- ruby: 2.5.9
gemfile: gemfiles/sidekiq_6.x.gemfile
- ruby: 2.5.9
gemfile: gemfiles/sidekiq_7.x.gemfile
- ruby: 2.6.10
gemfile: gemfiles/activejob_7.0.x.gemfile
- ruby: 2.6.10
gemfile: gemfiles/activejob_7.1.x.gemfile
- ruby: 2.6.10
gemfile: gemfiles/activejob_7.2.x.gemfile
- ruby: 2.6.10
gemfile: gemfiles/activejob_8.0.x.gemfile
- ruby: 2.6.10
gemfile: gemfiles/sidekiq_6.x.gemfile
- ruby: 2.6.10
gemfile: gemfiles/sidekiq_7.x.gemfile
- ruby: 2.7.7
- ruby: 2.7.8
gemfile: gemfiles/activejob_4.2.x.gemfile
- ruby: 2.7.7
- ruby: 2.7.8
gemfile: gemfiles/activejob_7.2.x.gemfile
- ruby: 2.7.7
- ruby: 2.7.8
gemfile: gemfiles/activejob_8.0.x.gemfile
- ruby: 2.7.7
- ruby: 2.7.8
gemfile: gemfiles/activejob_8.1.x.gemfile
- ruby: 2.7.8
gemfile: gemfiles/sidekiq_4.x.gemfile
- ruby: 3.0.5
- ruby: 2.7.8
gemfile: gemfiles/sidekiq_8.x.gemfile
Copy link

Copilot AI Jan 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CI configuration references 'gemfiles/sidekiq_8.x.gemfile' in the exclusion matrix, but this gemfile doesn't exist in the repository. This will cause CI failures when the workflow tries to resolve these exclusions.

Copilot uses AI. Check for mistakes.
- ruby: 3.0.7
gemfile: gemfiles/activejob_4.2.x.gemfile
- ruby: 3.0.5
- ruby: 3.0.7
gemfile: gemfiles/activejob_5.2.x.gemfile
- ruby: 3.0.5
- ruby: 3.0.7
gemfile: gemfiles/activejob_7.2.x.gemfile
- ruby: 3.0.5
- ruby: 3.0.7
gemfile: gemfiles/activejob_8.0.x.gemfile
- ruby: 3.0.5
- ruby: 3.0.7
gemfile: gemfiles/activejob_8.1.x.gemfile
- ruby: 3.0.7
gemfile: gemfiles/sidekiq_4.x.gemfile
- ruby: 3.0.5
- ruby: 3.0.7
gemfile: gemfiles/sidekiq_5.x.gemfile
- ruby: 3.1.3
- ruby: 3.0.7
gemfile: gemfiles/sidekiq_8.x.gemfile
Copy link

Copilot AI Jan 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CI configuration references 'gemfiles/sidekiq_8.x.gemfile' in the exclusion matrix, but this gemfile doesn't exist in the repository. This will cause CI failures when the workflow tries to resolve these exclusions.

Copilot uses AI. Check for mistakes.
- ruby: 3.1.7
gemfile: gemfiles/activejob_4.2.x.gemfile
- ruby: 3.1.3
- ruby: 3.1.7
gemfile: gemfiles/activejob_5.2.x.gemfile
- ruby: 3.1.3
- ruby: 3.1.7
gemfile: gemfiles/activejob_6.0.x.gemfile
- ruby: 3.1.3
- ruby: 3.1.7
gemfile: gemfiles/activejob_8.0.x.gemfile
- ruby: 3.1.3
- ruby: 3.1.7
gemfile: gemfiles/activejob_8.1.x.gemfile
- ruby: 3.1.7
gemfile: gemfiles/sidekiq_4.x.gemfile
- ruby: 3.1.7
gemfile: gemfiles/sidekiq_5.x.gemfile
- ruby: 3.1.7
gemfile: gemfiles/sidekiq_8.x.gemfile
Copy link

Copilot AI Jan 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CI configuration references 'gemfiles/sidekiq_8.x.gemfile' in the exclusion matrix, but this gemfile doesn't exist in the repository. This will cause CI failures when the workflow tries to resolve these exclusions.

Copilot uses AI. Check for mistakes.
- ruby: 3.2.9
gemfile: gemfiles/activejob_4.2.x.gemfile
- ruby: 3.2.9
gemfile: gemfiles/activejob_5.2.x.gemfile
- ruby: 3.2.9
gemfile: gemfiles/activejob_6.0.x.gemfile
- ruby: 3.2.9
gemfile: gemfiles/activejob_7.1.x.gemfile
- ruby: 3.2.9
gemfile: gemfiles/sidekiq_4.x.gemfile
- ruby: 3.2.9
gemfile: gemfiles/sidekiq_5.x.gemfile
- ruby: 3.3.9
gemfile: gemfiles/activejob_4.2.x.gemfile
- ruby: 3.3.9
gemfile: gemfiles/activejob_5.2.x.gemfile
- ruby: 3.3.9
gemfile: gemfiles/activejob_6.0.x.gemfile
- ruby: 3.3.9
gemfile: gemfiles/activejob_7.1.x.gemfile
- ruby: 3.3.9
gemfile: gemfiles/sidekiq_4.x.gemfile
- ruby: 3.1.3
- ruby: 3.3.9
gemfile: gemfiles/sidekiq_5.x.gemfile
- ruby: 3.2.0
- ruby: 3.4.7
gemfile: gemfiles/activejob_4.2.x.gemfile
- ruby: 3.2.0
- ruby: 3.4.7
gemfile: gemfiles/activejob_5.2.x.gemfile
- ruby: 3.2.0
- ruby: 3.4.7
gemfile: gemfiles/activejob_6.0.x.gemfile
- ruby: 3.2.0
- ruby: 3.4.7
gemfile: gemfiles/activejob_7.1.x.gemfile
- ruby: 3.2.0
- ruby: 3.4.7
gemfile: gemfiles/sidekiq_4.x.gemfile
- ruby: 3.2.0
- ruby: 3.3.9
Copy link

Copilot AI Jan 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This exclusion incorrectly uses ruby 3.3.9 but should use ruby 3.4.7, as indicated by the previous exclusions for Ruby 3.4.7. This is likely a copy-paste error.

Suggested change
- ruby: 3.3.9
- ruby: 3.4.7

Copilot uses AI. Check for mistakes.
gemfile: gemfiles/sidekiq_5.x.gemfile

steps:
Expand Down
14 changes: 12 additions & 2 deletions .rubocop.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
require: rubocop-rspec
plugins:
- rubocop-rspec

AllCops:
TargetRubyVersion: 2.5
TargetRubyVersion: 2.7
NewCops: enable
Exclude:
- gemfiles/**/*
Expand Down Expand Up @@ -66,3 +67,12 @@ RSpec/AnyInstance:
RSpec/LetSetup:
Exclude:
- spec/active_job/uniqueness/sidekiq_patch_spec.rb

RSpec/IncludeExamples:
Enabled: false

Naming/PredicateMethod:
Enabled: false

Lint/FloatComparison:
Enabled: false
12 changes: 8 additions & 4 deletions Appraisals
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ appraise 'activejob-5.2.x' do
end

appraise 'activejob-6.0.x' do
gem 'activejob', '~> 6.0.5'
gem 'activejob', '~> 6.0.6'
end

appraise 'activejob-6.1.x' do
gem 'activejob', '~> 6.1.6'
gem 'activejob', '~> 6.1.7'
end

appraise 'activejob-7.0.x' do
gem 'activejob', '~> 7.0.3'
gem 'activejob', '~> 7.0.8'
end

appraise 'activejob-7.1.x' do
Expand All @@ -32,6 +32,10 @@ appraise 'activejob-8.0.x' do
gem 'activejob', '>= 8.0.0.rc1', '< 8.1'
end

appraise 'activejob-8.1.x' do
gem 'activejob', '>= 8.1.0.rc1', '< 8.2'
end

appraise 'sidekiq-4.x' do
gem 'sidekiq', '~> 4.2'
gem 'activejob', '~> 5.2'
Expand All @@ -47,5 +51,5 @@ appraise 'sidekiq-6.x' do
end

appraise 'sidekiq-7.x' do
gem 'sidekiq', '~> 7.0'
gem 'sidekiq', '~> 7.3'
Copy link

Copilot AI Jan 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Appraisals file specifies sidekiq version ~> 7.3, but the generated gemfile explicitly excludes version 7.3.9. These should be consistent - either update the Appraisals file to match the exclusion pattern or remove the exclusion from the gemfile.

Suggested change
gem 'sidekiq', '~> 7.3'
gem 'sidekiq', '~> 7.3', '!= 7.3.9'

Copilot uses AI. Check for mistakes.
end
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

## [Unreleased](https://github.com/veeqo/activejob-uniqueness/compare/v0.4.0...HEAD)

- [#91](https://github.com/veeqo/activejob-uniqueness/pull/91) Add ActiveJob 8.1 support by [@viralpraxis](https://github.com/viralpraxis)

## [0.4.0](https://github.com/veeqo/activejob-uniqueness/compare/v0.3.2...v0.4.0) - 2024-12-07

Expand Down
11 changes: 9 additions & 2 deletions activejob-uniqueness.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,20 @@ Gem::Specification.new do |spec|

spec.required_ruby_version = '>= 2.5'

spec.add_dependency 'activejob', '>= 4.2', '< 8.1'
spec.add_dependency 'activejob', '>= 4.2', '< 8.2'
spec.add_dependency 'activesupport', '>= 4.2', '< 8.2'
spec.add_dependency 'redlock', '>= 2.0', '< 3'

spec.add_development_dependency 'mutex_m'
spec.add_development_dependency 'bigdecimal'
spec.add_development_dependency 'logger'
spec.add_development_dependency 'base64'
spec.add_development_dependency 'appraisal', '~> 2.3.0'
spec.add_development_dependency 'bundler', '>= 2.0'
spec.add_development_dependency 'pry-byebug', '> 3.6.0'
spec.add_development_dependency 'rspec', '~> 3.0'
spec.add_development_dependency 'rubocop', '~> 1.28'
spec.add_development_dependency 'rubocop-rspec', '~> 2.10'
spec.add_development_dependency 'rubocop-rspec', '~> 3.7'
spec.add_development_dependency 'readline'
spec.add_development_dependency 'irb'
end
2 changes: 1 addition & 1 deletion gemfiles/activejob_6.0.x.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@

source "https://rubygems.org"

gem "activejob", "~> 6.0.5"
gem "activejob", "~> 6.0.6"

gemspec path: "../"
2 changes: 1 addition & 1 deletion gemfiles/activejob_6.1.x.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@

source "https://rubygems.org"

gem "activejob", "~> 6.1.6"
gem "activejob", "~> 6.1.7"

gemspec path: "../"
2 changes: 1 addition & 1 deletion gemfiles/activejob_7.0.x.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@

source "https://rubygems.org"

gem "activejob", "~> 7.0.3"
gem "activejob", "~> 7.0.8"

gemspec path: "../"
7 changes: 7 additions & 0 deletions gemfiles/activejob_8.1.x.gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# This file was generated by Appraisal

source "https://rubygems.org"

gem "activejob", ">= 8.1.0.rc1", "< 8.2"

gemspec path: "../"
2 changes: 1 addition & 1 deletion gemfiles/sidekiq_7.x.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@

source "https://rubygems.org"

gem "sidekiq", "~> 7.0"
gem "sidekiq", "~> 7.0", "!= 7.3.9"

gemspec path: "../"
2 changes: 2 additions & 0 deletions lib/active_job/uniqueness.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# frozen_string_literal: true

require 'logger'

require 'active_job'
require 'redlock'

Expand Down
62 changes: 32 additions & 30 deletions lib/active_job/uniqueness/configuration.rb
Original file line number Diff line number Diff line change
@@ -1,52 +1,54 @@
# frozen_string_literal: true

require 'openssl'

module ActiveJob
module Uniqueness
# Use /config/initializer/activejob_uniqueness.rb to configure ActiveJob::Uniqueness
# Use config/initializer/activejob_uniqueness.rb to configure ActiveJob::Uniqueness
Copy link

Copilot AI Jan 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The path in the comment has been updated from '/config/initializer/activejob_uniqueness.rb' to 'config/initializer/activejob_uniqueness.rb' (removed leading slash). However, this should actually be 'config/initializers/activejob_uniqueness.rb' (note 'initializers' is plural) to match the standard Rails conventions.

Suggested change
# Use config/initializer/activejob_uniqueness.rb to configure ActiveJob::Uniqueness
# Use config/initializers/activejob_uniqueness.rb to configure ActiveJob::Uniqueness

Copilot uses AI. Check for mistakes.
#
# ActiveJob::Uniqueness.configure do |c|
# c.lock_ttl = 3.hours
# end
#
class Configuration
include ActiveSupport::Configurable

config_accessor(:lock_ttl) { 86_400 } # 1.day
config_accessor(:lock_prefix) { 'activejob_uniqueness' }
config_accessor(:on_conflict) { :raise }
config_accessor(:on_redis_connection_error) { :raise }
config_accessor(:redlock_servers) { [ENV.fetch('REDIS_URL', 'redis://localhost:6379')] }
config_accessor(:redlock_options) { { retry_count: 0 } }
config_accessor(:lock_strategies) { {} }

config_accessor(:digest_method) do
require 'openssl'
OpenSSL::Digest::MD5
end
module Validations
def on_conflict=(action)
validate_on_conflict_action!(action)

def on_conflict=(action)
validate_on_conflict_action!(action)
super
end

config.on_conflict = action
end
def validate_on_conflict_action!(action)
return if action.nil? || %i[log raise].include?(action) || action.respond_to?(:call)

def validate_on_conflict_action!(action)
return if action.nil? || %i[log raise].include?(action) || action.respond_to?(:call)
raise ActiveJob::Uniqueness::InvalidOnConflictAction, "Unexpected '#{action}' action on conflict"
end

raise ActiveJob::Uniqueness::InvalidOnConflictAction, "Unexpected '#{action}' action on conflict"
end
def on_redis_connection_error=(action)
validate_on_redis_connection_error!(action)

def on_redis_connection_error=(action)
validate_on_redis_connection_error!(action)
super
end

config.on_redis_connection_error = action
def validate_on_redis_connection_error!(action)
return if action.nil? || action == :raise || action.respond_to?(:call)

raise ActiveJob::Uniqueness::InvalidOnConflictAction,
"Unexpected '#{action}' action on_redis_connection_error"
end
end

def validate_on_redis_connection_error!(action)
return if action.nil? || action == :raise || action.respond_to?(:call)
class_attribute :lock_ttl, default: 86_400
class_attribute :lock_prefix, default: 'activejob_uniqueness'
class_attribute :on_conflict, default: :raise
class_attribute :on_redis_connection_error, default: :raise
class_attribute :redlock_servers, default: [ENV.fetch('REDIS_URL', 'redis://localhost:6379')]
class_attribute :redlock_options, default: { retry_count: 0 }
class_attribute :lock_strategies, default: {}

raise ActiveJob::Uniqueness::InvalidOnConflictAction, "Unexpected '#{action}' action on_redis_connection_error"
end
class_attribute :digest_method, default: OpenSSL::Digest::MD5

Copy link

Copilot AI Jan 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using class_attribute with instance creation of Configuration may not work as expected. The class_attribute macro is designed for class-level attributes, but here Configuration instances are being created with Configuration.new. The old ActiveSupport::Configurable provided instance-level configuration through config_accessor, which creates instance methods. With class_attribute, all instances will share the same class-level values, which means changes to one Configuration instance will affect all instances. This is likely a breaking change that could cause unexpected behavior in applications that create multiple configuration instances.

Suggested change
# Instance-level accessors defaulting to class-level configuration.
#
# This allows multiple Configuration instances to have independent values
# while preserving the existing class-level API.
def lock_ttl
defined?(@lock_ttl) && !@lock_ttl.nil? ? @lock_ttl : self.class.lock_ttl
end
def lock_ttl=(value)
@lock_ttl = value
end
def lock_prefix
defined?(@lock_prefix) && !@lock_prefix.nil? ? @lock_prefix : self.class.lock_prefix
end
def lock_prefix=(value)
@lock_prefix = value
end
def on_conflict
defined?(@on_conflict) && !@on_conflict.nil? ? @on_conflict : self.class.on_conflict
end
def on_conflict=(value)
@on_conflict = value
end
def on_redis_connection_error
defined?(@on_redis_connection_error) && !@on_redis_connection_error.nil? ?
@on_redis_connection_error : self.class.on_redis_connection_error
end
def on_redis_connection_error=(value)
@on_redis_connection_error = value
end
def redlock_servers
defined?(@redlock_servers) && !@redlock_servers.nil? ? @redlock_servers : self.class.redlock_servers
end
def redlock_servers=(value)
@redlock_servers = value
end
def redlock_options
defined?(@redlock_options) && !@redlock_options.nil? ? @redlock_options : self.class.redlock_options
end
def redlock_options=(value)
@redlock_options = value
end
def lock_strategies
defined?(@lock_strategies) && !@lock_strategies.nil? ? @lock_strategies : self.class.lock_strategies
end
def lock_strategies=(value)
@lock_strategies = value
end
def digest_method
defined?(@digest_method) && !@digest_method.nil? ? @digest_method : self.class.digest_method
end
def digest_method=(value)
@digest_method = value
end

Copilot uses AI. Check for mistakes.
prepend Validations
end
end
end
Loading