exception_transformer 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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: []