@@ -99,24 +99,31 @@ class Exception
9999 # rdoc-file=error.c
100100 # - backtrace -> array or nil
101101 # -->
102- # Returns a backtrace value for `self`; the returned value depends on the form
103- # of the stored backtrace value:
102+ # Returns the backtrace (the list of code locations that led to the exception),
103+ # as an array of strings.
104104 #
105- # * Array of Thread::Backtrace::Location objects: returns the array of strings
106- # given by `Exception#backtrace_locations.map {|loc| loc.to_s }`. This is
107- # the normal case, where the backtrace value was stored by Kernel#raise.
108- # * Array of strings: returns that array. This is the unusual case, where the
109- # backtrace value was explicitly stored as an array of strings.
110- # * `nil`: returns `nil`.
105+ # Example (assuming the code is stored in the file named `t.rb`):
111106 #
112- # Example:
107+ # def division(numerator, denominator)
108+ # numerator / denominator
109+ # end
113110 #
114111 # begin
115- # 1 / 0
116- # rescue => x
117- # x.backtrace.take(2)
112+ # division(1, 0)
113+ # rescue => ex
114+ # p ex.backtrace
115+ # # ["t.rb:2:in 'Integer#/'", "t.rb:2:in 'Object#division'", "t.rb:6:in '<main>'"]
116+ # loc = ex.backtrace.first
117+ # p loc.class
118+ # # String
118119 # end
119- # # => ["(irb):132:in `/'", "(irb):132:in `<top (required)>'"]
120+ #
121+ # The value returned by this method migth be adjusted when raising (see
122+ # Kernel#raise), or during intermediate handling by #set_backtrace.
123+ #
124+ # See also #backtrace_locations that provide the same value, as structured
125+ # objects. (Note though that two values might not be consistent with each other
126+ # when backtraces are manually adjusted.)
120127 #
121128 # see [Backtraces](rdoc-ref:exceptions.md@Backtraces).
122129 #
@@ -126,20 +133,37 @@ class Exception
126133 # rdoc-file=error.c
127134 # - backtrace_locations -> array or nil
128135 # -->
129- # Returns a backtrace value for `self`; the returned value depends on the form
130- # of the stored backtrace value:
136+ # Returns the backtrace (the list of code locations that led to the exception),
137+ # as an array of Thread::Backtrace::Location instances.
131138 #
132- # * Array of Thread::Backtrace::Location objects: returns that array.
133- # * Array of strings or `nil`: returns `nil`.
139+ # Example (assuming the code is stored in the file named `t.rb`):
134140 #
135- # Example:
141+ # def division(numerator, denominator)
142+ # numerator / denominator
143+ # end
136144 #
137145 # begin
138- # 1 / 0
139- # rescue => x
140- # x.backtrace_locations.take(2)
146+ # division(1, 0)
147+ # rescue => ex
148+ # p ex.backtrace_locations
149+ # # ["t.rb:2:in 'Integer#/'", "t.rb:2:in 'Object#division'", "t.rb:6:in '<main>'"]
150+ # loc = ex.backtrace_locations.first
151+ # p loc.class
152+ # # Thread::Backtrace::Location
153+ # p loc.path
154+ # # "t.rb"
155+ # p loc.lineno
156+ # # 2
157+ # p loc.label
158+ # # "Integer#/"
141159 # end
142- # # => ["(irb):150:in `/'", "(irb):150:in `<top (required)>'"]
160+ #
161+ # The value returned by this method might be adjusted when raising (see
162+ # Kernel#raise), or during intermediate handling by #set_backtrace.
163+ #
164+ # See also #backtrace that provide the same value as an array of strings. (Note
165+ # though that two values might not be consistent with each other when backtraces
166+ # are manually adjusted.)
143167 #
144168 # See [Backtraces](rdoc-ref:exceptions.md@Backtraces).
145169 #
@@ -294,15 +318,100 @@ class Exception
294318 # rdoc-file=error.c
295319 # - set_backtrace(value) -> value
296320 # -->
297- # Sets the backtrace value for `self`; returns the given + value:
321+ # Sets the backtrace value for `self`; returns the given ` value`.
298322 #
299- # x = RuntimeError.new('Boom')
300- # x.set_backtrace(%w[foo bar baz]) # => ["foo", "bar", "baz"]
301- # x.backtrace # => ["foo", "bar", "baz"]
323+ # The `value` might be:
302324 #
303- # The given `value` must be an array of strings, a single string, or `nil`.
325+ # * an array of Thread::Backtrace::Location;
326+ # * an array of String instances;
327+ # * a single String instance; or
328+ # * `nil`.
329+ #
330+ # Using array of Thread::Backtrace::Location is the most consistent option: it
331+ # sets both #backtrace and #backtrace_locations. It should be preferred when
332+ # possible. The suitable array of locations can be obtained from
333+ # Kernel#caller_locations, copied from another error, or just set to the
334+ # adjusted result of the current error's #backtrace_locations:
335+ #
336+ # require 'json'
337+ #
338+ # def parse_payload(text)
339+ # JSON.parse(text) # test.rb, line 4
340+ # rescue JSON::ParserError => ex
341+ # ex.set_backtrace(ex.backtrace_locations[2...])
342+ # raise
343+ # end
304344 #
305- # Does not affect the value returned by #backtrace_locations.
345+ # parse_payload('{"wrong: "json"')
346+ # # test.rb:4:in 'Object#parse_payload': unexpected token at '{"wrong: "json"' (JSON::ParserError)
347+ # #
348+ # # An error points to the body of parse_payload method,
349+ # # hiding the parts of the backtrace related to the internals
350+ # # of the "json" library
351+ #
352+ # # The error has both #backtace and #backtrace_locations set
353+ # # consistently:
354+ # begin
355+ # parse_payload('{"wrong: "json"')
356+ # rescue => ex
357+ # p ex.backtrace
358+ # # ["test.rb:4:in 'Object#parse_payload'", "test.rb:20:in '<main>'"]
359+ # p ex.backtrace_locations
360+ # # ["test.rb:4:in 'Object#parse_payload'", "test.rb:20:in '<main>'"]
361+ # end
362+ #
363+ # When the desired stack of locations is not available and should be constructed
364+ # from scratch, an array of strings or a singular string can be used. In this
365+ # case, only #backtrace is affected:
366+ #
367+ # def parse_payload(text)
368+ # JSON.parse(text)
369+ # rescue JSON::ParserError => ex
370+ # ex.set_backtrace(["dsl.rb:34", "framework.rb:1"])
371+ # # The error have the new value in #backtrace:
372+ # p ex.backtrace
373+ # # ["dsl.rb:34", "framework.rb:1"]
374+ #
375+ # # but the original one in #backtrace_locations
376+ # p ex.backtrace_locations
377+ # # [".../json/common.rb:221:in 'JSON::Ext::Parser.parse'", ...]
378+ # end
379+ #
380+ # parse_payload('{"wrong: "json"')
381+ #
382+ # Calling #set_backtrace with `nil` clears up #backtrace but doesn't affect
383+ # #backtrace_locations:
384+ #
385+ # def parse_payload(text)
386+ # JSON.parse(text)
387+ # rescue JSON::ParserError => ex
388+ # ex.set_backtrace(nil)
389+ # p ex.backtrace
390+ # # nil
391+ # p ex.backtrace_locations
392+ # # [".../json/common.rb:221:in 'JSON::Ext::Parser.parse'", ...]
393+ # end
394+ #
395+ # parse_payload('{"wrong: "json"')
396+ #
397+ # On reraising of such an exception, both #backtrace and #backtrace_locations is
398+ # set to the place of reraising:
399+ #
400+ # def parse_payload(text)
401+ # JSON.parse(text)
402+ # rescue JSON::ParserError => ex
403+ # ex.set_backtrace(nil)
404+ # raise # test.rb, line 7
405+ # end
406+ #
407+ # begin
408+ # parse_payload('{"wrong: "json"')
409+ # rescue => ex
410+ # p ex.backtrace
411+ # # ["test.rb:7:in 'Object#parse_payload'", "test.rb:11:in '<main>'"]
412+ # p ex.backtrace_locations
413+ # # ["test.rb:7:in 'Object#parse_payload'", "test.rb:11:in '<main>'"]
414+ # end
306415 #
307416 # See [Backtraces](rdoc-ref:exceptions.md@Backtraces).
308417 #
@@ -358,16 +467,16 @@ class Exception
358467 # Output:
359468 #
360469 # "divided by 0"
361- # ["t.rb:3:in ` /': divided by 0 (ZeroDivisionError)",
362- # "\tfrom t.rb:3:in ` baz'",
363- # "\tfrom t.rb:10:in ` bar'",
364- # "\tfrom t.rb:11:in ` foo'",
365- # "\tfrom t.rb:12:in ` <main>'"]
366- # ["t.rb:3:in ` /': \e[1mdivided by 0 (\e[1;4mZeroDivisionError\e[m\e[1m)\e[m",
367- # "\tfrom t.rb:3:in ` baz'",
368- # "\tfrom t.rb:10:in ` bar'",
369- # "\tfrom t.rb:11:in ` foo'",
370- # "\tfrom t.rb:12:in ` <main>'"]
470+ # ["t.rb:3:in 'Integer# /': divided by 0 (ZeroDivisionError)",
471+ # "\tfrom t.rb:3:in 'Object# baz'",
472+ # "\tfrom t.rb:10:in 'Object# bar'",
473+ # "\tfrom t.rb:11:in 'Object# foo'",
474+ # "\tfrom t.rb:12:in ' <main>'"]
475+ # ["t.rb:3:in 'Integer# /': \e[1mdivided by 0 (\e[1;4mZeroDivisionError\e[m\e[1m)\e[m",
476+ # "\tfrom t.rb:3:in 'Object# baz'",
477+ # "\tfrom t.rb:10:in 'Object# bar'",
478+ # "\tfrom t.rb:11:in 'Object# foo'",
479+ # "\tfrom t.rb:12:in ' <main>'"]
371480 #
372481 # An overriding method should be careful with ANSI code enhancements; see
373482 # [Messages](rdoc-ref:exceptions.md@Messages).
0 commit comments