sidekiq-delay_extensions 7.0.0 → 7.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Changes.md +17 -0
- data/README.md +2 -0
- data/lib/sidekiq/delay_extensions/action_mailer.rb +19 -9
- data/lib/sidekiq/delay_extensions/active_record.rb +12 -10
- data/lib/sidekiq/delay_extensions/api.rb +3 -2
- data/lib/sidekiq/delay_extensions/class_methods.rb +12 -10
- data/lib/sidekiq/delay_extensions/generic_job.rb +51 -0
- data/lib/sidekiq/delay_extensions/generic_proxy.rb +57 -0
- data/lib/sidekiq/delay_extensions/testing.rb +1 -1
- data/lib/sidekiq/delay_extensions/version.rb +1 -1
- data/lib/sidekiq/delay_extensions/yaml.rb +66 -0
- data/lib/sidekiq/delay_extensions.rb +4 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '09822a56faaa3bddfd6accbbff7d36e709cd62d41123b3b42c54523ef74460a7'
|
4
|
+
data.tar.gz: f4c9f5b96cd11aff2f7790b5384b698f52c331a99260651f16f106632a96d15b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1ec8ced317007db2bd65436b7c575c01d741efc86273f2d8407637612864636f4757bf8efe5c28f021435fa6f88d20b86f33915bc0bf81b22bdebeb5d4aa145f
|
7
|
+
data.tar.gz: 9f43ee40a9aad89506d1a2aa51ed041b97ce499e8fb8d156e67c4b6e3c21e308e3337eb46ac0f8b1d156d39fe0e61ceaee88533cc840f5dcf20fd99bcd2875bd
|
data/Changes.md
CHANGED
@@ -2,6 +2,23 @@
|
|
2
2
|
|
3
3
|
[See Sidekiq for its changes](https://github.com/mperham/sidekiq/blob/main/Changes.md)
|
4
4
|
|
5
|
+
Unreleased
|
6
|
+
---------
|
7
|
+
|
8
|
+
7.1.0
|
9
|
+
---------
|
10
|
+
|
11
|
+
- New `Sidekiq::DelayExtensions::GenericJob` superclass for DelayedMailer, DelayedModel, DelayedClass
|
12
|
+
- it has a `_perform` method which accepts the unmarshalled and processed
|
13
|
+
`(target, method_name, *args, **kwargs)` and can be overridden or extended as needed.
|
14
|
+
- New (opt-in) `Sidekiq::DelayExtensions::GenericProxy` which can parse JSON or YAML delayed arguments
|
15
|
+
into a `target`, `method_name`, `args`, and `kwargs` before.
|
16
|
+
- New (opt-in) setting `Sidekiq::DelayExtensions.use_generic_proxy` (defaults to false).
|
17
|
+
- When false, there is no delayed proxy changes; the original `Sidekiq::DelayExtensions::Proxy` is used.
|
18
|
+
- When true, the new `Sidekiq::DelayExtensions::GenericProxy` is used, which handles both `*args` and `**kwargs` more naturally.
|
19
|
+
Be sure to test this works for you as expected when turning this on.
|
20
|
+
- Chore: Load YAML consistently via `::Sidekiq::DelayExtensions::YAML`
|
21
|
+
|
5
22
|
7.0.0
|
6
23
|
---------
|
7
24
|
|
data/README.md
CHANGED
@@ -32,6 +32,8 @@ In your initializers, include the line:
|
|
32
32
|
Upgrading (IMPORTANT): Also add
|
33
33
|
|
34
34
|
# To handle any existing delayed jobs at time of upgrade.
|
35
|
+
module Sidekiq::Extensions
|
36
|
+
end
|
35
37
|
Sidekiq::Extensions::DelayedClass = Sidekiq::DelayExtensions::DelayedClass
|
36
38
|
Sidekiq::Extensions::DelayedModel = Sidekiq::DelayExtensions::DelayedModel
|
37
39
|
Sidekiq::Extensions::DelayedMailer = Sidekiq::DelayExtensions::DelayedMailer
|
@@ -12,12 +12,14 @@ module Sidekiq
|
|
12
12
|
# UserMailer.delay.send_welcome_email(new_user)
|
13
13
|
# UserMailer.delay_for(5.days).send_welcome_email(new_user)
|
14
14
|
# UserMailer.delay_until(5.days.from_now).send_welcome_email(new_user)
|
15
|
-
class DelayedMailer
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
15
|
+
class DelayedMailer < GenericJob
|
16
|
+
def _perform(target, method_name, *args, **kwargs)
|
17
|
+
msg =
|
18
|
+
if kwargs.empty?
|
19
|
+
target.public_send(method_name, *args)
|
20
|
+
else
|
21
|
+
target.public_send(method_name, *args, **kwargs)
|
22
|
+
end
|
21
23
|
# The email method can return nil, which causes ActionMailer to return
|
22
24
|
# an undeliverable empty message.
|
23
25
|
if msg
|
@@ -29,16 +31,24 @@ module Sidekiq
|
|
29
31
|
end
|
30
32
|
|
31
33
|
module ActionMailer
|
34
|
+
def sidekiq_delay_proxy
|
35
|
+
if Sidekiq::DelayExtensions.use_generic_proxy
|
36
|
+
GenericProxy
|
37
|
+
else
|
38
|
+
Proxy
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
32
42
|
def sidekiq_delay(options = {})
|
33
|
-
|
43
|
+
sidekiq_delay_proxy.new(DelayedMailer, self, options)
|
34
44
|
end
|
35
45
|
|
36
46
|
def sidekiq_delay_for(interval, options = {})
|
37
|
-
|
47
|
+
sidekiq_delay_proxy.new(DelayedMailer, self, options.merge("at" => Time.now.to_f + interval.to_f))
|
38
48
|
end
|
39
49
|
|
40
50
|
def sidekiq_delay_until(timestamp, options = {})
|
41
|
-
|
51
|
+
sidekiq_delay_proxy.new(DelayedMailer, self, options.merge("at" => timestamp.to_f))
|
42
52
|
end
|
43
53
|
alias_method :delay, :sidekiq_delay
|
44
54
|
alias_method :delay_for, :sidekiq_delay_for
|
@@ -14,26 +14,28 @@ module Sidekiq
|
|
14
14
|
# Please note, this is not recommended as this will serialize the entire
|
15
15
|
# object to Redis. Your Sidekiq jobs should pass IDs, not entire instances.
|
16
16
|
# This is here for backwards compatibility with Delayed::Job only.
|
17
|
-
class DelayedModel
|
18
|
-
include Sidekiq::Job
|
19
|
-
|
20
|
-
def perform(yml)
|
21
|
-
(target, method_name, args) = YAML.load(yml)
|
22
|
-
target.__send__(method_name, *args)
|
23
|
-
end
|
17
|
+
class DelayedModel < GenericJob
|
24
18
|
end
|
25
19
|
|
26
20
|
module ActiveRecord
|
21
|
+
def sidekiq_delay_proxy
|
22
|
+
if Sidekiq::DelayExtensions.use_generic_proxy
|
23
|
+
GenericProxy
|
24
|
+
else
|
25
|
+
Proxy
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
27
29
|
def sidekiq_delay(options = {})
|
28
|
-
|
30
|
+
sidekiq_delay_proxy.new(DelayedModel, self, options)
|
29
31
|
end
|
30
32
|
|
31
33
|
def sidekiq_delay_for(interval, options = {})
|
32
|
-
|
34
|
+
sidekiq_delay_proxy.new(DelayedModel, self, options.merge("at" => Time.now.to_f + interval.to_f))
|
33
35
|
end
|
34
36
|
|
35
37
|
def sidekiq_delay_until(timestamp, options = {})
|
36
|
-
|
38
|
+
sidekiq_delay_proxy.new(DelayedModel, self, options.merge("at" => timestamp.to_f))
|
37
39
|
end
|
38
40
|
alias_method :delay, :sidekiq_delay
|
39
41
|
alias_method :delay_for, :sidekiq_delay_for
|
@@ -41,12 +41,13 @@ module Sidekiq
|
|
41
41
|
# vs.
|
42
42
|
# https://github.com/mperham/sidekiq/blob/v7.0.1/lib/sidekiq/api.rb#L374-L411
|
43
43
|
def safe_load(content, default)
|
44
|
-
yield(*YAML.
|
44
|
+
yield(*Sidekiq::DelayExtensions::YAML.unsafe_load(content))
|
45
45
|
rescue => ex
|
46
46
|
# #1761 in dev mode, it's possible to have jobs enqueued which haven't been loaded into
|
47
47
|
# memory yet so the YAML can't be loaded.
|
48
48
|
# TODO is this still necessary? Zeitwerk reloader should handle?
|
49
|
-
Sidekiq.
|
49
|
+
sidekiq_env = ::Sidekiq.default_configuration[:environment] || ENV["APP_ENV"] || ENV["RAILS_ENV"] || ENV["RACK_ENV"]
|
50
|
+
::Sidekiq.logger.warn "Unable to load YAML: #{ex.message}" unless sidekiq_env == "development"
|
50
51
|
default
|
51
52
|
end
|
52
53
|
end
|
@@ -12,26 +12,28 @@ module Sidekiq
|
|
12
12
|
# User.delay.delete_inactive
|
13
13
|
# Wikipedia.delay.download_changes_for(Date.today)
|
14
14
|
#
|
15
|
-
class DelayedClass
|
16
|
-
include Sidekiq::Job
|
17
|
-
|
18
|
-
def perform(yml)
|
19
|
-
(target, method_name, args) = YAML.load(yml)
|
20
|
-
target.__send__(method_name, *args)
|
21
|
-
end
|
15
|
+
class DelayedClass < GenericJob
|
22
16
|
end
|
23
17
|
|
24
18
|
module Klass
|
19
|
+
def sidekiq_delay_proxy
|
20
|
+
if Sidekiq::DelayExtensions.use_generic_proxy
|
21
|
+
GenericProxy
|
22
|
+
else
|
23
|
+
Proxy
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
25
27
|
def sidekiq_delay(options = {})
|
26
|
-
|
28
|
+
sidekiq_delay_proxy.new(DelayedClass, self, options)
|
27
29
|
end
|
28
30
|
|
29
31
|
def sidekiq_delay_for(interval, options = {})
|
30
|
-
|
32
|
+
sidekiq_delay_proxy.new(DelayedClass, self, options.merge("at" => Time.now.to_f + interval.to_f))
|
31
33
|
end
|
32
34
|
|
33
35
|
def sidekiq_delay_until(timestamp, options = {})
|
34
|
-
|
36
|
+
sidekiq_delay_proxy.new(DelayedClass, self, options.merge("at" => timestamp.to_f))
|
35
37
|
end
|
36
38
|
alias_method :delay, :sidekiq_delay
|
37
39
|
alias_method :delay_for, :sidekiq_delay_for
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Sidekiq
|
4
|
+
module DelayExtensions
|
5
|
+
class GenericJob
|
6
|
+
include Sidekiq::Job
|
7
|
+
|
8
|
+
def perform(yml)
|
9
|
+
if !Sidekiq::DelayExtensions.use_generic_proxy
|
10
|
+
(target, method_name, args) = ::Sidekiq::DelayExtensions::YAML.unsafe_load(yml)
|
11
|
+
return _perform(target, method_name, *args)
|
12
|
+
end
|
13
|
+
(target, method_name, args, kwargs) = ::Sidekiq::DelayExtensions::YAML.unsafe_load(yml)
|
14
|
+
if target.is_a?(String)
|
15
|
+
target_klass = target.safe_constantize
|
16
|
+
if target_klass
|
17
|
+
target = target_klass
|
18
|
+
else
|
19
|
+
fail NameError, "uninitialized constant #{target}. Peforming: #{yml.inspect}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
has_no_kwargs = kwargs.nil? || kwargs.empty? # rubocop:disable Rails/Blank
|
23
|
+
if has_no_kwargs
|
24
|
+
if args.is_a?(Array) && args.last.is_a?(Hash) # && args.last.keys.any? { |key| key.is_a?(Symbol) }
|
25
|
+
# rehydrate keys
|
26
|
+
kwargs = args.pop.symbolize_keys
|
27
|
+
has_no_kwargs = kwargs.empty?
|
28
|
+
elsif args.is_a?(Hash)
|
29
|
+
# rehydrate keys
|
30
|
+
kwargs = args.symbolize_keys
|
31
|
+
args = []
|
32
|
+
has_no_kwargs = kwargs.empty?
|
33
|
+
end
|
34
|
+
end
|
35
|
+
if has_no_kwargs
|
36
|
+
_perform(target, method_name, *args)
|
37
|
+
else
|
38
|
+
_perform(target, method_name, *args, **kwargs)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def _perform(target, method_name, *args, **kwargs)
|
43
|
+
if kwargs.empty?
|
44
|
+
target.__send__(method_name, *args)
|
45
|
+
else
|
46
|
+
target.__send__(method_name, *args, **kwargs)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -6,6 +6,9 @@ module Sidekiq
|
|
6
6
|
module DelayExtensions
|
7
7
|
SIZE_LIMIT = 8_192
|
8
8
|
|
9
|
+
singleton_class.attr_accessor :use_generic_proxy
|
10
|
+
self.use_generic_proxy = false
|
11
|
+
|
9
12
|
class Proxy < BasicObject
|
10
13
|
def initialize(performable, target, options = {})
|
11
14
|
@performable = performable
|
@@ -29,5 +32,59 @@ module Sidekiq
|
|
29
32
|
"display_class" => "#{@target}.#{name}"}.merge(@opts))
|
30
33
|
end
|
31
34
|
end
|
35
|
+
|
36
|
+
class GenericProxy < BasicObject
|
37
|
+
def initialize(performable, target, options = {})
|
38
|
+
@performable = performable
|
39
|
+
@target = target
|
40
|
+
@opts = options
|
41
|
+
end
|
42
|
+
|
43
|
+
def method_missing(name, *args, **kwargs) # rubocop:disable Style/MissingRespondToMissing
|
44
|
+
begin
|
45
|
+
has_no_kwargs = kwargs.nil? || kwargs.empty?
|
46
|
+
valid_json_args =
|
47
|
+
if has_no_kwargs
|
48
|
+
# if args.last.is_a?(Hash) && args.last.keys.any? { |key| key.is_a?(Symbol) }
|
49
|
+
# kwargs = args.pop.symbolize_keys
|
50
|
+
[*args]
|
51
|
+
else
|
52
|
+
[*args, ::JSON.parse(::JSON.dump(kwargs))]
|
53
|
+
end
|
54
|
+
obj = [@target.name, name&.to_s, valid_json_args]
|
55
|
+
marshalled = ::JSON.dump(obj)
|
56
|
+
rescue ::JSON::ParserError
|
57
|
+
obj = if kwargs&.any?
|
58
|
+
[@target.name, name&.to_s, *args, **kwargs]
|
59
|
+
else
|
60
|
+
[@target.name, name&.to_s, *args]
|
61
|
+
end
|
62
|
+
::Sidekiq.logger.warn {
|
63
|
+
"Non-JSON args passed to Sidekiq delayed job. obj=#{obj.inspect}"
|
64
|
+
}
|
65
|
+
marshalled = ::YAML.dump(obj)
|
66
|
+
end
|
67
|
+
# Debug notes:
|
68
|
+
# marshalled = ::YAML.dump(valid_json_args)
|
69
|
+
# marshalled = ::YAML.to_json(valid_json_args)
|
70
|
+
# value = {
|
71
|
+
# target_name: @target.name,
|
72
|
+
# name: name,
|
73
|
+
# args: args,
|
74
|
+
# kwargs: kwargs,
|
75
|
+
# valid_json_args: valid_json_args,
|
76
|
+
# obj: obj,
|
77
|
+
# marshalled: marshalled,
|
78
|
+
# }
|
79
|
+
# ::STDOUT.puts value.inspect
|
80
|
+
if marshalled.size > SIZE_LIMIT
|
81
|
+
::Sidekiq.logger.warn { "#{@target}.#{name} job argument is #{marshalled.bytesize} bytes, you should refactor it to reduce the size" }
|
82
|
+
end
|
83
|
+
valid_opts = @opts.stringify_keys
|
84
|
+
@performable.client_push({"class" => @performable,
|
85
|
+
"args" => [marshalled],
|
86
|
+
"display_class" => "#{@target}.#{name}"}.merge(valid_opts))
|
87
|
+
end
|
88
|
+
end
|
32
89
|
end
|
33
90
|
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Sidekiq
|
4
|
+
module DelayExtensions
|
5
|
+
module YAML
|
6
|
+
STDLIB_YAML = ::YAML
|
7
|
+
|
8
|
+
if STDLIB_YAML.respond_to?(:unsafe_load)
|
9
|
+
# https://github.com/ruby/psych/blob/v4.0.3/lib/psych.rb#L271-L323
|
10
|
+
# def self.unsafe_load(yaml, filename: nil, fallback: false, symbolize_names: false, freeze: false)
|
11
|
+
def self.unsafe_load(yaml, **kwargs)
|
12
|
+
STDLIB_YAML.unsafe_load(yaml, **kwargs)
|
13
|
+
end
|
14
|
+
|
15
|
+
# def self.safe_load(yaml, permitted_classes: [], permitted_symbols: [], aliases: false, filename: nil, fallback: nil, symbolize_names: false, freeze: false)
|
16
|
+
def self.safe_load(yaml, **kwargs)
|
17
|
+
STDLIB_YAML.safe_load(yaml, **kwargs)
|
18
|
+
end
|
19
|
+
|
20
|
+
# def self.unsafe_load_file(filename, **kwargs)
|
21
|
+
def self.unsafe_load_file(filename, **kwargs)
|
22
|
+
STDLIB_YAML.unsafe_load_file(filename, **kwargs)
|
23
|
+
end
|
24
|
+
|
25
|
+
# def self.safe_load_file(filename, **kwargs)
|
26
|
+
def self.safe_load_file(filename, **kwargs)
|
27
|
+
STDLIB_YAML.safe_load_file(filename, **kwargs)
|
28
|
+
end
|
29
|
+
else
|
30
|
+
# https://github.com/ruby/psych/blob/v3.1.0/lib/psych.rb#L271-L328
|
31
|
+
# vs.
|
32
|
+
# https://github.com/ruby/psych/blob/v4.0.3/lib/psych.rb#L271-L323
|
33
|
+
# def self.load( yaml, filename: nil, fallback: false, symbolize_names: false)
|
34
|
+
# vs.
|
35
|
+
# def self.unsafe_load(yaml, filename: nil, fallback: false, symbolize_names: false, freeze: false)
|
36
|
+
def self.unsafe_load(yaml, **kwargs)
|
37
|
+
STDLIB_YAML.load(yaml, **kwargs)
|
38
|
+
end
|
39
|
+
|
40
|
+
# def self.safe_load(yaml, permitted_classes: [], permitted_symbols: [], aliases: false, filename: nil, fallback: nil, symbolize_names: false)
|
41
|
+
# vs.
|
42
|
+
# def self.safe_load(yaml, permitted_classes: [], permitted_symbols: [], aliases: false, filename: nil, fallback: nil, symbolize_names: false, freeze: false)
|
43
|
+
def self.safe_load(yaml, **kwargs)
|
44
|
+
STDLIB_YAML.safe_load(yaml, **kwargs)
|
45
|
+
end
|
46
|
+
|
47
|
+
# def self.load_file filename, fallback: false
|
48
|
+
# vs.
|
49
|
+
# def self.unsafe_load_file(filename, **kwargs)
|
50
|
+
def self.unsafe_load_file(filename, **kwargs)
|
51
|
+
STDLIB_YAML.load_file(filename, **kwargs)
|
52
|
+
end
|
53
|
+
|
54
|
+
# n/a
|
55
|
+
# vs.
|
56
|
+
# def self.safe_load_file(filename, **kwargs)
|
57
|
+
def self.safe_load_file(filename, **kwargs)
|
58
|
+
STDLIB_YAML.load_file(filename, **kwargs)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
# NOTE(BF): In case someone calls YAML.load
|
62
|
+
# from within the gem.
|
63
|
+
singleton_class.alias_method :load, :unsafe_load
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -5,6 +5,10 @@ require "sidekiq"
|
|
5
5
|
module Sidekiq
|
6
6
|
module DelayExtensions
|
7
7
|
def self.enable_delay!
|
8
|
+
require "sidekiq/delay_extensions/yaml"
|
9
|
+
|
10
|
+
require "sidekiq/delay_extensions/generic_job"
|
11
|
+
|
8
12
|
if defined?(::ActiveSupport)
|
9
13
|
require "sidekiq/delay_extensions/active_record"
|
10
14
|
require "sidekiq/delay_extensions/action_mailer"
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sidekiq-delay_extensions
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 7.
|
4
|
+
version: 7.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mike Perham
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date: 2024-
|
12
|
+
date: 2024-02-09 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: sidekiq
|
@@ -42,9 +42,11 @@ files:
|
|
42
42
|
- lib/sidekiq/delay_extensions/active_record.rb
|
43
43
|
- lib/sidekiq/delay_extensions/api.rb
|
44
44
|
- lib/sidekiq/delay_extensions/class_methods.rb
|
45
|
+
- lib/sidekiq/delay_extensions/generic_job.rb
|
45
46
|
- lib/sidekiq/delay_extensions/generic_proxy.rb
|
46
47
|
- lib/sidekiq/delay_extensions/testing.rb
|
47
48
|
- lib/sidekiq/delay_extensions/version.rb
|
49
|
+
- lib/sidekiq/delay_extensions/yaml.rb
|
48
50
|
- sidekiq-delay_extensions.gemspec
|
49
51
|
homepage: https://github.com/gemhome/sidekiq-delay_extensions/wiki/Delayed-extensions
|
50
52
|
licenses:
|