Class: Bijou::ErrorFormatter

Inherits:
Object
  • Object
show all
Defined in:
lib/bijou/errorformatter.rb

Overview

The error formatter is used at runtime to locate the origin of errors with respect to the source Bijou file. During processing, the text in a Bijou file is turned into Ruby code and cached as a separate file. It is the class contained in this file that is the runtime representation of a Bijou component. When a runtime error occurs, the exception refers to a location in the cached file. The ErrorFormatter class is used to find the location in the original Bijou file by referring to #line comments embedded in the cached file and back tracking from there.

Direct Known Subclasses

ErrorFormatterHTML, ErrorFormatterText

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeErrorFormatter

Returns a new instance of ErrorFormatter.



24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/bijou/errorformatter.rb', line 24

def initialize
  @title = ''
  @error = nil
  @errors = []
  @warnings = []
  @stack = []
  @adjacent_lines = 0

  # Context information
  @filename = ''
  @context_lines = []
  @error_line = 0
  @error_column = 0
  @context = nil
end

Instance Attribute Details

#errorObject

Returns the value of attribute error.



22
23
24
# File 'lib/bijou/errorformatter.rb', line 22

def error
  @error
end

#errorsObject

Returns the value of attribute errors.



22
23
24
# File 'lib/bijou/errorformatter.rb', line 22

def errors
  @errors
end

#stackObject

Returns the value of attribute stack.



22
23
24
# File 'lib/bijou/errorformatter.rb', line 22

def stack
  @stack
end

#titleObject

Returns the value of attribute title.



22
23
24
# File 'lib/bijou/errorformatter.rb', line 22

def title
  @title
end

#warningsObject

Returns the value of attribute warnings.



22
23
24
# File 'lib/bijou/errorformatter.rb', line 22

def warnings
  @warnings
end

Class Method Details

.find_eval_line(error) ⇒ Object



186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
# File 'lib/bijou/errorformatter.rb', line 186

def self.find_eval_line(error)
  line = 0

  #
  # This section requires a heuristic to find the most likely line that
  # is the origin of the error.
  #
  if error.to_s =~ /\(eval\)\:(\d+)\: parse error/
    return $1.to_i
  elsif error.to_s =~ /\(eval\)\:(\d+)\:/
    # Any eval line
    return $1.to_i
  end
  return 0
end

.format_error(format, adjacent_lines, context = nil) ⇒ Object



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/bijou/errorformatter.rb', line 52

def self.format_error(format, adjacent_lines, context=nil)
  if format == :html
    formatter = ErrorFormatterHTML.new
  else
    formatter = ErrorFormatterText.new
  end

  @context = context

  result = ''

  error_line = 0
  error_column = 0

  error = $!

  if $!.kind_of?(Bijou::ParseError)
    formatter.title = $!.to_s

    filename = error.filename

    formatter.errors.replace(error.diagnostics.errors)
    formatter.warnings.replace(error.diagnostics.warnings)

    if error.diagnostics.errors
      # We use the first error as the origin.
      selected_error = error.diagnostics.errors[0]
      error_line = selected_error.line
      error_column = selected_error.column
    end
  else
    if $!.kind_of?(Bijou::EvalError)
      formatter.error = error.cause
      filename = error.filename
      cachename = error.cachename
    else
      formatter.error = $!

      # REVIEW: Are there any cases where the context would override
      # the EvalError location?
      if context
        filename = context.source_filename
        cachename = context.cache_filename
      elsif error.respond_to?('filename')
        filename = error.filename
        cachename = error.cachename
      end
    end

    # First, look for '(eval):#' in the error itself.
    eval_line = self.find_eval_line(formatter.error)
    if eval_line == 0
      # If not found, walk the stack.
      $@.each {|err|
        eval_line = self.find_eval_line(err)
        if eval_line > 0
          break
        end
      }
    end

    # Find the location in the original source using the line numbers 
    # in the cache.
    if eval_line > 0 && cachename
      error_line = self.get_source_file_line(cachename, eval_line)
    end
  end
    
  if filename && File.exists?(filename) && error_line > 0
    lines = self.get_file_context(filename, error_line, adjacent_lines)
    
    formatter.set_context(filename, lines, adjacent_lines,
                          error_line, error_column)
  else
    # TODO: Provide a default message using the best available info.
  end

  formatter.stack.replace($@)      

  result = formatter.format

  return result
end

.get_file_context(filename, line, adjacent_lines) ⇒ Object



136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/bijou/errorformatter.rb', line 136

def self.get_file_context(filename, line, adjacent_lines)
  file = File.new(filename, "r")

  min = line > adjacent_lines ? line - adjacent_lines : 1
  counter = 1;
  list = []

  while line = file.gets
    line.chomp!

    if counter >= min
      list.push({ 'number' => counter, 'text' => line })
      if counter >= min + 2 * adjacent_lines
        break
      end
    end

    counter += 1
  end

  file.close

  return list
end

.get_source_file_line(cachename, eval_line) ⇒ Object



161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
# File 'lib/bijou/errorformatter.rb', line 161

def self.get_source_file_line(cachename, eval_line)
  file = File.new(cachename, "r")
  counter = 1
  most_recent_line = 0
  while line = file.gets
    line.chomp!
    
    if line =~ /\#line\s+(\d+)/
        most_recent_line = $1.to_i
        # Start back by one to account for the comment line.
        most_recent_line -= 1
    else
        most_recent_line += 1
    end

    if counter >= eval_line
      return most_recent_line
    end

    counter += 1
  end
  file.close
  return 0
end

Instance Method Details

#formatObject

Raises:

  • (StandardException)


48
49
50
# File 'lib/bijou/errorformatter.rb', line 48

def format
  raise StandardException, "Invalid error formatter"
end

#set_context(filename, lines, context, error_line, error_column) ⇒ Object



40
41
42
43
44
45
46
# File 'lib/bijou/errorformatter.rb', line 40

def set_context(filename, lines, context, error_line, error_column)
  @filename = filename
  @context_lines = lines
  @context = context
  @error_line = error_line
  @error_column = error_column
end