action_subscriber 5.0.3.pre1-java → 5.1.0-java

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7291284b5fd618c348a69062e5ce01edc02f34044b68634b2f89c719679887c8
4
- data.tar.gz: cba17122b1c1ff5d9cdd61cde066f3bb15a8515c8229866aaf73acaa93885585
3
+ metadata.gz: 83a060130b450c10189a3016180bb83c106ce9a09e22a3ea13aab674552117e7
4
+ data.tar.gz: e5da3d9bfa655d51abb1a290db3d2ca9a8e9c821a5fd2fa9db1c3346cf14c6d2
5
5
  SHA512:
6
- metadata.gz: 826639bdac9d33133e8a62999a813615f76a01d2f12305418fb6014dff9c5639d4815a3c7d204f930c936f23cbd0ee96e23b81cdd1f7f70be6faa3c4a5b6ab4b
7
- data.tar.gz: 33d0ee6088708a2395136b627a503c6a9262ab2ee13a5935c7e2339622e65c13b79fdc294c9707fe4a4d61f9f93a9a0ec95436e70a3cc656b51e78340f2c5798
6
+ metadata.gz: e57c7ba81153c42043c3b7ca8e4ccc1a83a2d8d80c5e72e867bfbae0fcefb00c67f200ba6c0c7a714353826d2df69f39baaee90727c65a5589ce1a9a9fd00221
7
+ data.tar.gz: 9b46cc26440fc1309636adf6ca728c8577f085298022359523e9f20a6264ef902335e626a9f2f0fcf4348aa2c5d75557aff2d1cf4ba1abfefc5e3db8ebe448f7
data/README.md CHANGED
@@ -160,7 +160,7 @@ This also allows the broker to send messages as quickly as it wants down to your
160
160
 
161
161
  ### manual_acknowledgement!
162
162
 
163
- This mode leaves it up to the subscriber to handle acknowledging or rejecting messages. In your subscriber you can just call <code>acknowledge</code> or <code>reject</code>.
163
+ This mode leaves it up to the subscriber to handle acknowledging or rejecting messages. In your subscriber you can just call <code>acknowledge</code>, <code>reject</code>, or <code>nack</code>.
164
164
 
165
165
  ### at_most_once!
166
166
 
@@ -172,6 +172,11 @@ Rabbit is told to expect message acknowledgements, but sending the acknowledgeme
172
172
  We send the acknowledgement right after calling your subscriber.
173
173
  If an error is raised your message will be retried on a sent back to rabbitmq and retried on an exponential backoff schedule.
174
174
 
175
+ ### safe_nack
176
+ If you turn on acknowledgements and a message is not acknowledged by your code manually or using one of the filters above the `ErrorHandler` middleware
177
+ which wraps the entire block with call <code>nack</code> this is a last resort so the connection does not get backed up in cases of unexpected or
178
+ unhandled errors.
179
+
175
180
  ### redeliver
176
181
 
177
182
  A message can be sent to "redeliver" with `::ActionSubscriber::MessageRetry.redeliver_message_with_backoff` or the DSL method `redeliver` and optionally
@@ -33,6 +33,7 @@ Gem::Specification.new do |spec|
33
33
  spec.add_development_dependency "active_publisher", "~> 0.1.5"
34
34
  spec.add_development_dependency "activerecord", ">= 3.2"
35
35
  spec.add_development_dependency "bundler", ">= 1.6"
36
+ spec.add_development_dependency "pry-coolline"
36
37
  spec.add_development_dependency "pry-nav"
37
38
  spec.add_development_dependency "rabbitmq_http_api_client", "~> 1.2.0"
38
39
  spec.add_development_dependency "rspec", "~> 3.0"
@@ -44,6 +44,7 @@ module ActionSubscriber
44
44
  :message_id => properties.message_id,
45
45
  :routing_key => delivery_info.routing_key,
46
46
  :queue => queue.name,
47
+ :uses_acknowledgements => route.acknowledgements?,
47
48
  }
48
49
  env = ::ActionSubscriber::Middleware::Env.new(route.subscriber, encoded_payload, properties)
49
50
  run_env(env, threadpool)
@@ -9,10 +9,9 @@ module ActionSubscriber
9
9
  !!@_at_least_once
10
10
  end
11
11
 
