active_job_exception_handler 0.0.1

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 35ef233d0b45467e77ddedf1db4781f7afc49995
4
+ data.tar.gz: 0c1ebe0c56515afba4788383388b5875b4c9dfca
5
+ SHA512:
6
+ metadata.gz: 8b69f694263edf50d59ed2549d45d835fdb73a5d6d88c87ed4973f306157d57f76717af04353d8e51afba97e312b4e5321a5f58a0f881cef31dae2495ea89ce6
7
+ data.tar.gz: 827d1b59aa93ffda14c7ae64a7ebbbbad186ae73ce8c94e5fce9b3333002cb870d8fe7f39becda6283d92b52c98307f2fb85596aa3ae31ad7b09951e71449b2f
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2017 []().
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,149 @@
1
+ # ActiveJobExceptionHandler
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/active_job_exception_handler.svg)](http://badge.fury.io/rb/active_job_exception_handler)
4
+
5
+ <!-- Tocer[start]: Auto-generated, don't remove. -->
6
+
7
+ # Table of Contents
8
+
9
+ - [Features](#features)
10
+ - [Screencasts](#screencasts)
11
+ - [Requirements](#requirements)
12
+ - [Setup](#setup)
13
+ - [Usage](#usage)
14
+ - [Tests](#tests)
15
+ - [Versioning](#versioning)
16
+ - [Code of Conduct](#code-of-conduct)
17
+ - [Contributions](#contributions)
18
+ - [License](#license)
19
+ - [History](#history)
20
+ - [Credits](#credits)
21
+
22
+ <!-- Tocer[finish]: Auto-generated, don't remove. -->
23
+
24
+ # Features
25
+
26
+ # Screencasts
27
+
28
+ # Requirements
29
+
30
+ 0. [Ruby 2.4.1](https://www.ruby-lang.org)
31
+
32
+ # Setup
33
+
34
+ For a secure install, type the following (recommended):
35
+
36
+ gem cert --add <(curl --location --silent /gem-public.pem)
37
+ gem install active_job_exception_handler --trust-policy MediumSecurity
38
+
39
+ NOTE: A HighSecurity trust policy would be best but MediumSecurity enables signed gem verification
40
+ while allowing the installation of unsigned dependencies since they are beyond the scope of this
41
+ gem.
42
+
43
+ For an insecure install, type the following (not recommended):
44
+
45
+ gem install active_job_exception_handler
46
+
47
+ Add the following to your Gemfile:
48
+
49
+ gem "active_job_exception_handler"
50
+
51
+ # Usage
52
+
53
+ Initialize it like this:
54
+
55
+ ```ruby
56
+ class ApplicationJob < ActiveJob::Base
57
+
58
+ before_perform :initialize_exception_handler
59
+
60
+ def perform(normal:, credentials: {}, spawn_job_id:, exception_handler: default_exception_handler)
61
+ @exception_handler = exception_handler
62
+ initialize_exception_handling
63
+
64
+ exception_handler.process! do
65
+ return if skip?
66
+ collector.run run_options
67
+ emitter.flush!
68
+ kapost_client.update_credentials(adapter.updated_tokens) if adapter.refreshed_tokens?
69
+ perform_successful
70
+ end
71
+ end
72
+
73
+ protected
74
+
75
+ def exception_handler
76
+ @exception_handler ||= ExceptionHandler.new(exception_context: exception_context)
77
+ end
78
+
79
+ def exception_context
80
+ ExceptionContext.new(
81
+ source: self.class.name,
82
+ queue: queue_name,
83
+ args: arguments
84
+ )
85
+ end
86
+
87
+ def initialize_exception_handling
88
+ # implement this method to define how errors are handled
89
+ end
90
+ end
91
+ ```
92
+
93
+ And then use it in jobs like this:
94
+
95
+ ```ruby
96
+ class MyJob < ApplicationJob
97
+
98
+ def perform(...)
99
+ # ...
100
+ end
101
+
102
+ protected
103
+
104
+ def initialize_exception_handling
105
+ exception_handler.add HTTPServerError, :retryables # Transient HTTP error
106
+ exception_handler.add HTTPAuthenticationError, :unretryables # User credentials are wrong, don't retry
107
+ exception_handler.add HTTPNotFoundError, :ignorables # Page is gone, we don't care anymore
108
+ end
109
+
110
+ end
111
+ ```
112
+
113
+ # Tests
114
+
115
+ To test, run:
116
+
117
+ bundle exec rake
118
+
119
+ # Versioning
120
+
121
+ Read [Semantic Versioning](http://semver.org) for details. Briefly, it means:
122
+
123
+ - Major (X.y.z) - Incremented for any backwards incompatible public API changes.
124
+ - Minor (x.Y.z) - Incremented for new, backwards compatible, public API enhancements/fixes.
125
+ - Patch (x.y.Z) - Incremented for small, backwards compatible, bug fixes.
126
+
127
+ # Code of Conduct
128
+
129
+ Please note that this project is released with a [CODE OF CONDUCT](CODE_OF_CONDUCT.md). By
130
+ participating in this project you agree to abide by its terms.
131
+
132
+ # Contributions
133
+
134
+ Read [CONTRIBUTING](CONTRIBUTING.md) for details.
135
+
136
+ # License
137
+
138
+ Copyright (c) 2017 []().
139
+ Read [LICENSE](LICENSE.md) for details.
140
+
141
+ # History
142
+
143
+ Read [CHANGES](CHANGES.md) for details.
144
+ Built with [Gemsmith](https://github.com/bkuhlmann/gemsmith).
145
+
146
+ # Credits
147
+
148
+ Developed by [Paul Sadauskas]() and [Brooke Kuhlmann]() at
149
+ [Kapost](www.kapost.com).
@@ -0,0 +1,118 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ExceptionHandler
4
+ RetryableError = Class.new(StandardError)
5
+
6
+ TYPES = %i[ignorables retryables unretryables].freeze
7
+
8
+ attr_reader(*TYPES)
9
+ attr_reader :error
10
+
11
+ def initialize(exception_context:,
12
+ rescue_retryable_errors:,
13
+ exception_processors: [ExceptionHandlerStatsdProcessor.new,
14
+ ExceptionHandlerActionableFaultProcessor.new],
15
+ logger: ActiveSupport::TaggedLogging.new(Logger.new(STDOUT)))
16
+ @exception_context = exception_context
17
+ @exception_processors = exception_processors
18
+ @rescue_retryable_errors = rescue_retryable_errors
19
+ @logger = logger
20
+ @error = nil
21
+ @ignorables = []
22
+ @retryables = [Timeout::Error, Net::HTTPError, Errno::ECONNREFUSED]
23
+ @unretryables = []
24
+ end
25
+
26
+ def add(error, type)
27
+ fail(StandardError, "Invalid error category: #{type}.") unless TYPES.include?(type)
28
+ public_send(type) << error
29
+ end
30
+
31
+ def error?
32
+ error.present?
33
+ end
34
+
35
+ def process!
36
+ yield if block_given?
37
+
38
+ rescue *ignorables => error
39
+ handle_ignorable(error)
40
+
41
+ rescue *retryables => error
42
+ handle_retryable(error)
43
+
44
+ rescue *unretryables => error
45
+ handle_unretryable(error)
46
+
47
+ rescue StandardError => error
48
+ handle_unknown(error)
49
+ end
50
+
51
+ private
52
+
53
+ attr_reader :exception_context, :logger, :rescue_retryable_errors, :exception_processors
54
+
55
+ # Common actions that we want to perform for any type of error
56
+ def common_actions(error, type)
57
+ @error = error
58
+
59
+ log error, type
60
+
61
+ exception_processors.each do |processor|
62
+ processor.process(error, type, exception_context)
63
+ end
64
+ end
65
+
66
+ # Exceptions that we can't do anything about
67
+ def handle_ignorable(error)
68
+ common_actions(error, :ignorables)
69
+ end
70
+
71
+ # Transient exceptions that might go away upon a retry.
72
+ # Raise `ExceptionHandler::RetryableError` so shoryuken will pick up the
73
+ # job according to the retry intervals.
74
+ # Configure honeybadger to ignore this exception in the initializer.
75
+ #
76
+ # NOTE: Retries only apply to ApplicationJobs and
77
+ # don't have meaning for other objects like Collectors.
78
+ def handle_retryable(error)
79
+ common_actions(error, :retryables)
80
+
81
+ fail RetryableError unless rescue_retryable_errors
82
+ end
83
+
84
+ # Exceptions that we can't do anything about, but want to be notified on. This
85
+ # category should include errors where we might want to notify the customer, so
86
+ # they can take some action
87
+ def handle_unretryable(error)
88
+ common_actions(error, :unretryables)
89
+ end
90
+
91
+ # Unknown errors
92
+ # - Instrument and bubble out
93
+ def handle_unknown(error)
94
+ common_actions(error, :unknown)
95
+
96
+ fail error
97
+ end
98
+
99
+ def log(error, type)
100
+ return unless ENV["DUMP_EXCEPTIONS"]
101
+
102
+ logger.tagged self.class do |tagged_logger|
103
+ tagged_logger.info "Caught #{type} error: #{error.message}"
104
+ tagged_logger.info error.backtrace.join("\n")
105
+ end
106
+ end
107
+
108
+ class ExceptionContext
109
+ include Virtus.model
110
+ include ActiveModel::Validations
111
+
112
+ attribute :source, String
113
+ attribute :queue, String
114
+ attribute :args, Array
115
+
116
+ validates :source, :queue, :args, presence: true
117
+ end
118
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ActiveJobExceptionHandler
4
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveJobExceptionHandler
4
+ # Gem identity information.
5
+ module Identity
6
+ def self.name
7
+ "active_job_exception_handler"
8
+ end
9
+
10
+ def self.label
11
+ "ActiveJobExceptionHandler"
12
+ end
13
+
14
+ def self.version
15
+ "0.0.1"
16
+ end
17
+
18
+ def self.version_label
19
+ "#{label} #{version}"
20
+ end
21
+ end
22
+ end
metadata ADDED
@@ -0,0 +1,177 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: active_job_exception_handler
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Paul Sadauskas
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-03-31 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '12.0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '12.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: gemsmith
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '9.2'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '9.2'
41
+ - !ruby/object:Gem::Dependency
42
+ name: pry
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0.10'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0.10'
55
+ - !ruby/object:Gem::Dependency
56
+ name: pry-byebug
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.4'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.4'
69
+ - !ruby/object:Gem::Dependency
70
+ name: pry-state
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '0.1'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '0.1'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '3.5'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '3.5'
97
+ - !ruby/object:Gem::Dependency
98
+ name: guard-rspec
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '4.7'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '4.7'
111
+ - !ruby/object:Gem::Dependency
112
+ name: reek
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '4.5'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '4.5'
125
+ - !ruby/object:Gem::Dependency
126
+ name: rubocop
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '0.47'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '0.47'
139
+ description:
140
+ email:
141
+ - psadauskas@gmail.com
142
+ executables: []
143
+ extensions: []
144
+ extra_rdoc_files:
145
+ - README.md
146
+ - LICENSE.md
147
+ files:
148
+ - LICENSE.md
149
+ - README.md
150
+ - lib/active_job_exception_handler.rb
151
+ - lib/active_job_exception_handler/exception_context.rb
152
+ - lib/active_job_exception_handler/identity.rb
153
+ homepage: ''
154
+ licenses:
155
+ - MIT
156
+ metadata: {}
157
+ post_install_message:
158
+ rdoc_options: []
159
+ require_paths:
160
+ - lib
161
+ required_ruby_version: !ruby/object:Gem::Requirement
162
+ requirements:
163
+ - - "~>"
164
+ - !ruby/object:Gem::Version
165
+ version: '2.4'
166
+ required_rubygems_version: !ruby/object:Gem::Requirement
167
+ requirements:
168
+ - - ">="
169
+ - !ruby/object:Gem::Version
170
+ version: '0'
171
+ requirements: []
172
+ rubyforge_project:
173
+ rubygems_version: 2.6.11
174
+ signing_key:
175
+ specification_version: 4
176
+ summary: ''
177
+ test_files: []