structured_warnings 0.2.0 → 0.3.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.
Files changed (87) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +200 -0
  3. data/lib/{structured_warnings/dynamic.rb → dynamic.rb} +0 -0
  4. data/lib/structured_warnings.rb +19 -9
  5. data/lib/structured_warnings/base.rb +132 -0
  6. data/lib/structured_warnings/kernel.rb +5 -65
  7. data/lib/structured_warnings/minitest.rb +5 -0
  8. data/lib/structured_warnings/test.rb +3 -5
  9. data/lib/structured_warnings/test/assertions.rb +93 -98
  10. data/lib/structured_warnings/test/warner.rb +32 -35
  11. data/lib/structured_warnings/test_unit.rb +5 -0
  12. data/lib/structured_warnings/version.rb +3 -0
  13. data/lib/structured_warnings/warner.rb +13 -11
  14. data/lib/structured_warnings/warning.rb +61 -119
  15. data/lib/warning.rb +9 -0
  16. metadata +53 -92
  17. data/Gemfile +0 -3
  18. data/Gemfile.lock +0 -21
  19. data/History.txt +0 -36
  20. data/License.txt +0 -20
  21. data/README.rdoc +0 -137
  22. data/Rakefile +0 -55
  23. data/doc/DeprecatedMethodWarning.html +0 -105
  24. data/doc/DeprecatedSignatureWarning.html +0 -106
  25. data/doc/DeprecationWarning.html +0 -106
  26. data/doc/Dynamic.html +0 -125
  27. data/doc/Object.html +0 -117
  28. data/doc/README_rdoc.html +0 -283
  29. data/doc/StandardWarning.html +0 -104
  30. data/doc/StructuredWarnings.html +0 -125
  31. data/doc/StructuredWarnings/ClassMethods.html +0 -192
  32. data/doc/StructuredWarnings/Kernel.html +0 -222
  33. data/doc/StructuredWarnings/Test.html +0 -97
  34. data/doc/StructuredWarnings/Test/Assertions.html +0 -272
  35. data/doc/StructuredWarnings/Test/Warner.html +0 -208
  36. data/doc/StructuredWarnings/Warner.html +0 -162
  37. data/doc/Test.html +0 -95
  38. data/doc/Test/Unit.html +0 -95
  39. data/doc/Warning.html +0 -398
  40. data/doc/Warning/ClassMethods.html +0 -278
  41. data/doc/created.rid +0 -10
  42. data/doc/css/fonts.css +0 -167
  43. data/doc/css/rdoc.css +0 -590
  44. data/doc/fonts/Lato-Light.ttf +0 -0
  45. data/doc/fonts/Lato-LightItalic.ttf +0 -0
  46. data/doc/fonts/Lato-Regular.ttf +0 -0
  47. data/doc/fonts/Lato-RegularItalic.ttf +0 -0
  48. data/doc/fonts/SourceCodePro-Bold.ttf +0 -0
  49. data/doc/fonts/SourceCodePro-Regular.ttf +0 -0
  50. data/doc/images/add.png +0 -0
  51. data/doc/images/arrow_up.png +0 -0
  52. data/doc/images/brick.png +0 -0
  53. data/doc/images/brick_link.png +0 -0
  54. data/doc/images/bug.png +0 -0
  55. data/doc/images/bullet_black.png +0 -0
  56. data/doc/images/bullet_toggle_minus.png +0 -0
  57. data/doc/images/bullet_toggle_plus.png +0 -0
  58. data/doc/images/date.png +0 -0
  59. data/doc/images/delete.png +0 -0
  60. data/doc/images/find.png +0 -0
  61. data/doc/images/loadingAnimation.gif +0 -0
  62. data/doc/images/macFFBgHack.png +0 -0
  63. data/doc/images/package.png +0 -0
  64. data/doc/images/page_green.png +0 -0
  65. data/doc/images/page_white_text.png +0 -0
  66. data/doc/images/page_white_width.png +0 -0
  67. data/doc/images/plugin.png +0 -0
  68. data/doc/images/ruby.png +0 -0
  69. data/doc/images/tag_blue.png +0 -0
  70. data/doc/images/tag_green.png +0 -0
  71. data/doc/images/transparent.png +0 -0
  72. data/doc/images/wrench.png +0 -0
  73. data/doc/images/wrench_orange.png +0 -0
  74. data/doc/images/zoom.png +0 -0
  75. data/doc/index.html +0 -121
  76. data/doc/js/darkfish.js +0 -161
  77. data/doc/js/jquery.js +0 -4
  78. data/doc/js/navigation.js +0 -142
  79. data/doc/js/navigation.js.gz +0 -0
  80. data/doc/js/search.js +0 -109
  81. data/doc/js/search_index.js +0 -1
  82. data/doc/js/search_index.js.gz +0 -0
  83. data/doc/js/searcher.js +0 -228
  84. data/doc/js/searcher.js.gz +0 -0
  85. data/doc/table_of_contents.html +0 -200
  86. data/structured_warnings.gemspec +0 -24
  87. data/test/structured_warnings_test.rb +0 -187
