sidekiq-status 0.6.0 → 3.0.3

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.
Files changed (39) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/ci.yaml +53 -0
  3. data/.gitignore +4 -1
  4. data/.gitlab-ci.yml +17 -0
  5. data/Appraisals +11 -0
  6. data/CHANGELOG.md +36 -18
  7. data/Gemfile +0 -4
  8. data/README.md +129 -50
  9. data/Rakefile +2 -0
  10. data/gemfiles/sidekiq_6.1.gemfile +7 -0
  11. data/gemfiles/sidekiq_6.x.gemfile +7 -0
  12. data/gemfiles/sidekiq_7.x.gemfile +7 -0
  13. data/lib/sidekiq-status/client_middleware.rb +46 -8
  14. data/lib/sidekiq-status/redis_adapter.rb +18 -0
  15. data/lib/sidekiq-status/redis_client_adapter.rb +14 -0
  16. data/lib/sidekiq-status/server_middleware.rb +76 -20
  17. data/lib/sidekiq-status/sidekiq_extensions.rb +7 -0
  18. data/lib/sidekiq-status/storage.rb +19 -9
  19. data/lib/sidekiq-status/testing/inline.rb +10 -0
  20. data/lib/sidekiq-status/version.rb +1 -1
  21. data/lib/sidekiq-status/web.rb +116 -25
  22. data/lib/sidekiq-status/worker.rb +12 -5
  23. data/lib/sidekiq-status.rb +40 -5
  24. data/sidekiq-status.gemspec +7 -4
  25. data/spec/environment.rb +1 -0
  26. data/spec/lib/sidekiq-status/client_middleware_spec.rb +15 -12
  27. data/spec/lib/sidekiq-status/server_middleware_spec.rb +66 -22
  28. data/spec/lib/sidekiq-status/web_spec.rb +62 -15
  29. data/spec/lib/sidekiq-status/worker_spec.rb +19 -1
  30. data/spec/lib/sidekiq-status_spec.rb +94 -21
  31. data/spec/spec_helper.rb +104 -26
  32. data/spec/support/test_jobs.rb +71 -6
  33. data/web/sidekiq-status-single-web.png +0 -0
  34. data/web/views/status.erb +118 -0
  35. data/web/views/status_not_found.erb +6 -0
  36. data/web/views/statuses.erb +108 -22
  37. metadata +74 -13
  38. data/.travis.yml +0 -16
  39. data/gemfiles/Gemfile.sidekiq-2 +0 -5
@@ -1,16 +1,34 @@
1
+ require 'sidekiq-status'
2
+
1
3
  class StubJob
2
4
  include Sidekiq::Worker
3
5
  include Sidekiq::Status::Worker
4
6
 
5
- sidekiq_options 'retry' => 'false'
7
+ sidekiq_options 'retry' => false
8
+
9
+ def perform(*args)
10
+ end
11
+ end
12
+
13
+ class StubNoStatusJob
14
+ include Sidekiq::Worker
15
+
16
+ sidekiq_options 'retry' => false
6
17
 
7
18
  def perform(*args)
8
19
  end
9
20
  end
10
21
 
22
+
23
+ class ExpiryJob < StubJob
24
+ def expiration
25
+ 15
26
+ end
27
+ end
28
+
11
29
  class LongJob < StubJob
12
30
  def perform(*args)
13
- sleep args[0] || 1
31
+ sleep args[0] || 0.25
14
32
  end
15
33
  end
16
34
 
@@ -23,6 +41,13 @@ class DataJob < StubJob
23
41
  end
24
42
  end
25
43
 
44
+ class CustomDataJob < StubJob
45
+ def perform
46
+ store({mister_cat: 'meow'})
47
+ sleep 0.5
48
+ end
49
+ end
50
+
26
51
  class ProgressJob < StubJob
27
52
  def perform
28
53
  total 500
@@ -54,25 +79,65 @@ class FailingJob < StubJob
54
79
  end
55
80
  end
56
81
 
