sidekiq-pauzer 1.0.0.alpha → 1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b38aee70ca050a4c5c0f8c41d81bae5b755bb4a73fd1b5ddb7b365fb61ba3f40
4
- data.tar.gz: e233b4b60fc065a12c29f809ad196f763f05a54d7bec640c338dce6ec2c62984
3
+ metadata.gz: b31cb662c56453b04bebf1a887af9515aa0d86a74f9c9e0c6bb79513c499ed7e
4
+ data.tar.gz: c57e7667afcabea723ffdb57f5ae38f53ab756c22f2952bb1344d660df806bd4
5
5
  SHA512:
6
- metadata.gz: 8e106b6c3e162eafddf56da121c6d3af3319928a145c1991f9c09ceeb54751eb68a15f716731e5bf2feb15fd785a7062ce19db5523a79b8a3be2e1889af39c94
7
- data.tar.gz: 1db224ec249b5729e903a0ad8fc9f58235e8a39c5bc39f00a2e11a426c3a0ec20fb216faa5bf144ef20bbfa93f4dc94d8599546ab46052724142a89420e29a07
6
+ metadata.gz: 5456887979763bf5c4c741fa2dec95c4454222f77d65f212f4472a68dd248880a7b18687a6df999d9c6c25291d01027c01c4fed4630948d579cd9f2174f8635e
7
+ data.tar.gz: af94a161c39ff35abd29aa35a110c69d25144fd7b3d54b63bae65c64053694813e1eb7a8741e35182342b9e3c9b8de041290e8f46ffc3da6e34b6f489988365b
data/README.adoc CHANGED
@@ -34,6 +34,25 @@ Sidekiq.configure_server do |config|
34
34
  end
35
35
  ----
36
36
 
37
+ === API
38
+
39
+ This gem enhances Sidekiq's Queue API with:
40
+
41
+ [source, ruby]
42
+ ----
43
+ Sidekiq::Queue.new("critical").paused? # => true|false
44
+ Sidekiq::Queue.new("critical").pause!
45
+ Sidekiq::Queue.new("critical").unpause!
46
+ ----
47
+
48
+ Those translates to:
49
+
50
+ [source, ruby]
51
+ ----
52
+ Sidekiq::Pauzer.paused?("critical") # => true|false
53
+ Sidekiq::Pauzer.pause!("critical")
54
+ Sidekiq::Pauzer.unpause!("critical")
55
+ ----
37
56
 
38
57
  === Adding Pause/Resume Button to the Queues Tab
39
58
 
@@ -47,7 +66,39 @@ require "sidekiq/pauzer/web"
47
66
  ----
48
67
 
49
68
  NOTE: If you are using custom Sidekiq views path, then you will need to call
50
- (after requiring `sidekiq/pauzer/web`): `Sidekiq::Pauzer.unpatch_views!`.
69
+ (after requiring `sidekiq/pauzer/web`): `Sidekiq::Pauzer::Web.unpatch_views!`.
70
+
71
+
72
+ == Supported Ruby Versions
73
+
74
+ This library aims to support and is tested against the following Ruby versions:
75
+
76
+ * Ruby 2.7.x
77
+ * Ruby 3.0.x
78
+ * Ruby 3.1.x
79
+ * Ruby 3.2.x
80
+
81
+ If something doesn't work on one of these versions, it's a bug.
82
+
83
+ This library may inadvertently work (or seem to work) on other Ruby versions,
84
+ however support will only be provided for the versions listed above.
85
+
86
+ If you would like this library to support another Ruby version or
87
+ implementation, you may volunteer to be a maintainer. Being a maintainer
88
+ entails making sure all tests run and pass on that implementation. When
89
+ something breaks on your implementation, you will be responsible for providing
90
+ patches in a timely fashion. If critical issues for a particular implementation
91
+ exist at the time of a major release, support for that Ruby version may be
92
+ dropped.
93
+
94
+
95
+ == Supported Sidekiq Versions
96
+
97
+ This library aims to support and work with following Sidekiq versions:
98
+
99
+ * Sidekiq 6.5.x
100
+ * Sidekiq 7.0.x
101
+ * Sidekiq 7.1.x
51
102
 
