ezml 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|