ezml 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.DS_Store +0 -0
- data/.gitignore +11 -0
- data/.rspec +3 -0
- data/.travis.yml +5 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +149 -0
- data/LICENSE.txt +21 -0
- data/README.md +140 -0
- data/Rakefile +6 -0
- data/bin/ezml +9 -0
- data/examples/helloworld.ezml +44 -0
- data/examples/helloworld.html +61 -0
- data/examples/template.ezml +69 -0
- data/examples/template.html +102 -0
- data/ezml.gemspec +40 -0
- data/lib/ezml.rb +8 -0
- data/lib/ezml/attribute_builder.rb +141 -0
- data/lib/ezml/attribute_compiler.rb +172 -0
- data/lib/ezml/attribute_parser.rb +133 -0
- data/lib/ezml/buffer.rb +166 -0
- data/lib/ezml/compiler.rb +322 -0
- data/lib/ezml/engine.rb +107 -0
- data/lib/ezml/error.rb +59 -0
- data/lib/ezml/escapable.rb +46 -0
- data/lib/ezml/exec.rb +348 -0
- data/lib/ezml/filters.rb +249 -0
- data/lib/ezml/generator.rb +38 -0
- data/lib/ezml/helpers.rb +299 -0
- data/lib/ezml/options.rb +142 -0
- data/lib/ezml/parser.rb +826 -0
- data/lib/ezml/template_engine.rb +106 -0
- data/lib/ezml/temple_line_counter.rb +26 -0
- data/lib/ezml/util.rb +156 -0
- data/lib/ezml/version.rb +3 -0
- metadata +199 -0
@@ -0,0 +1,322 @@
|
|
1
|
+
require_relative 'attribute_builder'
|
2
|
+
require_relative 'attribute_compiler'
|
3
|
+
require_relative 'temple_line_counter'
|
4
|
+
|
5
|
+
module EZML
|
6
|
+
|
7
|
+
class Compiler
|
8
|
+
include EZML::Util
|
9
|
+
|
10
|
+
attr_accessor :options
|
11
|
+
|
12
|
+
def initialize(options)
|
13
|
+
@options = Options.wrap(options)
|
14
|
+
@to_merge = []
|
15
|
+
@temple = [:multi]
|
16
|
+
@node = nil
|
17
|
+
@filters = Filters.defined.merge(options[:filters])
|
18
|
+
@attribute_compiler = AttributeCompiler.new(@options)
|
19
|
+
end
|
20
|
+
|
21
|
+
def call(node)
|
22
|
+
compile(node)
|
23
|
+
@temple
|
24
|
+
end
|
25
|
+
|
26
|
+
def compile(node)
|
27
|
+
parent, @node = @node, node
|
28
|
+
if node.children.empty?
|
29
|
+
send(:"compile_#{node.type}")
|
30
|
+
else
|
31
|
+
send(:"compile_#{node.type}") {node.children.each {|c| compile c}}
|
32
|
+
end
|
33
|
+
ensure
|
34
|
+
@node = parent
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def compile_root
|
40
|
+
@output_line = 1
|
41
|
+
yield if block_given?
|
42
|
+
flush_merged_text
|
43
|
+
end
|
44
|
+
|
45
|
+
def compile_plain
|
46
|
+
push_text("#{@node.value[:text]}\n")
|
47
|
+
end
|
48
|
+
|
49
|
+
def nuke_inner_whitespace?(node)
|
50
|
+
if node.value && node.value[:nuke_inner_whitespace]
|
51
|
+
true
|
52
|
+
elsif node.parent
|
53
|
+
nuke_inner_whitespace?(node.parent)
|
54
|
+
else
|
55
|
+
false
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def compile_script(&block)
|
60
|
+
push_script(@node.value[:text],
|
61
|
+
:preserve_script => @node.value[:preserve],
|
62
|
+
:escape_html => @node.value[:escape_html],
|
63
|
+
:nuke_inner_whitespace => nuke_inner_whitespace?(@node),
|
64
|
+
&block)
|
65
|
+
end
|
66
|
+
|
67
|
+
def compile_silent_script
|
68
|
+
return if @options.suppress_eval
|
69
|
+
push_silent(@node.value[:text])
|
70
|
+
keyword = @node.value[:keyword]
|
71
|
+
|
72
|
+
if block_given?
|
73
|
+
yield
|
74
|
+
push_silent("end", :can_suppress) unless @node.value[:dont_push_end]
|
75
|
+
elsif keyword == "end"
|
76
|
+
if @node.parent.children.last.equal?(@node)
|
77
|
+
# Since this "end" is ending the block,
|
78
|
+
# we don't need to generate an additional one
|
79
|
+
@node.parent.value[:dont_push_end] = true
|
80
|
+
end
|
81
|
+
# Don't restore dont_* for end because it isn't a conditional branch.
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def compile_ezml_comment; end
|
86
|
+
|
87
|
+
def compile_tag
|
88
|
+
t = @node.value
|
89
|
+
|
90
|
+
# Get rid of whitespace outside of the tag if we need to
|
91
|
+
rstrip_buffer! if t[:nuke_outer_whitespace]
|
92
|
+
|
93
|
+
if @options.suppress_eval
|
94
|
+
object_ref = :nil
|
95
|
+
parse = false
|
96
|
+
value = t[:parse] ? nil : t[:value]
|
97
|
+
dynamic_attributes = EZML::Parser::DynamicAttributes.new
|
98
|
+
else
|
99
|
+
object_ref = t[:object_ref]
|
100
|
+
parse = t[:parse]
|
101
|
+
value = t[:value]
|
102
|
+
dynamic_attributes = t[:dynamic_attributes]
|
103
|
+
end
|
104
|
+
|
105
|
+
if @options[:trace]
|
106
|
+
t[:attributes].merge!({"data-trace" => @options.filename.split('/views').last << ":" << @node.line.to_s})
|
107
|
+
end
|
108
|
+
|
109
|
+
push_text("<#{t[:name]}")
|
110
|
+
push_temple(@attribute_compiler.compile(t[:attributes], object_ref, dynamic_attributes))
|
111
|
+
push_text(
|
112
|
+
if t[:self_closing] && @options.xhtml?
|
113
|
+
" />#{"\n" unless t[:nuke_outer_whitespace]}"
|
114
|
+
else
|
115
|
+
">#{"\n" unless (t[:self_closing] && @options.html?) ? t[:nuke_outer_whitespace] : (!block_given? || t[:preserve_tag] || t[:nuke_inner_whitespace])}"
|
116
|
+
end
|
117
|
+
)
|
118
|
+
|
119
|
+
if value && !parse
|
120
|
+
push_text("#{value}</#{t[:name]}>#{"\n" unless t[:nuke_outer_whitespace]}")
|
121
|
+
end
|
122
|
+
|
123
|
+
return if t[:self_closing]
|
124
|
+
|
125
|
+
if value.nil?
|
126
|
+
yield if block_given?
|
127
|
+
rstrip_buffer! if t[:nuke_inner_whitespace]
|
128
|
+
push_text("</#{t[:name]}>#{"\n" unless t[:nuke_outer_whitespace]}")
|
129
|
+
return
|
130
|
+
end
|
131
|
+
|
132
|
+
if parse
|
133
|
+
push_script(value, t.merge(:in_tag => true))
|
134
|
+
push_text("</#{t[:name]}>#{"\n" unless t[:nuke_outer_whitespace]}")
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def compile_comment
|
139
|
+
condition = "#{@node.value[:conditional]}>" if @node.value[:conditional]
|
140
|
+
revealed = @node.value[:revealed]
|
141
|
+
|
142
|
+
open = "<!--#{condition}#{'<!-->' if revealed}"
|
143
|
+
|
144
|
+
close = "#{'<!--' if revealed}#{'<![endif]' if condition}-->"
|
145
|
+
|
146
|
+
unless block_given?
|
147
|
+
push_text("#{open} ")
|
148
|
+
|
149
|
+
if @node.value[:parse]
|
150
|
+
push_script(@node.value[:text], :in_tag => true, :nuke_inner_whitespace => true)
|
151
|
+
else
|
152
|
+
push_text(@node.value[:text])
|
153
|
+
end
|
154
|
+
|
155
|
+
push_text(" #{close}\n")
|
156
|
+
return
|
157
|
+
end
|
158
|
+
|
159
|
+
push_text("#{open}\n")
|
160
|
+
yield if block_given?
|
161
|
+
push_text("#{close}\n")
|
162
|
+
end
|
163
|
+
|
164
|
+
def compile_doctype
|
165
|
+
doctype = text_for_doctype
|
166
|
+
push_text("#{doctype}\n") if doctype
|
167
|
+
end
|
168
|
+
|
169
|
+
def compile_filter
|
170
|
+
unless filter = @filters[@node.value[:name]]
|
171
|
+
name = @node.value[:name]
|
172
|
+
if ["maruku", "textile"].include?(name)
|
173
|
+
raise Error.new(Error.message(:install_ezml_contrib, name), @node.line - 1)
|
174
|
+
else
|
175
|
+
raise Error.new(Error.message(:filter_not_defined, name), @node.line - 1)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
filter.internal_compile(self, @node.value[:text])
|
179
|
+
end
|
180
|
+
|
181
|
+
def text_for_doctype
|
182
|
+
if @node.value[:type] == "xml"
|
183
|
+
return nil if @options.html?
|
184
|
+
wrapper = @options.attr_wrapper
|
185
|
+
return "<?xml version=#{wrapper}1.0#{wrapper} encoding=#{wrapper}#{@node.value[:encoding] || "utf-8"}#{wrapper} ?>"
|
186
|
+
end
|
187
|
+
|
188
|
+
if @options.html5?
|
189
|
+
'<!DOCTYPE html>'
|
190
|
+
else
|
191
|
+
if @options.xhtml?
|
192
|
+
if @node.value[:version] == "1.1"
|
193
|
+
'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">'
|
194
|
+
elsif @node.value[:version] == "5"
|
195
|
+
'<!DOCTYPE html>'
|
196
|
+
else
|
197
|
+
case @node.value[:type]
|
198
|
+
when "strict"; '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">'
|
199
|
+
when "frameset"; '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">'
|
200
|
+
when "mobile"; '<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.2//EN" "http://www.openmobilealliance.org/tech/DTD/xhtml-mobile12.dtd">'
|
201
|
+
when "rdfa"; '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML+RDFa 1.0//EN" "http://www.w3.org/MarkUp/DTD/xhtml-rdfa-1.dtd">'
|
202
|
+
when "basic"; '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML Basic 1.1//EN" "http://www.w3.org/TR/xhtml-basic/xhtml-basic11.dtd">'
|
203
|
+
else '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
elsif @options.html4?
|
208
|
+
case @node.value[:type]
|
209
|
+
when "strict"; '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">'
|
210
|
+
when "frameset"; '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">'
|
211
|
+
else '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">'
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
def push_silent(text, can_suppress = false)
|
218
|
+
flush_merged_text
|
219
|
+
return if can_suppress && @options.suppress_eval?
|
220
|
+
newline = (text == "end") ? ";" : "\n"
|
221
|
+
@temple << [:code, "#{resolve_newlines}#{text}#{newline}"]
|
222
|
+
@output_line = @output_line + text.count("\n") + newline.count("\n")
|
223
|
+
end
|
224
|
+
|
225
|
+
def push_text(text)
|
226
|
+
@to_merge << [:text, text]
|
227
|
+
end
|
228
|
+
|
229
|
+
def push_temple(temple)
|
230
|
+
flush_merged_text
|
231
|
+
@temple.concat([[:newline]] * resolve_newlines.count("\n"))
|
232
|
+
@temple << temple
|
233
|
+
@output_line += TempleLineCounter.count_lines(temple)
|
234
|
+
end
|
235
|
+
|
236
|
+
def flush_merged_text
|
237
|
+
return if @to_merge.empty?
|
238
|
+
|
239
|
+
@to_merge.each do |type, val|
|
240
|
+
case type
|
241
|
+
when :text
|
242
|
+
@temple << [:static, val]
|
243
|
+
when :script
|
244
|
+
@temple << [:dynamic, val]
|
245
|
+
else
|
246
|
+
raise SyntaxError.new("[EZML BUG] Undefined entry in EZML::Compiler@to_merge.")
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
@to_merge = []
|
251
|
+
end
|
252
|
+
|
253
|
+
def push_script(text, opts = {})
|
254
|
+
return if @options.suppress_eval?
|
255
|
+
|
256
|
+
no_format = !(opts[:preserve_script] || opts[:preserve_tag] || opts[:escape_html])
|
257
|
+
|
258
|
+
unless block_given?
|
259
|
+
push_generated_script(no_format ? "(#{text}\n).to_s" : build_script_formatter("(#{text}\n)", opts))
|
260
|
+
push_text("\n") unless opts[:in_tag] || opts[:nuke_inner_whitespace]
|
261
|
+
return
|
262
|
+
end
|
263
|
+
|
264
|
+
flush_merged_text
|
265
|
+
push_silent "ezml_temp = #{text}"
|
266
|
+
yield
|
267
|
+
push_silent('end', :can_suppress) unless @node.value[:dont_push_end]
|
268
|
+
@temple << [:dynamic, no_format ? 'ezml_temp.to_s;' : build_script_formatter('ezml_temp', opts)]
|
269
|
+
end
|
270
|
+
|
271
|
+
def build_script_formatter(text, opts)
|
272
|
+
text = "(#{text}).to_s"
|
273
|
+
if opts[:escape_html]
|
274
|
+
text = "::EZML::Helpers.html_escape(#{text})"
|
275
|
+
end
|
276
|
+
if opts[:nuke_inner_whitespace]
|
277
|
+
text = "(#{text}).strip"
|
278
|
+
end
|
279
|
+
if opts[:preserve_tag]
|
280
|
+
text = "_ezmlout.fix_textareas!(::EZML::Helpers.preserve(#{text}))"
|
281
|
+
elsif opts[:preserve_script]
|
282
|
+
text = "_ezmlout.fix_textareas!(::EZML::Helpers.find_and_preserve(#{text}, _ezmlout.options[:preserve]))"
|
283
|
+
end
|
284
|
+
"#{text};"
|
285
|
+
end
|
286
|
+
|
287
|
+
def push_generated_script(text)
|
288
|
+
@to_merge << [:script, resolve_newlines + text]
|
289
|
+
@output_line += text.count("\n")
|
290
|
+
end
|
291
|
+
|
292
|
+
def resolve_newlines
|
293
|
+
diff = @node.line - @output_line
|
294
|
+
return "" if diff <= 0
|
295
|
+
@output_line = @node.line
|
296
|
+
"\n" * diff
|
297
|
+
end
|
298
|
+
|
299
|
+
def rstrip_buffer!(index = -1)
|
300
|
+
last = @to_merge[index]
|
301
|
+
if last.nil?
|
302
|
+
push_silent("_ezmlout.rstrip!", false)
|
303
|
+
return
|
304
|
+
end
|
305
|
+
|
306
|
+
case last.first
|
307
|
+
when :text
|
308
|
+
last[1] = last[1].rstrip
|
309
|
+
if last[1].empty?
|
310
|
+
@to_merge.slice! index
|
311
|
+
rstrip_buffer! index
|
312
|
+
end
|
313
|
+
when :script
|
314
|
+
last[1].gsub!(/\(ezml_temp, (.*?)\);$/, '(ezml_temp.rstrip, \1);')
|
315
|
+
rstrip_buffer! index - 1
|
316
|
+
else
|
317
|
+
raise SyntaxError.new("[EZML BUG] Undefined entry in EZML::Compiler@to_merge.")
|
318
|
+
end
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
end
|
data/lib/ezml/engine.rb
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
require_relative 'parser'
|
4
|
+
require_relative 'compiler'
|
5
|
+
require_relative 'options'
|
6
|
+
require_relative 'helpers'
|
7
|
+
require_relative 'buffer'
|
8
|
+
require_relative 'filters'
|
9
|
+
require_relative 'error'
|
10
|
+
require_relative 'template_engine'
|
11
|
+
|
12
|
+
module EZML
|
13
|
+
|
14
|
+
class Engine
|
15
|
+
extend Forwardable
|
16
|
+
include EZML::Util
|
17
|
+
|
18
|
+
attr_accessor :options
|
19
|
+
|
20
|
+
attr_accessor :indentation
|
21
|
+
|
22
|
+
def_delegators :compiler, :precompiled, :precompiled_method_return_value
|
23
|
+
|
24
|
+
def options_for_buffer
|
25
|
+
@options.for_buffer
|
26
|
+
end
|
27
|
+
|
28
|
+
def initialize(template, options = {})
|
29
|
+
@options = Options.new(options)
|
30
|
+
|
31
|
+
@template = check_ezml_encoding(template) do |msg, line|
|
32
|
+
raise EZML::Error.new(msg, line)
|
33
|
+
end
|
34
|
+
|
35
|
+
@template_engine = TemplateEngine.new(options)
|
36
|
+
@template_engine.compile(@template)
|
37
|
+
end
|
38
|
+
|
39
|
+
#Deprecated
|
40
|
+
def compiler
|
41
|
+
@template_engine
|
42
|
+
end
|
43
|
+
|
44
|
+
def render(scope = Object.new, locals = {}, &block)
|
45
|
+
parent = scope.instance_variable_defined?(:@ezml_buffer) ? scope.instance_variable_get(:@ezml_buffer) : nil
|
46
|
+
buffer = EZML::Buffer.new(parent, @options.for_buffer)
|
47
|
+
|
48
|
+
if scope.is_a?(Binding)
|
49
|
+
scope_object = eval("self", scope)
|
50
|
+
scope = scope_object.instance_eval{binding} if block_given?
|
51
|
+
else
|
52
|
+
scope_object = scope
|
53
|
+
scope = scope_object.instance_eval{binding}
|
54
|
+
end
|
55
|
+
|
56
|
+
set_locals(locals.merge(:_ezmlout => buffer, :_erbout => buffer.buffer), scope, scope_object)
|
57
|
+
|
58
|
+
scope_object.extend(EZML::Helpers)
|
59
|
+
scope_object.instance_variable_set(:@ezml_buffer, buffer)
|
60
|
+
begin
|
61
|
+
eval(@template_engine.precompiled_with_return_value, scope, @options.filename, @options.line)
|
62
|
+
rescue ::SyntaxError => e
|
63
|
+
raise SyntaxError, e.message
|
64
|
+
end
|
65
|
+
ensure
|
66
|
+
scope_object.instance_variable_set(:@ezml_buffer, buffer.upper) if buffer
|
67
|
+
end
|
68
|
+
alias_method :to_html, :render
|
69
|
+
|
70
|
+
def render_proc(scope = Object.new, *local_names)
|
71
|
+
if scope.is_a?(Binding)
|
72
|
+
scope_object = eval("self", scope)
|
73
|
+
else
|
74
|
+
scope_object = scope
|
75
|
+
scope = scope_object.instance_eval{binding}
|
76
|
+
end
|
77
|
+
|
78
|
+
begin
|
79
|
+
str = @template_engine.precompiled_with_ambles(local_names)
|
80
|
+
eval(
|
81
|
+
"Proc.new { |*_ezml_locals| _ezml_locals = _ezml_locals[0] || {}; #{str}}\n",
|
82
|
+
scope,
|
83
|
+
@options.filename,
|
84
|
+
@options.line
|
85
|
+
)
|
86
|
+
rescue ::SyntaxError => e
|
87
|
+
raise SyntaxError, e.message
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def def_method(object, name, *local_names)
|
92
|
+
method = object.is_a?(Module) ? :module_eval : :instance_eval
|
93
|
+
|
94
|
+
object.send(method, "def #{name}(_ezml_locals = {}); #{@template_engine.precompiled_with_ambles(local_names)}; end",
|
95
|
+
@options.filename, @options.line)
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
|
100
|
+
def set_locals(locals, scope, scope_object)
|
101
|
+
scope_object.instance_variable_set :@_ezml_locals, locals
|
102
|
+
set_locals = locals.keys.map { |k| "#{k} = @_ezml_locals[#{k.inspect}]" }.join("\n")
|
103
|
+
eval(set_locals, scope)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
data/lib/ezml/error.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
module EZML
|
2
|
+
|
3
|
+
class Error < StandardError
|
4
|
+
MESSAGES = {
|
5
|
+
:bad_script_indent => '"%s" is indented at wrong level: expected %d, but was at %d.',
|
6
|
+
:cant_run_filter => 'Can\'t run "%s" filter; you must require its dependencies first',
|
7
|
+
:cant_use_tabs_and_spaces => "Indentation can't use both tabs and spaces.",
|
8
|
+
:deeper_indenting => "The line was indented %d levels deeper than the previous line.",
|
9
|
+
:filter_not_defined => 'Filter "%s" is not defined.',
|
10
|
+
:gem_install_filter_deps => '"%s" filter\'s %s dependency missing: try installing it or adding it to your Gemfile',
|
11
|
+
:illegal_element => "Illegal element: classes and ids must have values.",
|
12
|
+
:illegal_nesting_content => "Illegal nesting: nesting within a tag that already has content is illegal.",
|
13
|
+
:illegal_nesting_header => "Illegal nesting: nesting within a header command is illegal.",
|
14
|
+
:illegal_nesting_line => "Illegal nesting: content can't be both given on the same line as %%%s and nested within it.",
|
15
|
+
:illegal_nesting_plain => "Illegal nesting: nesting within plain text is illegal.",
|
16
|
+
:illegal_nesting_self_closing => "Illegal nesting: nesting within a self-closing tag is illegal.",
|
17
|
+
:inconsistent_indentation => "Inconsistent indentation: %s used for indentation, but the rest of the document was indented using %s.",
|
18
|
+
:indenting_at_start => "Indenting at the beginning of the document is illegal.",
|
19
|
+
:install_ezml_contrib => 'To use the "%s" filter, please install the ezml-contrib gem.',
|
20
|
+
:invalid_attribute_list => 'Invalid attribute list: %s.',
|
21
|
+
:invalid_filter_name => 'Invalid filter name ":%s".',
|
22
|
+
:invalid_tag => 'Invalid tag: "%s".',
|
23
|
+
:missing_if => 'Got "%s" with no preceding "if"',
|
24
|
+
:no_ruby_code => "There's no Ruby code for %s to evaluate.",
|
25
|
+
:self_closing_content => "Self-closing tags can't have content.",
|
26
|
+
:unbalanced_brackets => 'Unbalanced brackets.',
|
27
|
+
:no_end => <<-END
|
28
|
+
You don't need to use "- end" in EZML. Un-indent to close a block:
|
29
|
+
- if foo?
|
30
|
+
%strong Foo!
|
31
|
+
- else
|
32
|
+
Not foo.
|
33
|
+
%p This line is un-indented, so it isn't part of the "if" block
|
34
|
+
END
|
35
|
+
}
|
36
|
+
|
37
|
+
def self.message(key, *args)
|
38
|
+
string = MESSAGES[key] or raise "[EZML BUG] No error messages for #{key}"
|
39
|
+
(args.empty? ? string : string % args).rstrip
|
40
|
+
end
|
41
|
+
|
42
|
+
# The line of the template on which the error occurred.
|
43
|
+
#
|
44
|
+
# @return [Fixnum]
|
45
|
+
attr_reader :line
|
46
|
+
|
47
|
+
# @param message [String] The error message
|
48
|
+
# @param line [Fixnum] See \{#line}
|
49
|
+
def initialize(message = nil, line = nil)
|
50
|
+
super(message)
|
51
|
+
@line = line
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class SyntaxError < Error; end
|
56
|
+
|
57
|
+
class InvalidAttributeNameError < SyntaxError; end
|
58
|
+
|
59
|
+
end
|