52
103
 
53
104
  == Development
@@ -13,15 +13,15 @@ module Sidekiq
13
13
  false
14
14
  end
15
15
 
16
- def pause!(redis, key, queue)
16
+ def add(redis, key, queue)
17
17
  redis.sadd(key, queue)
18
18
  end
19
19
 
20
- def unpause!(redis, key, queue)
20
+ def remove(redis, key, queue)
21
21
  redis.srem(key, queue)
22
22
  end
23
23
 
24
- def paused_queues(redis, key)
24
+ def list(redis, key)
25
25
  # Cursor is not atomic, so there may be duplicates because of
26
26
  # concurrent update operations
27
27
  # See: https://redis.io/commands/scan/#scan-guarantees
@@ -1,31 +1,30 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "../runtime"
4
+
3
5
  module Sidekiq
4
6
  module Pauzer
5
7
  module Adapters
6
8
  # redis-client adapter
7
9
  module RedisClient
8
- SIDEKIQ_SEVEN = Gem::Version.new("7.0.0").freeze
9
- SIDEKIQ_VERSION = Gem::Version.new(Sidekiq::VERSION).freeze
10
-
11
10
  class << self
12
11
  def adapts?(redis)
13
- return true if SIDEKIQ_SEVEN <= SIDEKIQ_VERSION
12
+ return true if Runtime::SIDEKIQ_SEVEN
14
13
  return true if defined?(::RedisClient) && redis.is_a?(::RedisClient)
15
14
  return true if defined?(::RedisClient::Decorator::Client) && redis.is_a?(::RedisClient::Decorator::Client)
16
15
 
17
16
  false
18
17
  end
19
18
 
20
- def pause!(redis, key, queue)
19
+ def add(redis, key, queue)
21
20
  redis.call("SADD", key, queue)
22
21
  end
23
22
 
24
- def unpause!(redis, key, queue)
23
+ def remove(redis, key, queue)
25
24
  redis.call("SREM", key, queue)
26
25
  end
27
26
 
28
- def paused_queues(redis, key)
27
+ def list(redis, key)
29
28
  # Cursor is not atomic, so there may be duplicates because of
30
29
  # concurrent update operations
31
30
  # See: https://redis.io/commands/scan/#scan-guarantees
@@ -3,37 +3,47 @@
3
3
  require "sidekiq"
4
4
  require "sidekiq/fetch"
5
5
 
6
+ require_relative "./runtime"
7
+
6
8
  module Sidekiq
7
9
  module Pauzer
8
10
  # Default Sidekiq's BasicFetch infused with Pauzer
9
11
  class BasicFetch < Sidekiq::BasicFetch
10
12
  private
11
13
 
12
- if Gem::Version.new("7.0.0") <= Gem::Version.new(Sidekiq::VERSION)
14
+ if Runtime::SIDEKIQ_SEVEN
13
15
  def queues_cmd
14
- if @strictly_ordered_queues
15
- @queues - Pauzer.paused_queues
16
- else
17
- permute = (@queues - Pauzer.paused_queues)
18
- permute.shuffle!
19
- permute.uniq!
20
- permute
21
- end
16
+ queues =
17
+ if @strictly_ordered_queues
18
+ @queues
19
+ else
20
+ permute = @queues.dup
21
+ permute.shuffle!
22
+ permute.uniq!
23
+ permute
24
+ end
25
+
26
+ excluding_paused(queues)
22
27
  end
23
28
  else
24
29
  def queues_cmd
25
- if @strictly_ordered_queues
26
- *queues, timeout = @queues
30
+ queues =
31
+ if @strictly_ordered_queues
32
+ @queues[0...-1]
33
+ else
34
+ permute = @queues.dup
35
+ permute.shuffle!
36
+ permute.uniq!
37
+ permute
38
+ end
27
39
 
