shoryuken 5.2.1 → 5.3.1

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: bfdadf90a17ca05fbad43a5d38efc22179415f06b661b587cf5b087d0c200174
4
- data.tar.gz: ca814528eaef23167aca0003fa62e180944ce3bc686d54728c9c30f82732a385
3
+ metadata.gz: c730786f3c97f263b00320693e0ea95af12495bb83805a39461de93b526b4d2a
4
+ data.tar.gz: a7a7fbb4bd5a1c006c1573261c607c6c77968575af901a8671dea4da6e4282c5
5
5
  SHA512:
6
- metadata.gz: 680c478f59a3feb96c2ed27c6d169c3ad78d37191acf374dff456365a12611b3dba4c0b077e29808d55867446ab2a5008eca6db11f0707aa1a38caea5561d8ee
7
- data.tar.gz: d0e80fc907715d307d6657f6f507643f732c417dc59f888c7123288a416f662a458aa15878516c79b4b9730ef5a306ba33d778354fc8d5cd7cbe448fd76a6a9c
6
+ metadata.gz: 3bc9237605e5f9667f0e22bdae6ca1553b2f2a08357f2f7da465fa4f812d3abcafd6ef21c05ef553dac13dcb405fa658d13bada5be235743974978fc7829b6d2
7
+ data.tar.gz: 54737eb8fc32b4a486d62f1ef626336e0493ae70475efb1a99e163ebd960d2e38c99506ccf8cafcd3502bd52a2279619601150634e7dbf3134b38913bb0283e8
@@ -9,8 +9,8 @@ jobs:
9
9
  name: All Specs
10
10
  strategy:
11
11
  matrix:
12
- ruby: ['2.4.4', '2.5.1', '2.6.3']
13
- gemfile: ['Gemfile', 'Gemfile.aws-sdk-core-v2']
12
+ ruby: ['2.4', '2.5', '2.6', '2.7', '3.0']
13
+ gemfile: ['Gemfile', 'gemfiles/aws_sdk_core_2.gemfile']
14
14
  runs-on: ubuntu-20.04
15
15
  services:
16
16
  moto_sqs:
data/.gitignore CHANGED
@@ -25,4 +25,4 @@ shoryuken.yml
25
25
  *.log
26
26
  .env
27
27
  rubocop.html
28
- .byebug_history
28
+ .byebug_history
data/.rubocop.yml CHANGED
@@ -23,7 +23,7 @@ Metrics/AbcSize:
23
23
  # because codeclimate already give that for us with more details
24
24
  Enabled: false
25
25
 
26
- Metrics/LineLength:
26
+ Layout/LineLength:
27
27
  Max: 125
28
28
 
29
29
  Style/Alias:
data/Appraisals CHANGED
@@ -1,3 +1,11 @@
1
+ appraise 'aws_sdk_core_2' do
2
+ group :test do
3
+ gem 'aws-sdk-core', '~> 2'
4
+ gem 'webrick' # required for Ruby 3
5
+ remove_gem 'aws-sdk-sqs'
6
+ end
7
+ end
8
+
1
9
  appraise 'rails_4_2' do
2
10
  group :test do
3
11
  gem 'activejob', '~> 4.2'