12
- def at_most_once!(ack_every_n_messages = 1)
12
+ def at_most_once!
13
13
  @_acknowledge_messages = true
14
14
  @_at_most_once = true
15
- @_ack_every_n_messages = ack_every_n_messages
16
15
  end
17
16
 
18
17
  def at_most_once?
@@ -55,10 +54,6 @@ module ActionSubscriber
55
54
  !!@_manual_acknowedgement
56
55
  end
57
56
 
58
- def ack_every_n_messages
59
- @_ack_every_n_messages || 1
60
- end
61
-
62
57
  def no_acknowledgement!
63
58
  @_acknowledge_messages = false
64
59
  end
@@ -136,41 +131,6 @@ module ActionSubscriber
136
131
  end
137
132
  end
138
133
 
139
- def _run_action_at_most_once_multiple_with_filters(env, action)
140
- processed_acknowledgement = false
141
- rejected_message = false
142
- if env.delivery_tag % ack_every_n_messages == 0 # tags are monotonically increasing integers
143
- processed_acknowledgement = env.acknowledge(true)
144
- else
145
- processed_acknowledgement = true # we are not acknowledging on this message and will wait for the offset to acknowledge
146
- end
147
-
148
- _run_action_with_filters(env, action)
149
- ensure
150
- rejected_message = env.reject if !processed_acknowledgement
151
-
152
- if !rejected_message && !processed_acknowledgement
153
- $stdout << <<-UNREJECTABLE
154
- CANNOT ACKNOWLEDGE OR REJECT THE MESSAGE
155
-
156
- This is a exceptional state for ActionSubscriber to enter and puts the current
157
- Process in the position of "I can't get new work from RabbitMQ, but also
158
- can't acknowledge or reject the work that I currently have" ... While rare
159
- this state can happen.
160
-
161
- Instead of continuing to try to process the message ActionSubscriber is
162
- sending a Kill signal to the current running process to gracefully shutdown
163
- so that the RabbitMQ server will purge any outstanding acknowledgements. If
164
- you are running a process monitoring tool (like Upstart) the Subscriber
165
- process will be restarted and be able to take on new work.
166
-
167
- ** Running a process monitoring tool like Upstart is recommended for this reason **
168
- UNREJECTABLE
169
-
170
- Process.kill(:TERM, Process.pid)
171
- end
172
- end
173
-
174
134
  def _run_action_at_least_once_with_filters(env, action)
175
135
  processed_acknowledgement = false
176
136
  rejected_message = false
@@ -212,10 +172,8 @@ module ActionSubscriber
212
172
  case
213
173
  when at_least_once?
214
174
  _run_action_at_least_once_with_filters(env, action)
215
- when at_most_once? && ack_every_n_messages <= 1 # Acknowledging every single message
175
+ when at_most_once?
216
176
  _run_action_at_most_once_with_filters(env, action)
217
- when at_most_once? && ack_every_n_messages > 1 # Acknowledging messages in offset groups (every 10 messages or whatever the offset is)
218
- _run_action_at_most_once_multiple_with_filters(env, action)
219
177
  else
220
178
  _run_action_with_filters(env, action)
221
179
  end
@@ -42,6 +42,7 @@ module ActionSubscriber
42
42
  :message_id => metadata.message_id,
43
43
  :routing_key => metadata.routing_key,
44
44
  :queue => queue.name,
45
+ :uses_acknowledgements => route.acknowledgements?,
45
46
  }
46
47
  env = ::ActionSubscriber::Middleware::Env.new(route.subscriber, encoded_payload, properties)
47
48
  run_env(env, threadpool)
@@ -1,3 +1,4 @@
1
+ require "action_subscriber/logging"
1
2
  require "action_subscriber/middleware/decoder"
2
3
  require "action_subscriber/middleware/env"
3
4
  require "action_subscriber/middleware/error_handler"
@@ -6,11 +7,26 @@ require "action_subscriber/middleware/runner"
6
7
 
7
8
  module ActionSubscriber
8
9
  module Middleware
10
+
11
+ class Builder < ::Middleware::Builder
12
+ include ::ActionSubscriber::Logging
13
+
14
+ def print_middleware_stack
15
+ logger.info "Middlewares ["
16
+
17
+ stack.each do |middleware|
18
+ logger.info "#{middleware}"
19
+ end
20
+
21
+ logger.info "]"
22
+ end
23
+ end
24
+
9
25
  def self.initialize_stack
