slim 0.9.2 → 0.9.3
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.
- data/.gitignore +18 -0
- data/.yardopts +2 -0
- data/CHANGES +147 -0
- data/Gemfile +16 -0
- data/LICENSE +21 -0
- data/README.md +31 -36
- data/benchmarks/run.rb +114 -0
- data/benchmarks/src/complex.erb +23 -0
- data/benchmarks/src/complex.haml +18 -0
- data/benchmarks/src/complex.slim +17 -0
- data/benchmarks/src/complex_view.rb +15 -0
- data/extra/slim-mode.el +409 -0
- data/extra/test.slim +49 -0
- data/lib/slim/command.rb +0 -6
- data/lib/slim/compiler.rb +40 -51
- data/lib/slim/embedded_engine.rb +69 -41
- data/lib/slim/end_inserter.rb +5 -3
- data/lib/slim/engine.rb +9 -11
- data/lib/slim/filter.rb +10 -13
- data/lib/slim/grammar.rb +19 -0
- data/lib/slim/interpolation.rb +7 -5
- data/lib/slim/parser.rb +48 -33
- data/lib/slim/sections.rb +22 -26
- data/lib/slim/template.rb +7 -7
- data/lib/slim/version.rb +3 -1
- data/lib/slim/wrapper.rb +1 -0
- data/slim.gemspec +34 -0
- data/test/helper.rb +5 -1
- data/test/slim/test_code_escaping.rb +3 -3
- data/test/slim/test_code_evaluation.rb +49 -2
- data/test/slim/test_embedded_engines.rb +3 -1
- data/test/slim/test_html_escaping.rb +8 -0
- data/test/slim/test_html_structure.rb +18 -0
- data/test/slim/test_pretty.rb +8 -3
- data/test/slim/test_ruby_errors.rb +27 -2
- metadata +38 -46
data/lib/slim/filter.rb
CHANGED
@@ -6,28 +6,25 @@ module Slim
|
|
6
6
|
# of the expression.
|
7
7
|
#
|
8
8
|
# @api private
|
9
|
-
class Filter < Temple::Filter
|
10
|
-
#
|
11
|
-
|
9
|
+
class Filter < Temple::HTML::Filter
|
10
|
+
# Pass-through handler
|
11
|
+
def on_slim_embedded(type, content)
|
12
|
+
[:slim, :embedded, code, compile(content)]
|
13
|
+
end
|
12
14
|
|
15
|
+
# Pass-through handler
|
13
16
|
def on_slim_control(code, content)
|
14
17
|
[:slim, :control, code, compile(content)]
|
15
18
|
end
|
16
19
|
|
17
|
-
|
18
|
-
|
20
|
+
# Pass-through handler
|
21
|
+
def on_slim_condcomment(condition, content)
|
22
|
+
[:slim, :condcomment, condition, compile(content)]
|
19
23
|
end
|
20
24
|
|
25
|
+
# Pass-through handler
|
21
26
|
def on_slim_output(code, escape, content)
|
22
27
|
[:slim, :output, code, escape, compile(content)]
|
23
28
|
end
|
24
|
-
|
25
|
-
def on_slim_tag(name, attrs, closed, content)
|
26
|
-
[:slim, :tag, name, compile(attrs), closed, compile(content)]
|
27
|
-
end
|
28
|
-
|
29
|
-
def on_slim_attrs(*attrs)
|
30
|
-
[:slim, :attrs, *attrs.map {|k, v| [k, compile(v)] }]
|
31
|
-
end
|
32
29
|
end
|
33
30
|
end
|
data/lib/slim/grammar.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
module Slim
|
2
|
+
# Slim expression grammar
|
3
|
+
# @api private
|
4
|
+
module Grammar
|
5
|
+
extend Temple::Grammar
|
6
|
+
|
7
|
+
Expression <<
|
8
|
+
[:slim, :control, String, Expression] |
|
9
|
+
[:slim, :condcomment, String, Expression] |
|
10
|
+
[:slim, :output, Bool, String, Expression] |
|
11
|
+
[:slim, :interpolate, String] |
|
12
|
+
[:slim, :embedded, String, Expression] |
|
13
|
+
[:slim, :directive, Value('doctype'), String]
|
14
|
+
|
15
|
+
HTMLAttr <<
|
16
|
+
[:slim, :attr, String, Bool, String]
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
data/lib/slim/interpolation.rb
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
module Slim
|
2
2
|
# Perform interpolation of #{var_name} in the
|
3
|
-
# expressions `[:slim, :
|
3
|
+
# expressions `[:slim, :interpolate, string]`.
|
4
4
|
#
|
5
5
|
# @api private
|
6
6
|
class Interpolation < Filter
|
7
|
-
# Handle
|
7
|
+
# Handle interpolate expression `[:slim, :interpolate, string]`
|
8
8
|
#
|
9
|
-
# @param [String] string Static
|
9
|
+
# @param [String] string Static interpolate
|
10
10
|
# @return [Array] Compiled temple expression
|
11
|
-
def
|
11
|
+
def on_slim_interpolate(string)
|
12
12
|
# Interpolate variables in text (#{variable}).
|
13
13
|
# Split the text into multiple dynamic and static parts.
|
14
14
|
block = [:multi]
|
@@ -22,7 +22,7 @@ module Slim
|
|
22
22
|
# Interpolation
|
23
23
|
string, code = parse_expression($')
|
24
24
|
escape = code !~ /^\{.*\}$/
|
25
|
-
block << [:
|
25
|
+
block << [:escape, escape, [:dynamic, escape ? code : code[1..-2]]]
|
26
26
|
when /^([^#]+|#)/
|
27
27
|
# Static text
|
28
28
|
block << [:static, $&]
|
@@ -32,6 +32,8 @@ module Slim
|
|
32
32
|
block
|
33
33
|
end
|
34
34
|
|
35
|
+
protected
|
36
|
+
|
35
37
|
def parse_expression(string)
|
36
38
|
stack, code = [], ''
|
37
39
|
|
data/lib/slim/parser.rb
CHANGED
@@ -37,7 +37,14 @@ module Slim
|
|
37
37
|
# @param [String] str Slim code
|
38
38
|
# @return [Array] Temple expression representing the code
|
39
39
|
def call(str)
|
40
|
-
|
40
|
+
# Set string encoding if option is set
|
41
|
+
if options[:encoding] && str.respond_to?(:encoding)
|
42
|
+
old = str.encoding
|
43
|
+
str = str.dup if str.frozen?
|
44
|
+
str.force_encoding(options[:encoding])
|
45
|
+
# Fall back to old encoding if new encoding is invalid
|
46
|
+
str.force_encoding(old_enc) unless str.valid_encoding?
|
47
|
+
end
|
41
48
|
|
42
49
|
lineno = 0
|
43
50
|
result = [:multi]
|
@@ -110,7 +117,7 @@ module Slim
|
|
110
117
|
# This line happens to be indented deeper (or equal) than the block start character (|, ', /).
|
111
118
|
# This means that it's a part of the block.
|
112
119
|
|
113
|
-
|
120
|
+
unless in_comment
|
114
121
|
# The indentation of first line of the text block determines the text base indentation.
|
115
122
|
newline = text_indent ? "\n" : ''
|
116
123
|
text_indent ||= indent
|
@@ -120,7 +127,7 @@ module Slim
|
|
120
127
|
syntax_error! 'Unexpected text indentation', line, lineno if offset < 0
|
121
128
|
|
122
129
|
# Generate the additional spaces in front.
|
123
|
-
stacks.last << [:slim, :
|
130
|
+
stacks.last << [:slim, :interpolate, newline + (' ' * offset) + line]
|
124
131
|
end
|
125
132
|
|
126
133
|
stacks.last << [:newline]
|
@@ -166,29 +173,36 @@ module Slim
|
|
166
173
|
end
|
167
174
|
|
168
175
|
case line[0]
|
169
|
-
when
|
170
|
-
# Found a block.
|
171
|
-
ch = line.slice!(0)
|
172
|
-
|
173
|
-
# We're now expecting the next line to be indented, so we'll need
|
174
|
-
# to push a block to the stack.
|
176
|
+
when ?/
|
177
|
+
# Found a comment block.
|
175
178
|
block = [:multi]
|
176
|
-
stacks.last << if
|
177
|
-
# Additional whitespace in front
|
178
|
-
[:multi, block, [:slim, :text, ' ']]
|
179
|
-
elsif ch == ?/ && line[0] == ?!
|
179
|
+
stacks.last << if line =~ %r{^/!( ?)(.*)$}
|
180
180
|
# HTML comment
|
181
|
-
|
182
|
-
|
181
|
+
block_indent = indent
|
182
|
+
text_indent = block_indent + ($1 ? 2 : 1)
|
183
|
+
block << [:slim, :interpolate, $2] if $2
|
184
|
+
[:html, :comment, block]
|
185
|
+
elsif line =~ %r{^/\[\s*(.*?)\s*\]\s*$}
|
186
|
+
# HTML conditional comment
|
187
|
+
[:slim, :condcomment, $1, block]
|
183
188
|
else
|
184
|
-
|
189
|
+
# Slim comment
|
190
|
+
block_indent = indent
|
191
|
+
in_comment = true
|
185
192
|
block
|
186
193
|
end
|
187
194
|
stacks << block
|
195
|
+
when ?|, ?'
|
196
|
+
# Found a text block.
|
197
|
+
# We're now expecting the next line to be indented, so we'll need
|
198
|
+
# to push a block to the stack.
|
199
|
+
block = [:multi]
|
188
200
|
block_indent = indent
|
189
|
-
|
190
|
-
|
191
|
-
|
201
|
+
stacks.last << (line.slice!(0) == ?' ?
|
202
|
+
[:multi, block, [:static, ' ']] : block)
|
203
|
+
stacks << block
|
204
|
+
unless line.strip.empty?
|
205
|
+
block << [:slim, :interpolate, line.sub(/^( )/, '')]
|
192
206
|
text_indent = block_indent + ($1 ? 2 : 1)
|
193
207
|
end
|
194
208
|
when ?-
|
@@ -213,8 +227,8 @@ module Slim
|
|
213
227
|
else
|
214
228
|
if line =~ /^(\w+):\s*$/
|
215
229
|
# Embedded template detected. It is treated as block.
|
216
|
-
block = [:
|
217
|
-
stacks.last << [:newline] << block
|
230
|
+
block = [:multi]
|
231
|
+
stacks.last << [:newline] << [:slim, :embedded, $1, block]
|
218
232
|
stacks << block
|
219
233
|
block_indent = indent
|
220
234
|
next
|
@@ -276,13 +290,13 @@ module Slim
|
|
276
290
|
# Now we'll have to find all the attributes. We'll store these in an
|
277
291
|
# nested array: [[name, value], [name2, value2]]. The value is a piece
|
278
292
|
# of Ruby code.
|
279
|
-
attributes = [:
|
293
|
+
attributes = [:html, :attrs]
|
280
294
|
|
281
295
|
# Find any literal class/id attributes
|
282
296
|
while line =~ CLASS_ID_REGEX
|
283
297
|
# The class/id attribute is :static instead of :slim :text,
|
284
298
|
# because we don't want text interpolation in .class or #id shortcut
|
285
|
-
attributes << [ATTR_SHORTHAND[$1], [:static, $2]]
|
299
|
+
attributes << [:html, :attr, ATTR_SHORTHAND[$1], [:static, $2]]
|
286
300
|
line = $'
|
287
301
|
end
|
288
302
|
|
@@ -296,21 +310,22 @@ module Slim
|
|
296
310
|
|
297
311
|
# Parse attributes
|
298
312
|
while line =~ ATTR_REGEX
|
299
|
-
|
313
|
+
name = $1
|
300
314
|
line = $'
|
301
315
|
if line =~ QUOTED_VALUE_REGEX
|
302
316
|
# Value is quoted (static)
|
303
317
|
line = $'
|
304
|
-
attributes << [
|
318
|
+
attributes << [:html, :attr, name, [:slim, :interpolate, $1[1..-2]]]
|
305
319
|
else
|
306
320
|
# Value is ruby code
|
307
|
-
|
308
|
-
|
321
|
+
escape = line[0] != ?=
|
322
|
+
line, code = parse_ruby_attribute(orig_line, escape ? line : line[1..-1], lineno, delimiter)
|
323
|
+
attributes << [:slim, :attr, name, escape, code]
|
309
324
|
end
|
310
325
|
end
|
311
326
|
|
312
327
|
# Find ending delimiter
|
313
|
-
|
328
|
+
unless delimiter.empty?
|
314
329
|
if line =~ /^\s*#{Regexp.escape delimiter}/
|
315
330
|
line = $'
|
316
331
|
else
|
@@ -319,7 +334,7 @@ module Slim
|
|
319
334
|
end
|
320
335
|
|
321
336
|
content = [:multi]
|
322
|
-
tag = [:
|
337
|
+
tag = [:html, :tag, tag, attributes, content]
|
323
338
|
|
324
339
|
if line =~ /^\s*=(=?)/
|
325
340
|
# Handle output code
|
@@ -329,14 +344,14 @@ module Slim
|
|
329
344
|
[tag, block, broken_line, nil]
|
330
345
|
elsif line =~ /^\s*\//
|
331
346
|
# Closed tag
|
332
|
-
tag
|
347
|
+
tag.pop
|
333
348
|
[tag, block, nil, nil]
|
334
349
|
elsif line =~ /^\s*$/
|
335
350
|
# Empty line
|
336
351
|
[tag, content, nil, nil]
|
337
352
|
else
|
338
353
|
# Handle text content
|
339
|
-
content << [:slim, :
|
354
|
+
content << [:slim, :interpolate, line.sub(/^( )/, '')]
|
340
355
|
[tag, content, nil, orig_line.size - line.size + ($1 ? 1 : 0)]
|
341
356
|
end
|
342
357
|
end
|
@@ -371,7 +386,7 @@ module Slim
|
|
371
386
|
end
|
372
387
|
end
|
373
388
|
|
374
|
-
syntax_error! "Expected closing attribute delimiter #{stack.last}", orig_line, lineno
|
389
|
+
syntax_error! "Expected closing attribute delimiter #{stack.last}", orig_line, lineno unless stack.empty?
|
375
390
|
syntax_error! 'Invalid empty attribute', orig_line, lineno if value.empty?
|
376
391
|
|
377
392
|
# Remove attribute wrapper which doesn't belong to the ruby code
|
@@ -381,7 +396,7 @@ module Slim
|
|
381
396
|
return line, value
|
382
397
|
end
|
383
398
|
|
384
|
-
#
|
399
|
+
# Helper for raising exceptions
|
385
400
|
def syntax_error!(message, *args)
|
386
401
|
raise SyntaxError.new(message, options[:file], *args)
|
387
402
|
end
|
data/lib/slim/sections.rb
CHANGED
@@ -20,7 +20,7 @@ module Slim
|
|
20
20
|
dictionary = options[:dictionary]
|
21
21
|
dictionary = "Slim::Wrapper.new(#{dictionary})" if options[:dictionary_access] == :wrapped
|
22
22
|
[:multi,
|
23
|
-
[:
|
23
|
+
[:code, "_slimdict = #{dictionary}"],
|
24
24
|
super]
|
25
25
|
else
|
26
26
|
exp
|
@@ -36,38 +36,34 @@ module Slim
|
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
39
|
+
def on_slim_output(escape, name, content)
|
40
|
+
raise 'Output statements with content are forbidden in sections mode' if !empty_exp?(content)
|
41
|
+
[:slim, :output, escape, access(name), content]
|
42
|
+
end
|
43
|
+
|
44
|
+
protected
|
45
|
+
|
39
46
|
def on_slim_inverted_section(name, content)
|
40
|
-
tmp =
|
47
|
+
tmp = unique_name
|
41
48
|
[:multi,
|
42
|
-
[:
|
43
|
-
[:
|
44
|
-
|
45
|
-
[:block, 'end']]
|
49
|
+
[:code, "#{tmp} = #{access name}"],
|
50
|
+
[:if, "!#{tmp} || #{tmp}.respond_to?(:empty) && #{tmp}.empty?",
|
51
|
+
compile(content)]]
|
46
52
|
end
|
47
53
|
|
48
54
|
def on_slim_section(name, content)
|
49
55
|
content = compile(content)
|
50
|
-
tmp1, tmp2 =
|
56
|
+
tmp1, tmp2 = unique_name, unique_name
|
51
57
|
|
52
|
-
[:
|
53
|
-
[:
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
content,
|
62
|
-
[:block, 'end'],
|
63
|
-
[:block, "_slimdict = #{tmp2}"],
|
64
|
-
[:block, 'end'],
|
65
|
-
[:block, 'end']]
|
66
|
-
end
|
67
|
-
|
68
|
-
def on_slim_output(escape, name, content)
|
69
|
-
raise 'Output statements with content are forbidden in sections mode' if !empty_exp?(content)
|
70
|
-
[:slim, :output, escape, access(name), content]
|
58
|
+
[:if, "#{tmp1} = #{access name}",
|
59
|
+
[:if, "#{tmp1} == true",
|
60
|
+
content,
|
61
|
+
[:multi,
|
62
|
+
# Wrap map in array because maps implement each
|
63
|
+
[:code, "#{tmp1} = [#{tmp1}] if #{tmp1}.respond_to?(:has_key?) || !#{tmp1}.respond_to?(:map)"],
|
64
|
+
[:code, "#{tmp2} = _slimdict"],
|
65
|
+
[:block, "#{tmp1}.each do |_slimdict|", content],
|
66
|
+
[:code, "_slimdict = #{tmp2}"]]]]
|
71
67
|
end
|
72
68
|
|
73
69
|
private
|
data/lib/slim/template.rb
CHANGED
@@ -6,12 +6,12 @@ module Slim
|
|
6
6
|
if Object.const_defined?(:Rails)
|
7
7
|
# Rails template implementation for Slim
|
8
8
|
# @api public
|
9
|
-
RailsTemplate = Temple::Templates::Rails(Slim::Engine,
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
9
|
+
RailsTemplate = Temple::Templates::Rails(Slim::Engine,
|
10
|
+
:register_as => :slim,
|
11
|
+
# Use rails-specific generator. This is necessary
|
12
|
+
# to support block capturing. Disable the internal slim capturing.
|
13
|
+
# Rails takes care of the capturing by itself.
|
14
|
+
:generator => Temple::Generators::RailsOutputBuffer,
|
15
|
+
:disable_capture => true)
|
16
16
|
end
|
17
17
|
end
|
data/lib/slim/version.rb
CHANGED
data/lib/slim/wrapper.rb
CHANGED
data/slim.gemspec
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.dirname(__FILE__) + '/lib/slim/version'
|
3
|
+
require 'date'
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = 'slim'
|
7
|
+
s.version = Slim::VERSION
|
8
|
+
s.date = Date.today.to_s
|
9
|
+
s.authors = ['Andrew Stone', 'Fred Wu', 'Daniel Mendler']
|
10
|
+
s.email = ['andy@stonean.com', 'ifredwu@gmail.com', 'mail@daniel-mendler.de']
|
11
|
+
s.summary = 'Slim is a template language.'
|
12
|
+
s.description = 'Slim is a template language whose goal is reduce the syntax to the essential parts without becoming cryptic.'
|
13
|
+
s.homepage = 'http://github.com/stonean/slim'
|
14
|
+
s.extra_rdoc_files = %w(README.md)
|
15
|
+
s.rdoc_options = %w(--charset=UTF-8)
|
16
|
+
s.rubyforge_project = s.name
|
17
|
+
|
18
|
+
s.files = `git ls-files`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = %w(lib)
|
21
|
+
|
22
|
+
s.add_runtime_dependency('temple', ['~> 0.3.0']) unless ENV['SLIM_USE_TEMPLE']
|
23
|
+
s.add_runtime_dependency('tilt', ['~> 1.2'])
|
24
|
+
|
25
|
+
s.add_development_dependency('rake', ['>= 0.8.7'])
|
26
|
+
s.add_development_dependency('haml', ['>= 3.1.0'])
|
27
|
+
s.add_development_dependency('sass', ['>= 3.1.0'])
|
28
|
+
s.add_development_dependency('minitest', ['>= 0'])
|
29
|
+
s.add_development_dependency('rcov', ['>= 0'])
|
30
|
+
s.add_development_dependency('rdiscount', ['>= 0'])
|
31
|
+
s.add_development_dependency('liquid', ['>= 0'])
|
32
|
+
s.add_development_dependency('yard', ['>= 0'])
|
33
|
+
s.add_development_dependency('creole', ['>= 0'])
|
34
|
+
end
|
data/test/helper.rb
CHANGED
@@ -3,13 +3,17 @@
|
|
3
3
|
require 'rubygems'
|
4
4
|
require 'minitest/unit'
|
5
5
|
require 'slim'
|
6
|
+
require 'slim/grammar'
|
6
7
|
|
7
8
|
MiniTest::Unit.autorun
|
8
9
|
|
10
|
+
Slim::Engine.after Slim::Parser, Temple::Filters::Validator, :grammar => Slim::Grammar
|
11
|
+
Slim::Engine.before Slim::Compiler, Temple::Filters::Validator, :grammar => Slim::Grammar
|
12
|
+
Slim::Engine.before Temple::HTML::Pretty, Temple::Filters::Validator
|
13
|
+
|
9
14
|
class TestSlim < MiniTest::Unit::TestCase
|
10
15
|
def setup
|
11
16
|
@env = Env.new
|
12
|
-
# Slim::Engine.set_default_options :debug => true
|
13
17
|
end
|
14
18
|
|
15
19
|
def teardown
|
@@ -33,7 +33,7 @@ p = HtmlSafeString.new("<strong>Hello World\\n, meet \\"Slim\\"</strong>.")
|
|
33
33
|
assert_html "<p><strong>Hello World\n, meet \"Slim\"</strong>.</p>", source, :use_html_safe => true
|
34
34
|
end
|
35
35
|
|
36
|
-
def
|
36
|
+
def test_render_with_disable_escape_false
|
37
37
|
source = %q{
|
38
38
|
= "<p>Hello</p>"
|
39
39
|
== "<p>World</p>"
|
@@ -42,12 +42,12 @@ p = HtmlSafeString.new("<strong>Hello World\\n, meet \\"Slim\\"</strong>.")
|
|
42
42
|
assert_html "<p>Hello</p><p>World</p>", source
|
43
43
|
end
|
44
44
|
|
45
|
-
def
|
45
|
+
def test_render_with_disable_escape_true
|
46
46
|
source = %q{
|
47
47
|
= "<p>Hello</p>"
|
48
48
|
== "<p>World</p>"
|
49
49
|
}
|
50
50
|
|
51
|
-
assert_html "<p>Hello</p><p>World</p>", source, :
|
51
|
+
assert_html "<p>Hello</p><p>World</p>", source, :disable_escape => true
|
52
52
|
end
|
53
53
|
end
|