1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
|
# frozen_string_literal: true
require "active_support/core_ext/enumerable"
module ActionView
# = Action View Errors
class ActionViewError < StandardError #:nodoc:
end
class EncodingError < StandardError #:nodoc:
end
class WrongEncodingError < EncodingError #:nodoc:
def initialize(string, encoding)
@string, @encoding = string, encoding
end
def message
@string.force_encoding(Encoding::ASCII_8BIT)
"Your template was not saved as valid #{@encoding}. Please " \
"either specify #{@encoding} as the encoding for your template " \
"in your text editor, or mark the template with its " \
"encoding by inserting the following as the first line " \
"of the template:\n\n# encoding: <name of correct encoding>.\n\n" \
"The source of your template was:\n\n#{@string}"
end
end
class MissingTemplate < ActionViewError #:nodoc:
attr_reader :path
def initialize(paths, path, prefixes, partial, details, *)
@path = path
prefixes = Array(prefixes)
template_type = if partial
"partial"
elsif /layouts/i.match?(path)
"layout"
else
"template"
end
if partial && path.present?
path = path.sub(%r{([^/]+)$}, "_\\1")
end
searched_paths = prefixes.map { |prefix| [prefix, path].join("/") }
out = "Missing #{template_type} #{searched_paths.join(", ")} with #{details.inspect}. Searched in:\n"
out += paths.compact.map { |p| " * #{p.to_s.inspect}\n" }.join
super out
end
end
class Template
# The Template::Error exception is raised when the compilation or rendering of the template
# fails. This exception then gathers a bunch of intimate details and uses it to report a
# precise exception message.
class Error < ActionViewError #:nodoc:
SOURCE_CODE_RADIUS = 3
# Override to prevent #cause resetting during re-raise.
attr_reader :cause
def initialize(template)
super($!.message)
set_backtrace($!.backtrace)
@cause = $!
@template, @sub_templates = template, nil
end
def file_name
@template.identifier
end
def sub_template_message
if @sub_templates
"Trace of template inclusion: " +
@sub_templates.collect(&:inspect).join(", ")
else
""
end
end
def source_extract(indentation = 0, output = :console)
return unless num = line_number
num = num.to_i
source_code = @template.source.split("\n")
start_on_line = [ num - SOURCE_CODE_RADIUS - 1, 0 ].max
end_on_line = [ num + SOURCE_CODE_RADIUS - 1, source_code.length].min
indent = end_on_line.to_s.size + indentation
return unless source_code = source_code[start_on_line..end_on_line]
formatted_code_for(source_code, start_on_line, indent, output)
end
def sub_template_of(template_path)
@sub_templates ||= []
@sub_templates << template_path
end
def line_number
@line_number ||=
if file_name
regexp = /#{Regexp.escape File.basename(file_name)}:(\d+)/
$1 if message =~ regexp || backtrace.find { |line| line =~ regexp }
end
end
def annotated_source_code
source_extract(4)
end
private
def source_location
if line_number
"on line ##{line_number} of "
else
"in "
end + file_name
end
def formatted_code_for(source_code, line_counter, indent, output)
start_value = (output == :html) ? {} : []
source_code.inject(start_value) do |result, line|
line_counter += 1
if output == :html
result.update(line_counter.to_s => "%#{indent}s %s\n" % ["", line])
else
result << "%#{indent}s: %s" % [line_counter, line]
end
end
end
end
end
TemplateError = Template::Error
class SyntaxErrorInTemplate < TemplateError #:nodoc
def initialize(template, offending_code_string)
@offending_code_string = offending_code_string
super(template)
end
def message
<<~MESSAGE
Encountered a syntax error while rendering template: check #{@offending_code_string}
MESSAGE
end
def annotated_source_code
@offending_code_string.split("\n").map.with_index(1) { |line, index|
indentation = " " * 4
"#{index}:#{indentation}#{line}"
}
end
end
end
|