xqsr3 0.17.2 → 0.18.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 18672bcef5bd6994d1ff9c2dc5be6289ca79042e
4
- data.tar.gz: cc79b90f538dbdc1273853d73d4b13cea0fab110
3
+ metadata.gz: fdaa43c901071d88f6fe3737998e4cd6a741c69b
4
+ data.tar.gz: c6876d3f0814a79a4c9a37bcb313c834ebae4096
5
5
  SHA512:
6
- metadata.gz: 16ae7d901c5ef0a916497c34fb9edfb342c0f774071a1b6f3de6a922c452d1de639caee0e587c18e566a7164b4b5303dff2ab09acb03624dc4edcb1df954cde4
7
- data.tar.gz: d77073d7e4fdcc461c75b5ab6d2e522573f113647b72cf7db16e3e7c8c209403cb37d5bbac96fda35cf697594f84b0ae88864e06a975785f7cbd6838de5352a6
6
+ metadata.gz: 308f153ed24f39fa50494df2099e8978b5aa55c99b5b016a48ab03e73f9cff058f00d8056364a87695fe16497a0c1b3d70225c9146b4fb1e23bd4fd2cef250ca
7
+ data.tar.gz: 7590f907009d95b6057aec1573d7841c39ea2bbd8ff0713fa29356ae3d3836e39121c587285144db6d6f8e05019744caf493c6ead4682d1e648354b2e66c4c46
@@ -0,0 +1,194 @@
1
+
2
+ # ######################################################################## #
3
+ # File: lib/xqsr3/diagnostics/exceptions/with_cause.rb
4
+ #
5
+ # Purpose: Definition of the WithCause inclusion module
6
+ #
7
+ # Created: 16th December 2017
8
+ # Updated: 17th December 2017
9
+ #
10
+ # Home: http://github.com/synesissoftware/xqsr3
11
+ #
12
+ # Author: Matthew Wilson
13
+ #
14
+ # Copyright (c) 2017, Matthew Wilson and Synesis Software
15
+ # All rights reserved.
16
+ #
17
+ # Redistribution and use in source and binary forms, with or without
18
+ # modification, are permitted provided that the following conditions are
19
+ # met:
20
+ #
21
+ # * Redistributions of source code must retain the above copyright notice,
22
+ # this list of conditions and the following disclaimer.
23
+ #
24
+ # * Redistributions in binary form must reproduce the above copyright
25
+ # notice, this list of conditions and the following disclaimer in the
26
+ # documentation and/or other materials provided with the distribution.
27
+ #
28
+ # * Neither the names of the copyright holder nor the names of its
29
+ # contributors may be used to endorse or promote products derived from
30
+ # this software without specific prior written permission.
31
+ #
32
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
33
+ # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
34
+ # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
35
+ # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
36
+ # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
37
+ # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
38
+ # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
39
+ # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
40
+ # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
41
+ # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
42
+ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
43
+ #
44
+ # ######################################################################## #
45
+
46
+
47
+ # ##########################################################
48
+ # ::Xqsr3::Diagnostics::Exceptions::WithCause
49
+
50
+ =begin
51
+ =end
52
+
53
+ module Xqsr3
54
+ module Diagnostics
55
+ module Exceptions
56
+
57
+ # This inclusion module adds to an exception class the means to chain a
58
+ # cause (aka inner-exception), which is then exposed with the +cause+
59
+ # attribute
60
+ #
61
+ # *Examples:*
62
+ #
63
+ # Passing an exception cause as a parameter
64
+ #
65
+ #
66
+ #
67
+ module WithCause
68
+
69
+ # Defines an initializer for an exception class that allows a cause (aka
70
+ # an inner exception) to be specified, either as the first or last
71
+ # argument or as a +:cause+ option
72
+ #
73
+ # === Signature
74
+ #
75
+ # * *Parameters:*
76
+ # -
77
+ # - +option+::
78
+ #
79
+ # * *Options:*
80
+ # - +:cause+ - The exception to be used as a cause, and ensures that
81
+ # that is not inferred from the arguments. May be +nil+ to ensure
82
+ # that no cause is inferred
83
+ def initialize(*args, **options)
84
+
85
+ @uses_cause_message = false
86
+
87
+ cz = options[:cause]
88
+
89
+ if cz
90
+
91
+ options = options.reject { |k, v| k == :cause }
92
+
93
+ @has_implicit_message = args.empty?
94
+
95
+ super *args
96
+
97
+ warn 'unexpected implicit message' if @has_implicit_message && self.message != self.class.to_s
98
+
99
+ @cause = cz
100
+ else
101
+
102
+ cz_ix = args.index { |arg| ::Exception === arg }
103
+
104
+ if cz_ix
105
+
106
+ args = args.dup
107
+
108
+ cz = args.delete_at cz_ix
109
+
110
+ if args.empty?
111
+
112
+ if !(cz.message || '').empty? && cz.class.to_s != cz.message
113
+
114
+ @uses_cause_message = true
115
+
116
+ args = [ cz.message ]
117
+ end
118
+ end
119
+ else
120
+
121
+ cz = $!
122
+ end
123
+
124
+ @has_implicit_message = args.empty?
125
+
126
+ super *args
127
+
128
+ warn 'unexpected implicit message' if @has_implicit_message && self.message != self.class.to_s
129
+
130
+ @cause = cz
131
+ end
132
+
133
+ @options = options
134
+ end
135
+
136
+ # The cause / inner-exception, if any, specified to the instance
137
+ # initialiser
138
+ attr_reader :cause
139
+
140
+ # The options passed to the initialiser, with +:cause+ removed, if
141
+ # present
142
+ attr_reader :options
143
+
144
+ def chained_message **options
145
+
146
+ return message unless cause
147
+ return message if @uses_cause_message
148
+
149
+ m = message
150
+ c = cause
151
+ cm = c.respond_to?(:chained_message) ? c.chained_message(**options) : c.message
152
+
153
+ return m if (cm || '').empty?
154
+ return cm if (m || '').empty?
155
+
156
+ sep = options[:separator] || ': '
157
+
158
+ "#{m}#{sep}#{cm}"
159
+ end
160
+
161
+ # An array of exceptions in the chain, excluding +self+
162
+ def chainees
163
+
164
+ return [] unless cause
165
+
166
+ r = [ cause ]
167
+
168
+ r += cause.chainees if cause.respond_to? :chainees
169
+
170
+ r
171
+ end
172
+
173
+ # An array of exceptions in the chain, including +self+
174
+ def exceptions
175
+
176
+ [ self ] + chainees
177
+ end
178
+
179
+ # A combination of the backtrace(s) of all chained exception(s)
180
+ def chained_backtrace
181
+
182
+ b = backtrace
183
+
184
+ return b unless cause
185
+
186
+ cb = cause.respond_to?(:chained_backtrace) ? cause.chained_backtrace : cause.backtrace
187
+
188
+ (cb - b) + b
189
+ end
190
+ end
191
+
192
+ end # module Exceptions
193
+ end # module Diagnostics
194
+ end # module Xqsr3
data/lib/xqsr3/version.rb CHANGED
@@ -50,7 +50,7 @@
50
50
  module Xqsr3
