yard 0.8.1 → 0.8.2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of yard might be problematic. Click here for more details.

Files changed (65) hide show
  1. data/ChangeLog +192 -0
  2. data/README.md +9 -2
  3. data/Rakefile +8 -14
  4. data/lib/yard.rb +1 -1
  5. data/lib/yard/autoload.rb +3 -2
  6. data/lib/yard/cli/graph.rb +28 -10
  7. data/lib/yard/cli/yardoc.rb +4 -1
  8. data/lib/yard/code_objects/proxy.rb +22 -17
  9. data/lib/yard/docstring_parser.rb +7 -7
  10. data/lib/yard/globals.rb +2 -2
  11. data/lib/yard/handlers/base.rb +3 -2
  12. data/lib/yard/handlers/c/handler_methods.rb +1 -0
  13. data/lib/yard/handlers/c/init_handler.rb +7 -5
  14. data/lib/yard/handlers/c/override_comment_handler.rb +9 -1
  15. data/lib/yard/handlers/ruby/class_condition_handler.rb +4 -2
  16. data/lib/yard/handlers/ruby/legacy/class_condition_handler.rb +4 -2
  17. data/lib/yard/handlers/ruby/legacy/mixin_handler.rb +4 -6
  18. data/lib/yard/handlers/ruby/mixin_handler.rb +3 -3
  19. data/lib/yard/handlers/ruby/visibility_handler.rb +1 -1
  20. data/lib/yard/i18n/locale.rb +50 -0
  21. data/lib/yard/i18n/text.rb +110 -9
  22. data/lib/yard/logging.rb +99 -8
  23. data/lib/yard/parser/c/c_parser.rb +1 -1
  24. data/lib/yard/parser/source_parser.rb +5 -4
  25. data/lib/yard/registry.rb +20 -12
  26. data/lib/yard/registry_store.rb +6 -1
  27. data/lib/yard/rubygems/doc_manager.rb +9 -5
  28. data/lib/yard/serializers/yardoc_serializer.rb +1 -0
  29. data/lib/yard/server/commands/base.rb +3 -2
  30. data/lib/yard/server/doc_server_serializer.rb +2 -0
  31. data/lib/yard/server/templates/default/method_details/html/permalink.erb +4 -0
  32. data/lib/yard/server/templates/default/method_details/html/setup.rb +4 -0
  33. data/lib/yard/tags/default_factory.rb +12 -4
  34. data/lib/yard/tags/directives.rb +1 -0
  35. data/lib/yard/templates/engine.rb +13 -6
  36. data/lib/yard/templates/template_options.rb +8 -1
  37. data/spec/cli/graph_spec.rb +10 -0
  38. data/spec/cli/yri_spec.rb +12 -2
  39. data/spec/code_objects/proxy_spec.rb +19 -3
  40. data/spec/handlers/c/class_handler_spec.rb +1 -2
  41. data/spec/handlers/c/init_handler_spec.rb +11 -0
  42. data/spec/handlers/c/override_comment_handler_spec.rb +3 -0
  43. data/spec/handlers/class_condition_handler_spec.rb +5 -0
  44. data/spec/handlers/dsl_handler_spec.rb +1 -0
  45. data/spec/handlers/examples/class_condition_handler_001.rb.txt +8 -0
  46. data/spec/handlers/mixin_handler_spec.rb +2 -1
  47. data/spec/i18n/locale_spec.rb +62 -0
  48. data/spec/i18n/text_spec.rb +144 -35
  49. data/spec/logging_spec.rb +21 -0
  50. data/spec/parser/c_parser_spec.rb +36 -0
  51. data/spec/parser/source_parser_spec.rb +11 -8
  52. data/spec/registry_spec.rb +26 -0
  53. data/spec/rubygems/doc_manager_spec.rb +112 -0
  54. data/spec/tags/default_factory_spec.rb +8 -2
  55. data/spec/tags/directives_spec.rb +7 -0
  56. data/spec/templates/examples/module001.html +0 -4
  57. data/spec/templates/examples/module002.html +0 -1
  58. data/spec/templates/examples/module003.html +0 -1
  59. data/spec/templates/examples/module004.html +171 -172
  60. data/spec/templates/module_spec.rb +4 -0
  61. data/templates/default/fulldoc/html/js/app.js +24 -18
  62. data/templates/default/fulldoc/html/setup.rb +5 -1
  63. data/templates/default/module/html/attribute_details.erb +1 -2
  64. metadata +9 -4
  65. data/lib/yard/server/templates/default/fulldoc/html/js/live.js +0 -17