28
- (queues - Pauzer.paused_queues) << timeout
29
- else
30
- permute = (@queues - Pauzer.paused_queues)
31
- permute.shuffle!
32
- permute.uniq!
33
- permute << { timeout: Sidekiq::BasicFetch::TIMEOUT }
34
- end
40
+ excluding_paused(queues) << { timeout: Sidekiq::BasicFetch::TIMEOUT }
35
41
  end
36
42
  end
43
+
44
+ def excluding_paused(queues)
45
+ queues - Pauzer.paused_queue_names.map { |name| "queue:#{name}" }
46
+ end
37
47
  end
38
48
  end
39
49
  end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "sidekiq"
4
+ require "sidekiq/api"
5
+
6
+ module Sidekiq
7
+ module Pauzer
8
+ module Patches
9
+ # @private
10
+ module Queue
11
+ def self.apply!
12
+ Sidekiq::Queue.prepend(self)
13
+ end
14
+
15
+ def paused?
16
+ Pauzer.paused?(name)
17
+ end
18
+
19
+ def pause!
20
+ Pauzer.pause!(name)
21
+ nil
22
+ end
23
+
24
+ def unpause!
25
+ Pauzer.unpause!(name)
26
+ nil
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "erb"
4
+
5
+ require "sidekiq"
6
+ require "sidekiq/web"
7
+
8
+ module Sidekiq
9
+ module Pauzer
10
+ module Patches
11
+ # @private
12
+ module WebAction
13
+ PAUZER_QUEUES_TEMPLATE =
14
+ ERB.new(File.read(File.expand_path("../../../../web/views/queues.erb", __dir__))).src
15
+
16
+ class << self
17
+ def apply!
18
+ revert!
19
+
20
+ Sidekiq::WebAction.class_eval <<-RUBY, __FILE__, __LINE__ + 1 # rubocop:disable Style/DocumentDynamicEvalDefinition
21
+ def _erb_queues
22
+ #{PAUZER_QUEUES_TEMPLATE}
23
+ end
24
+ RUBY
25
+ end
26
+
27
+ def revert!
28
+ Sidekiq::WebAction.remove_method(:_erb_queues) if Sidekiq::WebAction.method_defined?(:_erb_queues)
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "sidekiq"
4
+ require "sidekiq/web"
5
+
6
+ module Sidekiq
7
+ module Pauzer
8
+ module Patches
9
+ # @private
10
+ module WebApplication
11
+ class << self
12
+ def apply!
13
+ remove_theirs_queue_update_route
14
+ register_ours_queue_update_route
15
+ end
16
+
17
+ private
18
+
19
+ def remove_theirs_queue_update_route
20
+ Sidekiq::WebApplication
21
+ .instance_variable_get(:@routes)
22
+ .fetch(Sidekiq::WebRouter::POST)
23
+ .delete_if { |route| route.pattern == "/queues/:name" }
24
+ end
25
+
26
+ def register_ours_queue_update_route # rubocop:disable Metrics/MethodLength
27
+ Sidekiq::WebApplication.class_exec do
28
+ post "/queues/:name" do
29
+ queue = Sidekiq::Queue.new(route_params[:name])
30
+
31
+ if params["pause"]
32
+ queue.pause!
33
+ elsif params["unpause"]
34
+ queue.unpause!
35
+ else
36
+ queue.clear
37
+ end
38
+
39
+ redirect "#{root_path}queues"
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -12,12 +12,10 @@ module Sidekiq
12
12
 
13
13
  class Refresher < Concurrent::TimerTask; end
14
14
 
15
- QUEUE_PREFIX = "queue:"
16
-
17
15
  # @param config [Config]
18
16
  def initialize(config)
19
17
  @mutex = Mutex.new
20
- @queues = []
18
+ @names = []
21
19
  @redis_key = config.redis_key
22
20
  @refresher = initialize_refresher(config.refresh_rate)
23
21
  end
@@ -25,39 +23,41 @@ module Sidekiq
25
23
  def each(&block)
26
24
  return to_enum __method__ unless block
27
25
 