@@ -0,0 +1,5 @@
1
+ require 'structured_warnings/test'
2
+
3
+ Minitest::Test.class_eval do
4
+ include StructuredWarnings::Test::Assertions
5
+ end
@@ -1,6 +1,4 @@
1
- require "structured_warnings/test/warner"
2
- require "structured_warnings/test/assertions"
1
+ module StructuredWarnings::Test; end
3
2
 
4
- Test::Unit::TestCase.class_eval do
5
- include StructuredWarnings::Test::Assertions
6
- end
3
+ require 'structured_warnings/test/warner'
4
+ require 'structured_warnings/test/assertions'
@@ -1,111 +1,106 @@
1
- module StructuredWarnings
2
- # This module ecapsulates all extensions to support <code>test/unit</code>.
3
- module Test
4
- module Assertions
5
- # :call-seq:
6
- # assert_no_warn(message = nil) {|| ...}
7
- # assert_no_warn(warning_class, message) {|| ...}
8
- # assert_no_warn(warning_instance) {|| ...}
9
- #
10
- # Asserts that the given warning was not emmitted. It may be restricted
11
- # to a certain subtree of warnings and/or message.
12
- #
13
- # def foo
14
- # warn DeprecatedMethodWarning, "used foo, use bar instead"
15
- # bar
16
- # end
17
- #
18
- # assert_no_warn(StandardWarning) { foo } # passes
19
- #
20
- # assert_no_warn(DeprecationWarning) { foo } # fails
21
- # assert_no_warn() { foo } # fails
22
- #
23
- # See assert_warn for more examples.
24
- #
25
- # *Note*: It is currently not possible to add a custom failure message.
26
- def assert_no_warn(*args)
27
- warning, message = parse_arguments(args)
1
+ # This module ecapsulates all extensions to support <code>test/unit</code>.
2
+ module StructuredWarnings::Test::Assertions
3
+ # :call-seq:
4
+ # assert_no_warn(message = nil) {|| ...}
5
+ # assert_no_warn(warning_class, message) {|| ...}
6
+ # assert_no_warn(warning_instance) {|| ...}
7
+ #
8
+ # Asserts that the given warning was not emmitted. It may be restricted
9
+ # to a certain subtree of warnings and/or message.
10
+ #
11
+ # def foo
12
+ # warn StructuredWarnings::DeprecatedMethodWarning, 'used foo, use bar instead'
13
+ # bar
14
+ # end
15
+ #
16
+ # assert_no_warn(StructuredWarnings::StandardWarning) { foo } # passes
17
+ #
18
+ # assert_no_warn(StructuredWarnings::DeprecationWarning) { foo } # fails
19
+ # assert_no_warn() { foo } # fails
20
+ #
21
+ # See assert_warn for more examples.
22
+ #
23
+ # *Note*: It is currently not possible to add a custom failure message.
24
+ def assert_no_warn(*args)
25
+ warning, message = parse_arguments(args)
28
26
 
29
- w = StructuredWarnings::Test::Warner.new
30
- StructuredWarnings::with_warner(w) do
31
- yield
32
- end
33
- assert_block("<#{args_inspect(args)}> has been emitted.") do
34
- !w.warned?(warning, message)
35
- end
36
- end
27
+ w = StructuredWarnings::Test::Warner.new
28
+ StructuredWarnings::with_warner(w) do
29
+ yield
30
+ end
37
31
 