@@ -29,25 +29,25 @@ module YARD
29
29
  class DocstringParser
30
30
  # @return [String] the parsed text portion of the docstring,
31
31
  # with tags removed.
32
- attr_reader :text
32
+ attr_accessor :text
33
33
 
34
34
  # @return [String] the complete input string to the parser.
35
- attr_reader :raw_text
35
+ attr_accessor :raw_text
36
36
 
37
37
  # @return [Array<Tag>] the list of meta-data tags identified
38
38
  # by the parser
39
- attr_reader :tags
39
+ attr_accessor :tags
40
40
 
41
41
  # @return [Array<Directive>] a list of directives identified
42
42
  # by the parser. This list will not be passed on to the
43
43
  # Docstring object.
44
- attr_reader :directives
44
+ attr_accessor :directives
45
45
 
46
46
  # @return [OpenStruct] any arbitrary state to be passed between
47
47
  # tags during parsing. Mainly used by directives to coordinate
48
48
  # behaviour (so that directives can be aware of other directives
49
49
  # used in a docstring).
50
- attr_reader :state
50
+ attr_accessor :state
51
51
 
52
52
  # @return [CodeObjects::Base, nil] the object associated with
53
53
  # the docstring being parsed. May be nil if the docstring is
@@ -177,7 +177,7 @@ module YARD
177
177
 
178
178
  # Creates a tag from the {Tags::DefaultFactory tag factory}.
179
179
  #
180
- # To add an already created tag object, use {#add_tag}
180
+ # To add an already created tag object, append it to {#tags}.
181
181
  #
182
182
  # @param [String] tag_name the tag name
183
183
  # @param [String] tag_buf the text attached to the tag with newlines removed.
@@ -264,7 +264,7 @@ module YARD
264
264
  self.after_parse_callbacks << block
265
265
  end
266
266
 
267
- # @return [Array<Proc>] the {#after_parse} callback proc objects
267
+ # @return [Array<Proc>] the {after_parse} callback proc objects
268
268
  def self.after_parse_callbacks
269
269
  @after_parse_callbacks ||= []
270
270
  end
@@ -4,9 +4,9 @@
4
4
  #
5
5
  # @see YARD::CodeObjects::Proxy
6
6
  # @see YARD::Registry.resolve
7
- def P(namespace, name = nil)
7
+ def P(namespace, name = nil, type = nil)
8
8
  namespace, name = nil, namespace if name.nil?
9
- YARD::Registry.resolve(namespace, name, false, true)
9
+ YARD::Registry.resolve(namespace, name, false, true, type)
10
10
  end
11
11
 
12
12
  # The global {YARD::Logger} instance
@@ -356,12 +356,13 @@ module YARD
356
356
  opts = {
357
357
  :namespace => namespace,
358
358
  :scope => :instance,
359
- :owner => owner || namespace
359
+ :owner => owner || namespace,
360
+ :visibility => nil
360
361
  }.update(opts)
361
362
 
362
363
  ns, vis, sc, oo = namespace, visibility, scope, owner
363
364
  self.namespace = opts[:namespace]
364
- self.visibility = :public
365
+ self.visibility = opts[:visibility] || :public
365
366
  self.scope = opts[:scope]
366
367
  self.owner = opts[:owner]
367
368
 
@@ -61,6 +61,7 @@ module YARD
61
61
 
62
62
  def handle_alias(var_name, new_name, old_name)
63
63
  namespace = namespace_for_variable(var_name)
64
+ return if namespace.nil?
64
65
  new_meth, old_meth = new_name.to_sym, old_name.to_sym
65
66
  old_obj = namespace.child(:name => old_meth, :scope => :instance)
66
67
  new_obj = register MethodObject.new(namespace, new_meth, :instance) do |o|
@@ -1,15 +1,17 @@
1
1
  # Handles the Init_Libname() method
2
2
  class YARD::Handlers::C::InitHandler < YARD::Handlers::C::Base
3
- MATCH = %r{\A\s*(?:static\s+)?void\s+[Ii]nit_(\w+)\s*}
3
+ MATCH = %r{\A\s*(?:static\s+)?void\s+(?:[Ii]nit_)?(\w+)\s*}
4
4
  handles MATCH
5
5
  statement_class ToplevelStatement