51
51
 
52
52
  # Current version of the Xqsr3 library
53
- VERSION = '0.17.2'
53
+ VERSION = '0.18.1'
54
54
 
55
55
  private
56
56
  VERSION_PARTS_ = VERSION.split(/[.]/).collect { |n| n.to_i } # :nodoc:
@@ -0,0 +1,262 @@
1
+ #! /usr/bin/env ruby
2
+
3
+ $:.unshift File.join(File.dirname(__FILE__), '../../../../lib')
4
+
5
+ require 'xqsr3/diagnostics/exceptions/with_cause'
6
+
7
+ require 'test/unit'
8
+
9
+ class Test_WithCause < Test::Unit::TestCase
10
+
11
+ class SomeExceptionWithCause < ::Exception
12
+
13
+ include ::Xqsr3::Diagnostics::Exceptions::WithCause
14
+ end
15
+
16
+ def test_no_ctor_args
17
+
18
+ x = SomeExceptionWithCause.new
19
+
20
+ assert_nil x.cause
21
+ assert_equal SomeExceptionWithCause.to_s, x.message
22
+ assert_equal SomeExceptionWithCause.to_s, x.chained_message
23
+ assert_empty x.chainees
24
+ assert_equal [ x ], x.exceptions
25
+ assert_nil x.backtrace
26
+ assert_empty x.options
27
+ end
28
+
29
+ def test_1_ctor_arg_that_is_a_message
30
+
31
+ msg = 'stuff'
32
+
33
+ x = SomeExceptionWithCause.new msg
34
+
35
+ assert_nil x.cause
36
+ assert_equal msg, x.message
37
+ assert_equal msg, x.chained_message
38
+ assert_empty x.chainees
39
+ assert_equal [ x ], x.exceptions
40
+ assert_nil x.backtrace
41
+ assert_empty x.options
42
+ end
43
+
44
+ def test_1_ctor_arg_that_is_a_cause_and_has_no_message
45
+
46
+ c = RuntimeError.new
47
+
48
+ x = SomeExceptionWithCause.new c
49
+
50
+ assert_not_nil x.cause
51
+ assert_equal SomeExceptionWithCause.to_s, x.message
52
+ assert_equal "#{SomeExceptionWithCause.to_s}: #{RuntimeError.to_s}", x.chained_message
53
+ assert_not_empty x.chainees
54
+ assert_equal [ c ], x.chainees
55
+ assert_equal [ x, c ], x.exceptions
56
+ assert_nil x.backtrace
57
+ assert_empty x.options
58
+ end
59
+
60
+ def test_1_ctor_arg_that_is_a_cause_and_has_a_message
61
+
62
+ c = RuntimeError.new 'blah'
63
+
64
+ x = SomeExceptionWithCause.new c
65
+
66
+ assert_not_nil x.cause
67
+ assert_equal 'blah', x.message
68
+ assert_equal 'blah', x.chained_message
69
+ assert_not_empty x.chainees
70
+ assert_equal [ c ], x.chainees
71
+ assert_equal [ x, c ], x.exceptions
72
+ assert_nil x.backtrace
73
+ assert_empty x.options
74
+ end
75
+
76
+ def test_2_ctor_args_that_are_message_and_cause
77
+
78
+ msg = 'stuff'
79
+
80
+ c = RuntimeError.new
81
+
82
+ x = SomeExceptionWithCause.new msg, c
83
+
84
+ assert_not_nil x.cause
85
+ assert_equal msg, x.message
86
+ assert_equal "#{msg}: #{RuntimeError.to_s}", x.chained_message
87
+ assert_not_empty x.chainees
88
+ assert_equal [ c ], x.chainees
89
+ assert_equal [ x, c ], x.exceptions
90
+ assert_nil x.backtrace
91
+ assert_empty x.options
92
+ end
93
+
94
+ def test_2_ctor_args_that_are_message_and_cause_that_has_a_message
95
+
96
+ msg = 'stuff'
97
+
98
+ c = RuntimeError.new 'blah'
99
+
100
+ x = SomeExceptionWithCause.new msg, c
101
+
102
+ assert_not_nil x.cause
103
+ assert_equal msg, x.message
104
+ assert_equal 'stuff: blah', x.chained_message
105
+
106
+ assert_not_empty x.chainees
107
+ assert_equal [ c ], x.chainees
108
+ assert_equal [ x, c ], x.exceptions
109
+ assert_nil x.backtrace
110
+ assert_empty x.options
111
+ end
112
+
113
+ def test_cause_in_options
114
+
115
+ c = RuntimeError.new 'inner'
116
+
117
+ x = SomeExceptionWithCause.new 'outer', cause: c
118
+
119
+ assert_not_nil x.cause
120
+ assert_equal 'outer', x.message
121
+ assert_equal 'outer: inner', x.chained_message
122
+
123
+ assert_not_empty x.chainees
124
+ assert_equal [ c ], x.chainees
125
+ assert_equal [ x, c ], x.exceptions
126
+ assert_nil x.backtrace
127
+ assert_empty x.options
128
+ end
129
+
130
+
131
+
132
+ class GrandchildException < Exception
133
+
134
+ include ::Xqsr3::Diagnostics::Exceptions::WithCause
135
+ end
136
+
137
+ class ChildException < Exception
138
+
139
+ def initialize(*args, **options)
140
+
141
+ super *args, **options
142
+ end
143
+
144
+ include ::Xqsr3::Diagnostics::Exceptions::WithCause
145
+ end
146
+
147
+ class ParentException < Exception
148
+
149
+ include ::Xqsr3::Diagnostics::Exceptions::WithCause
150
+
151
+ def initialize(*args, **options)
152
+
153
+ super *args, **options
154
+ end
155
+ end
156
+
157
+ class GrandparentException < Exception
158
+
159
+ include ::Xqsr3::Diagnostics::Exceptions::WithCause
160
+ end
161
+
162
+
163
+ def test_four_levels
164
+
165
+ gc = GrandchildException.new 'gc'
166
+
167
+ c = ChildException.new 'c', gc
168
+
169
+ p = ParentException.new c, 'p'
170
+
171
+ gp = GrandparentException.new 'gp', cause: p
172
+
173
+ assert_equal 'gp: p: c: gc', gp.chained_message
174
+ assert_equal 'gp-p-c-gc', gp.chained_message(separator: '-')
175
+ end
176
+ end
177
+
178
+ class Test_WithCause_throwing < Test::Unit::TestCase
179
+
180
+ class SomeExceptionWithCause < ::Exception
181
+
182
+ include ::Xqsr3::Diagnostics::Exceptions::WithCause
183
+ end
184
+
185
+ def f m
186
+
187
+ raise SomeExceptionWithCause, m
188
+ end
189
+
190
+ def g m, n
191
+
192
+ begin
193
+
194
+ f n
195
+ rescue Exception => x
196
+
197
+ raise SomeExceptionWithCause.new m, x
198
+ end
199
+ end
200
+
201
+ def h m, n, o
202
+
203
+ begin
204
+
205
+ g n, 0
206
+ rescue Exception => x
207
+
208
+ raise SomeExceptionWithCause.new m, x
209
+ end
210
+ end
211
+
212
+ def test_one_level
213
+
214
+ begin
215
+
216
+ f 'one-level'
217
+
218
+ assert false, 'should not get here!'
219
+ rescue Exception => x
220
+
221
+ assert_nil x.cause
222
+ assert_equal 'one-level', x.message
223
+ assert_equal 'one-level', x.chained_message
224
+ assert_empty x.chainees
225
+ assert_not_empty x.backtrace
226
+
227
+ x_bt0 = x.backtrace[0]
228
+
229
+ assert /:in\s+\`f\'\s*$/ =~ x_bt0, 'not receieved from f()'
230
+ end
231
+ end
232
+
233
+ def test_two_levels
234
+
235
+ begin
236
+
237
+ g 'two-levels', 'one-level'
238
+
239
+ assert false, 'should not get here!'
240
+ rescue Exception => x
241
+
242
+ assert_not_nil x.cause
243
+ assert_equal 'two-levels', x.message
244
+ assert_equal 'two-levels: one-level', x.chained_message
245
+ assert_not_empty x.chainees
246
+ assert_kind_of SomeExceptionWithCause, x.chainees[0]
247
+ assert_not_empty x.backtrace
248
+ assert_not_empty x.cause.backtrace
249
+
250
+ x_bt0 = x.backtrace[0]
251
+
252
+ assert /:in\s+\`rescue in g\'\s*$/ =~ x_bt0, 'not receieved from g()'
253
+
254
+ c_bt0 = x.cause.backtrace[0]
255
+
256
+ assert /:in\s+\`f\'\s*$/ =~ c_bt0, 'not receieved from f()'
257
+
258
+ assert_not_empty x.chained_backtrace
259
+ end
260
+ end
261
+ end
262
+
@@ -0,0 +1,12 @@
1
+ #! /usr/bin/env ruby
2
+ #
3
+ # executes all other tests
4
+
5
+ this_dir = File.expand_path(File.dirname(__FILE__))
6
+
7
+ # all tc_*rb in current directory
8
+ Dir[File.join(this_dir, 'tc_*rb')].each { |file| require file }
9
+
10
+ # all ts_*rb in immediate sub-directories
11
+ Dir[File.join(this_dir, '*', 'ts_*rb')].each { |file| require file }
12
+
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: xqsr3
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.17.2
4
+ version: 0.18.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matt Wilson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-12-16 00:00:00.000000000 Z
11
+ date: 2017-12-17 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: |
14
14
  eXtensions by fine Quantum for Standard Ruby and 3rd-party libraries is a
@@ -25,6 +25,7 @@ files:
25
25
  - lib/xqsr3/containers/multi_map.rb
26
26
  - lib/xqsr3/conversion/bool_parser.rb
27
27
  - lib/xqsr3/diagnostics/exception_utilities.rb
28
+ - lib/xqsr3/diagnostics/exceptions/with_cause.rb
28
29
  - lib/xqsr3/doc_.rb
29
30
  - lib/xqsr3/extensions/array/join_with_or.rb
30
31
  - lib/xqsr3/extensions/enumerable/collect_with_index.rb
@@ -73,6 +74,8 @@ files:
73
74
  - test/unit/containers/ts_all.rb
74
75
  - test/unit/conversion/tc_to_bool.rb
75
76
  - test/unit/conversion/ts_all.rb
77
+ - test/unit/diagnostics/exceptions/tc_with_cause.rb
78
+ - test/unit/diagnostics/exceptions/ts_all.rb
76
79
  - test/unit/diagnostics/tc_exception_utilities.rb
77
80
  - test/unit/diagnostics/ts_all.rb
78
81
  - test/unit/extensions/enumerable/tc_collect_with_index.rb