38
- # :call-seq:
39
- # assert_warn(message = nil) {|| ...}
40
- # assert_warn(warning_class, message) {|| ...}
41
- # assert_warn(warning_instance) {|| ...}
42
- #
43
- # Asserts that the given warning was emmitted. It may be restricted to a
44
- # certain subtree of warnings and/or message.
45
- #
46
- # def foo
47
- # warn DeprecatedMethodWarning, "used foo, use bar instead"
48
- # bar
49
- # end
50
- #
51
- # # passes
52
- # assert_warn(DeprecatedMethodWarning) { foo }
53
- # assert_warn(DeprecationWarning) { foo }
54
- # assert_warn() { foo }
55
- # assert_warn(Warning, "used foo, use bar instead") { foo }
56
- # assert_warn(Warning, /use bar/) { foo }
57
- # assert_warn(Warning.new("used foo, use bar instead")) { foo }
58
- #
59
- # # fails
60
- # assert_warn(StandardWarning) { foo }
61
- # assert_warn(Warning, /deprecated/) { foo }
62
- # assert_warn(Warning.new) { foo }
63
- #
64
- # *Note*: It is currently not possible to add a custom failure message.
65
- def assert_warn(*args)
66
- warning, message = parse_arguments(args)
32
+ assert_equal(false, w.warned?(warning, message), "<#{args_inspect(args)}> has been emitted.")
33
+ end
67
34
 
68
- w = StructuredWarnings::Test::Warner.new
69
- StructuredWarnings::with_warner(w) do
70
- yield
71
- end
72
- assert_block("<#{args_inspect(args)}> has not been emitted.") do
73
- w.warned?(warning, message)
74
- end
75
- end
35
+ # :call-seq:
36
+ # assert_warn(message = nil) {|| ...}
37
+ # assert_warn(warning_class, message) {|| ...}
38
+ # assert_warn(warning_instance) {|| ...}
39
+ #
40
+ # Asserts that the given warning was emmitted. It may be restricted to a
41
+ # certain subtree of warnings and/or message.
42
+ #
43
+ # def foo
44
+ # warn StructuredWarnings::DeprecatedMethodWarning, 'used foo, use bar instead'
45
+ # bar
46
+ # end
47
+ #
48
+ # # passes
49
+ # assert_warn(StructuredWarnings::DeprecatedMethodWarning) { foo }
50
+ # assert_warn(StructuredWarnings::DeprecationWarning) { foo }
51
+ # assert_warn() { foo }
52
+ # assert_warn(StructuredWarnings::Base, 'used foo, use bar instead') { foo }
53
+ # assert_warn(StructuredWarnings::Base, /use bar/) { foo }
54
+ # assert_warn(StructuredWarnings::Base.new('used foo, use bar instead')) { foo }
55
+ #
56
+ # # fails
57
+ # assert_warn(StructuredWarnings::StandardWarning) { foo }
58
+ # assert_warn(StructuredWarnings::Base, /deprecated/) { foo }
59
+ # assert_warn(StructuredWarnings::Base.new) { foo }
60
+ #
61
+ # *Note*: It is currently not possible to add a custom failure message.
62
+ def assert_warn(*args)
63
+ warning, message = parse_arguments(args)
76
64
 
77
- private
78
- def parse_arguments(args)
79
- args = args.clone
80
- first = args.shift
81
- if first.is_a? Class and first <= Warning
82
- warning = first
83
- message = args.shift
65
+ w = StructuredWarnings::Test::Warner.new
66
+ StructuredWarnings::with_warner(w) do
67
+ yield
68
+ end
84
69
 
85
- elsif first.is_a? Warning
86
- warning = first.class
87
- message = first.message
70
+ assert_equal(true, w.warned?(warning, message), "<#{args_inspect(args)}> has not been emitted.")
71
+ end
88
72
 
89
- elsif first.is_a? String
90
- warning = StandardWarning
91
- message = first
73
+ private
92
74
 
93
- else
94
- warning = Warning
95
- message = nil
96
- end
75
+ def parse_arguments(args)
76
+ args = args.clone
77
+ first = args.shift
78
+ if first.is_a? Class and first <= StructuredWarnings::Base
79
+ warning = first
80
+ message = args.shift
97
81
 