10
- builder = ::Middleware::Builder.new(:runner_class => ::ActionSubscriber::Middleware::Runner)
26
+ builder = ::ActionSubscriber::Middleware::Builder.new(:runner_class => ::ActionSubscriber::Middleware::Runner)
11
27
 
12
- builder.use ErrorHandler
13
- builder.use Decoder
28
+ builder.use ::ActionSubscriber::Middleware::ErrorHandler
29
+ builder.use ::ActionSubscriber::Middleware::Decoder
14
30
 
15
31
  builder
16
32
  end
@@ -38,7 +38,7 @@ module ActionSubscriber
38
38
  end
39
39
 
40
40
  def call(env)
41
- def call(env)
41
+ def call(env) # redefines so it only gets called once
42
42
  ::ActiveRecord::Base.connection_pool.with_connection do
43
43
  @app.call(env)
44
44
  end
@@ -18,7 +18,7 @@ module ActionSubscriber
18
18
  private
19
19
 
20
20
  def decoder
21
- ActionSubscriber.config.decoder[env.content_type]
21
+ ::ActionSubscriber.config.decoder[env.content_type]
22
22
  end
23
23
 
24
24
  def decoder?
@@ -7,7 +7,6 @@ module ActionSubscriber
7
7
 
8
8
  attr_reader :action,
9
9
  :content_type,
10
- :delivery_tag,
11
10
  :encoded_payload,
12
11
  :exchange,
13
12
  :headers,
@@ -34,26 +33,54 @@ module ActionSubscriber
34
33
  @delivery_tag = properties.fetch(:delivery_tag)
35
34
  @encoded_payload = encoded_payload
36
35
  @exchange = properties.fetch(:exchange)
37
- @headers = properties.fetch(:headers) || {}
38
- @message_id = properties.fetch(:message_id) || ::SecureRandom.hex(3)
36
+ @has_been_acked = false
37
+ @has_been_nacked = false
38
+ @has_been_rejected = false
39
+ @headers = properties.fetch(:headers, {})
40
+ @message_id = properties.fetch(:message_id, ::SecureRandom.hex(3))
39
41
  @queue = properties.fetch(:queue)
40
42
  @routing_key = properties.fetch(:routing_key)
41
43
  @subscriber = subscriber
44
+ @uses_acknowledgements = properties.fetch(:uses_acknowledgements, false)
42
45
  end
43
46
 
44
- def acknowledge(acknowledge_multiple_messages = false)
47
+ def acknowledge
45
48
  fail ::RuntimeError, "you can't acknowledge messages under the polling API" unless @channel
49
+ acknowledge_multiple_messages = false
50
+ @has_been_acked = true
46
51
  @channel.ack(@delivery_tag, acknowledge_multiple_messages)
47
52
  true
48
53
  end
49
54
 
55
+ def nack
56
+ fail ::RuntimeError, "you can't acknowledge messages under the polling API" unless @channel
57
+ nack_multiple_messages = false
58
+ requeue_message = true
59
+ @has_been_nacked = true
60
+ @channel.nack(@delivery_tag, nack_multiple_messages, requeue_message)
61
+ true
62
+ end
63
+
50
64
  def reject
51
65
  fail ::RuntimeError, "you can't acknowledge messages under the polling API" unless @channel
52
66
  requeue_message = true
67
+ @has_been_rejected = true
53
68
  @channel.reject(@delivery_tag, requeue_message)
54
69
  true
55
70
  end
56
71
 
72
+ def safe_acknowledge
73
+ acknowledge if uses_acknowledgements? && @channel && !has_used_delivery_tag?
74
+ end
75
+
76
+ def safe_nack
77
+ nack if uses_acknowledgements? && @channel && !has_used_delivery_tag?
78
+ end
79
+
80
+ def safe_reject
81
+ reject if uses_acknowledgements? && @channel && !has_used_delivery_tag?
82
+ end
83
+
57
84
  def to_hash