28
- @mutex.synchronize { @queues.dup }.each(&block)
26
+ @mutex.synchronize { @names.dup }.each(&block)
29
27
 
30
28
  self
31
29
  end
32
30
 
33
- def pause!(queue)
34
- queue = normalize_queue_name(queue)
35
-
36
- Sidekiq.redis { |conn| Adapters[conn].pause!(conn, @redis_key, queue) }
31
+ # @param name [#to_s]
32
+ def pause!(name)
33
+ Sidekiq.redis { |conn| Adapters[conn].add(conn, @redis_key, name.to_s) }
37
34
 
38
35
  refresh
39
36
  end
40
37
 
41
- def unpause!(queue)
42
- queue = normalize_queue_name(queue)
43
-
44
- Sidekiq.redis { |conn| Adapters[conn].unpause!(conn, @redis_key, queue) }
38
+ # @param name [#to_s]
39
+ def unpause!(name)
40
+ Sidekiq.redis { |conn| Adapters[conn].remove(conn, @redis_key, name.to_s) }
45
41
 
46
42
  refresh
47
43
  end
48
44
 
49
- def paused?(queue)
50
- include?(normalize_queue_name(queue))
45
+ # @param name [#to_s]
46
+ # @return [Boolean]
47
+ def paused?(name)
48
+ include?(name.to_s)
51
49
  end
52
50
 
53
51
  def start_refresher
54
52
  @refresher.execute
55
- nil
53
+
54
+ self
56
55
  end
57
56
 
58
57
  def stop_refresher
59
58
  @refresher.shutdown
60
- nil
59
+
60
+ self
61
61
  end
62
62
 
63
63
  def refresher_running?
@@ -74,19 +74,13 @@ module Sidekiq
74
74
 
75
75
  def refresh
76
76
  @mutex.synchronize do
77
- paused_queues = Sidekiq.redis do |conn|
78
- Adapters[conn].paused_queues(conn, @redis_key)
79
- end
77
+ names = Sidekiq.redis { |conn| Adapters[conn].list(conn, @redis_key) }
80
78
 
81
- @queues.replace(paused_queues)
79
+ @names.replace(names)
82
80
  end
83
81
 
84
82
  self
85
83
  end
86
-
87
- def normalize_queue_name(queue)
88
- queue.dup.delete_prefix(QUEUE_PREFIX)
89
- end
90
84
  end
91
85
  end
92
86
  end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "sidekiq"
4
+
5
+ module Sidekiq
6
+ module Pauzer
7
+ module Runtime
8
+ SIDEKIQ_SEVEN = Gem::Version.new("7.0.0") <= Gem::Version.new(Sidekiq::VERSION)
9
+ end
10
+ end
11
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Sidekiq
4
4
  module Pauzer
5
- VERSION = "1.0.0.alpha"
5
+ VERSION = "1.1.0"
6
6
  end
7
7
  end
@@ -1,46 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "erb"
4
- require "sidekiq"
5
- require "sidekiq/web"
6
-
7
- require_relative "../pauzer"
3
+ require_relative "./patches/web_action"
4
+ require_relative "./patches/web_application"
8
5
 
9
6
  module Sidekiq
10
7
  module Pauzer
11
- def self.unpatch_views!
12
- WebAction.remove_method(:_erb_queues)
13
- end
14
- end
15
-
16
- class WebApplication
17
- @routes[Sidekiq::WebRouter::POST].delete_if do |web_route|
18
- web_route.pattern == "/queues/:name"
19
- end
20
-
21
- post "/queues/:name" do
22
- queue = Sidekiq::Queue.new(route_params[:name])
23
-
24
- if params["pause"]
25
- queue.pause!
26
- elsif params["unpause"]
27
- queue.unpause!
28
- else
29
- queue.clear
8
+ module Web
9
+ def self.unpatch_views!
10
+ Patches::WebAction.revert!
30
11
  end
31
-
32
- redirect "#{root_path}queues"
33
12
  end
34
13
  end