82
+ class FailingNoStatusJob < StubNoStatusJob
83
+ def perform
84
+ raise StandardError
85
+ end
86
+ end
87
+
88
+ class RetryAndFailJob < StubJob
89
+ sidekiq_options retry: 1
90
+
91
+ def perform
92
+ raise StandardError
93
+ end
94
+ end
95
+
96
+ class FailingHardJob < StubJob
97
+ def perform
98
+ raise Exception
99
+ end
100
+ end
101
+
102
+ class FailingHardNoStatusJob < StubNoStatusJob
103
+ def perform
104
+ raise Exception
105
+ end
106
+ end
107
+
57
108
  class ExitedJob < StubJob
58
109
  def perform
59
110
  raise SystemExit
60
111
  end
61
112
  end
62
113
 
114
+ class ExitedNoStatusJob < StubNoStatusJob
115
+ def perform
116
+ raise SystemExit
117
+ end
118
+ end
119
+
63
120
  class InterruptedJob < StubJob
64
121
  def perform
65
122
  raise Interrupt
66
123
  end
67
124
  end
68
125
 
126
+ class InterruptedNoStatusJob < StubNoStatusJob
127
+ def perform
128
+ raise Interrupt
129
+ end
130
+ end
131
+
69
132
  class RetriedJob < StubJob
70
- sidekiq_options 'retry' => 'true'
71
- def perform()
133
+
134
+ sidekiq_options retry: true
135
+ sidekiq_retry_in do |count| 3 end # 3 second delay > job timeout in test suite
136
+
137
+ def perform
72
138
  Sidekiq.redis do |conn|
73
139
  key = "RetriedJob_#{jid}"
74
- sleep 1
75
- unless conn.exists key
140
+ if [0, false].include? conn.exists(key)
76
141
  conn.set key, 'tried'
77
142
  raise StandardError
78
143
  end