98
- unless args.empty?
99
- raise ArgumentError,
100
- "wrong number of arguments (#{args.size + 2} for 2)"
101
- end
82
+ elsif first.is_a? StructuredWarnings::Base
83
+ warning = first.class
84
+ message = first.message
102
85
 
103
- return warning, message
104
- end
86
+ elsif first.is_a? String
87
+ warning = StructuredWarnings::StandardWarning
88
+ message = first
105
89
 
106
- def args_inspect(args)
107
- args.map { |a| a.inspect }.join(", ")
108
- end
90
+ else
91
+ warning = StructuredWarnings::Base
92
+ message = nil
109
93
  end
94
+
95
+ unless args.empty?
96
+ raise ArgumentError,
97
+ "wrong number of arguments (#{args.size + 2} for 2)"
98
+ end
99
+
100
+ return warning, message
101
+ end
102
+
103
+ def args_inspect(args)
104
+ args.map { |a| a.inspect }.join(', ')
110
105
  end
111
106
  end
@@ -1,39 +1,36 @@
1
- module StructuredWarnings
2
- module Test
3
- # This warner is used in Assertions#assert_warn and
4
- # Assertions#assert_no_warn blocks. It captures all warnings in format and
5
- # provides access to them using the warned? method.
6
- class Warner < StructuredWarnings::Warner
7
- # Overrides the public interface of StructuredWarnings::Warner. This
8
- # method always returns nil to avoid warnings on stdout during assert_warn
9
- # and assert_no_warn blocks.
10
- def format(warning, message, call_stack)
11
- given_warnings << warning.new(message)
12
- nil
13
- end
14
-
15
- # Returns true if any warning or a subclass of warning was emitted.
16
- def warned?(warning, message = nil)
17
- case message
18
- when Regexp
19
- given_warnings.any? {|w| w.is_a?(warning) && w.message =~ message}
20
- when String
21
- given_warnings.any? {|w| w.is_a?(warning) && w.message == message}
22
- when nil
23
- given_warnings.any? {|w| w.is_a?(warning)}
24
- else
25
- raise ArgumentError, "Unkown argument for 'message'"
26
- end
27
- end
1
+ # This warner is used in Assertions#assert_warn and
2
+ # Assertions#assert_no_warn blocks. It captures all warnings in format and
3
+ # provides access to them using the warned? method.
4
+ class StructuredWarnings::Test::Warner < StructuredWarnings::Warner
5
+ # Overrides the public interface of StructuredWarnings::Warner. This
6
+ # method always returns nil to avoid warnings on stdout during assert_warn
7
+ # and assert_no_warn blocks.
8
+ def format(warning, message, call_stack)
9
+ given_warnings << warning.new(message)
10
+ nil
11
+ end
28
12
 
29
- # :stopdoc:
30
- protected
31
- # Returns an array of all warning classes, that were given to this
32
- # warner's format method, including duplications.
33
- def given_warnings
34
- @given_warnings ||= []
35
- end
36
- # :startdoc:
13
+ # Returns true if any warning or a subclass of warning was emitted.
14
+ def warned?(warning, message = nil)
15
+ case message
16
+ when Regexp
17
+ given_warnings.any? {|w| w.is_a?(warning) && w.message =~ message}
18
+ when String
19
+ given_warnings.any? {|w| w.is_a?(warning) && w.message == message}
20
+ when nil
21
+ given_warnings.any? {|w| w.is_a?(warning)}
22
+ else
23
+ raise ArgumentError, "Unkown argument type for 'message': #{message.class.inspect}"
37
24
  end
38
25
  end
26
+
27
+ # :stopdoc:
28
+ protected
29
+
30
+ # Returns an array of all warning classes, that were given to this
31
+ # warner's format method, including duplications.
32
+ def given_warnings
33
+ @given_warnings ||= []
34
+ end
35
+ # :startdoc:
39
36
  end