6
6
 
7
7
  process do
8
8
  parse_block
9
- ns = namespace_for_variable(statement.declaration[MATCH, 1])
10
- if ns.is_a?(YARD::CodeObjects::NamespaceObject) && ns.docstring.blank?
11
- if statement.comments
12
- register_docstring(ns, statement.comments.source, statement)
9
+ if decl = statement.declaration[MATCH, 1]
10
+ ns = namespace_for_variable(decl)
11
+ if ns.is_a?(YARD::CodeObjects::NamespaceObject) && ns.docstring.blank?
12
+ if statement.comments
13
+ register_docstring(ns, statement.comments.source, statement)
14
+ end
13
15
  end
14
16
  end
15
17
  end
@@ -16,7 +16,15 @@ class YARD::Handlers::C::OverrideCommentHandler < YARD::Handlers::C::Base
16
16
  when :module
17
17
  obj = YARD::CodeObjects::ModuleObject.new(:root, name)
18
18
  end
19
- register_docstring(obj, statement.source, statement) if obj
19
+ register(obj)
20
20
  end
21
21
  end
22
+
23
+ def register_docstring(object, docstring = statement.source, stmt = statement)
24
+ super
25
+ end
26
+
27
+ def register_file_info(object, file = parser.file, line = statement.line, comments = statement.comments)
28
+ super
29
+ end
22
30
  end
@@ -75,10 +75,12 @@ class YARD::Handlers::Ruby::ClassConditionHandler < YARD::Handlers::Ruby::Base
75
75
  end
76
76
 
77
77
  def parse_then_block
78
- parse_block(statement.then_block)
78
+ parse_block(statement.then_block, :visibility => visibility)
79
79
  end
80
80
 
81
81
  def parse_else_block
82
- parse_block(statement.else_block) if statement.else_block
82
+ if statement.else_block
83
+ parse_block(statement.else_block, :visibility => visibility)
84
+ end
83
85
  end
84
86
  end
@@ -65,7 +65,7 @@ class YARD::Handlers::Ruby::Legacy::ClassConditionHandler < YARD::Handlers::Ruby
65
65
 
66
66
  # @since 0.5.5
67
67
  def parse_then_block
68
- parse_block
68
+ parse_block(:visibility => visibility)
69
69
  end
70
70
 
71
71
  # @since 0.5.5
@@ -74,7 +74,9 @@ class YARD::Handlers::Ruby::Legacy::ClassConditionHandler < YARD::Handlers::Ruby
74
74
  stmtlist = YARD::Parser::Ruby::Legacy::StatementList
75
75
  stmtlist.new(statement.block).each do |stmt|
76
76
  if TkELSE === stmt.tokens.first
77
- parser.process(stmtlist.new(stmt.block))
77
+ push_state(:visibility => visibility) do
78
+ parser.process(stmtlist.new(stmt.block))
79
+ end
78
80
  end
79
81
  end
80
82
  end
@@ -27,13 +27,11 @@ class YARD::Handlers::Ruby::Legacy::MixinHandler < YARD::Handlers::Ruby::Legacy:
27
27
  raise YARD::Parser::UndocumentableError
28
28
  end
29
29
 
30
- obj = Proxy.new(namespace, mixmatch)
31
-
32
- case obj
33
- when Proxy
34
- obj.type = :module
30
+ case obj = Proxy.new(namespace, mixmatch)
35
31
  when ConstantObject # If a constant is included, use its value as the real object
36
- obj = Proxy.new(namespace, obj.value)
32
+ obj = Proxy.new(namespace, obj.value, :module)
33
+ else
34
+ obj = Proxy.new(namespace, mixmatch, :module)
37
35
  end
38
36
 
39
37
  namespace.mixins(scope).unshift(obj) unless namespace.mixins(scope).include?(obj)
@@ -25,10 +25,10 @@ class YARD::Handlers::Ruby::MixinHandler < YARD::Handlers::Ruby::Base
25
25
  raise YARD::Parser::UndocumentableError if mixin.first.type == :ident
26
26
 
27
27
  case obj = Proxy.new(namespace, mixin.source)
28
- when Proxy
29
- obj.type = :module
30
28
  when ConstantObject # If a constant is included, use its value as the real object
31
- obj = Proxy.new(namespace, obj.value)
29
+ obj = Proxy.new(namespace, obj.value, :module)
30
+ else
31
+ obj = Proxy.new(namespace, mixin.source, :module)
32
32
  end