Binary file
@@ -0,0 +1,118 @@
1
+ <% require 'cgi'; def h(v); CGI.escape_html(v.to_s); end %>
2
+ <style>
3
+ .progress {
4
+ background-color: #C8E1ED;
5
+ }
6
+ .progress-percentage {
7
+ margin: 5px;
8
+ padding-left: 4px;
9
+ color: #333;
10
+ text-align: left;
11
+ text-shadow: 0 0 5px white;
12
+ font-weight: bold;
13
+ font-size: 14px;
14
+ }
15
+ </style>
16
+
17
+ <h3>
18
+ Job Status: <%= h @status["jid"] %>
19
+ <span class='label label-<%= h @status["label"] %>'>
20
+ <%= h @status["status"] %>
21
+ </span>
22
+ </h3>
23
+
24
+ <div class="progress" style="height: 30px;">
25
+ <div class="progress-bar" role="progressbar" aria-valuenow="<%= @status["pct_complete"].to_i %>" aria-valuemin="0" aria-valuemax="100" style="width: <%= @status["pct_complete"].to_i %>%">
26
+ <div class="progress-percentage">
27
+ <%= @status["pct_complete"].to_i %>%
28
+ </div>
29
+ </div>
30
+ </div>
31
+
32
+ <div class="panel panel-default" style="background-color: inherit">
33
+ <div class="panel-body">
34
+ <h4><%= h @status["worker"] %></h4>
35
+
36
+ <div class="row">
37
+ <div class="col-sm-2">
38
+ <strong>Arguments</strong>
39
+ </div>
40
+ <div class="col-sm-10">
41
+ <p><%= @status["args"].empty? ? "<i>none</i>" : h(@status["args"]) %></p>
42
+ </div>
43
+ </div>
44
+
45
+ <div class="row">
46
+ <div class="col-sm-2">
47
+ <strong>Message</strong>
48
+ </div>
49
+ <div class="col-sm-10">
50
+ <p><%= h(@status["message"]) || "<i>none</i>" %></p>
51
+ </div>
52
+ </div>
53
+
54
+ <div class="row">
55
+ <div class="col-sm-2">
56
+ <strong>Last Updated</strong>
57
+ </div>
58
+ <div class="col-sm-10">
59
+ <p>
60
+ <% secs = Time.now.to_i - @status["update_time"].to_i %>
61
+ <% if secs > 0 %>
62
+ <%= ChronicDuration.output(secs, :weeks => true, :units => 2) %> ago
63
+ <% else %>
64
+ Now
65
+ <% end %>
66
+ </p>
67
+ </div>
68
+ </div>
69
+
70
+ <div class="row">
71
+ <div class="col-sm-2">
72
+ <strong>Elapsed Time</strong>
73
+ </div>
74
+ <div class="col-sm-10">
75
+ <p>
76
+ <% if @status["elapsed"] %>
77
+ <%= ChronicDuration.output(@status["elapsed"].to_i, :weeks => true, :units => 2) %>
78
+ <% end %>
79
+ </p>
80
+ </div>
81
+ </div>
82
+
83
+ <div class="row">
84
+ <div class="col-sm-2">
85
+ <strong>ETA</strong>
86
+ </div>
87
+ <div class="col-sm-10">
88
+ <p>
89
+ <% if @status["eta"] %>
90
+ <%= ChronicDuration.output(@status["eta"].to_i, :weeks => true, :units => 2) %>
91
+ <% end %>
92
+ </p>
93
+ </div>
94
+ </div>
95
+
96
+ <% if @status["custom"].any? %>
97
+ <hr>
98
+ <% @status["custom"].each do |key, val| %>
99
+ <div class="row">
100
+ <div class="col-sm-2">
101
+ <strong><%= key %></strong>
102
+ </div>
103
+ <div class="col-sm-10">
104
+ <% if val && val.include?("\n") %>
105
+ <pre><%= h val %></pre>
106
+ <% else %>
107
+ <p><%= h(val) || "<i>none</i>" %></p>
108
+ <% end %>
109
+ </div>
110
+ </div>
111
+ <% end %>
112
+ <% end %>
113
+ </div>
114
+ </div>
115
+
116
+ <a href="<%= root_path %>statuses">
117
+ <small>&larr; back to all statuses</small>
118
+ </a>
@@ -0,0 +1,6 @@
1
+ <% require 'cgi'; def h(v); CGI.escape_html(v.to_s); end %>
2
+ <h3>Job Status: <%= h params[:jid] %></h3>
3
+
4
+ <div role="alert">
5
+ <strong>Uh oh!</strong> That job can't be found. It may have expired already.
6
+ </div>
@@ -1,11 +1,11 @@
1
-
1
+ <% require 'cgi'; def h(v); CGI.escape_html(v.to_s); end %>
2
2
  <style>
3
3
  .progress {
4
4
  background-color: #C8E1ED;
5
5
  }
6
6
  .bar {
7
- background-color: #2897cb;
8
- color: white;
7
+ background-color: #2897cb;
8
+ color: white;
9
9
  text-shadow: 0 0 0;
10
10
  }
11
11
  .message {
@@ -13,35 +13,96 @@
13
13
  font-weight: bold; padding-left: 4px;
14
14
  color: #333;
15
15
  }