@@ -0,0 +1,5 @@
1
+ require 'structured_warnings/test'
2
+
3
+ Test::Unit::TestCase.class_eval do
4
+ include StructuredWarnings::Test::Assertions
5
+ end
@@ -0,0 +1,3 @@
1
+ module StructuredWarnings
2
+ VERSION = "0.3.0"
3
+ end
@@ -1,13 +1,15 @@
1
- module StructuredWarnings
2
- # The Warner class implements a very simple interface. It simply formats
3
- # a warning, so it is more than just the message itself. This default
4
- # warner uses a format comparable to warnings emitted by rb_warn including
5
- # the place where the "thing that caused the warning" resides.
6
- class Warner
7
- # Warner.new.format(DeprecationWarning, "more info..", caller)
8
- # # => "demo.rb:5 : more info.. (DeprecationWarning)"
9
- def format(warning, message, stack)
10
- "#{stack.shift} : #{message} (#{warning})"
11
- end
1
+ # The Warner class implements a very simple interface. It simply formats
2
+ # a warning, so it is more than just the message itself. This default
3
+ # warner uses a format comparable to warnings emitted by rb_warn including
4
+ # the place where the "thing that caused the warning" resides.
5
+ class StructuredWarnings::Warner
6
+ # Warner.new.format(StructuredWarning::DeprecationWarning, "more info..", caller)
7
+ # # => "demo.rb:5 : more info.. (StructuredWarning::DeprecationWarning)"
8
+ def format(warning, message, stack)
9
+ frame = stack.shift
10
+ # This file contains the backwards compatibility code for Ruby 2.3 and
11
+ # lower, let's skip it
12
+ frame = stack.shift if frame.include? 'lib/structured_warnings/kernel.rb'
13
+ "#{frame} : #{message} (#{warning})"
12
14
  end
13
15
  end
@@ -1,129 +1,71 @@
1
- # Descendents of class Warning are used to raise structured warnings. They
2
- # programmers to explicitly supress certain kinds of warnings and provide
3
- # additional information in contrast to the plain warn method. They
4
- # implement an Exception-like interface and carry information about the
5
- # warning -- its type (the warning's class name), an optional descriptive
6
- # string, and optional traceback information. Programs may subclass Warning
7
- # to add additional information.
8
- #
9
- # Large portions of this class's documentation are taken from the Exception
10
- # RDoc.
11
- class Warning
12
- # Construct a new Warning object, optionally passing in a message.
13
- def initialize(message = nil)
14
- @message = message
15
- @backtrace = caller(1)
16
- end
17
-
18
-
19
- # call-seq:
20
- # warning.backtrace => array
1
+ # This module encapsulates the extensions to Warning, that are introduced
2
+ # by this library.
3
+ module StructuredWarnings::Warning
4
+ # :call-seq:
5
+ # warn(message = nil)
6
+ # warn(warning_class, message)
7
+ # warn(warning_instance)
21
8
  #
22
- # Returns any backtrace associated with the warning. The backtrace
23
- # is an array of strings, each containing either ``filename:lineNo: in
24
- # `method''' or ``filename:lineNo.''
25
- def backtrace
26
- @backtrace
27
- end
28
-
29
- # Sets the backtrace information associated with warning. The argument must
30
- # be an array of String objects in the format described in
31
- # Exception#backtrace.
32
- def set_backtrace(backtrace)
33
- @backtrace = backtrace
34
- end
35
-
36
- # Returns warning's message (or the name of the warning if no message is set).
37
- def to_s
38
- @message || self.class.name
39
- end
40
- alias_method :to_str, :to_s
41
- alias_method :message, :to_s
42
-
43
- # Return this warning's class name and message
44
- def inspect
45
- "#<#{self.class}: #{self}>"
46
- end
9
+ # This method provides a +raise+-like interface. It extends the default
10
+ # warn in ::Warning to allow the use of structured warnings.
11
+ #
12
+ # Internally it uses the StructuredWarnings::warner to format a message
13
+ # based on the given warning class, the message and a stack frame.
14
+ # The return value is passed to super, which is likely the implementation
15
+ # in ::Warning. That way, it is less likely, that structured_warnings
16
+ # interferes with other extensions.
17
+ #
18
+ # If the warner returns nil or an empty string the underlying warn will not
19
+ # be called. That way, warnings may be transferred to other devices without
20
+ # the need to redefine ::Warning#warn.
21
+ #
22
+ # Just like the original version, this method does not take command line
23
+ # switches or verbosity levels into account. In order to deactivate all
24
+ # warnings use <code>StructuredWarnings::Base.disable</code>.
25
+ #
26
+ # warn "This is an old-style warning" # This will emit a StructuredWarnings::StandardWarning
27
+ #
28
+ # class Foo
29
+ # def bar
30
+ # warn StructuredWarnings::DeprecationWarning, "Never use bar again, use beer"
31
+ # end
32
+ # def beer
33
+ # "Ahhh"
34
+ # end
35
+ # end
36
+ #
37
+ # warn StructuredWarnings::Base.new("The least specific warning you can get")
38
+ #
39
+ def warn(*args)
40
+ first = args.shift
41
+ if first.is_a? Class and first <= StructuredWarnings::Base
42
+ warning = first
43
+ message = args.shift
47
44
 
