exception_transformer 0.1.0

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: d7ed6231e5c5ec775142f9bcbefbf8e5611491f11ca8210d97bd883a3f91f494
4
+ data.tar.gz: 8e676ba345439a39b6b36cf1139e2554127d4f1d8842982c115f7aa0ec85f982
5
+ SHA512:
6
+ metadata.gz: 495784ad163c57718c3d7ea4891937a5e2f909fefe4d9d6c6396da0663ec160d63570999e4adfb9447ec39bb9a0112d8b9f9c9dcbfc990a6994a72cf01136ff3
7
+ data.tar.gz: 4e2fabcfccd2915c81ece88e93aa20b6f9eb70fc20541d3597762bcc28307707b21276f3042d35bd95e5e9a7323d46d52ffee4580a532c4075d3c1ab8b9fbb9c
@@ -0,0 +1,102 @@
1
+ module ExceptionTransformer
2
+ # Include this module when declaring an exception class to add
3
+ # the `reportable?` flag to individual exceptions. The presence
4
+ # of this flag can then be checked when capturing exceptions to
5
+ # send to a crash reporter.
6
+ #
7
+ # @example Conditionally reporting exceptions
8
+ # class MyError < StandardError
9
+ # include ExceptionTransformer::ReportableException
10
+ # end
11
+ #
12
+ # begin
13
+ # # do something that may fail
14
+ # rescue MyError => e
15
+ # CrashReport.new(e) if e.reportable?
16
+ # end
17
+ #
18
+ # This flag can be set at the instance level with `mark_reportable!`.
19
+ # Alternatively, the class method `as_reportable` returns a subclass
20
+ # for which `reportable?` is true when raised.
21
+ module Reportable
22
+ def self.included(base)
23
+ raise TypeError, "#{base} is not a type of Exception" unless base <= Exception
24
+ base.extend ClassMethods
25
+ end
26
+
27
+ module ClassMethods
28
+ # Returns a subclass 'Reportable_<name>' of the current class that
29
+ # includes `Reportable`. This subclass is created the first time
30
+ # time this method is called and reused for subsequent invocations.
31
+ def as_reportable
32
+ return self if self <= ReportableException
33
+
34
+ name = reportable_name
35
+ mod = parent
36
+
37
+ mod.const_defined?(name) ? mod.const_get(name) : mod.const_set(name, build_reportable)
38
+ end
39
+
40
+ def unload_reportable
41
+ name = reportable_name
42
+ mod = parent
43
+
44
+ mod.send(:remove_const, name) if mod.const_defined?(name)
45
+ end
46
+
47
+ def reported_class
48
+ @reported_class ||= self
49
+ end
50
+
51
+ def reported_class=(klass)
52
+ @reported_class = klass
53
+ end
54
+
55
+ private
56
+
57
+ def build_reportable
58
+ super_class = self
59
+ Class.new(super_class) do
60
+ include ReportableException
61
+ self.reported_class = super_class
62
+ end
63
+ end
64
+
65
+ def reportable_name
66
+ [ReportableException.name, self.name].map(&:demodulize).join("_")
67
+ end
68
+ end
69
+
70
+ def reportable?
71
+ @reportable ||= false
72
+ end
73
+
74
+ def mark_reportable!
75
+ @reportable = true
76
+ end
77
+ end
78
+
79
+ # Every `Reportable` that includes `ReportableException` will be
80
+ # raised with the `reportable?` flag set to true.
81
+ module ReportableException
82
+ def self.included(base)
83
+ base.include(Reportable) unless base <= Reportable
84
+ base.extend ClassMethods
85
+ end
86
+
87
+ module ClassMethods
88
+ def exception(*args)
89
+ if reported_class == self
90
+ super.tap(&:mark_reportable!)
91
+ else
92
+ reported_class.exception(*args).tap(&:mark_reportable!)
93
+ end
94
+ end
95
+ end
96
+
97
+ def exception(*args)
98
+ mark_reportable!
99
+ super
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,92 @@
1
+ class ExceptionTransformer::Transformer
2
+ attr_accessor :strategy, :validator, :delegate, :mappings
3
+
4
+ MAX_MESSAGE_SIZE = 100
5
+
6
+ def initialize(strategy)
7
+ self.strategy = strategy
8
+ self.mappings = {}
9
+ end
10
+
11
+ def register_target(target, exceptions)
12
+ case strategy
13
+ when :validate
14
+ self.validator = target
15
+ when :delegate
16
+ self.delegate = target
17
+ when :rewrite, :regex
18
+ exceptions.each do |klass|
19
+ self.mappings[klass] = target
20
+ end
21
+ end
22
+ end
23
+
24
+ def after_rescue(obj, e, calling_method, except: [], use_default: true, opts: {})
25
+ with_reporting do
26
+ case strategy
27
+ when :delegate
28
+ obj.instance_exec(e, calling_method, opts, &delegate)
29
+ when :rewrite, :regex
30
+ exception, message = find_target(e, except, use_default)
31
+ end
32
+
33
+ if exception.present?
34
+ raise exception, message.first(MAX_MESSAGE_SIZE), e.backtrace
35
+ else
36
+ # Couldn't transform the exception to a defined mapping.
37
+ raise e
38
+ end
39
+ end
40
+ end
41
+
42
+ def after_yield(obj, result, calling_method, except: [], use_default: true, opts: {})
43
+ with_reporting do
44
+ case strategy
45
+ when :validate
46
+ obj.instance_exec(result, calling_method, opts, &validator)
47
+ end
48
+ end
49
+ end
50
+
51
+ private
52
+
53
+ # @return [Array] `[exception, message]`
54
+ def find_target(e, exclude, use_default_match)
55
+ case strategy
56
+ when :rewrite
57
+ exception = find_mapping(e, exclude)
58
+ message = e.message
59
+ when :regex
60
+ patterns = find_mapping(e, exclude) || {}
61
+
62
+ pattern = patterns.keys.find { |re| re.is_a?(Regexp) && e.message =~ re }
63
+ pattern ||= :default if use_default_match
64
+
65
+ exception = patterns[pattern]
66
+ message = e.message.length <= MAX_MESSAGE_SIZE ? e.message : pattern.inspect.gsub(/([^\w\s]|i\z)*/, '')
67
+ end
68
+
69
+ [exception, message]
70
+ end
71
+
72
+ # @return [StandardError, Hash]
73
+ def find_mapping(e, exclude)
74
+ mappings
75
+ .select { |klass| e.is_a?(klass) && !exclude.include?(klass) }
76
+ .sort_by { |klass, _| klass.ancestors.count }
77
+ .last.try(:[], 1)
78
+ end
79
+
80
+ # Report all exceptions that occur except those
81
+ # with the `reportable?` flag set to false.
82
+ def with_reporting
83
+ yield
84
+ rescue => e
85
+ unless e.respond_to?(:reportable?) && !e.reportable?
86
+ # @examples
87
+ # Raven.capture_exception(e)
88
+ end
89
+
90
+ raise
91
+ end
92
+ end
@@ -0,0 +1,3 @@
1
+ module ExceptionTransformer
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,82 @@
1
+ require 'exception_transformer/version'
2
+ require 'exception_transformer/transformer'
3
+
4
+ require 'active_support'
5
+ require 'active_support/core_ext'
6
+
7
+ require 'byebug'
8
+
9
+ module ExceptionTransformer
10
+ def self.included base
11
+ base.send :include, InstanceMethods
12
+ base.extend ClassMethods
13
+ end
14
+
15
+ module ClassMethods
16
+ # Add exceptions to be transformed in `handle_exceptions` block.
17
+ # @examples
18
+ # 1. Transform several errors to a single error:
19
+ #
20
+ # transform_exceptions FooError, BazError, to: BarError
21
+ #
22
+ # 2. Transform a single error based on it's message:
23
+ #
24
+ # transform_exceptions FooError, where: {
25
+ # /Invalid API key/i => BarError,
26
+ # :default => RuntimeError
27
+ # }
28
+ #
29
+ # To prevent *all* errors being caught via the `:default` branch,
30
+ # pass `use_default: false` to `handle_exceptions`.
31
+ #
32
+ # 3. Validate a response with a Proc that takes two parameters. The
33
+ # first parameter is the response, and the second is the calling method.
34
+ #
35
+ # transform_exceptions validate: proc { |response, action| ... }
36
+ #
37
+ # 4. Inspect an error with a Proc that takes two parameters. The
38
+ # first parameter is the error, and the second is the calling method.
39
+ #
40
+ # transform_exceptions with: proc { |err, action| ... }
41
+ def transform_exceptions(*exceptions, group: :default, to: nil, where: nil, with: nil, validate: nil)
42
+ strategies = { validate: validate, delegate: with, rewrite: to, regex: where }
43
+
44
+ strategy = strategies.keys.find { |s| strategies[s].present? }
45
+ target = strategies[strategy]
46
+
47
+ transformer = find_or_create_exception_transformer(group, strategy)
48
+ transformer.register_target(target, exceptions)
49
+ end
50
+
51
+ def find_exception_transformer(group)
52
+ exception_transformers[group]
53
+ end
54
+
55
+ def find_or_create_exception_transformer(group, strategy)
56
+ exception_transformers[group] ||= Transformer.new(strategy)
57
+ end
58
+
59
+ private
60
+
61
+ def exception_transformers
62
+ @exception_transformers ||= {}
63
+ end
64
+ end
65
+
66
+ module InstanceMethods
67
+ def handle_exceptions(group = :default, **opts)
68
+ # NOTE: `base_label` returns the label of this frame without decoration,
69
+ # i.e. if `label` was 'block in test', then `base_label` would be `test`.
70
+ calling_method = caller_locations(1, 1)[0].base_label
71
+ transformer = self.class.find_exception_transformer(group)
72
+
73
+ result = yield
74
+
75
+ transformer.after_yield(self, result, calling_method, opts)
76
+
77
+ result
78
+ rescue => e
79
+ transformer.after_rescue(self, e, calling_method, opts)
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,106 @@
1
+ require 'exception_transformer/reportable'
2
+ require 'helpers/class_helpers'
3
+
4
+ describe ExceptionTransformer::Reportable do
5
+ include ClassHelpers
6
+
7
+ context 'when included' do
8
+ context 'in a class that is not an Exception' do
9
+ before(:each) { build_class :NotAnException }
10
+
11
+ it 'raises TypeError' do
12
+ expect { NotAnException.include ExceptionTransformer::Reportable }
13
+ .to raise_error(TypeError)
14
+ end
15
+ end
16
+
17
+ context 'in an Exception' do
18
+ before(:each) { build_class :MyException, Exception }
19
+
20
+ it 'it should extend it with class methods' do
21
+ expect { MyException.include ExceptionTransformer::Reportable }
22
+ .to change { MyException.singleton_class.ancestors }
23
+ .to include(ExceptionTransformer::Reportable::ClassMethods)
24
+ end
25
+ end
26
+ end
27
+
28
+ shared_context :reportable, define_reportable: true do
29
+ before(:each) do
30
+ build_class :MyException, Exception do
31
+ include ExceptionTransformer::Reportable
32
+ end
33
+ end
34
+
35
+ after(:each) { MyException.unload_reportable }
36
+ end
37
+
38
+ describe '::as_reportable', define_reportable: true do
39
+ it 'returns a subclass of ReportableException' do
40
+ expect(MyException.as_reportable).to be < ExceptionTransformer::ReportableException
41
+ end
42
+
43
+ it 'is idempotent' do
44
+ reportable_exception = MyException.as_reportable
45
+ expect(reportable_exception).to equal(reportable_exception.as_reportable)
46
+ end
47
+
48
+ describe 'the return value' do
49
+ it 'is a subclass of the caller' do
50
+ expect(MyException.as_reportable).to be < MyException
51
+ end
52
+
53
+ it 'is a constant' do
54
+ expect(MyException.as_reportable.name).not_to be_nil
55
+ end
56
+
57
+ context 'when raised' do
58
+ it 'is reportable' do
59
+ expect { raise MyException.as_reportable, 'oops!' }.to raise_error(be_reportable, 'oops!')
60
+ end
61
+ end
62
+ end
63
+ end
64
+
65
+ describe '::unload_reportable', define_reportable: true do
66
+ context 'when the reportable exception is defined' do
67
+ it 'removes the constant' do
68
+ reportable_exception = MyException.as_reportable
69
+ MyException.unload_reportable
70
+ expect(Object.const_defined?(reportable_exception.name)).to be false
71
+ end
72
+
73
+ it 'returns it' do
74
+ reportable_exception = MyException.as_reportable
75
+ expect(MyException.unload_reportable).to equal(reportable_exception)
76
+ end
77
+ end
78
+
79
+ context 'when the reportable exception is not defined' do
80
+ it 'returns nil' do
81
+ expect(MyException.unload_reportable).to be_nil
82
+ end
83
+ end
84
+ end
85
+
86
+ describe '#reportable?', define_reportable: true do
87
+ context 'when ReportableException is not included' do
88
+ context 'when the exception is raised' do
89
+ it 'is false' do
90
+ # assert !MyException.include?(ExceptionTransformer::ReportableException)
91
+ expect { raise MyException.new }.to raise_error do |e|
92
+ expect(e).not_to be_reportable
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
98
+
99
+ describe '#mark_reportable!', define_reportable: true do
100
+ it 'marks the exception as reportable' do
101
+ exception = MyException.new
102
+ expect { exception.mark_reportable! }
103
+ .to change { exception.reportable? }.from(false).to(true)
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,291 @@
1
+ require 'exception_transformer'
2
+
3
+ describe ExceptionTransformer do
4
+ class FooError < StandardError; end
5
+ class BarError < StandardError; end
6
+
7
+ class BazError < StandardError; end
8
+ class QuxError < StandardError; end
9
+
10
+ class ClassWithExceptionTransformer
11
+ include ExceptionTransformer
12
+ end
13
+
14
+ def build_obj(&block)
15
+ Class.new(ClassWithExceptionTransformer, &block).new
16
+ end
17
+
18
+ it 'returns the result if there are no errors' do
19
+ obj = build_obj do
20
+ transform_exceptions FooError, to: BarError
21
+ def task; :task end
22
+ end
23
+
24
+ expect(obj.task).to be(:task)
25
+ end
26
+
27
+ it 'should preserve exception messages' do
28
+ obj = build_obj do
29
+ transform_exceptions FooError, to: BarError
30
+
31
+ def task; handle_exceptions { raise FooError, 'oops' } end
32
+ end
33
+
34
+ expect{obj.task}.to raise_error(BarError, 'oops')
35
+ end
36
+
37
+ context 'when using :validate strategy' do
38
+ it 'transforms exceptions' do
39
+ obj = build_obj do
40
+ transform_exceptions validate: proc { |res, action|
41
+ unless res == :success
42
+ fail FooError
43
+ end
44
+ }
45
+
46
+ def task1; handle_exceptions { :success } end
47
+ def task2; handle_exceptions { :failure } end
48
+ end
49
+
50
+ expect{obj.task1}.not_to raise_error
51
+ expect{obj.task2}.to raise_error(FooError)
52
+ end
53
+
54
+ it 'executes the proc in instance context' do
55
+ obj = build_obj do
56
+ transform_exceptions validate: proc { |res, action|
57
+ raise FooError unless @foo
58
+ }
59
+
60
+ def task1; handle_exceptions { @foo = :foo } end
61
+ def task2; handle_exceptions { @foo = nil } end
62
+ end
63
+
64
+ expect{obj.task1}.not_to raise_error
65
+ expect{obj.task2}.to raise_error(FooError)
66
+ end
67
+ end
68
+
69
+ context 'when using :delegate strategy' do
70
+ it 'transforms exceptions' do
71
+ obj = build_obj do
72
+ transform_exceptions with: proc { |e, action|
73
+ next unless e.is_a? FooError
74
+ raise BazError
75
+ }
76
+
77
+ def task1; handle_exceptions { raise FooError } end
78
+ def task2; handle_exceptions { raise BarError } end
79
+ end
80
+
81
+ expect{obj.task1}.to raise_error(BazError)
82
+ expect{obj.task2}.to raise_error(BarError)
83
+ end
84
+
85
+ it 'executes the proc in instance context' do
86
+ obj = build_obj do
87
+ transform_exceptions with: proc { |err, action|
88
+ if @foo
89
+ raise FooError
90
+ else
91
+ raise BarError
92
+ end
93
+ }
94
+
95
+ def task1
96
+ handle_exceptions do
97
+ @foo = :foo
98
+ raise
99
+ end
100
+ end
101
+
102
+ def task2
103
+ handle_exceptions do
104
+ @foo = nil
105
+ raise
106
+ end
107
+ end
108
+ end
109
+
110
+ expect{obj.task1}.to raise_error(FooError)
111
+ expect{obj.task2}.to raise_error(BarError)
112
+ end
113
+
114
+ it 'doesnt decorate the caller_location label' do
115
+ obj = build_obj do
116
+ transform_exceptions with: proc { |res, action|
117
+ raise action
118
+ }
119
+
120
+ def task1
121
+ handle_exceptions { raise }
122
+ end
123
+
124
+ def task2
125
+ [:foo, :bar].each do |sym|
126
+ handle_exceptions { raise }
127
+ end
128
+ end
129
+ end
130
+
131
+ expect{obj.task1}.to raise_error('task1')
132
+ expect{obj.task2}.to raise_error('task2')
133
+ end
134
+ end
135
+
136
+ it 'should limit exception messages to certain length' do
137
+ obj = build_obj do
138
+ transform_exceptions FooError, to: BarError
139
+
140
+ def task; handle_exceptions { raise FooError, 'oops' * 50 } end
141
+ end
142
+
143
+ begin
144
+ obj.task
145
+ rescue BarError => e
146
+ expect(e.message.length).to be <= ExceptionTransformer::Transformer::MAX_MESSAGE_SIZE
147
+ end
148
+ end
149
+
150
+ context 'when using :rewrite strategy' do
151
+ it 'transforms exceptions' do
152
+ obj = build_obj do
153
+ transform_exceptions FooError, to: BarError
154
+
155
+ def task; handle_exceptions { raise FooError } end
156
+ end
157
+
158
+ expect{obj.task}.to raise_error(BarError)
159
+ end
160
+
161
+ it 'transforms exceptions by inheritance order' do
162
+ obj = build_obj do
163
+ transform_exceptions FooError, to: BarError
164
+ transform_exceptions StandardError, to: QuxError
165
+
166
+ def task1; handle_exceptions { raise FooError } end
167
+ def task2; handle_exceptions { raise ArgumentError} end
168
+ end
169
+
170
+ expect{obj.task1}.to raise_error(BarError)
171
+ expect{obj.task2}.to raise_error(QuxError)
172
+ end
173
+
174
+ it 'doesnt transform exceptions to be skipped' do
175
+ obj = build_obj do
176
+ transform_exceptions FooError, to: BarError
177
+
178
+ def task; handle_exceptions(except: [FooError]) { raise FooError, 'oops' } end
179
+ end
180
+
181
+ expect{obj.task}.to raise_error(FooError)
182
+ end
183
+ end
184
+
185
+ context 'when using :regex strategy' do
186
+ it 'is able to transform exceptions' do
187
+ obj = build_obj do
188
+ transform_exceptions FooError, where: {
189
+ /oops/ => BarError,
190
+ :default => StandardError
191
+ }
192
+
193
+ def task1; handle_exceptions { raise FooError, 'oops' } end
194
+ def task2; handle_exceptions { raise FooError } end
195
+ end
196
+
197
+ expect{obj.task1}.to raise_error(BarError)
198
+ expect{obj.task2}.to raise_error(StandardError)
199
+ end
200
+
201
+ it 'should fallback to the default transformation unless specified' do
202
+ obj = build_obj do
203
+ transform_exceptions FooError, where: {
204
+ /oops/ => BarError,
205
+ :default => StandardError
206
+ }
207
+
208
+ def task1; handle_exceptions { raise FooError, 'oops' } end
209
+
210
+ def task2
211
+ handle_exceptions use_default: false do
212
+ raise FooError
213
+ end
214
+ end
215
+ end
216
+
217
+ expect{obj.task1}.to raise_error(BarError)
218
+ expect{obj.task2}.to raise_error(FooError)
219
+ end
220
+
221
+ it 'should keep the exception message if < 100 characters' do
222
+ obj = build_obj do
223
+ transform_exceptions FooError, where: {
224
+ /oops/ => BarError,
225
+ :default => StandardError
226
+ }
227
+
228
+ def task1; handle_exceptions { raise FooError, 'oops it broke' } end
229
+ end
230
+
231
+ begin
232
+ obj.task1
233
+ rescue BarError => e
234
+ expect(e.message).to eq('oops it broke')
235
+ end
236
+ end
237
+
238
+ it 'should use a readable version of the regex if message > 100 characters' do
239
+ obj = build_obj do
240
+ transform_exceptions FooError, where: {
241
+ /oops/ => BarError,
242
+ :default => StandardError
243
+ }
244
+
245
+ def task1; handle_exceptions { raise FooError, 'oops' + ((0..9).to_a.join * 10) } end
246
+ end
247
+
248
+ begin
249
+ obj.task1
250
+ rescue BarError => e
251
+ expect(e.message).to eq('oops')
252
+ end
253
+ end
254
+ end
255
+
256
+ describe '::find_exception_transformer' do
257
+ let!(:obj) do
258
+ build_obj do
259
+ transform_exceptions FooError, to: BarError, group: :a
260
+ transform_exceptions FooError, to: BazError, group: :b
261
+ end
262
+ end
263
+
264
+ it 'returns the transformer defined for the given group' do
265
+ transformer = obj.class.find_exception_transformer(:a)
266
+
267
+ expect(transformer).to be
268
+ expect(transformer.mappings).to eq(FooError => BarError)
269
+ end
270
+ end
271
+
272
+ describe '#handle_exceptions' do
273
+ context 'when a group is provided' do
274
+ let!(:obj) do
275
+ build_obj do
276
+ transform_exceptions FooError, to: BarError, group: :a
277
+ transform_exceptions FooError, to: BazError, group: :b
278
+
279
+ def task(group)
280
+ handle_exceptions(group) { raise FooError }
281
+ end
282
+ end
283
+ end
284
+
285
+ it 'should use the transformer defined for the given group' do
286
+ expect { obj.task(:a) }.to raise_error(BarError)
287
+ expect { obj.task(:b) }.to raise_error(BazError)
288
+ end
289
+ end
290
+ end
291
+ end
@@ -0,0 +1,13 @@
1
+ module ClassHelpers
2
+ # Defines a named class, descending from `super_class`.
3
+ # The constant assigned to the the class will be restored
4
+ # to it's original state when an example completes.
5
+ #
6
+ # @param name [Symbol, String] the constant name
7
+ # @param super_class [Class] the class the return value inherits from
8
+ # @yield [] block passed to `Class.new`
9
+ # @return [Class]
10
+ def build_class(name, super_class = Object, &block)
11
+ stub_const(name.to_s, Class.new(super_class, &block))
12
+ end
13
+ end
@@ -0,0 +1,16 @@
1
+ require "bundler/setup"
2
+ require "exception_transformer"
3
+
4
+ RSpec.configure do |config|
5
+ # Enable flags like --only-failures and --next-failure
6
+ config.example_status_persistence_file_path = ".rspec_status"
7
+
8
+ # Disable RSpec exposing methods globally on `Module` and `main`
9
+ config.disable_monkey_patching!
10
+
11
+ config.expect_with :rspec do |c|
12
+ c.syntax = :expect
13
+ end
14
+
15
+ config.expose_dsl_globally = true
16
+ end
metadata ADDED
@@ -0,0 +1,124 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: exception_transformer
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Patrick McLaren
8
+ - Allina Dolor
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2019-01-29 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '1.17'
21
+ type: :development
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '1.17'
28
+ - !ruby/object:Gem::Dependency
29
+ name: rake
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '10.0'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '10.0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: rspec
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '3.0'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: '3.0'
56
+ - !ruby/object:Gem::Dependency
57
+ name: byebug
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - "~>"
61
+ - !ruby/object:Gem::Version
62
+ version: '10.0'
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: '10.0'
70
+ - !ruby/object:Gem::Dependency
71
+ name: activesupport
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - "~>"
75
+ - !ruby/object:Gem::Version
76
+ version: '5.0'
77
+ type: :runtime
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - "~>"
82
+ - !ruby/object:Gem::Version
83
+ version: '5.0'
84
+ description: Add exceptions to be transformed.
85
+ email:
86
+ - patrick@privy.com
87
+ - allina@privy.com
88
+ executables: []
89
+ extensions: []
90
+ extra_rdoc_files: []
91
+ files:
92
+ - lib/exception_transformer.rb
93
+ - lib/exception_transformer/reportable.rb
94
+ - lib/exception_transformer/transformer.rb
95
+ - lib/exception_transformer/version.rb
96
+ - spec/exception_transformer/reportable_spec.rb
97
+ - spec/exception_transformer_spec.rb
98
+ - spec/helpers/class_helpers.rb
99
+ - spec/spec_helper.rb
100
+ homepage: https://github.com/Privy/exception-transformer
101
+ licenses:
102
+ - MIT
103
+ metadata: {}
104
+ post_install_message:
105
+ rdoc_options: []
106
+ require_paths:
107
+ - lib
108
+ required_ruby_version: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ version: '0'
113
+ required_rubygems_version: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ requirements: []
119
+ rubyforge_project:
120
+ rubygems_version: 2.7.6
121
+ signing_key:
122
+ specification_version: 4
123
+ summary: Error Handling
124
+ test_files: []