58
85
  {
59
86
  :action => action,
@@ -65,6 +92,15 @@ module ActionSubscriber
65
92
  end
66
93
  alias_method :to_h, :to_hash
67
94
 
95
+ private
96
+
97
+ def has_used_delivery_tag?
98
+ @has_been_acked || @has_been_nacked || @has_been_rejected
99
+ end
100
+
101
+ def uses_acknowledgements?
102
+ @uses_acknowledgements
103
+ end
68
104
  end
69
105
  end
70
106
  end
@@ -9,8 +9,8 @@ module ActionSubscriber
9
9
 
10
10
  def call(env)
11
11
  @app.call(env)
12
- rescue => error
13
- logger.error "FAILED #{env.message_id}"
12
+ rescue Exception => error # make sure we capture any exception from the top of the hierarchy
13
+ logger.error { "FAILED #{env.message_id}" }
14
14
 
15
15
  # There is more to this rescue than meets the eye. MarchHare's java library will rescue errors
16
16
  # and attempt to close the channel with its default exception handler. To avoid this, we will
@@ -18,9 +18,11 @@ module ActionSubscriber
18
18
  # it should not re-raise. As a bonus, not killing these threads is better for your runtime :).
19
19
  begin
20
20
  ::ActionSubscriber.configuration.error_handler.call(error, env.to_h)
21
- rescue => error
22
- logger.error "ActionSubscriber error handler raised error, but should never raise. Error: #{error}"
21
+ rescue Exception => inner_error
22
+ logger.error { "ActionSubscriber error handler raised error, but should never raise. Error: #{inner_error}" }
23
23
  end
24
+ ensure
25
+ env.safe_nack # Make sure we attempt to `nack` a message that did not get processed if something fails
24
26
  end
25
27
  end
26
28
  end
@@ -1,6 +1,7 @@
1
1
  module ActionSubscriber
2
2
  module Middleware
3
3
  class Router
4
+ INSTRUMENT_KEY = "process_event.action_subscriber".freeze
4
5
  include ::ActionSubscriber::Logging
5
6
 
6
7
  def initialize(app)
@@ -8,9 +9,27 @@ module ActionSubscriber
8
9
  end
9
10
 
10
11
  def call(env)
11
- logger.info "START #{env.message_id} #{env.subscriber}##{env.action}"
12
- env.subscriber.run_action_with_filters(env, env.action)
13
- logger.info "FINISHED #{env.message_id}"
12
+ action = env.action
13
+ message_id = env.message_id
14
+ queue = env.queue
15
+ routing_key = env.routing_key
16
+ subscriber = env.subscriber
17
+
18
+ logger.info { "START #{message_id} #{subscriber}##{action}" }
19
+
20
+ instrument_call(subscriber, routing_key, queue) do
21
+ subscriber.run_action_with_filters(env, action)
22
+ end
23
+
24
+ logger.info { "FINISHED #{message_id}" }
25
+ end
26
+
27
+ private
28
+
29
+ def instrument_call(subscriber, routing_key, queue)
30
+ ::ActiveSupport::Notifications.instrument INSTRUMENT_KEY, :subscriber => subscriber.to_s, :routing_key => routing_key, :queue => queue do
31
+ yield
32
+ end
14
33
  end
15
34
  end
16
35
  end
@@ -6,8 +6,8 @@ module ActionSubscriber
6
6
  require "action_subscriber/middleware/active_record/connection_management"
7
7
  require "action_subscriber/middleware/active_record/query_cache"
8
8
 
9
- ::ActionSubscriber.config.middleware.use ::ActionSubscriber::Middleware::ActiveRecord::ConnectionManagement
10
- ::ActionSubscriber.config.middleware.use ::ActionSubscriber::Middleware::ActiveRecord::QueryCache
9
+ ::ActionSubscriber.config.middleware.insert_after ::ActionSubscriber::Middleware::Decoder, ::ActionSubscriber::Middleware::ActiveRecord::ConnectionManagement
10
+ ::ActionSubscriber.config.middleware.insert_after ::ActionSubscriber::Middleware::ActiveRecord::ConnectionManagement, ::ActionSubscriber::Middleware::ActiveRecord::QueryCache
11
11
  end
12
12
  end
13
13
  end
@@ -12,7 +12,12 @@ module ActionSubscriber
12
12
  @routes = routes
13
13
  end
14
14
 
15
+ def print_middleware_stack
16
+ ::ActionSubscriber.config.middleware.print_middleware_stack
17
+ end
18
+
15
19
  def print_subscriptions
20
+ print_middleware_stack
16
21
  routes.group_by(&:subscriber).each do |subscriber, routes|
17
22
  logger.info subscriber.name
18
23
  routes.each do |route|
@@ -23,11 +28,6 @@ module ActionSubscriber
23
28
  logger.info " -- queue: #{route.queue}"
24
29
  logger.info " -- routing_key: #{route.routing_key}"
25
30
  logger.info " -- prefetch: #{route.prefetch}"
26
- if subscriber.at_most_once? && (route.prefetch < subscriber.ack_every_n_messages || subscriber.ack_every_n_messages <= 0)
27
- # https://www.rabbitmq.com/blog/2011/09/24/sizing-your-rabbits/
28
- logger.error "ERROR Subscriber has ack_every_n_messages as #{subscriber.ack_every_n_messages} and route has prefetch as #{route.prefetch}"
29
- fail "prefetch < ack_every_n_messages, deadlock will occur"
30
- end
31
31
  if route.acknowledgements != subscriber.acknowledge_messages?
32
32
  logger.error "WARNING subscriber has acknowledgements as #{subscriber.acknowledge_messages?} and route has acknowledgements as #{route.acknowledgements}"
33
33
  end
@@ -44,15 +44,20 @@ module ActionSubscriber
44
44
  end
45
45
 
46
46
  def wait_to_finish_with_timeout(timeout)
47
+ finisher_threads = []
48
+
47
49
  ::ActionSubscriber::ThreadPools.threadpools.map do |name, threadpool|
48
50
  logger.info " -- Threadpool #{name} (queued: #{threadpool.queue_length})"
49
- ::Thread.new do
50
- completed = threadpool.wait_for_termination(timeout)
51
+ finisher_threads << ::Thread.new(threadpool, timeout, name) do |internal_pool, internal_timeout, internal_name|
52
+ completed = internal_pool.wait_for_termination(internal_timeout)
53
+
51
54
  unless completed
52
- logger.error " -- FAILED #{name} did not finish shutting down within #{timeout}sec"
55
+ logger.error " -- FAILED #{internal_name} did not finish shutting down within #{internal_timeout}sec"
53
56
  end
54
57
  end
55
- end.each(&:join)
58
+ end
59
+
60
+ finisher_threads.each(&:join)
56
61
  end
57
62
 
58
63
  private
@@ -63,10 +68,8 @@ module ActionSubscriber
63
68
 
64
69
  def run_env(env, threadpool)
65
70
  logger.info "RECEIVED #{env.message_id} from #{env.queue}"
66
- ::ActiveSupport::Notifications.instrument "process_event.action_subscriber", :subscriber => env.subscriber.to_s, :routing_key => env.routing_key, :queue => env.queue do
67
- threadpool << lambda do
68
- ::ActionSubscriber.config.middleware.call(env)
69
- end
71
+ threadpool << lambda do
72
+ ::ActionSubscriber.config.middleware.call(env)
70
73
  end
71
74
  end
72
75
  end
@@ -7,6 +7,10 @@ module ActionSubscriber
7
7
  true
8
8
  end
9
9
 
10
+ def nack(delivery_tag, acknowledge_multiple, requeue_message)
11
+ true
12
+ end
13
+
10
14
  def reject(delivery_tag, requeue_message)
11
15
  true
12
16
  end
@@ -22,6 +26,7 @@ module ActionSubscriber
22
26
  :message_id => "MSG-123",
23
27
  :routing_key => "amigo.user.created",
24
28
  :queue => "test.amigo.user.created",
29
+ :uses_acknoledgements => false,
25
30
  }.freeze
