sidekiq-expected_failures 0.0.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +3 -0
- data/CHANGELOG.md +34 -0
- data/README.md +22 -2
- data/lib/sidekiq/expected_failures/middleware.rb +19 -7
- data/lib/sidekiq/expected_failures/version.rb +1 -1
- data/lib/sidekiq/expected_failures/web.rb +8 -20
- data/lib/sidekiq/expected_failures.rb +9 -0
- data/sidekiq-expected_failures.gemspec +2 -1
- data/test/lib/middleware_test.rb +66 -22
- data/test/lib/web_test.rb +5 -9
- data/test/test_helper.rb +6 -0
- data/test/test_workers.rb +11 -2
- data/web/assets/expected.js +1 -1
- data/web/views/expected_failures.erb +27 -10
- metadata +36 -20
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,37 @@
|
|
1
|
+
## 0.2.0
|
2
|
+
|
3
|
+
- [**breaking change**] ability to use Sidekiq's build-in `handle_exception`
|
4
|
+
method - in case you want to use airbrake or other exception notify service.
|
5
|
+
Since version `0.2.0` you need to provide `expected_failures` in a form of
|
6
|
+
a hash, for example:
|
7
|
+
|
8
|
+
``` ruby
|
9
|
+
class CustomizedWorker
|
10
|
+
include ::Sidekiq::Worker
|
11
|
+
sidekiq_options expected_failures: {
|
12
|
+
NotImplementedError => nil, # notification disabled
|
13
|
+
VeryOwn::CustomException => [10, 20, 50], # notify on 10th, 20th, 50th failure
|
14
|
+
ZeroDivisionError => 5 # notify on 5th failure
|
15
|
+
}
|
16
|
+
end
|
17
|
+
```
|
18
|
+
|
19
|
+
- removed `sinatra-assetpack` dependency - js assets are now served inline
|
20
|
+
(it seemed like a overkill to include that gem just for just two files)
|
21
|
+
|
22
|
+
- added option to configure exception handled by default (for all workers):
|
23
|
+
|
24
|
+
``` ruby
|
25
|
+
Sidekiq.configure_server do |config|
|
26
|
+
config.expected_failures = { AlwaysHandledExceptionByDefault => 1000 }
|
27
|
+
end
|
28
|
+
```
|
29
|
+
|
30
|
+
Note: if you specify `expected_failure`s for given worker defaults will be
|
31
|
+
discarded (for that worker).
|
32
|
+
|
33
|
+
- small front-end adjustments
|
34
|
+
|
1
35
|
## 0.0.1
|
2
36
|
|
3
37
|
- Initial release
|
data/README.md
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
[![Code Climate](https://codeclimate.com/github/emq/sidekiq-expected_failures.png)](https://codeclimate.com/github/emq/sidekiq-expected_failures)
|
4
4
|
[![Build Status](https://travis-ci.org/emq/sidekiq-expected_failures.png?branch=master)](https://travis-ci.org/emq/sidekiq-expected_failures)
|
5
|
+
[![Coverage Status](https://coveralls.io/repos/emq/sidekiq-expected_failures/badge.png)](https://coveralls.io/r/emq/sidekiq-expected_failures)
|
5
6
|
|
6
7
|
If you don't rely on standard sidekiq's retry behavior and you want to track exceptions, that will happen one way, or another - this thing is for you.
|
7
8
|
|
@@ -26,7 +27,7 @@ Let's say you do a lot of API requests to some not reliable reliable service. In
|
|
26
27
|
``` ruby
|
27
28
|
class ApiCallWorker
|
28
29
|
include ::Sidekiq::Worker
|
29
|
-
sidekiq_options expected_failures:
|
30
|
+
sidekiq_options expected_failures: { Timeout::Error => nil } # handle that exception, but disable notification
|
30
31
|
|
31
32
|
def perform(arguments)
|
32
33
|
# do some work
|
@@ -42,12 +43,29 @@ class ApiCallWorker
|
|
42
43
|
end
|
43
44
|
```
|
44
45
|
|
45
|
-
You can pass
|
46
|
+
You can pass a hash of exceptions to handle inside `sidekiq_options`. Each key-value pair may consist of:
|
47
|
+
- `exception => nil` - notifications disabled
|
48
|
+
- `exception => integer` - fires exception notify when x-th exception happens (on daily basis)
|
49
|
+
- `exception => [integer, integer]` - same as above but for each value
|
50
|
+
|
51
|
+
sidekiq-expected_failures utilizes sidekiq's [ExceptionHandler module][1] - so you might want to set some same limits for your exceptions and use Airbrake (for example) as a notification service to inform you that something bad is probably happing.
|
52
|
+
|
53
|
+
This is how web interface looks like:
|
46
54
|
|
47
55
|
![](http://i.imgur.com/7Fe8voD.jpg)
|
48
56
|
|
49
57
|
It logs each failed jobs to to redis list (per day) and keep global counters (per exception class as a single redis hash).
|
50
58
|
|
59
|
+
### Default expected failures
|
60
|
+
|
61
|
+
You can configure defaults for all your workers (overridden completely by specifying `expected_failures` hash inside `sidekiq_options` - per worker).
|
62
|
+
|
63
|
+
``` ruby
|
64
|
+
Sidekiq.configure_server do |config|
|
65
|
+
config.expected_failures = { ExceptionHandledByDefault => [1000, 2000] } # with notification enabled
|
66
|
+
end
|
67
|
+
```
|
68
|
+
|
51
69
|
### Usage with sidekiq-failures
|
52
70
|
|
53
71
|
Just be sure to load this one after `sidekiq-failures`, otherwise failed jobs will end up logged twice - and you probably don't want that.
|
@@ -69,3 +87,5 @@ This might change in the future to something more sane.
|
|
69
87
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
70
88
|
4. Push to the branch (`git push origin my-new-feature`)
|
71
89
|
5. Create new Pull Request
|
90
|
+
|
91
|
+
[1]: https://github.com/mperham/sidekiq/blob/master/lib/sidekiq/exception_handler.rb#L4
|
@@ -3,35 +3,47 @@ module Sidekiq
|
|
3
3
|
class Middleware
|
4
4
|
include Sidekiq::Util
|
5
5
|
|
6
|
+
attr_reader :handled_exceptions
|
7
|
+
|
6
8
|
def call(worker, msg, queue)
|
7
|
-
|
9
|
+
setup_exceptions(worker)
|
8
10
|
|
9
11
|
yield
|
10
12
|
|
11
|
-
rescue *
|
13
|
+
rescue *handled_exceptions.keys => ex
|
12
14
|
data = {
|
13
15
|
failed_at: Time.now.strftime("%Y/%m/%d %H:%M:%S %Z"),
|
14
16
|
args: msg['args'],
|
15
|
-
exception:
|
16
|
-
error:
|
17
|
+
exception: ex.class.to_s,
|
18
|
+
error: ex.message,
|
17
19
|
worker: msg['class'],
|
18
20
|
processor: "#{hostname}:#{process_id}-#{Thread.current.object_id}",
|
19
21
|
queue: queue
|
20
22
|
}
|
21
23
|
|
22
|
-
log_exception(data)
|
24
|
+
log_exception(data, ex, msg)
|
23
25
|
end
|
24
26
|
|
25
27
|
private
|
26
28
|
|
27
|
-
def
|
28
|
-
|
29
|
+
def setup_exceptions(worker)
|
30
|
+
@handled_exceptions = worker.class.get_sidekiq_options['expected_failures'] || Sidekiq.expected_failures
|
31
|
+
end
|
32
|
+
|
33
|
+
def exception_intervals(ex)
|
34
|
+
[handled_exceptions[ex.class]].flatten.compact
|
35
|
+
end
|
36
|
+
|
37
|
+
def log_exception(data, ex, msg)
|
38
|
+
result = Sidekiq.redis do |conn|
|
29
39
|
conn.multi do |m|
|
30
40
|
m.lpush("expected:#{today}", Sidekiq.dump_json(data))
|
31
41
|
m.sadd("expected:dates", today)
|
32
42
|
m.hincrby("expected:count", data[:exception], 1)
|
33
43
|
end
|
34
44
|
end
|
45
|
+
|
46
|
+
handle_exception(ex, msg) if exception_intervals(ex).include?(result[0])
|
35
47
|
end
|
36
48
|
|
37
49
|
def today
|
@@ -1,20 +1,9 @@
|
|
1
|
-
require 'sinatra/assetpack'
|
2
|
-
|
3
1
|
module Sidekiq
|
4
2
|
module ExpectedFailures
|
5
3
|
module Web
|
6
4
|
|
7
5
|
def self.registered(app)
|
8
|
-
web_dir
|
9
|
-
assets_dir = File.join(web_dir, "assets")
|
10
|
-
|
11
|
-
app.register Sinatra::AssetPack
|
12
|
-
|
13
|
-
app.assets do
|
14
|
-
serve '/js', from: assets_dir
|
15
|
-
js 'expected', ['/js/expected.js']
|
16
|
-
js 'bootstrap', ['/js/bootstrap.js']
|
17
|
-
end
|
6
|
+
web_dir = File.expand_path("../../../../web", __FILE__)
|
18
7
|
|
19
8
|
app.helpers do
|
20
9
|
def link_to_details(job)
|
@@ -24,14 +13,9 @@ module Sidekiq
|
|
24
13
|
end
|
25
14
|
end
|
26
15
|
|
27
|
-
app.
|
28
|
-
|
29
|
-
|
30
|
-
Sidekiq::ExpectedFailures.clear_old
|
31
|
-
when 'all'
|
32
|
-
Sidekiq::ExpectedFailures.clear_all
|
33
|
-
when 'counters'
|
34
|
-
Sidekiq::ExpectedFailures.clear_counters
|
16
|
+
app.post "/expected_failures/clear" do
|
17
|
+
if %w(old all counters).include?(params[:what])
|
18
|
+
Sidekiq::ExpectedFailures.send("clear_#{params[:what]}")
|
35
19
|
end
|
36
20
|
|
37
21
|
redirect "#{root_path}expected_failures"
|
@@ -48,6 +32,10 @@ module Sidekiq
|
|
48
32
|
@counters = Sidekiq::ExpectedFailures.counters
|
49
33
|
end
|
50
34
|
|
35
|
+
@javascript = %w(expected bootstrap).map do |file|
|
36
|
+
File.read(File.join(web_dir, "assets/#{file}.js"))
|
37
|
+
end.join
|
38
|
+
|
51
39
|
erb File.read(File.join(web_dir, "views/expected_failures.erb"))
|
52
40
|
end
|
53
41
|
end
|
@@ -4,6 +4,15 @@ require "sidekiq/expected_failures/middleware"
|
|
4
4
|
require "sidekiq/expected_failures/web"
|
5
5
|
|
6
6
|
module Sidekiq
|
7
|
+
|
8
|
+
def self.expected_failures=(exceptions)
|
9
|
+
@expected_failures = exceptions
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.expected_failures
|
13
|
+
@expected_failures || {}
|
14
|
+
end
|
15
|
+
|
7
16
|
module ExpectedFailures
|
8
17
|
|
9
18
|
def self.dates
|
@@ -18,11 +18,12 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.require_paths = ["lib"]
|
19
19
|
|
20
20
|
spec.add_dependency "sidekiq", ">= 2.15.0"
|
21
|
-
spec.add_dependency "sinatra-assetpack", "~> 0.3.1"
|
22
21
|
|
23
22
|
spec.add_development_dependency "bundler", "~> 1.3"
|
24
23
|
spec.add_development_dependency "rake"
|
25
24
|
spec.add_development_dependency "sinatra"
|
26
25
|
spec.add_development_dependency "rack-test"
|
27
26
|
spec.add_development_dependency "timecop", "~> 0.6.3"
|
27
|
+
spec.add_development_dependency "mocha", "~> 0.14.0"
|
28
|
+
spec.add_development_dependency "coveralls", "~> 0.7.0"
|
28
29
|
end
|
data/test/lib/middleware_test.rb
CHANGED
@@ -8,6 +8,7 @@ module Sidekiq
|
|
8
8
|
Sidekiq.redis = REDIS
|
9
9
|
Sidekiq.redis { |c| c.flushdb }
|
10
10
|
Timecop.freeze(Time.local(2013, 1, 10))
|
11
|
+
Sidekiq.expected_failures = nil
|
11
12
|
end
|
12
13
|
|
13
14
|
after { Timecop.return }
|
@@ -23,44 +24,53 @@ module Sidekiq
|
|
23
24
|
end
|
24
25
|
end
|
25
26
|
|
27
|
+
it 'can can be configured to handle exceptions by default' do
|
28
|
+
Sidekiq.expected_failures = { VeryOwn::CustomException => nil }
|
29
|
+
|
30
|
+
handler.call(RegularWorker.new, msg, 'default') do
|
31
|
+
raise VeryOwn::CustomException
|
32
|
+
end
|
33
|
+
|
34
|
+
assert_raises RuntimeError do
|
35
|
+
handler.call(RegularWorker.new, msg, 'default') do
|
36
|
+
raise "This is not handled by default"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
26
41
|
it 'respects build-in rescue and ensure blocks' do
|
27
42
|
assert_equal 0, $invokes
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
end
|
43
|
+
|
44
|
+
handler.call(SingleExceptionWorker.new, msg, 'default') do
|
45
|
+
begin
|
46
|
+
raise ZeroDivisionError.new("We go a problem, sir")
|
47
|
+
rescue ZeroDivisionError => e
|
48
|
+
$invokes += 1
|
49
|
+
raise e # and now this should be caught by middleware
|
50
|
+
ensure
|
51
|
+
$invokes += 1
|
38
52
|
end
|
39
53
|
end
|
54
|
+
|
40
55
|
assert_equal 2, $invokes
|
41
56
|
end
|
42
57
|
|
43
58
|
it 'handles all specified exceptions' do
|
44
|
-
|
45
|
-
|
46
|
-
raise NotImplementedError
|
47
|
-
end
|
59
|
+
handler.call(MultipleExceptionWorker.new, msg, 'default') do
|
60
|
+
raise NotImplementedError
|
48
61
|
end
|
49
62
|
|
50
|
-
|
51
|
-
|
52
|
-
raise VeryOwn::CustomException
|
53
|
-
end
|
63
|
+
handler.call(MultipleExceptionWorker.new, msg, 'default') do
|
64
|
+
raise VeryOwn::CustomException
|
54
65
|
end
|
55
66
|
end
|
56
67
|
|
57
68
|
it 'logs exceptions' do
|
58
|
-
|
59
|
-
|
60
|
-
raise ZeroDivisionError
|
61
|
-
end
|
69
|
+
handler.call(SingleExceptionWorker.new, msg, 'default') do
|
70
|
+
raise ZeroDivisionError
|
62
71
|
end
|
63
72
|
|
73
|
+
|
64
74
|
assert_equal(['2013-01-10'], redis("smembers", "expected:dates"))
|
65
75
|
assert_match(/custom_argument/, redis("lrange", "expected:2013-01-10", 0, -1)[0])
|
66
76
|
end
|
@@ -100,6 +110,40 @@ module Sidekiq
|
|
100
110
|
assert_equal 5, redis("llen", "expected:2013-05-15")
|
101
111
|
assert_equal(['2013-05-15', '2013-01-10'].sort, redis("smembers", "expected:dates").sort)
|
102
112
|
end
|
113
|
+
|
114
|
+
describe 'exception notify' do
|
115
|
+
|
116
|
+
it 'can be configured to notify once' do
|
117
|
+
exception = ZeroDivisionError.new
|
118
|
+
handler.expects(:handle_exception).with(exception, msg).once.returns(true)
|
119
|
+
|
120
|
+
50.times do
|
121
|
+
handler.call(CustomizedWorker.new, msg, 'default') do
|
122
|
+
raise exception
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
it 'can be configured to notify multiple number of times' do
|
128
|
+
handler.expects(:handle_exception).times(3).returns(true)
|
129
|
+
|
130
|
+
60.times do
|
131
|
+
handler.call(CustomizedWorker.new, msg, 'default') do
|
132
|
+
raise VeryOwn::CustomException
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
it 'can be configured not to notify at all' do
|
138
|
+
handler.expects(:handle_exception).never
|
139
|
+
|
140
|
+
60.times do
|
141
|
+
handler.call(CustomizedWorker.new, msg, 'default') do
|
142
|
+
raise NotImplementedError
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
103
147
|
end
|
104
148
|
end
|
105
149
|
end
|
data/test/lib/web_test.rb
CHANGED
@@ -9,10 +9,6 @@ module Sidekiq
|
|
9
9
|
Sidekiq::Web
|
10
10
|
end
|
11
11
|
|
12
|
-
def failed_count
|
13
|
-
Sidekiq.redis { |c| c.get("stat:failed") }
|
14
|
-
end
|
15
|
-
|
16
12
|
def create_sample_counter
|
17
13
|
redis("hset", "expected:count", "StandardError", 5)
|
18
14
|
redis("hset", "expected:count", "Custom::Error", 10)
|
@@ -80,7 +76,7 @@ module Sidekiq
|
|
80
76
|
get '/expected_failures'
|
81
77
|
last_response.body.must_match(/HardWorker/)
|
82
78
|
|
83
|
-
|
79
|
+
post '/expected_failures/clear', { what: 'all' }
|
84
80
|
last_response.status.must_equal(302)
|
85
81
|
last_response.location.must_match(/expected_failures$/)
|
86
82
|
|
@@ -93,7 +89,7 @@ module Sidekiq
|
|
93
89
|
last_response.body.must_match(/2013-09-10/)
|
94
90
|
last_response.body.must_match(/2013-09-09/)
|
95
91
|
|
96
|
-
|
92
|
+
post '/expected_failures/clear', { what: 'old' }
|
97
93
|
last_response.status.must_equal(302)
|
98
94
|
last_response.location.must_match(/expected_failures$/)
|
99
95
|
|
@@ -111,7 +107,7 @@ module Sidekiq
|
|
111
107
|
create_sample_failure
|
112
108
|
get '/expected_failures'
|
113
109
|
last_response.body.wont_match(/dl-horizontal/)
|
114
|
-
last_response.body.wont_match(/
|
110
|
+
last_response.body.wont_match(/All counters/i)
|
115
111
|
end
|
116
112
|
end
|
117
113
|
|
@@ -121,14 +117,14 @@ module Sidekiq
|
|
121
117
|
it 'displays counters' do
|
122
118
|
get '/expected_failures'
|
123
119
|
last_response.body.must_match(/dl-horizontal/)
|
124
|
-
last_response.body.must_match(/
|
120
|
+
last_response.body.must_match(/All counters/i)
|
125
121
|
end
|
126
122
|
|
127
123
|
it 'can clear counters' do
|
128
124
|
get '/expected_failures'
|
129
125
|
last_response.body.must_match(/Custom::Error/)
|
130
126
|
|
131
|
-
|
127
|
+
post '/expected_failures/clear', { what: 'counters' }
|
132
128
|
last_response.status.must_equal(302)
|
133
129
|
last_response.location.must_match(/expected_failures$/)
|
134
130
|
|
data/test/test_helper.rb
CHANGED
@@ -1,9 +1,15 @@
|
|
1
|
+
require 'coveralls'
|
2
|
+
Coveralls.wear!
|
3
|
+
|
1
4
|
Encoding.default_external = Encoding::UTF_8
|
2
5
|
Encoding.default_internal = Encoding::UTF_8
|
3
6
|
|
4
7
|
require "minitest/autorun"
|
5
8
|
require "minitest/spec"
|
6
9
|
require "minitest/mock"
|
10
|
+
require "minitest/pride"
|
11
|
+
require "mocha/setup"
|
12
|
+
|
7
13
|
|
8
14
|
require "timecop"
|
9
15
|
require "rack/test"
|
data/test/test_workers.rb
CHANGED
@@ -8,10 +8,19 @@ end
|
|
8
8
|
|
9
9
|
class SingleExceptionWorker
|
10
10
|
include ::Sidekiq::Worker
|
11
|
-
sidekiq_options expected_failures:
|
11
|
+
sidekiq_options expected_failures: { ZeroDivisionError => nil }
|
12
12
|
end
|
13
13
|
|
14
14
|
class MultipleExceptionWorker
|
15
15
|
include ::Sidekiq::Worker
|
16
|
-
sidekiq_options expected_failures:
|
16
|
+
sidekiq_options expected_failures: { NotImplementedError => nil, VeryOwn::CustomException => nil }
|
17
|
+
end
|
18
|
+
|
19
|
+
class CustomizedWorker
|
20
|
+
include ::Sidekiq::Worker
|
21
|
+
sidekiq_options expected_failures: {
|
22
|
+
NotImplementedError => nil,
|
23
|
+
VeryOwn::CustomException => [10, 20, 50],
|
24
|
+
ZeroDivisionError => 5
|
25
|
+
}
|
17
26
|
end
|
data/web/assets/expected.js
CHANGED
@@ -1,5 +1,6 @@
|
|
1
|
-
|
2
|
-
<%=
|
1
|
+
<script type="text/javascript">
|
2
|
+
<%= @javascript %>
|
3
|
+
</script>
|
3
4
|
|
4
5
|
<h3>Expected failures log
|
5
6
|
<% if @date %>
|
@@ -8,9 +9,7 @@
|
|
8
9
|
</h3>
|
9
10
|
|
10
11
|
<% unless @counters.empty? %>
|
11
|
-
<div class="well well-sm
|
12
|
-
<a href="<%= root_path %>expected_failures/clear/counters" class="btn btn-default btn-sm pull-right">Clear counters</span></a>
|
13
|
-
|
12
|
+
<div class="well well-sm">
|
14
13
|
<dl class="dl-horizontal" style="margin: 0">
|
15
14
|
<% @counters.each do |exception, count| %>
|
16
15
|
<dt><%= exception %></dt>
|
@@ -21,6 +20,26 @@
|
|
21
20
|
</div>
|
22
21
|
<% end %>
|
23
22
|
|
23
|
+
<% if @jobs.any? || @counters.any? %>
|
24
|
+
<form id="clear-jobs" method="post" class="form-inline pull-right" action="<%= root_path %>expected_failures/clear">
|
25
|
+
<label>Clear:</label>
|
26
|
+
<select name="what">
|
27
|
+
<option selected>Choose...</option>
|
28
|
+
<% if @jobs.any? %>
|
29
|
+
<optgroup label="Jobs">
|
30
|
+
<option value="old">Older than today</option>
|
31
|
+
<option value="all">All failed</option>
|
32
|
+
</optgroup>
|
33
|
+
<% end %>
|
34
|
+
<% if @counters.any? %>
|
35
|
+
<optgroup label="Counters">
|
36
|
+
<option value="counters">All counters</option>
|
37
|
+
</optgroup>
|
38
|
+
<% end %>
|
39
|
+
</select>
|
40
|
+
</form>
|
41
|
+
<% end %>
|
42
|
+
|
24
43
|
<% if @jobs.any? %>
|
25
44
|
<div class="modal fade" id="job-details">
|
26
45
|
<div class="modal-dialog">
|
@@ -56,13 +75,10 @@
|
|
56
75
|
</select>
|
57
76
|
</form>
|
58
77
|
|
59
|
-
<div class="pull-right">
|
60
|
-
<a href="<%= root_path %>expected_failures/clear/old" class="btn btn-default btn-sm">Clear older than today</a>
|
61
|
-
<a href="<%= root_path %>expected_failures/clear/all" class="btn btn-default btn-sm">Clear all failed</a>
|
62
|
-
</div>
|
63
|
-
|
64
78
|
<p class="clearfix"></p>
|
65
79
|
|
80
|
+
<%= erb :_paging, :locals => { :url => "#{root_path}expected_failures/#{@date}" } %>
|
81
|
+
|
66
82
|
<table id="expected" class="queues table table-hover table-bordered table-striped table-white">
|
67
83
|
<thead>
|
68
84
|
<th>Datetime</th>
|
@@ -85,6 +101,7 @@
|
|
85
101
|
<%= erb :_paging, :locals => { :url => "#{root_path}expected_failures/#{@date}" } %>
|
86
102
|
|
87
103
|
<% else %>
|
104
|
+
<p class="clearfix"></p>
|
88
105
|
<div class="alert alert-success">
|
89
106
|
No failed jobs found.
|
90
107
|
</div>
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sidekiq-expected_failures
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.2.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-12-01 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: sidekiq
|
@@ -27,22 +27,6 @@ dependencies:
|
|
27
27
|
- - ! '>='
|
28
28
|
- !ruby/object:Gem::Version
|
29
29
|
version: 2.15.0
|
30
|
-
- !ruby/object:Gem::Dependency
|
31
|
-
name: sinatra-assetpack
|
32
|
-
requirement: !ruby/object:Gem::Requirement
|
33
|
-
none: false
|
34
|
-
requirements:
|
35
|
-
- - ~>
|
36
|
-
- !ruby/object:Gem::Version
|
37
|
-
version: 0.3.1
|
38
|
-
type: :runtime
|
39
|
-
prerelease: false
|
40
|
-
version_requirements: !ruby/object:Gem::Requirement
|
41
|
-
none: false
|
42
|
-
requirements:
|
43
|
-
- - ~>
|
44
|
-
- !ruby/object:Gem::Version
|
45
|
-
version: 0.3.1
|
46
30
|
- !ruby/object:Gem::Dependency
|
47
31
|
name: bundler
|
48
32
|
requirement: !ruby/object:Gem::Requirement
|
@@ -123,6 +107,38 @@ dependencies:
|
|
123
107
|
- - ~>
|
124
108
|
- !ruby/object:Gem::Version
|
125
109
|
version: 0.6.3
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: mocha
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ~>
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: 0.14.0
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ~>
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: 0.14.0
|
126
|
+
- !ruby/object:Gem::Dependency
|
127
|
+
name: coveralls
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
129
|
+
none: false
|
130
|
+
requirements:
|
131
|
+
- - ~>
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: 0.7.0
|
134
|
+
type: :development
|
135
|
+
prerelease: false
|
136
|
+
version_requirements: !ruby/object:Gem::Requirement
|
137
|
+
none: false
|
138
|
+
requirements:
|
139
|
+
- - ~>
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: 0.7.0
|
126
142
|
description: If you don't rely on sidekiq' retry behavior, you handle exceptions on
|
127
143
|
your own and want to keep track of them - this thing is for you.
|
128
144
|
email:
|
@@ -166,7 +182,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
166
182
|
version: '0'
|
167
183
|
segments:
|
168
184
|
- 0
|
169
|
-
hash:
|
185
|
+
hash: 3929689485539629877
|
170
186
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
171
187
|
none: false
|
172
188
|
requirements:
|
@@ -175,7 +191,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
175
191
|
version: '0'
|
176
192
|
segments:
|
177
193
|
- 0
|
178
|
-
hash:
|
194
|
+
hash: 3929689485539629877
|
179
195
|
requirements: []
|
180
196
|
rubyforge_project:
|
181
197
|
rubygems_version: 1.8.25
|