33
33
 
34
34
  namespace.mixins(scope).unshift(obj) unless namespace.mixins(scope).include?(obj)
@@ -9,7 +9,7 @@ class YARD::Handlers::Ruby::VisibilityHandler < YARD::Handlers::Ruby::Base
9
9
  return if (ident = statement.jump(:ident)) == statement
10
10
  case statement.type
11
11
  when :var_ref, :vcall
12
- self.visibility = ident.first
12
+ self.visibility = ident.first.to_sym
13
13
  when :fcall, :command
14
14
  statement[1].traverse do |node|
15
15
  case node.type
@@ -0,0 +1,50 @@
1
+ require "gettext/tools/poparser"
2
+ require "gettext/runtime/mofile"
3
+
4
+ module YARD
5
+ module I18n
6
+ # +Locale+ is a unit of translation. It has {#name} and a set of
7
+ # messages.
8
+ #
9
+ # @since 0.8.2
10
+ class Locale
11
+ # @return [String] the name of the locale. It used IETF language
12
+ # tag format +[language[_territory][.codeset][@modifier]]+.
13
+ # @see http://tools.ietf.org/rfc/bcp/bcp47.txt
14
+ # BCP 47 - Tags for Identifying Languages
15
+ attr_reader :name
16
+
17
+ # Creates a locale for +name+ locale.
18
+ #
19
+ # @param [String] name the locale name.
20
+ def initialize(name)
21
+ @name = name
22
+ @messages = {}
23
+ end
24
+
25
+ # Loads translation messages from +locale_directory+/{#name}.po.
26
+ #
27
+ # @param [String] locale_directory the directory path that has
28
+ # {#name}.po.
29
+ # @return [Boolean] +true+ if PO file exists, +false+ otherwise.
30
+ def load(locale_directory)
31
+ po_file = File.join(locale_directory, "#{@name}.po")
32
+ return false unless File.exist?(po_file)
33
+
34
+ parser = GetText::PoParser.new
35
+ parser.report_warning = false
36
+ data = GetText::MoFile.new
37
+ parser.parse_file(po_file, data)
38
+ @messages.merge!(data)
39
+ true
40
+ end
41
+
42
+ # @param [String] message the translation target message.
43
+ # @return [String] translated message. If tarnslation isn't
44
+ # registered, the +message+ is returned.
45
+ def translate(message)
46
+ @messages[message] || message
47
+ end
48
+ end
49
+ end
50
+ end
@@ -32,6 +32,48 @@ module YARD
32
32
  # extracted paragraph.
33
33
  # @return [void]
34
34
  def extract_messages
35
+ parse do |part|
36
+ line_no = part[:line_no]
37
+ case part[:type]
38
+ when :markup, :empty_line
39
+ # ignore
40
+ when :attribute
41
+ yield(:attribute, part[:name], part[:value], part[:line_no])
42
+ when :paragraph
43
+ yield(:paragraph, part[:paragraph], part[:line_no])
44
+ end
45
+ end
46
+ end
47
+
48
+ # Translates into +locale+.
49
+ #
50
+ # @param [Locale] locale the translation target locale.
51
+ # @return [String] translated text.
52
+ def translate(locale)
53
+ translated_text = ""
54
+ parse do |part|
55
+ case part[:type]
56
+ when :markup
57
+ translated_text << part[:line]
58
+ when :attribute
59
+ prefix = "#{part[:prefix]}#{part[:name]}#{part[:infix]}"
60
+ value = locale.translate(part[:value])
61
+ suffix = part[:suffix]
62
+ translated_text << "#{prefix}#{value}#{suffix}"
63
+ when :paragraph
64
+ translated_text << locale.translate(part[:paragraph])
65
+ when :empty_line
66
+ translated_text << part[:line]
67
+ else
68
+ raise "should not reach here: unexpected type: #{type}"
69
+ end
70
+ end
71
+ translated_text
72
+ end
73
+
74
+ private
75
+
76
+ def parse(&block)
35
77
  paragraph = ""
36
78
  paragraph_start_line = 0
37
79
  line_no = 0
@@ -42,29 +84,88 @@ module YARD
42
84
  if in_header
43
85
  case line
44
86
  when /^#!\S+\s*$/