16
- .header{
16
+ .actions {
17
+ text-align: center;
18
+ }
19
+ .header {
17
20
  text-align: center;
18
21
  }
22
+ .header_update_time {
23
+ width: 10%;
24
+ }
25
+ .header_pct_complete {
26
+ width: 45%;
27
+ }
28
+ .btn-warning {
29
+ background-image: linear-gradient(#f0ad4e, #eea236)
30
+ }
31
+ .nav-container {
32
+ display: flex;
33
+ line-height: 45px;
34
+ }
35
+ .nav-container .pull-right {
36
+ float: none !important;
37
+ }
38
+ .nav-container .pagination {
39
+ display: flex;
40
+ align-items: center;
41
+ }
42
+ .nav-container .per-page, .filter-status {
43
+ display: flex;
44
+ align-items: center;
45
+ margin: 20px 0 20px 10px;
46
+ white-space: nowrap;
47
+ }
48
+ .nav-container .per-page SELECT {
49
+ margin: 0 0 0 5px;
50
+ }
19
51
  </style>
52
+ <script>
53
+ function setPerPage(select){
54
+ window.location = select.options[select.selectedIndex].getAttribute('data-url')
55
+ }
56
+ </script>
57
+ <div style="display: flex; justify-content: space-between;">
58
+ <h3 class="wi">Recent job statuses</h3>
59
+ <div class="nav-container">
60
+ <%= erb :_paging, locals: { url: "#{root_path}statuses" } %>
61
+ <div class="filter-status">
62
+ Filter Status:
63
+ <select class="form-control" onchange="setPerPage(this)">
64
+ <% (['all', 'complete', 'failed', 'interrupted', 'queued', 'retrying', 'stopped', 'working']).each do |status| %>
65
+ <option data-url="?<%= qparams(status: status)%>" value="<%= status %>" <%= 'selected="selected"' if status == (params[:status]) %>><%= status %></option>
66
+ <% end %>
67
+ </select>
68
+ </div>
20
69
 
21
- <h3 class="wi">Recent job statuses</h3>
22
- <table class="table table-hover table-bordered table-striped table-white">
70
+ <div class="per-page">
71
+ Per page:
72
+ <select class="form-control" onchange="setPerPage(this)">
73
+ <% (Sidekiq::Status::Web.per_page_opts + ['all']).each do |num| %>
74
+ <option data-url="?<%= qparams(page: 1, per_page: num)%>" value="<%= num %>" <%= 'selected="selected"' if num.to_s == (params[:per_page] || @count) %>><%= num %></option>
75
+ <% end %>
76
+ </select>
77
+ </div>
78
+ </div>
79
+ </div>
80
+ <table class="table table-hover table-bordered table-striped">
23
81
  <tr>
24
- <th class="header">Worker/jid</th>
25
- <th class="header">Arguments</th>
26
- <th class="header">Status</th>
27
- <th class="header" style="width: 10%;">Last Updated</th>
28
- <th class="header" style="width: 45%;">Progress</th>
82
+ <% @headers.each do |hdr| %>
83
+ <th class="header <%= h hdr[:class] %> header_<%= h hdr[:id] %>">
84
+ <a href="<%= h hdr[:url] %>"><%= h hdr[:name] %></a>
85
+ </th>
86
+ <% end %>
87
+ <th class="header">
88
+ Actions
89
+ </th>
29
90
  </tr>
30
91
  <% @statuses.each do |container| %>
31
92
  <tr>
32
93
  <td>
33
- <div title='<%= container.jid %>'><%= container.worker %></div>
94
+ <div title='<%= h container["jid"] %>'><a href="<%= root_path %>statuses/<%= h container["jid"] %>"><%= h container["worker"] %></a></div>
34
95
  </td>
35
96
  <td>
36
- <div class='args' title='<%= container.jid %>'><%= container.args %></div>
97
+ <div class='args' title='<%= h container["jid"] %>'><%= h container["args"] %></div>
37
98
  </td>
38
99
  <td style='text-align: center;'>
39
- <div class='label label-<%= container.label %>'><%= container.status %></div>
100
+ <div class='label label-<%= h container["label"] %>'><%= h container["status"] %></div>
40
101
  </td>
41
- <% secs = Time.now.to_i - container.update_time.to_i %>
42
- <td style='text-align: center;'>
102
+ <% secs = Time.now.to_i - container["update_time"].to_i %>
103
+ <td style='text-align: center; white-space: nowrap;' title="<%= Time.at(container["update_time"].to_i) %>">
43
104
  <% if secs > 0 %>
44
- <%= secs %> sec<%= secs == 1 ? '' : 's' %> ago
105
+ <%= ChronicDuration.output(secs, :weeks => true, :units => 2) %> ago
45
106
  <% else %>
46
107
  Now
47
108
  <% end %>
@@ -49,15 +110,40 @@
49
110
  <td>
50
111
  <div class="progress progress-striped" style="margin-bottom: 0">
51
112
  <div class='message' style='text-align:right; padding-right:0.5em; background-color: transparent; float:right;'>
52
- <%= container.message %>
113
+ <%= h container["message"] %>
53
114
  </div>
54
- <% if container.pct_complete.to_i > 0 %>
55
- <div class="bar message" style="width: <%= container.pct_complete %>%;">
56
- <%= container.pct_complete %>%
115
+ <% if container["pct_complete"].to_i > 0 %>
116
+ <div class="bar message" style="width: <%= h container["pct_complete"] %>%;">
117
+ <%= h container["pct_complete"] %>%
57
118
  </div>
58
119
  <% end %>
59
120
  </div>
60
121
  </td>
122
+ <td style='text-align: center; white-space: nowrap;'>
123
+ <% if container["elapsed"] %>
124
+ <%= ChronicDuration.output(container["elapsed"].to_i, :weeks => true, :units => 2) %>
125
+ <% end %>
126
+ </td>
127
+ <td style='text-align: center; white-space: nowrap;'>
128
+ <% if container["eta"] %>
129
+ <%= ChronicDuration.output(container["eta"].to_i, :weeks => true, :units => 2) %>
130
+ <% end %>
131
+ </td>
132
+ <td>
133
+ <div class="actions">
134
+ <form action="statuses" method="post">
135
+ <input type="hidden" name="jid" value="<%= h container["jid"] %>" />
136
+ <%= csrf_tag %>
137
+ <% if container["status"] == "complete" %>
138
+ <input type="hidden" name="_method" value="delete" />
139
+ <input type="submit" class="btn btn-danger btn-xs" value="Remove" />
140
+ <% elsif container["status"] == "failed" %>
141
+ <input type="hidden" name="_method" value="put" />
142
+ <input type="submit" class="btn btn-warning btn-xs" value="Retry Now" />
143
+ <% end %>
144
+ </form>
145
+ </div>
146
+ </td>
61
147
  </tr>
62
148
  <% end %>
63
149
  <% if @statuses.empty? %>
@@ -65,4 +151,4 @@
65
151
  <td colspan="6"></td>
66
152
  </tr>
67
153
  <% end %>
68
- </table>
154
+ </table>
metadata CHANGED
@@ -1,14 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sidekiq-status
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 3.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Evgeniy Tsvigun
8
- autorequire:
8
+ - Kenaniah Cerny
9
+ autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2015-12-27 00:00:00.000000000 Z
12
+ date: 2023-05-04 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: sidekiq
@@ -16,14 +17,62 @@ dependencies:
16
17
  requirements:
17
18
  - - ">="
18
19
  - !ruby/object:Gem::Version
19
- version: '2.7'
20
+ version: '6.0'
21
+ - - "<"
22
+ - !ruby/object:Gem::Version
23
+ version: '8'
20
24
  type: :runtime
21
25
  prerelease: false
22
26
  version_requirements: !ruby/object:Gem::Requirement
23
27
  requirements:
24
28
  - - ">="
25
29
  - !ruby/object:Gem::Version
26
- version: '2.7'
30
+ version: '6.0'
31
+ - - "<"
32
+ - !ruby/object:Gem::Version
33
+ version: '8'
34
+ - !ruby/object:Gem::Dependency
35
+ name: chronic_duration
36
+ requirement: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ type: :runtime
42
+ prerelease: false
43
+ version_requirements: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ - !ruby/object:Gem::Dependency
49
+ name: appraisal
50
+ requirement: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: colorize
64
+ requirement: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ type: :development
70
+ prerelease: false
71
+ version_requirements: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
27
76
  - !ruby/object:Gem::Dependency
28
77
  name: rack-test
29
78
  requirement: !ruby/object:Gem::Requirement
@@ -80,31 +129,40 @@ dependencies:
80
129
  - - ">="
81
130
  - !ruby/object:Gem::Version
82
131
  version: '0'
83
- description:
132
+ description:
84
133
  email:
85
134
  - utgarda@gmail.com
135
+ - kenaniah@gmail.com
86
136
  executables: []
87
137
  extensions: []
88
138
  extra_rdoc_files: []
89
139
  files:
140
+ - ".github/workflows/ci.yaml"
90
141
  - ".gitignore"
142
+ - ".gitlab-ci.yml"
91
143
  - ".rspec"
92
- - ".travis.yml"
144
+ - Appraisals
93
145
  - CHANGELOG.md
94
146
  - Gemfile
95
147
  - LICENSE
96
148
  - README.md
97
149
  - Rakefile
98
- - gemfiles/Gemfile.sidekiq-2
150
+ - gemfiles/sidekiq_6.1.gemfile
151
+ - gemfiles/sidekiq_6.x.gemfile
152
+ - gemfiles/sidekiq_7.x.gemfile
99
153
  - lib/sidekiq-status.rb
100
154
  - lib/sidekiq-status/client_middleware.rb
155
+ - lib/sidekiq-status/redis_adapter.rb
156
+ - lib/sidekiq-status/redis_client_adapter.rb
101
157
  - lib/sidekiq-status/server_middleware.rb
158
+ - lib/sidekiq-status/sidekiq_extensions.rb
102
159
  - lib/sidekiq-status/storage.rb
103
160
  - lib/sidekiq-status/testing/inline.rb
104
161
  - lib/sidekiq-status/version.rb
105
162
  - lib/sidekiq-status/web.rb
106
163
  - lib/sidekiq-status/worker.rb
107
164
  - sidekiq-status.gemspec
165
+ - spec/environment.rb
108
166
  - spec/lib/sidekiq-status/client_middleware_spec.rb
109
167
  - spec/lib/sidekiq-status/server_middleware_spec.rb
110
168
  - spec/lib/sidekiq-status/testing_spec.rb
@@ -113,13 +171,16 @@ files:
113
171
  - spec/lib/sidekiq-status_spec.rb
114
172
  - spec/spec_helper.rb
115
173
  - spec/support/test_jobs.rb
174
+ - web/sidekiq-status-single-web.png
116
175
  - web/sidekiq-status-web.png
176
+ - web/views/status.erb
177
+ - web/views/status_not_found.erb
117
178
  - web/views/statuses.erb
118
- homepage: http://github.com/utgarda/sidekiq-status
179
+ homepage: https://github.com/kenaniah/sidekiq-status
119
180
  licenses:
120
181
  - MIT
121
182
  metadata: {}
122
- post_install_message:
183
+ post_install_message:
123
184
  rdoc_options: []
124
185
  require_paths:
125
186
  - lib
@@ -134,12 +195,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
134
195
  - !ruby/object:Gem::Version
135
196
  version: '0'
136
197
  requirements: []
137
- rubyforge_project:
138
- rubygems_version: 2.4.5.1
139
- signing_key:
198
+ rubygems_version: 3.4.1
199
+ signing_key:
140
200
  specification_version: 4
141
201
  summary: An extension to the sidekiq message processing to track your jobs
142
202
  test_files:
203
+ - spec/environment.rb
143
204
  - spec/lib/sidekiq-status/client_middleware_spec.rb
144
205
  - spec/lib/sidekiq-status/server_middleware_spec.rb
145
206
  - spec/lib/sidekiq-status/testing_spec.rb
data/.travis.yml DELETED
@@ -1,16 +0,0 @@
1
- language: ruby
2
- rvm:
3
- - 2.0
4
- - 2.1
5
- - 2.2
6
- - rbx-2
7
- gemfile:
8
- - gemfiles/Gemfile.sidekiq-2
9
- - Gemfile
10
- before_install:
11
- - gem update --system
12
- - gem update bundler
13
- services: redis
14
- matrix:
15
- allow_failures:
16
- - rvm: rbx-2
@@ -1,5 +0,0 @@
1
- source "https://rubygems.org"
2
-
3
- gemspec path: '..'
4
-
5
- gem 'sidekiq', '~> 2.17'