26
31
 
27
32
  # Create a new subscriber instance. Available options are:
@@ -1,3 +1,3 @@
1
1
  module ActionSubscriber
2
- VERSION = "5.0.3.pre1"
2
+ VERSION = "5.1.0"
3
3
  end
@@ -3,8 +3,10 @@ class BaconSubscriber < ActionSubscriber::Base
3
3
 
4
4
  def served
5
5
  $messages << "#{payload}::#{$messages.size}"
6
- if $messages.size > 2
6
+ if $messages.size > 3
7
7
  acknowledge
8
+ elsif $messages.size > 2
9
+ nack
8
10
  else
9
11
  reject
10
12
  end
@@ -20,12 +22,12 @@ describe "Manual Message Acknowledgment", :integration => true do
20
22
  end
21
23
  let(:subscriber) { BaconSubscriber }
22
24
 
23
- it "retries rejected messages and stops retrying acknowledged messages" do
25
+ it "retries rejected/nacked messages and stops retrying acknowledged messages" do
24
26
  ::ActionSubscriber.start_subscribers!
25
27
  ::ActivePublisher.publish("bacon.served", "BACON!", "events")
26
28
 
27
- verify_expectation_within(2.0) do
28
- expect($messages).to eq(Set.new(["BACON!::0", "BACON!::1", "BACON!::2"]))
29
+ verify_expectation_within(2.5) do
30
+ expect($messages).to eq(Set.new(["BACON!::0", "BACON!::1", "BACON!::2", "BACON!::3"]))
29
31
  end
