sidekiq-debouncer 2.0.1 → 3.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 78b258ab5ad21facbcf96f6aab96f30e38542ea24a7c5fd9e23ee99057b67e6d
4
- data.tar.gz: 19db1ba7e392a4de9dcedfa8e236b8e8e334d7de56e71d8241aeb4e2d20b5e0a
3
+ metadata.gz: c98e0c985f240350757ce46298f39cb5586d7e01966f78bd3a886c8b4a78b7e0
4
+ data.tar.gz: e1f7ceda05cd4cc580fc5d318da59ccaf9a8a3f737020524c76a147add5fa5e3
5
5
  SHA512:
6
- metadata.gz: 4c30876a9db412ee65a844c966fec6b4f2c84e33f5eb0393773c7db0ad9fd5d6c394b4c9a2f01e31178c93dcc5d5443e829e03f90a9c1db81814f594153af290
7
- data.tar.gz: 44e2d19a313ca3d07daaed130a9fb8a21721a6e6ed17756ed21028017096dd528d6b6a2d482a624095822c23158a9777620ae6dbeb0e541b8e2c7acebd003f68
6
+ metadata.gz: 3175ce8da01db8a593290273a4a7c392133792de93c04a9091662ca96ab24caeea8a60e17e0ab7a7fd16e8b8273d184b62a421deb58374430e26f720f0708927
7
+ data.tar.gz: 2e42dedd11a4b6c05e9c4af5446803a1fab05daa7fe762b83ef3846b0f5c8cb253bf6f03e1bbbdc8e21311245087526c89098955c088d040c0c09606bba526da
data/CHANGELOG.md CHANGED
@@ -1,3 +1,12 @@
1
+ ## [3.0.0] - 2024-10-22
2
+ - Complete rewrite of the library ([#25](https://github.com/paladinsoftware/sidekiq-debouncer/pull/25))
3
+ - Read only Web UI ([#26](https://github.com/paladinsoftware/sidekiq-debouncer/pull/26))
4
+ - Drop support for sidekiq 6.x and ruby 2.7 ([#28](https://github.com/paladinsoftware/sidekiq-debouncer/pull/28))
5
+ - Respect sidekiq_options overridden by .set ([#27](https://github.com/paladinsoftware/sidekiq-debouncer/pull/27))
6
+
7
+ ## [2.0.2] - 2023-03-13
8
+ - support Sidekiq::Testing
9
+
1
10
  ## [2.0.1] - 2023-03-04
2
11
  - don't remove debounce key in redis to avoid invalid debouncing
3
12
 
data/README.md CHANGED
@@ -99,10 +99,22 @@ Sidekiq.configure_client do |config|
99
99
  end
100
100
  ```
101
101
 
102
+ ## Web UI
103
+ Add `require 'sidekiq/debouncer/web'` after `require 'sidekiq/web'`.
104
+
102
105
  ## Testing
103
106
 
104
107
  In order to test the behavior of `sidekiq-debouncer` it is necessary to disable testing mode. It is the limitation of internal implementation.
105
108
 
109
+ Sidekiq::Debouncer will not debounce the jobs in testing mode, instead they'll be executed like normal jobs (pushed to fake queue or executed inline depending on settings).
110
+ Server middleware needs to be added to `Sidekiq::Testing` chain:
111
+
112
+ ```ruby
113
+ Sidekiq::Testing.server_middleware do |chain|
114
+ chain.add Sidekiq::Debouncer::Middleware::Server
115
+ end
116
+ ```
117
+
106
118
  ## License
107
119
 
108
120
  MIT Licensed. See LICENSE.txt for details.
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "sidekiq/scheduled"
4
+
5
+ module Sidekiq
6
+ module Debouncer
7
+ class Enq < ::Sidekiq::Scheduled::Enq
8
+ extend LuaCommands
9
+
10
+ LUA_ZPOPBYSCORE_WITHSCORE = File.read(File.expand_path("../lua/zpopbyscore_withscore.lua", __FILE__))
11
+ LUA_ZPOPBYSCORE_MULTI = File.read(File.expand_path("../lua/zpopbyscore_multi.lua", __FILE__))
12
+
13
+ define_lua_command(:zpopbyscore_withscore, LUA_ZPOPBYSCORE_WITHSCORE)
14
+ define_lua_command(:zpopbyscore_multi, LUA_ZPOPBYSCORE_MULTI)
15
+
16
+ def enqueue_jobs
17
+ redis do |conn|
18
+ while !@done && (job, score = zpopbyscore_withscore(conn, [Sidekiq::Debouncer::SET], [Time.now.to_f.to_s]))
19
+ job_args = zpopbyscore_multi(conn, [job], [score])
20
+
21
+ final_args = job_args.map { |elem| Sidekiq.load_json(elem.split("-", 2)[1]) }
22
+ job_class = job.split("/")[2]
23
+ klass = Object.const_get(job_class)
24
+
25
+ @client.push({"args" => final_args, "class" => klass, "debounce_key" => job})
26
+
27
+ logger.debug { "enqueued #{Sidekiq::Debouncer::SET}: #{job}" }
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sidekiq
4
+ module Debouncer
5
+ class Job
6
+ include Sidekiq::JobUtil
7
+
8
+ attr_reader :key, :score
9
+
10
+ def initialize(key, score)
11
+ @key = key
12
+ @score = score
13
+ end
14
+
15
+ def at
16
+ Time.at(score).utc
17
+ end
18
+
19
+ def args
20
+ @_args ||= Sidekiq.redis { |conn| conn.call("ZRANGE", key, "-inf", "+inf", "BYSCORE") }
21
+ .map { |elem| Sidekiq.load_json(elem.split("-", 2)[1]) }
22
+ end
23
+
24
+ def queue
25
+ normalized["queue"]
26
+ end
27
+
28
+ def klass
29
+ key.split("/")[2]
30
+ end
31
+
32
+ alias_method :display_class, :klass
33
+
34
+ private
35
+
36
+ def normalized
37
+ @_normalized ||= normalize_item({"args" => args, "class" => Object.const_get(klass), "debounce_key" => key})
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sidekiq
4
+ module Debouncer
5
+ module Launcher
6
+ def initialize(config, **kwargs)
7
+ @debounce_poller = Sidekiq::Debouncer::Poller.new(config)
8
+ super
9
+ end
10
+
11
+ def run
12
+ super
13
+ @debounce_poller.start
14
+ end
15
+
16
+ def quiet
17
+ super
18
+ @debounce_poller.terminate
19
+ end
20
+
21
+ def stop
22
+ @debounce_poller.terminate
23
+ super
24
+ end
25
+ end
26
+ end
27
+ end
28
+
29
+ Sidekiq.configure_server do
30
+ require "sidekiq/launcher"
31
+
32
+ ::Sidekiq::Launcher.prepend(Sidekiq::Debouncer::Launcher)
33
+ end
@@ -0,0 +1,4 @@
1
+ en:
2
+ Debounces: Debounces
3
+ DebounceKey: Debounce Key
4
+ NoDebouncesFound: No debounces found
@@ -1,22 +1,10 @@
1
1
  local set = KEYS[1]
2
2
  local debounce_key = KEYS[2]
3
3
 
4
- local job = ARGV[1]
4
+ local args = ARGV[1]
5
5
  local time = ARGV[2]
6
6
  local ttl = ARGV[3]
7
7
 
8
- local existing_debounce = redis.call("GET", debounce_key)
9
-
10
- if existing_debounce then
11
- redis.call("DEL", debounce_key)
12
- -- skip if job wasn't found in schedule set
13
- if redis.call("ZREM", set, existing_debounce) > 0 then
14
- local new_args = cjson.decode(job)['args'][1]
15
- local new_job = cjson.decode(existing_debounce)
16
- table.insert(new_job['args'], new_args)
17
- job = cjson.encode(new_job)
18
- end
19
- end
20
-
21
- redis.call("SET", debounce_key, job, "EX", ttl)
22
- redis.call("ZADD", set, time, job)
8
+ redis.call("ZADD", set, time, debounce_key)
9
+ redis.call("ZADD", debounce_key, time, args)
10
+ redis.call("EXPIRE", debounce_key, ttl)
@@ -0,0 +1,5 @@
1
+ local key, now = KEYS[1], ARGV[1]
2
+ local jobs = redis.call("ZRANGE", key, "-inf", now, "BYSCORE")
3
+ redis.call("ZREMRANGEBYSCORE", key, "-inf", now)
4
+
5
+ return jobs
@@ -0,0 +1,6 @@
1
+ local key, now = KEYS[1], ARGV[1]
2
+ local jobs = redis.call("ZRANGE", key, "-inf", now, "BYSCORE", "LIMIT", 0, 1, "WITHSCORES")
3
+ if jobs[1] then
4
+ redis.call("ZREM", key, jobs[1])
5
+ return {jobs[1], jobs[2]}
6
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "digest/sha1"
4
+
5
+ module Sidekiq
6
+ module Debouncer
7
+ module LuaCommands
8
+ def define_lua_command(command, script)
9
+ sha = Digest::SHA1.hexdigest(script)
10
+ define_method(command) do |conn, keys, argv|
11
+ retryable = true
12
+ begin
13
+ conn.call("EVALSHA", sha, keys.size, *keys, *argv)
14
+ rescue RedisClient::CommandError => e
15
+ raise if !e.message.start_with?("NOSCRIPT") || !retryable
16
+
17
+ # upload script to redis cache and retry
18
+ conn.call("SCRIPT", "LOAD", script)
19
+ retryable = false
20
+ retry
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "digest/sha1"
3
+ require "securerandom"
4
4
 
5
5
  module Sidekiq
6
6
  module Debouncer
@@ -11,10 +11,11 @@ module Sidekiq
11
11
  # job from schedule set when another debounce occurs.
12
12
  class Client
13
13
  include Sidekiq::ClientMiddleware
14
+ extend Sidekiq::Debouncer::LuaCommands
14
15
 
15
16
  LUA_DEBOUNCE = File.read(File.expand_path("../../lua/debounce.lua", __FILE__))
16
- LUA_DEBOUNCE_SHA = Digest::SHA1.hexdigest(LUA_DEBOUNCE)
17
- REDIS_ERROR_CLASS = defined?(RedisClient::CommandError) ? RedisClient::CommandError : Redis::CommandError
17
+
18
+ define_lua_command(:redis_debounce, LUA_DEBOUNCE)
18
19
 
19
20
  def initialize(options = {})
20
21
  @debounce_key_ttl = options.fetch(:ttl, 60 * 60 * 24) # 24 hours by default
@@ -28,9 +29,6 @@ module Sidekiq
28
29
  return job if !klass.get_sidekiq_options["debounce"] || job["debounce_key"]
29
30
 
30
31
  debounce(klass, job)
31
-
32
- # prevent normal sidekiq flow
33
- false
34
32
  end
35
33
 
36
34
  private
@@ -38,27 +36,31 @@ module Sidekiq
38
36
  def debounce(klass, job)
39
37
  raise NotSupportedError, "perform_at is not supported with debounce" if job.key?("at")
40
38
 
41
- options = debounce_options(klass)
39
+ options = debounce_options(klass, job)
42
40
  key = debounce_key(klass, job, options)
43
41
  time = (options[:time].to_f + Time.now.to_f).to_s
44
42
 
45
- job["debounce_key"] = key
46
- job["args"] = [job["args"]]
47
- job.delete("debounce")
43
+ return job.merge("args" => [job["args"]], "debounce_key" => key) if testing?
44
+
45
+ args_stringified = "#{SecureRandom.hex(12)}-#{Sidekiq.dump_json(job["args"])}"
48
46
 
49
47
  redis do |connection|
50
- redis_debounce(connection, keys: ["schedule", key], argv: [Sidekiq.dump_json(job), time, @debounce_key_ttl])
48
+ redis_debounce(connection, [Sidekiq::Debouncer::SET, key], [args_stringified, time, @debounce_key_ttl])
51
49
  end
50
+
51
+ # prevent normal sidekiq flow
52
+ false
52
53
  end
53
54
 
54
55
  def debounce_key(klass, job, options)
55
56
  method = options[:by]
56
57
  result = method.is_a?(Symbol) ? klass.send(method, job["args"]) : method.call(job["args"])
57
- "debounce/#{klass.name}/#{result}"
58
+ "debounce/v3/#{klass.name}/#{result}"
58
59
  end
59
60
 
60
- def debounce_options(klass)
61
- options = klass.get_sidekiq_options["debounce"].transform_keys(&:to_sym)
61
+ def debounce_options(klass, job)
62
+ options = job.fetch("debounce") { klass.get_sidekiq_options["debounce"] }
63
+ options = options.transform_keys(&:to_sym)
62
64
 
63
65
  raise MissingArgumentError, "'by' attribute not provided" unless options[:by]
64
66
  raise MissingArgumentError, "'time' attribute not provided" unless options[:time]
@@ -66,18 +68,8 @@ module Sidekiq
66
68
  options
67
69
  end
68
70
 
69
- def redis_debounce(connection, keys:, argv:)
70
- retryable = true
71
- begin
72
- connection.call("EVALSHA", LUA_DEBOUNCE_SHA, keys.size, *keys, *argv)
73
- rescue REDIS_ERROR_CLASS => e
74
- raise if !e.message.start_with?("NOSCRIPT") || !retryable
75
-
76
- # upload script to redis cache and retry
77
- connection.call("SCRIPT", "LOAD", LUA_DEBOUNCE)
78
- retryable = false
79
- retry
80
- end
71
+ def testing?
72
+ defined?(Sidekiq::Testing) && Sidekiq::Testing.enabled?
81
73
  end
82
74
  end
83
75
  end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "sidekiq/scheduled"
4
+
5
+ module Sidekiq
6
+ module Debouncer
7
+ class Poller < ::Sidekiq::Scheduled::Poller
8
+ def initialize(config)
9
+ super
10
+ @enq = Sidekiq::Debouncer::Enq.new(config)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sidekiq
4
+ module Debouncer
5
+ class Set < Sidekiq::JobSet
6
+ def initialize
7
+ super Sidekiq::Debouncer::SET
8
+ end
9
+
10
+ def fetch_by_key(key)
11
+ score = Sidekiq.redis { |conn| conn.zscore(Sidekiq::Debouncer::SET, key) }
12
+ Job.new(key, score)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Sidekiq
4
4
  module Debouncer
5
- VERSION = "2.0.1"
5
+ VERSION = "3.0.0"
6
6
  end
7
7
  end
@@ -0,0 +1,48 @@
1
+ <header class='row'>
2
+ <div class='col-sm-5 pull-left'>
3
+ <h3><%= t('Debounces') %></h3>
4
+ </div>
5
+ <div class='col-sm-7 pull-right'>
6
+ <%= erb :_paging, locals: { url: "#{root_path}debounces" } %>
7
+ </div>
8
+ </header>
9
+
10
+ <% if @debounces.size > 0 %>
11
+ <form action="<%= root_path %>debounces" method="post">
12
+ <%= csrf_tag %>
13
+ <div class="table_container">
14
+ <table class="table table-striped table-bordered table-hover">
15
+ <thead>
16
+ <tr>
17
+ <th class="table-checkbox checkbox-column">
18
+ <label>
19
+ <input type="checkbox" class="check_all" />
20
+ </label>
21
+ </th>
22
+ <th><%= t('When') %></th>
23
+ <th><%= t('DebounceKey') %></th>
24
+ </tr>
25
+ </thead>
26
+ <% @debounces.each do |entry| %>
27
+ <tr>
28
+ <td class="table-checkbox">
29
+ <label>
30
+ <input type='checkbox' name='key[]' value='<%= entry.key %>' class='shift_clickable' />
31
+ </label>
32
+ </td>
33
+ <td>
34
+ <a href="<%= root_path %>debounces/<%= ::Base64.urlsafe_encode64(entry.key) %>"><%= relative_time(entry.at) %></a>
35
+ </td>
36
+ <td>
37
+ <a href="<%= root_path %>debounces/<%= ::Base64.urlsafe_encode64(entry.key) %>"><%= entry.key %></a>
38
+ </td>
39
+ </tr>
40
+ <% end %>
41
+ </table>
42
+ </div>
43
+ <!-- <input class="btn btn-danger pull-right flip" type="submit" name="delete" value="<%#= t('Delete') %>" />-->
44
+ <!-- <input class="btn btn-danger pull-right flip" type="submit" name="add_to_queue" value="<%#= t('AddToQueue') %>" />-->
45
+ </form>
46
+ <% else %>
47
+ <div class="alert alert-success"><%= t('NoDebouncesFound') %></div>
48
+ <% end %>
@@ -0,0 +1,38 @@
1
+ <div class="header-container">
2
+ <h3><%= t('Job') %></h3>
3
+ </div>
4
+
5
+ <div class="table_container">
6
+ <table class="table table-bordered table-striped table-hover">
7
+ <tbody>
8
+ <tr>
9
+ <th><%= t('Queue') %></th>
10
+ <td><%= @job.queue %></td>
11
+ </tr>
12
+ <tr>
13
+ <th><%= t('When') %></th>
14
+ <td><%= relative_time(@job.at) %></td>
15
+ </tr>
16
+ <tr>
17
+ <th><%= t('DebounceKey') %></th>
18
+ <td>
19
+ <%= @job.key %>
20
+ </td>
21
+ </tr>
22
+ <tr>
23
+ <th><%= t('Job') %></th>
24
+ <td>
25
+ <%= @job.display_class %>
26
+ </td>
27
+ </tr>
28
+ <tr>
29
+ <th><%= t('Arguments') %></th>
30
+ <td>
31
+ <code class="code-wrap">
32
+ <div class="args-extended"><%= display_args(@job.args, nil) %></div>
33
+ </code>
34
+ </td>
35
+ </tr>
36
+ </tbody>
37
+ </table>
38
+ </div>
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ if defined?(Sidekiq::Web)
4
+ require "sidekiq/debouncer/web_extension"
5
+
6
+ Sidekiq::Web.register Sidekiq::Debouncer::WebExtension
7
+ Sidekiq::Web.tabs["Debounces"] = "debounces"
8
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "base64"
4
+
5
+ module Sidekiq
6
+ module Debouncer
7
+ module WebExtension
8
+ def self.registered(app)
9
+ app.settings.locales << File.join(File.expand_path("..", __FILE__), "locales")
10
+
11
+ app.get "/debounces" do
12
+ view_path = File.join(File.expand_path("..", __FILE__), "views")
13
+
14
+ @count = (params["count"] || 25).to_i
15
+ (@current_page, @total_size, @debounces) = page("debouncer", params["page"], @count)
16
+ @debounces = @debounces.map { |key, score| Sidekiq::Debouncer::Job.new(key, score) }
17
+
18
+ render(:erb, File.read(File.join(view_path, "index.html.erb")))
19
+ end
20
+
21
+ app.get "/debounces/:key" do
22
+ view_path = File.join(File.expand_path("..", __FILE__), "views")
23
+
24
+ @job = Sidekiq::Debouncer::Set.new.fetch_by_key(Base64.urlsafe_decode64(route_params[:key]))
25
+
26
+ render(:erb, File.read(File.join(view_path, "show.html.erb")))
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -2,11 +2,16 @@
2
2
 
3
3
  require "sidekiq/debouncer/version"
4
4
  require "sidekiq/debouncer/errors"
5
+ require "sidekiq/debouncer/lua_commands"
5
6
  require "sidekiq/debouncer/middleware/client"
6
7
  require "sidekiq/debouncer/middleware/server"
8
+ require "sidekiq/debouncer/job"
9
+ require "sidekiq/debouncer/set"
7
10
 
8
11
  module Sidekiq
9
12
  module Debouncer
13
+ SET = "debouncer"
14
+
10
15
  def self.included(base)
11
16
  base.extend ClassMethods
12
17
  end
@@ -19,3 +24,9 @@ module Sidekiq
19
24
  end
20
25
  end
21
26
  end
27
+
28
+ Sidekiq.configure_server do
29
+ require "sidekiq/debouncer/enq"
30
+ require "sidekiq/debouncer/poller"
31
+ require "sidekiq/debouncer/launcher"
32
+ end
@@ -18,7 +18,7 @@ Gem::Specification.new do |gem|
18
18
  DESCRIPTION
19
19
  gem.homepage = "https://github.com/paladinsoftware/sidekiq-debouncer"
20
20
  gem.license = "MIT"
21
- gem.required_ruby_version = ">= 2.7.0"
21
+ gem.required_ruby_version = ">= 3.0.0"
22
22
 
23
23
  gem.files = Dir.glob("lib/**/*") + [
24
24
  "CHANGELOG.md",
@@ -27,11 +27,12 @@ Gem::Specification.new do |gem|
27
27
  "sidekiq-debouncer.gemspec"
28
28
  ]
29
29
 
30
- gem.add_dependency "sidekiq", ">= 6.5", "< 8.0"
30
+ gem.add_dependency "sidekiq", ">= 7.0", "< 8.0"
31
31
 
32
32
  gem.add_development_dependency "rspec", "~> 3.12.0"
33
33
  gem.add_development_dependency "timecop", "~> 0.9.6"
34
34
  gem.add_development_dependency "simplecov", "~> 0.22.0"
35
35
  gem.add_development_dependency "parallel", "~> 1.22.1"
36
36
  gem.add_development_dependency "standard", "~> 1.24.3"
37
+ gem.add_development_dependency "rack-test", "~> 2.1.0"
37
38
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sidekiq-debouncer
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.1
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sebastian Zuchmański
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2023-03-04 00:00:00.000000000 Z
12
+ date: 2024-10-22 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: sidekiq
@@ -17,7 +17,7 @@ dependencies:
17
17
  requirements:
18
18
  - - ">="
19
19
  - !ruby/object:Gem::Version
20
- version: '6.5'
20
+ version: '7.0'
21
21
  - - "<"
22
22
  - !ruby/object:Gem::Version
23
23
  version: '8.0'
@@ -27,7 +27,7 @@ dependencies:
27
27
  requirements:
28
28
  - - ">="
29
29
  - !ruby/object:Gem::Version
30
- version: '6.5'
30
+ version: '7.0'
31
31
  - - "<"
32
32
  - !ruby/object:Gem::Version
33
33
  version: '8.0'
@@ -101,6 +101,20 @@ dependencies:
101
101
  - - "~>"
102
102
  - !ruby/object:Gem::Version
103
103
  version: 1.24.3
104
+ - !ruby/object:Gem::Dependency
105
+ name: rack-test
106
+ requirement: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: 2.1.0
111
+ type: :development
112
+ prerelease: false
113
+ version_requirements: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: 2.1.0
104
118
  description: |
105
119
  Sidekiq extension that adds the ability to debounce job execution.
106
120
  Worker will postpone its execution after `wait time` have elapsed since the last time it was invoked.
@@ -117,11 +131,24 @@ files:
117
131
  - README.md
118
132
  - lib/sidekiq-debouncer.rb
119
133
  - lib/sidekiq/debouncer.rb
134
+ - lib/sidekiq/debouncer/enq.rb
120
135
  - lib/sidekiq/debouncer/errors.rb
136
+ - lib/sidekiq/debouncer/job.rb
137
+ - lib/sidekiq/debouncer/launcher.rb
138
+ - lib/sidekiq/debouncer/locales/en.yml
121
139
  - lib/sidekiq/debouncer/lua/debounce.lua
140
+ - lib/sidekiq/debouncer/lua/zpopbyscore_multi.lua
141
+ - lib/sidekiq/debouncer/lua/zpopbyscore_withscore.lua
142
+ - lib/sidekiq/debouncer/lua_commands.rb
122
143
  - lib/sidekiq/debouncer/middleware/client.rb
123
144
  - lib/sidekiq/debouncer/middleware/server.rb
145
+ - lib/sidekiq/debouncer/poller.rb
146
+ - lib/sidekiq/debouncer/set.rb
124
147
  - lib/sidekiq/debouncer/version.rb
148
+ - lib/sidekiq/debouncer/views/index.html.erb
149
+ - lib/sidekiq/debouncer/views/show.html.erb
150
+ - lib/sidekiq/debouncer/web.rb
151
+ - lib/sidekiq/debouncer/web_extension.rb
125
152
  - sidekiq-debouncer.gemspec
126
153
  homepage: https://github.com/paladinsoftware/sidekiq-debouncer
127
154
  licenses:
@@ -135,7 +162,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
135
162
  requirements:
136
163
  - - ">="
137
164
  - !ruby/object:Gem::Version
138
- version: 2.7.0
165
+ version: 3.0.0
139
166
  required_rubygems_version: !ruby/object:Gem::Requirement
140
167
  requirements:
141
168
  - - ">="