35
-
36
- class WebAction
37
- PAUZER_QUEUES_TEMPLATE =
38
- ERB.new(File.read(File.expand_path("../../../web/views/queues.erb", __dir__))).src
39
-
40
- class_eval <<-RUBY, __FILE__, __LINE__ + 1 # rubocop:disable Style/DocumentDynamicEvalDefinition
41
- def _erb_queues
42
- #{PAUZER_QUEUES_TEMPLATE}
43
- end
44
- RUBY
45
- end
46
14
  end
15
+
16
+ Sidekiq::Pauzer::Patches::WebAction.apply!
17
+ Sidekiq::Pauzer::Patches::WebApplication.apply!
@@ -6,6 +6,7 @@ require "sidekiq/api"
6
6
 
7
7
  require_relative "./pauzer/basic_fetch"
8
8
  require_relative "./pauzer/config"
9
+ require_relative "./pauzer/patches/queue"
9
10
  require_relative "./pauzer/queues"
10
11
  require_relative "./pauzer/version"
11
12
 
@@ -28,57 +29,123 @@ module Sidekiq
28
29
  module Pauzer
29
30
  MUTEX = Mutex.new
30
31
 
31
- @config = Config.new
32
+ @config = Config.new.freeze
32
33
  @queues = Queues.new(@config)
33
34
 
34
35
  class << self
35
36
  extend Forwardable
36
37
 
37
- def_delegators :@queues, :pause!, :unpause!, :paused?
38
+ # @!attribute [r] redis_key
39
+ # @see Config#redis_key
40
+ # @return [String]
41
+ def_delegators :@config, :redis_key
42
+
43
+ # @example
44
+ # Sidekiq::Pauzer.pause!("minor")
45
+ # Sidekiq::Pauzer.paused?("minor") # => true
46
+ #
47
+ # @param (see Queues#pause!)
48
+ # @return [void]
49
+ def pause!(name)
50
+ @queues.pause!(name)
51
+
52
+ nil
53
+ end
54
+
55
+ # @example
56
+ # Sidekiq::Pauzer.pause!("minor")
57
+ # Sidekiq::Pauzer.paused?("minor") # => true
58
+ # Sidekiq::Pauzer.unpause!("minor")
59
+ # Sidekiq::Pauzer.paused?("minor") # => false
60
+ #
61
+ # @param (see Queues#unpause!)
62
+ # @return [void]
63
+ def unpause!(name)
64
+ @queues.unpause!(name)
65
+
66
+ nil
67
+ end
68
+
69
+ # @example
70
+ # Sidekiq::Pauzer.pause!("minor")
71
+ # Sidekiq::Pauzer.paused?("minor") # => true
72
+ # Sidekiq::Pauzer.paused?("threat") # => false
73
+ #
74
+ # @see Queues#paused?
75
+ def paused?(name)
76
+ @queues.paused?(name)
77
+ end
38
78
 
79
+ # @example
80
+ # Sidekiq::Pauzer.pause!("minor")
81
+ # Sidekiq::Pauzer.paused_queue_names # => ["minor"]
82
+ #
83
+ # @return [Array<String>]
84
+ def paused_queue_names
85
+ @queues.to_a
86
+ end
87
+
88
+ # @deprecated Use {.paused_queue_names} instead.
89
+ # Will be removed in ‹2.0.0›.
90
+ #
91
+ # @example
92
+ # Sidekiq::Pauzer.pause!("minor")
93
+ # Sidekiq::Pauzer.paused_queues # => ["queue:minor"]
94
+ #
95
+ # @return [Array<String>]
39
96
  def paused_queues
40
- @queues.map { |queue| "#{Queues::QUEUE_PREFIX}#{queue}" }
97
+ @queues.map { |name| "queue:#{name}" }
41
98
  end
42
99
 
100
+ # Yields `config` for a block.
101
+ #
102
+ # @example
103
+ # Sidekiq::Pauzer.configure do |config|
104
+ # config.refresh_rate = 42
105
+ # end
106
+ #
107
+ # @yieldparam config [Config]
43
108
  def configure