30
32
  end
31
33
  end
@@ -7,14 +7,32 @@ describe ActionSubscriber::Middleware::ErrorHandler do
7
7
 
8
8
  it_behaves_like 'an action subscriber middleware'
9
9
 
10
- let(:error) { ::RuntimeError.new("Boom!") }
10
+ let(:load_error) { ::LoadError.new("Boom!") }
11
+ let(:runtime_error) { ::RuntimeError.new("Boom!") }
11
12
 
12
13
  context "when an exception occurs" do
13
- before { allow(app).to receive(:call).and_raise(error) }
14
+ context "LoadError" do
15
+ before { allow(app).to receive(:call).and_raise(load_error) }
16
+ it "calls the exception handler with a LoadError" do
17
+ handler = ::ActionSubscriber.configuration.error_handler
18
+ expect(handler).to receive(:call).with(load_error, env.to_h)
14
19
 
15
- it "calls the exception handler" do
16
- handler = ::ActionSubscriber.configuration.error_handler
17
- expect(handler).to receive(:call).with(error, env.to_h)
20
+ subject.call(env)
21
+ end
22
+ end
23
+
24
+ context "RuntimError" do
25
+ before { allow(app).to receive(:call).and_raise(runtime_error) }
26
+ it "calls the exception handler with a RuntimeError" do
27
+ handler = ::ActionSubscriber.configuration.error_handler
28
+ expect(handler).to receive(:call).with(runtime_error, env.to_h)
29
+
30
+ subject.call(env)
31
+ end
32
+ end
33
+
34
+ it "calls safe_nack after execution" do
35
+ expect(env).to receive(:safe_nack)
18
36
 
19
37
  subject.call(env)
20
38
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: action_subscriber
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.0.3.pre1
4
+ version: 5.1.0
5
5
  platform: java
6
6
  authors:
7
7
  - Brian Stien
@@ -12,7 +12,7 @@ authors:
12
12
  autorequire:
13
13
  bindir: bin
14
14
  cert_chain: []
15
- date: 2017-12-11 00:00:00.000000000 Z
15
+ date: 2018-01-26 00:00:00.000000000 Z
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
18
18
  requirement: !ruby/object:Gem::Requirement
@@ -126,6 +126,20 @@ dependencies:
126
126
  - - ">="
127
127
  - !ruby/object:Gem::Version
128
128
  version: '1.6'
129
+ - !ruby/object:Gem::Dependency
130
+ requirement: !ruby/object:Gem::Requirement
131
+ requirements:
132
+ - - ">="
133
+ - !ruby/object:Gem::Version
134
+ version: '0'
135
+ name: pry-coolline
136
+ prerelease: false
137
+ type: :development
138
+ version_requirements: !ruby/object:Gem::Requirement
139
+ requirements:
140
+ - - ">="
141
+ - !ruby/object:Gem::Version
142
+ version: '0'
129
143
  - !ruby/object:Gem::Dependency
130
144
  requirement: !ruby/object:Gem::Requirement
131
145
  requirements:
@@ -278,9 +292,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
278
292
  version: '0'
279
293
  required_rubygems_version: !ruby/object:Gem::Requirement
280
294
  requirements:
281
- - - ">"
295
+ - - ">="
282
296
  - !ruby/object:Gem::Version
283
- version: 1.3.1
297
+ version: '0'
284
298
  requirements: []
285
299
  rubyforge_project:
286
300
  rubygems_version: 2.6.11