48
- # This module extends Warning and each subclass. It may be used to activate
49
- # or deactivate a set of warnings.
50
- module ClassMethods
51
- # returns a Boolean, stating whether a warning of this type would be
52
- # emmitted or not.
53
- def active?
54
- StructuredWarnings::disabled_warnings.all? {|w| !(w >= self)}
55
- end
45
+ elsif first.is_a? StructuredWarnings::Base
46
+ warning = first.class
47
+ message = first.message
56
48
 
57
- # call-seq:
58
- # disable()
59
- # disable() {...}
60
- #
61
- # If called without a block, Warnings of this type will be disabled in the
62
- # current thread and all new child threads.
63
- #
64
- # warn("this will be printed") # creates a StandardWarning which is
65
- # # enabled by default
66
- #
67
- # Warning.disable
68
- #
69
- # warn("this will not be printed") # creates a StandardWarning which is
70
- # # currently disabled
71
- #
72
- # If called with a block, warnings of this type will be disabled in the
73
- # dynamic scope of the given block.
74
- #
75
- # Warning.disable do
76
- # warn("this will not be printed") # creates a StandardWarning which is
77
- # # currently disabled
78
- # end
79
- #
80
- # warn("this will be printed") # creates a StandardWarning which is
81
- # # currently enabled
82
- def disable
83
- if block_given?
84
- StructuredWarnings::with_disabled_warnings(
85
- StructuredWarnings.disabled_warnings | [self]) do
86
- yield
49
+ else
50
+ warning =
51
+ if caller.shift.include? 'lib/structured_warnings/kernel.rb'
52
+ StructuredWarnings::StandardWarning
53
+ else
54
+ StructuredWarnings::BuiltInWarning
87
55
  end
88
- else
89
- StructuredWarnings::disabled_warnings |= [self]
90
- end
56
+ message = first.to_s
91
57
  end
92
58
 
93
- # call-seq:
94
- # enable()
95
- # enable() {...}
96
- #
97
- # This method has the same semantics as disable, only with the opposite
98
- # outcome. In general the last assignment wins, so that disabled warnings
99
- # may be enabled again and so on.
100
- def enable
101
- if block_given?
102
- StructuredWarnings::with_disabled_warnings(
103
- StructuredWarnings.disabled_warnings - [self]) do
104
- yield
105
- end
106
- else
107
- StructuredWarnings::disabled_warnings -= [self]
108
- end
59
+ # If args is not empty, user passed an incompatible set of arguments.
60
+ # Maybe somebody else is overriding warn as well and knows, what to do.
61
+ # Better do nothing in this case. See #5
62
+ return super unless args.empty?
63
+
64
+ if warning.active?
65
+ output = StructuredWarnings.warner.format(warning, message, caller(1))
66
+ super(output) unless output.nil? or output.to_s.empty?
109
67
  end
110
68
  end
111
-
112
- extend ClassMethods
113
69
  end
114
70
 
115
- # This warning is used when calling #Kernel.warn without arguments.
116
- class StandardWarning < Warning; end
117
-
118
- # This is a general warning used to mark certain actions as deprecated. We
119
- # recommend to add a useful warning message, which alternative to use instead.
120
- class DeprecationWarning < Warning; end
121
-
122
- # This warning marks single methods as deprecated. We
123
- # recommend to add a useful warning message, which alternative to use instead.
124
- class DeprecatedMethodWarning < DeprecationWarning; end
125
-
126
- # This warning marks the given parameters for a certain methods as
127
- # deprecated. We recommend to add a useful warning message, which
128
- # alternative to use instead.
129
- class DeprecatedSignatureWarning < DeprecationWarning; end
71
+ Warning.extend StructuredWarnings::Warning