44
109
  MUTEX.synchronize do
45
- yield @config
110
+ config = @config.dup
111
+
112
+ yield config
113
+
114
+ @config = config.freeze
115
+
116
+ self
46
117
  ensure
47
- start_refresher = @queues.refresher_running?
48
- @queues.stop_refresher
49
- @queues = Queues.new(@config)
50
- @queues.start_refresher if start_refresher
118
+ reinit_queues
51
119
  end
52
120
  end
53
121
 
54
122
  def startup
55
123
  MUTEX.synchronize { @queues.start_refresher }
124
+
125
+ self
56
126
  end
57
127
 
58
128
  def shutdown
59
129
  MUTEX.synchronize { @queues.stop_refresher }
60
- end
61
- end
62
- end
63
-
64
- class Queue
65
- remove_method :paused?
66
130
 
67
- def paused?
68
- Pauzer.paused?(name)
69
- end
131
+ self
132
+ end
70
133
 
71
- def pause!
72
- Pauzer.pause!(name)
73
- end
134
+ private
74
135
 
75
- def unpause!
76
- Pauzer.unpause!(name)
136
+ def reinit_queues
137
+ start_refresher = @queues.refresher_running?
138
+ @queues.stop_refresher
139
+ @queues = Queues.new(@config)
140
+ @queues.start_refresher if start_refresher
141
+ end
77
142
  end
78
143
  end
79
144
 
80
145
  configure_server do |config|
81
- config.on(:startup) { Pauzer.startup }
146
+ config.on(:startup) { Pauzer.startup }
82
147
  config.on(:shutdown) { Pauzer.shutdown }
83
148
  end
84
149
  end
150
+
151
+ Sidekiq::Pauzer::Patches::Queue.apply!
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sidekiq-pauzer
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0.alpha
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexey Zapparov
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-05-03 00:00:00.000000000 Z
11
+ date: 2023-05-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
@@ -53,7 +53,11 @@ files:
53
53
  - lib/sidekiq/pauzer/adapters/redis_client.rb
54
54
  - lib/sidekiq/pauzer/basic_fetch.rb
55
55
  - lib/sidekiq/pauzer/config.rb
56
+ - lib/sidekiq/pauzer/patches/queue.rb
57
+ - lib/sidekiq/pauzer/patches/web_action.rb
58
+ - lib/sidekiq/pauzer/patches/web_application.rb
56
59
  - lib/sidekiq/pauzer/queues.rb
60
+ - lib/sidekiq/pauzer/runtime.rb
57
61
  - lib/sidekiq/pauzer/version.rb
58
62
  - lib/sidekiq/pauzer/web.rb
59
63
  - web/views/queues.erb
@@ -62,9 +66,9 @@ licenses:
62
66
  - MIT
63
67
  metadata:
64
68
  homepage_uri: https://gitlab.com/ixti/sidekiq-pauzer
65
- source_code_uri: https://gitlab.com/ixti/sidekiq-pauzer/tree/v1.0.0.alpha
69
+ source_code_uri: https://gitlab.com/ixti/sidekiq-pauzer/tree/v1.1.0
66
70
  bug_tracker_uri: https://gitlab.com/ixti/sidekiq-pauzer/issues
67
- changelog_uri: https://gitlab.com/ixti/sidekiq-pauzer/blob/v1.0.0.alpha/CHANGES.md
71
+ changelog_uri: https://gitlab.com/ixti/sidekiq-pauzer/blob/v1.1.0/CHANGES.md
68
72
  rubygems_mfa_required: 'true'
69
73
  post_install_message:
70
74
  rdoc_options: []
@@ -77,9 +81,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
77
81
  version: '2.7'
78
82
  required_rubygems_version: !ruby/object:Gem::Requirement
79
83
  requirements:
80
- - - ">"
84
+ - - ">="
81
85
  - !ruby/object:Gem::Version
82
- version: 1.3.1
86
+ version: '0'
83
87
  requirements: []
84
88
  rubygems_version: 3.4.10
85
89
  signing_key: