class Chef::Formatters::ErrorInspectors::CompileErrorInspector

CompileErrorInspector

Wraps exceptions that occur during the compile phase of a Chef run and tries to find the code responsible for the error.

Attributes

exception[R]
path[R]

Public Class Methods

new(path, exception) click to toggle source
# File lib/chef/formatters/error_inspectors/compile_error_inspector.rb, line 31
def initialize(path, exception)
  @path, @exception = path, exception
  @backtrace_lines_in_cookbooks = nil
  @file_lines = nil
  @culprit_backtrace_entry = nil
  @culprit_line = nil
end

Public Instance Methods

add_explanation(error_description) click to toggle source
# File lib/chef/formatters/error_inspectors/compile_error_inspector.rb, line 39
        def add_explanation(error_description)
          error_description.section(exception.class.name, exception.message)

          if found_error_in_cookbooks?
            traceback = filtered_bt.map { |line| "  #{line}" }.join("\n")
            error_description.section("Cookbook Trace: (most recent call first)", traceback)
            error_description.section("Relevant File Content:", context)
          end

          if exception_message_modifying_frozen?
            msg = <<-MESSAGE
            Ruby objects are often frozen to prevent further modifications
            when they would negatively impact the process (e.g. values inside
            Ruby's ENV class) or to prevent polluting other objects when default
            values are passed by reference to many instances of an object (e.g.
            the empty Array as a Chef resource default, passed by reference
            to every instance of the resource).

            Chef uses Object#freeze to ensure the default values of properties
            inside Chef resources are not modified, so that when a new instance
            of a Chef resource is created, and Object#dup copies values by
            reference, the new resource is not receiving a default value that
            has been by a previous instance of that resource.

            Instead of modifying an object that contains a default value for all
            instances of a Chef resource, create a new object and assign it to
            the resource's parameter, e.g.:

            fruit_basket = resource(:fruit_basket, 'default')

            # BAD: modifies 'contents' object for all new fruit_basket instances
            fruit_basket.contents << 'apple'

            # GOOD: allocates new array only owned by this fruit_basket instance
            fruit_basket.contents %w(apple)

            MESSAGE

            error_description.section("Additional information:", msg.gsub(/^ {6}/, ""))
          end
        end
backtrace_lines_in_cookbooks() click to toggle source
# File lib/chef/formatters/error_inspectors/compile_error_inspector.rb, line 136
def backtrace_lines_in_cookbooks
  @backtrace_lines_in_cookbooks ||=
    begin
      filters = Array(Chef::Config.cookbook_path).map { |p| /^#{Regexp.escape(p)}/i }
      r = exception.backtrace.select { |line| filters.any? { |filter| line =~ filter } }
      Chef::Log.trace("Filtered backtrace of compile error: #{r.join(",")}")
      r
    end
end
context() click to toggle source
# File lib/chef/formatters/error_inspectors/compile_error_inspector.rb, line 81
def context
  context_lines = []
  context_lines << "#{culprit_file}:\n\n"
  Range.new(display_lower_bound, display_upper_bound).each do |i|
    line_nr = (i + 1).to_s.rjust(3)
    indicator = (i + 1) == culprit_line ? ">> " : ":  "
    context_lines << "#{line_nr}#{indicator}#{file_lines[i]}"
  end
  context_lines.join("")
end
culprit_backtrace_entry() click to toggle source
# File lib/chef/formatters/error_inspectors/compile_error_inspector.rb, line 108
def culprit_backtrace_entry
  @culprit_backtrace_entry ||= begin
    bt_entry = filtered_bt.first
    Chef::Log.trace("Backtrace entry for compile error: '#{bt_entry}'")
    bt_entry
  end
end
culprit_file() click to toggle source
# File lib/chef/formatters/error_inspectors/compile_error_inspector.rb, line 124
def culprit_file
  @culprit_file ||= culprit_backtrace_entry[/^((?:.\:)?[^:]+):(\d+)/, 1]
end
culprit_line() click to toggle source
# File lib/chef/formatters/error_inspectors/compile_error_inspector.rb, line 116
def culprit_line
  @culprit_line ||= begin
    line_number = culprit_backtrace_entry[/^(?:.\:)?[^:]+:(\d+)/, 1].to_i
    Chef::Log.trace("Line number of compile error: '#{line_number}'")
    line_number
  end
end
display_lower_bound() click to toggle source
# File lib/chef/formatters/error_inspectors/compile_error_inspector.rb, line 92
def display_lower_bound
  lower = (culprit_line - 8)
  lower = 0 if lower < 0
  lower
end
display_upper_bound() click to toggle source
# File lib/chef/formatters/error_inspectors/compile_error_inspector.rb, line 98
def display_upper_bound
  upper = (culprit_line + 8)
  upper = file_lines.size if upper > file_lines.size
  upper
end
exception_message_modifying_frozen?() click to toggle source
# File lib/chef/formatters/error_inspectors/compile_error_inspector.rb, line 146
def exception_message_modifying_frozen?
  exception.message.include?("can't modify frozen")
end
file_lines() click to toggle source
# File lib/chef/formatters/error_inspectors/compile_error_inspector.rb, line 104
def file_lines
  @file_lines ||= IO.readlines(culprit_file)
end
filtered_bt() click to toggle source
# File lib/chef/formatters/error_inspectors/compile_error_inspector.rb, line 128
def filtered_bt
  backtrace_lines_in_cookbooks.count > 0 ? backtrace_lines_in_cookbooks : exception.backtrace
end
found_error_in_cookbooks?() click to toggle source
# File lib/chef/formatters/error_inspectors/compile_error_inspector.rb, line 132
def found_error_in_cookbooks?
  !backtrace_lines_in_cookbooks.empty?
end