45
- in_header = false unless line_no == 1
46
- when /^\s*#\s*@(\S+)\s*(.+?)\s*$/
47
- name, value = $1, $2
48
- yield(:attribute, name, value, line_no)
87
+ if line_no == 1
88
+ emit_markup_event(line, line_no, &block)
89
+ else
90
+ in_header = false
91
+ end
92
+ when /^(\s*#\s*@)(\S+)(\s*)(.+?)(\s*)$/
93
+ emit_attribute_event(Regexp.last_match, line_no, &block)
49
94
  else
50
95
  in_header = false
51
- next if line.chomp.empty?
96
+ if line.strip.empty?
97
+ emit_empty_line_event(line, line_no, &block)
98
+ next
99
+ end
52
100
  end
53
101
  next if in_header
54
102
  end
55
103
 
56
104
  case line
57
105
  when /^\s*$/
58
- next if paragraph.empty?
59
- yield(:paragraph, paragraph.rstrip, paragraph_start_line)
60
- paragraph = ""
106
+ if paragraph.empty?
107
+ emit_empty_line_event(line, line_no, &block)
108
+ else
109
+ paragraph << line
110
+ emit_paragraph_event(paragraph, paragraph_start_line, line_no,
111
+ &block)
112
+ paragraph = ""
113
+ end
61
114
  else
62
115
  paragraph_start_line = line_no if paragraph.empty?
63
116
  paragraph << line
64
117
  end
65
118
  end
119
+
66
120
  unless paragraph.empty?
67
- yield(:paragraph, paragraph.rstrip, paragraph_start_line)
121
+ emit_paragraph_event(paragraph, paragraph_start_line, line_no, &block)
122
+ end
123
+ end
124
+
125
+ def emit_markup_event(line, line_no)
126
+ part = {
127
+ :type => :markup,
128
+ :line => line,
129
+ :line_no => line_no,
130
+ }
131
+ yield(part)
132
+ end
133
+
134
+ def emit_attribute_event(match_data, line_no)
135
+ part = {
136
+ :type => :attribute,
137
+ :prefix => match_data[1],
138
+ :name => match_data[2],
139
+ :infix => match_data[3],
140
+ :value => match_data[4],
141
+ :suffix => match_data[5],
142
+ :line_no => line_no,
143
+ }
144
+ yield(part)
145
+ end
146
+
147
+ def emit_empty_line_event(line, line_no)
148
+ part = {
149
+ :type => :empty_line,
150
+ :line => line,
151
+ :line_no => line_no,
152
+ }
153
+ yield(part)
154
+ end
155
+
156
+ def emit_paragraph_event(paragraph, paragraph_start_line, line_no, &block)
157
+ paragraph_part = {
158
+ :type => :paragraph,
159
+ :line_no => paragraph_start_line,
160
+ }
161
+ match_data = /(\s*)\z/.match(paragraph)
162
+ if match_data
163
+ paragraph_part[:paragraph] = match_data.pre_match
164
+ yield(paragraph_part)
165
+ emit_empty_line_event(match_data[1], line_no, &block)
166
+ else
167
+ paragraph_part[:paragraph] = paragraph
168
+ yield(paragraph_part)
68
169
  end
69
170
  end
70
171
  end
@@ -1,24 +1,50 @@
1
1
  require 'logger'
2
+ require 'thread'
2
3
 
3
4
  module YARD
4
5
  # Handles console logging for info, warnings and errors.
5
6
  # Uses the stdlib Logger class in Ruby for all the backend logic.
6
7
  class Logger < ::Logger
7
- attr_writer :show_backtraces
8
+ # The list of characters displayed beside the progress bar to indicate
9
+ # "movement".
10
+ # @since 0.8.2
11
+ PROGRESS_INDICATORS = ["\u230C", "\u230D", "\u230E", "\u230F"]
12
+
13
+ # @return [IO] the IO object being logged to
14
+ # @since 0.8.2
15
+ attr_accessor :io
16
+
17
+ # @return [Boolean] whether backtraces should be shown (by default
18
+ # this is on).
8
19
  def show_backtraces; @show_backtraces || level == DEBUG end
20
+ attr_writer :show_backtraces
21
+
22
+ # @return [Boolean] whether progress indicators should be shown when
23
+ # logging CLIs (by default this is off).
24
+ def show_progress
25
+ return false if RUBY18 # threading is too ineffective for progress support
26
+ return false unless io.tty? # no TTY support on IO
27
+ return false if level > WARN # no progress in verbose/debug modes
28
+ @show_progress
29
+ end
30
+ attr_writer :show_progress
9
31
 
10
32
  # The logger instance
11
33
  # @return [Logger] the logger instance
12
- def self.instance(pipe = STDERR)
34
+ def self.instance(pipe = STDOUT)
13
35
  @logger ||= new(pipe)
14
36
  end
15
37
 
16
38
  # Creates a new logger
17
- def initialize(*args)
18
- super
39
+ def initialize(pipe, *args)
40
+ super(pipe, *args)
41
+ self.io = pipe
19
42
  self.show_backtraces = true
43
+ self.show_progress = false
20
44
  self.level = WARN
21
45
  self.formatter = method(:format_log)
46
+ @progress_indicator = 0
47
+ @mutex = Mutex.new
22
48
  end
23
49
 
24
50
  # Changes the debug level to DEBUG if $DEBUG is set
@@ -28,15 +54,80 @@ module YARD
28
54
  super
29
55
  end
30
56
 
57
+ # Captures the duration of a block of code for benchmark analysis. Also
58
+ # calls {#progress} on the message to display it to the user.
59
+ #
60
+ # @todo Implement capture storage for reporting of benchmarks
61
+ # @param [String] msg the message to display
62
+ # @param [Symbol, nil] nontty_log the level to log as if the output
63
+ # stream is not a TTY. Use +nil+ for no alternate logging.
64
+ # @yield a block of arbitrary code to benchmark
65
+ # @return [void]
66
+ def capture(msg, nontty_log = :debug, &block)
67
+ progress(msg, nontty_log)
68
+ yield
69
+ ensure
70
+ clear_progress
71
+ end
72
+
73
+ # Displays a progress indicator for a given message. This progress report
74
+ # is only displayed on TTY displays, otherwise the message is passed to
75
+ # the +nontty_log+ level.
76
+ #
77
+ # @param [String] msg the message to log
78
+ # @param [Symbol, nil] nontty_log the level to log as if the output
79
+ # stream is not a TTY. Use +nil+ for no alternate logging.
80
+ # @return [void]
81
+ # @since 0.8.2
82
+ def progress(msg, nontty_log = :debug)
83
+ send(nontty_log, msg) if nontty_log
84
+ return unless show_progress
85
+ icon = ""
86
+ if defined?(::Encoding)
87
+ icon = PROGRESS_INDICATORS[@progress_indicator] + " "
88
+ end
89
+ self << "\e[2K\e[?25l\e[1m#{icon}#{msg}\e[0m\r"
90
+ @mutex.synchronize do
91
+ @progress_msg = msg
92
+ @progress_indicator += 1
93
+ @progress_indicator %= PROGRESS_INDICATORS.size
94
+ end
95
+ Thread.new do
96
+ sleep(0.05)
97
+ @mutex.synchronize do
98
+ progress(msg + ".", nil) if @progress_msg == msg
99
+ end
100
+ end
101
+ end
102
+
103
+ # Clears the progress indicator in the TTY display.
104
+ # @return [void]
105
+ # @since 0.8.2
106
+ def clear_progress
107
+ return unless show_progress
108
+ self << "\e[?25h\e[2K"
109
+ @progress_msg = nil
110
+ end
111
+
112
+ # Displays an unformatted line to the logger output stream. Similar to
113
+ # the +#<<+ method, but adds a newline.
114
+ # @param [String] msg the message to display
115
+ # @return [void]
116
+ # @since 0.8.2
117
+ def puts(msg)
118
+ self << "#{msg}\n"
119
+ end
120
+
31
121
  # Prints the backtrace +exc+ to the logger as error data.
32
122
  #
33
123
  # @param [Array<String>] exc the backtrace list
124
+ # @param [Symbol] level_meth the level to log backtrace at
34
125
  # @return [void]
35
- def backtrace(exc)
126
+ def backtrace(exc, level_meth = :error)
36
127
  return unless show_backtraces
37
- error "#{exc.class.class_name}: #{exc.message}"
38
- error "Stack trace:" +
39
- exc.backtrace[0..5].map {|x| "\n\t#{x}" }.join + "\n"
128
+ send(level_meth, "#{exc.class.class_name}: #{exc.message}")
129
+ send(level_meth, "Stack trace:" +
130
+ exc.backtrace[0..5].map {|x| "\n\t#{x}" }.join + "\n")
40
131
  end
41
132
 
42
133
  # Warns that the Ruby environment does not support continuations. Applies