data/CHANGELOG.md CHANGED
@@ -1,3 +1,26 @@
1
+ ## [v5.3.1] - 2022-01-07
2
+
3
+ - (Bugfix) Fix issue where, when using the TSTP or USR1 signals for soft shutdowns, it was possible for shoryuken to terminate without first attempting to handle all messages it fetched from SQS
4
+ - [#676](https://github.com/ruby-shoryuken/shoryuken/pull/676)
5
+
6
+ ## [v5.3.0] - 2021-10-31
7
+
8
+ - (Refactor) Use Forwardable within Message to avoid method boilerplate
9
+ - [#681](https://github.com/ruby-shoryuken/shoryuken/pull/681)
10
+
11
+ - Add basic health check API
12
+ - [#679](https://github.com/ruby-shoryuken/shoryuken/pull/679)
13
+
14
+ ## [v5.2.3] - 2021-07-29
15
+
16
+ - Fire new `:utilization_update` event any time a worker pool's utilization changes
17
+ - [#673](https://github.com/ruby-shoryuken/shoryuken/pull/673)
18
+
19
+ ## [v5.2.2] - 2021-06-22
20
+
21
+ - When using ActiveJob queue name prefixing, avoid applying prefix to queues configured with a URL or ARN
22
+ - [#667](https://github.com/ruby-shoryuken/shoryuken/pull/667)
23
+
1
24
  ## [v5.2.1] - 2021-04-06
2
25
 
3
26
  - Reduce message batch sizes in `shoryuken sqs requeue` and `shoryuken sqs mv` commands
data/Gemfile CHANGED
@@ -16,5 +16,5 @@ end
16
16
  group :development do
17
17
  gem 'appraisal', git: 'https://github.com/thoughtbot/appraisal.git'
18
18
  gem 'pry-byebug', '3.9.0'
19
- gem 'rubocop'
19
+ gem 'rubocop', '<= 1.12'
20
20
  end
@@ -0,0 +1,21 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ group :test do
6
+ gem "activejob"
7
+ gem "aws-sdk-core", "~> 2"
8
+ gem "codeclimate-test-reporter", require: nil
9
+ gem "httparty"
10
+ gem "multi_xml"
11
+ gem "simplecov"
12
+ gem "webrick"
13
+ end
14
+
15
+ group :development do
16
+ gem "appraisal", git: "https://github.com/thoughtbot/appraisal.git"
17
+ gem "pry-byebug", "3.9.0"
18
+ gem "rubocop"
19
+ end
20
+
21
+ gemspec path: "../"
@@ -80,6 +80,8 @@ module Shoryuken
80
80
  end
81
81
 
82
82
  def prefix_active_job_queue_name(queue_name, weight)
83
+ return [queue_name, weight] if queue_name.start_with?('https://', 'arn:')
84
+
83
85
  queue_name_prefix = ::ActiveJob::Base.queue_name_prefix
84
86
  queue_name_delimiter = ::ActiveJob::Base.queue_name_delimiter
85
87
 
@@ -28,12 +28,30 @@ module Shoryuken
28
28
 
29
29
  initiate_stop
30
30
 
31
+ stop_new_dispatching
32
+ await_dispatching_in_progress
33
+
31
34
  executor.shutdown
32
35
  executor.wait_for_termination
33
36
  end
34
37
 
38
+ def healthy?
39
+ Shoryuken.groups.keys.all? do |group|
40
+ manager = @managers.find { |m| m.group == group }
41
+ manager && manager.running?
42
+ end
43
+ end
44
+
35
45
  private
36
46
 
47
+ def stop_new_dispatching
48
+ @managers.each(&:stop_new_dispatching)
49
+ end
50
+
51
+ def await_dispatching_in_progress
52
+ @managers.each(&:await_dispatching_in_progress)
53
+ end
54
+
37
55
  def executor
38
56
  @_executor ||= Shoryuken.launcher_executor || Concurrent.global_io_executor
39
57
  end
@@ -71,6 +89,7 @@ module Shoryuken
71
89
  def create_managers
72
90
  Shoryuken.groups.map do |group, options|
73
91
  Shoryuken::Manager.new(
92
+ group,
74
93
  Shoryuken::Fetcher.new(group),
75
94
  Shoryuken.polling_strategy(group).new(options[:queues], Shoryuken.delay(group)),
76
95
  options[:concurrency],
@@ -6,27 +6,47 @@ module Shoryuken
6
6
  # See https://github.com/phstc/shoryuken/issues/348#issuecomment-292847028
7
7
  MIN_DISPATCH_INTERVAL = 0.1
8
8
 
9
- def initialize(fetcher, polling_strategy, concurrency, executor)
10
- @fetcher = fetcher
11
- @polling_strategy = polling_strategy
12
- @max_processors = concurrency
13
- @busy_processors = Concurrent::AtomicFixnum.new(0)
14
- @executor = executor
15
- @running = Concurrent::AtomicBoolean.new(true)
9
+ attr_reader :group
10
+
11
+ def initialize(group, fetcher, polling_strategy, concurrency, executor)
12
+ @group = group
13
+ @fetcher = fetcher
14
+ @polling_strategy = polling_strategy
15
+ @max_processors = concurrency
16
+ @busy_processors = Concurrent::AtomicFixnum.new(0)
17
+ @executor = executor
18
+ @running = Concurrent::AtomicBoolean.new(true)
19
+ @stop_new_dispatching = Concurrent::AtomicBoolean.new(false)
20
+ @dispatching_release_signal = ::Queue.new
16
21
  end
17
22
 
18
23
  def start
24
+ fire_utilization_update_event
19
25
  dispatch_loop
20
26
  end
21
27
 
22
- private
28
+ def stop_new_dispatching
29
+ @stop_new_dispatching.make_true
30
+ end
31
+
32
+ def await_dispatching_in_progress
33
+ # There might still be a dispatching on-going, as the response from SQS could take some time
34
+ # We don't want to stop the process before processing incoming messages, as they would stay "in-flight" for some time on SQS
35
+ # We use a queue, as the dispatch_loop is running on another thread, and this is a efficient way of communicating between threads.
36
+ @dispatching_release_signal.pop
37
+ end
23
38
 
24
39
  def running?
25
40
  @running.true? && @executor.running?
26
41
  end
27
42
 
43
+ private
44
+
28
45
  def dispatch_loop
29
- return unless running?
46
+ if @stop_new_dispatching.true? || !running?
47
+ @dispatching_release_signal << 1
48
+ return
49
+ end
30
50
 
31
51
  @executor.post { dispatch }
32
52
  end
@@ -59,6 +79,8 @@ module Shoryuken
59
79
 
60
80
  def processor_done(queue)
61
81
  @busy_processors.decrement
82
+ fire_utilization_update_event
83
+
62
84
  client_queue = Shoryuken::Client.queues(queue)
63
85
  return unless client_queue.fifo?
64
86
  return unless @polling_strategy.respond_to?(:message_processed)
@@ -72,6 +94,7 @@ module Shoryuken
72
94
  logger.debug { "Assigning #{sqs_msg.message_id}" }
73
95
 
74
96
  @busy_processors.increment
97
+ fire_utilization_update_event
75
98
 
76
99
  Concurrent::Promise
77
100
  .execute(executor: @executor) { Processor.process(queue_name, sqs_msg) }
@@ -87,7 +110,6 @@ module Shoryuken
87
110
 
88
111
  def dispatch_single_messages(queue)
89
112
  messages = @fetcher.fetch(queue, ready)
90
-
91
113
  @polling_strategy.messages_found(queue.name, messages.size)
92
114
  messages.each { |message| assign(queue.name, message) }
93
115
  end
@@ -114,5 +136,13 @@ module Shoryuken
114
136
 
115
137
  @running.make_false
116
138
  end
139
+
140
+ def fire_utilization_update_event
141
+ fire_event :utilization_update, false, {
142
+ group: @group,
143
+ max_processors: @max_processors,
144
+ busy_processors: busy
145
+ }
146
+ end
117
147
  end
118
148
  end
@@ -1,5 +1,16 @@
1
1
  module Shoryuken
2
2
  class Message
3
+ extend Forwardable
4
+
5
+ def_delegators(:data,
6
+ :message_id,
7
+ :receipt_handle,
8
+ :md5_of_body,
9
+ :body,
10
+ :attributes,
11
+ :md5_of_message_attributes,
12
+ :message_attributes)
13
+
3
14
  attr_accessor :client, :queue_url, :queue_name, :data
4
15
 
5
16
  def initialize(client, queue, data)
@@ -29,33 +40,5 @@ module Shoryuken
29
40
  visibility_timeout: timeout
30
41
  )
31
42
  end
32
-
33
- def message_id
34
- data.message_id
35
- end
36
-
37
- def receipt_handle
38
- data.receipt_handle
39
- end
40
-
41
- def md5_of_body
42
- data.md5_of_body
43
- end
44
-
45
- def body
46
- data.body
47
- end
48
-
49
- def attributes
50
- data.attributes
51
- end
52
-
53
- def md5_of_message_attributes
54
- data.md5_of_message_attributes
55
- end
56
-
57
- def message_attributes
58
- data.message_attributes
59
- end
60
43
  end
61
44
  end
@@ -9,6 +9,7 @@ module Shoryuken
9
9
  lifecycle_events: {
10
10
  startup: [],
11
11
  dispatch: [],
12
+ utilization_update: [],
12
13
  quiet: [],
13
14
  shutdown: []
14
15
  }
@@ -55,6 +55,10 @@ module Shoryuken
55
55
  end
56
56
  end
57
57
 
58
+ def healthy?
59
+ (@launcher && @launcher.healthy?) || false
60
+ end
61
+
58
62
  private
59
63
 
60
64
  def initialize_concurrent_logger
@@ -1,3 +1,3 @@
1
1
  module Shoryuken
2
- VERSION = '5.2.1'.freeze
2
+ VERSION = '5.3.1'.freeze
3
3
  end
data/lib/shoryuken.rb CHANGED
@@ -45,6 +45,10 @@ module Shoryuken
45
45
  @_shoryuken_options ||= Shoryuken::Options.new
46
46
  end
47
47
 
48
+ def self.healthy?
49
+ Shoryuken::Runner.instance.healthy?
50
+ end
51
+
48
52
  def_delegators(
49
53
  :shoryuken_options,
50
54
  :active_job?,
@@ -57,11 +57,11 @@ RSpec.describe Shoryuken::EnvironmentLoader do
57
57
  ActiveJob::Base.queue_name_delimiter = '_'
58
58
 
59
59
  allow(Shoryuken).to receive(:active_job?).and_return(true)
60
- end
61
60
 
62
- specify do
63
61
  Shoryuken.active_job_queue_name_prefixing = true
62
+ end
64
63
 
64
+ specify do
65
65
  Shoryuken.options[:queues] = ['queue1', ['queue2', 2]]
66
66
 
67
67
  Shoryuken.options[:groups] = {
@@ -73,6 +73,26 @@ RSpec.describe Shoryuken::EnvironmentLoader do
73
73
  expect(Shoryuken.groups['default'][:queues]).to eq(%w[test_queue1 test_queue2 test_queue2])
74
74
  expect(Shoryuken.groups['group1'][:queues]).to eq(%w[test_group1_queue1 test_group1_queue2])
75
75
  end
76
+
77
+ it 'does not prefix url-based queues' do
78
+ Shoryuken.options[:queues] = ['https://example.com/test_queue1']
79
+ Shoryuken.options[:groups] = {'group1' => {queues: ['https://example.com/test_group1_queue1']}}
80
+
81
+ subject.load
82
+
83
+ expect(Shoryuken.groups['default'][:queues]).to(eq(['https://example.com/test_queue1']))
84
+ expect(Shoryuken.groups['group1'][:queues]).to(eq(['https://example.com/test_group1_queue1']))
85
+ end
86
+
87
+ it 'does not prefix arn-based queues' do
88
+ Shoryuken.options[:queues] = ['arn:aws:sqs:fake-region-1:1234:test_queue1']
89
+ Shoryuken.options[:groups] = {'group1' => {queues: ['arn:aws:sqs:fake-region-1:1234:test_group1_queue1']}}
90
+
91
+ subject.load
92
+
93
+ expect(Shoryuken.groups['default'][:queues]).to(eq(['arn:aws:sqs:fake-region-1:1234:test_queue1']))
94
+ expect(Shoryuken.groups['group1'][:queues]).to(eq(['arn:aws:sqs:fake-region-1:1234:test_group1_queue1']))
95
+ end
76
96
  end
77
97
  describe "#setup_options" do
78
98
  let (:cli_queues) { { "queue1"=> 10, "queue2" => 20 } }
@@ -0,0 +1,60 @@
1
+ require 'spec_helper'
2
+ require 'shoryuken/launcher'
3
+
4
+ RSpec.describe Shoryuken::Launcher do
5
+ let(:executor) do
6
+ # We can't use Concurrent.global_io_executor in these tests since once you
7
+ # shut down a thread pool, you can't start it back up. Instead, we create
8
+ # one new thread pool executor for each spec. We use a new
9
+ # CachedThreadPool, since that most closely resembles
10
+ # Concurrent.global_io_executor
11
+ Concurrent::CachedThreadPool.new auto_terminate: true
12
+ end
13
+
14
+ let(:first_group_manager) { double(:first_group_manager, group: 'first_group') }
15
+ let(:second_group_manager) { double(:second_group_manager, group: 'second_group') }
16
+ let(:first_queue) { "launcher_spec_#{SecureRandom.uuid}" }
17
+ let(:second_queue) { "launcher_spec_#{SecureRandom.uuid}" }
18
+
19
+ before do
20
+ Shoryuken.add_group('first_group', 1)
21
+ Shoryuken.add_group('second_group', 1)
22
+ Shoryuken.add_queue(first_queue, 1, 'first_group')
23
+ Shoryuken.add_queue(second_queue, 1, 'second_group')
24
+ allow(Shoryuken).to receive(:launcher_executor).and_return(executor)
25
+ allow(Shoryuken::Manager).to receive(:new).with('first_group', any_args).and_return(first_group_manager)
26
+ allow(Shoryuken::Manager).to receive(:new).with('second_group', any_args).and_return(second_group_manager)
27
+ allow(first_group_manager).to receive(:running?).and_return(true)
28
+ allow(second_group_manager).to receive(:running?).and_return(true)
29
+ end
30
+
31
+ describe '#healthy?' do
32
+ context 'when all groups have managers' do
33
+ context 'when all managers are running' do
34
+ it 'returns true' do
35
+ expect(subject.healthy?).to be true
36
+ end
37
+ end
38
+
39
+ context 'when one manager is not running' do
40
+ before do
41
+ allow(second_group_manager).to receive(:running?).and_return(false)
42
+ end
43
+
44
+ it 'returns false' do
45
+ expect(subject.healthy?).to be false
46
+ end
47
+ end
48
+ end
49
+
50
+ context 'when all groups do not have managers' do
51
+ before do
52
+ allow(second_group_manager).to receive(:group).and_return('some_random_group')
53
+ end
54
+
55
+ it 'returns false' do
56
+ expect(subject.healthy?).to be false
57
+ end
58
+ end
59
+ end
60
+ end
@@ -15,7 +15,7 @@ RSpec.describe Shoryuken::Manager do
15
15
  let(:concurrency) { 1 }
16
16
  let(:executor) { Concurrent::ImmediateExecutor.new }
17
17
 
18
- subject { Shoryuken::Manager.new(fetcher, polling_strategy, concurrency, executor) }
18
+ subject { Shoryuken::Manager.new('default', fetcher, polling_strategy, concurrency, executor) }
19
19
 
20
20
  before do
21
21
  allow(fetcher).to receive(:fetch).and_return([])
@@ -71,6 +71,11 @@ RSpec.describe Shoryuken::Manager do
71
71
 
72
72
  expect(fetcher).to receive(:fetch).with(q, concurrency).and_return(messages)
73
73
  expect(subject).to receive(:fire_event).with(:dispatch, false, queue_name: q.name)
74
+ expect(subject).to receive(:fire_event).with(:utilization_update,
75
+ false,
76
+ group: 'default',
77
+ busy_processors: 1,
78
+ max_processors: 1)
74
79
  expect(Shoryuken::Processor).to receive(:process).with(q, message)
75
80
  expect(Shoryuken.logger).to receive(:info).never
76
81
 
@@ -99,6 +104,11 @@ RSpec.describe Shoryuken::Manager do
99
104
  q = Shoryuken::Polling::QueueConfiguration.new(queue, {})
100
105
 
101
106
  expect(fetcher).to receive(:fetch).with(q, described_class::BATCH_LIMIT).and_return(messages)
107
+ expect(subject).to receive(:fire_event).with(:utilization_update,
108
+ false,
109
+ group: 'default',
110
+ busy_processors: 1,
111
+ max_processors: 1)
102
112
  expect(subject).to receive(:fire_event).with(:dispatch, false, queue_name: q.name)
103
113
  allow(subject).to receive(:batched_queue?).with(q).and_return(true)
104
114
  expect(Shoryuken::Processor).to receive(:process).with(q, messages)
@@ -163,4 +173,26 @@ RSpec.describe Shoryuken::Manager do
163
173
  end
164
174
  end
165
175
  end
176
+
177
+ describe '#running?' do
178
+ context 'when the executor is running' do
179
+ before do
180
+ allow(executor).to receive(:running?).and_return(true)
181
+ end
182
+
183
+ it 'returns true' do
184
+ expect(subject.running?).to be true
185
+ end
186
+ end
187
+
188
+ context 'when the executor is not running' do
189
+ before do
190
+ allow(executor).to receive(:running?).and_return(false)
191
+ end
192
+
193
+ it 'returns false' do
194
+ expect(subject.running?).to be false
195
+ end
196
+ end
197
+ end
166
198
  end
@@ -1,4 +1,13 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  RSpec.describe Shoryuken do
4
+ describe '.healthy?' do
5
+ before do
6
+ allow(Shoryuken::Runner).to receive(:instance).and_return(double(:instance, healthy?: :some_result))
7
+ end
8
+
9
+ it 'delegates to the runner instance' do
10
+ expect(described_class.healthy?).to eq(:some_result)
11
+ end
12
+ end
4
13
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shoryuken
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.2.1
4
+ version: 5.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Pablo Cantero
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-04-07 00:00:00.000000000 Z
11
+ date: 2022-01-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dotenv
@@ -112,7 +112,6 @@ files:
112
112
  - Appraisals
113
113
  - CHANGELOG.md
114
114
  - Gemfile
115
- - Gemfile.aws-sdk-core-v2
116
115
  - LICENSE
117
116
  - README.md
118
117
  - Rakefile
@@ -122,6 +121,7 @@ files:
122
121
  - examples/bootstrap_queues.rb
123
122
  - examples/default_worker.rb
124
123
  - gemfiles/.gitignore
124
+ - gemfiles/aws_sdk_core_2.gemfile
125
125
  - gemfiles/rails_4_2.gemfile
126
126
  - gemfiles/rails_5_2.gemfile
127
127
  - gemfiles/rails_6_0.gemfile
@@ -174,6 +174,7 @@ files:
174
174
  - spec/shoryuken/extensions/active_job_concurrent_send_adapter_spec.rb
175
175
  - spec/shoryuken/extensions/active_job_wrapper_spec.rb
176
176
  - spec/shoryuken/fetcher_spec.rb
177
+ - spec/shoryuken/launcher_spec.rb
177
178
  - spec/shoryuken/manager_spec.rb
178
179
  - spec/shoryuken/middleware/chain_spec.rb
179
180
  - spec/shoryuken/middleware/server/auto_delete_spec.rb
@@ -198,7 +199,7 @@ homepage: https://github.com/phstc/shoryuken
198
199
  licenses:
199
200
  - LGPL-3.0
200
201
  metadata: {}
201
- post_install_message:
202
+ post_install_message:
202
203
  rdoc_options: []
203
204
  require_paths:
204
205
  - lib
@@ -213,8 +214,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
213
214
  - !ruby/object:Gem::Version
214
215
  version: '0'
215
216
  requirements: []
216
- rubygems_version: 3.0.1
217
- signing_key:
217
+ rubygems_version: 3.0.3.1
218
+ signing_key:
218
219
  specification_version: 4
219
220
  summary: Shoryuken is a super efficient AWS SQS thread based message processor
220
221
  test_files:
@@ -231,6 +232,7 @@ test_files:
231
232
  - spec/shoryuken/extensions/active_job_concurrent_send_adapter_spec.rb
232
233
  - spec/shoryuken/extensions/active_job_wrapper_spec.rb
233
234
  - spec/shoryuken/fetcher_spec.rb
235
+ - spec/shoryuken/launcher_spec.rb
234
236
  - spec/shoryuken/manager_spec.rb
235
237
  - spec/shoryuken/middleware/chain_spec.rb
236
238
  - spec/shoryuken/middleware/server/auto_delete_spec.rb
@@ -1,13 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- # Specify your gem's dependencies in shoryuken.gemspec
4
- gemspec
5
-
6
- group :test do
7
- gem 'activejob', '~> 4'
8
- gem 'aws-sdk-core', '~> 2'
9
- gem 'codeclimate-test-reporter', require: nil
10
- gem 'httparty'
11
- gem 'multi_xml'
12
- gem 'simplecov'
13
- end