slim 0.7.0 → 0.7.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.
- data/Gemfile.lock +3 -6
- data/README.md +32 -56
- data/Rakefile +2 -2
- data/benchmarks/run.rb +49 -16
- data/lib/slim.rb +0 -8
- data/lib/slim/compiler.rb +17 -26
- data/lib/slim/embedded_engine.rb +18 -11
- data/lib/slim/end_inserter.rb +10 -11
- data/lib/slim/engine.rb +8 -3
- data/lib/slim/filter.rb +8 -37
- data/lib/slim/parser.rb +27 -25
- data/lib/slim/template.rb +2 -31
- data/lib/slim/version.rb +1 -1
- data/slim.gemspec +1 -2
- data/test/helper.rb +35 -8
- data/test/slim/test_code_blocks.rb +37 -2
- data/test/slim/test_code_escaping.rb +2 -2
- data/test/slim/test_code_evaluation.rb +31 -9
- data/test/slim/test_code_output.rb +9 -1
- data/test/slim/test_code_structure.rb +23 -13
- data/test/slim/test_html_structure.rb +32 -8
- data/test/slim/test_parser_errors.rb +2 -2
- data/test/slim/test_ruby_errors.rb +28 -0
- metadata +25 -42
- data/lib/slim/helpers.rb +0 -81
- data/test/slim/test_code_helpers.rb +0 -30
data/lib/slim/end_inserter.rb
CHANGED
@@ -10,7 +10,7 @@ module Slim
|
|
10
10
|
#
|
11
11
|
# @api private
|
12
12
|
class EndInserter < Filter
|
13
|
-
ELSE_REGEX = /^
|
13
|
+
ELSE_REGEX = /^else|elsif|when\b/
|
14
14
|
END_REGEX = /^end\b/
|
15
15
|
|
16
16
|
# Handle multi expression `[:multi, *exps]`
|
@@ -22,15 +22,14 @@ module Slim
|
|
22
22
|
|
23
23
|
exps.each do |exp|
|
24
24
|
if control?(exp)
|
25
|
-
if
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
end
|
25
|
+
raise 'Explicit end statements are forbidden' if exp[2] =~ END_REGEX
|
26
|
+
|
27
|
+
# Two control code in a row. If this one is *not*
|
28
|
+
# an else block, we should close the previous one.
|
29
|
+
append_end(result) if prev_indent && exp[2] !~ ELSE_REGEX
|
30
|
+
|
31
|
+
# Indent if the control code contains something.
|
32
|
+
prev_indent = !empty_exp?(exp[3])
|
34
33
|
elsif exp[0] != :newline && prev_indent
|
35
34
|
# This is *not* a control code, so we should close the previous one.
|
36
35
|
# Ignores newlines because they will be inserted after each line.
|
@@ -38,7 +37,7 @@ module Slim
|
|
38
37
|
prev_indent = false
|
39
38
|
end
|
40
39
|
|
41
|
-
result << compile(exp)
|
40
|
+
result << compile!(exp)
|
42
41
|
end
|
43
42
|
|
44
43
|
# The last line can be a control code too.
|
data/lib/slim/engine.rb
CHANGED
@@ -3,11 +3,16 @@ module Slim
|
|
3
3
|
# @api public
|
4
4
|
class Engine < Temple::Engine
|
5
5
|
use Slim::Parser, :file
|
6
|
+
filter :Debugger, :debug, :prefix => 'before end insertion'
|
6
7
|
use Slim::EndInserter
|
8
|
+
filter :Debugger, :debug, :prefix => 'after end insertion'
|
7
9
|
use Slim::EmbeddedEngine
|
8
|
-
use Slim::Compiler
|
9
|
-
|
10
|
-
|
10
|
+
use Slim::Compiler
|
11
|
+
filter :Debugger, :debug, :prefix => 'after compilation'
|
12
|
+
filter :EscapeHTML, :use_html_safe
|
13
|
+
use Temple::HTML::Pretty, :format, :attr_wrapper, :id_delimiter, :id_concat, :pretty,
|
14
|
+
:pretty => false, :attr_wrapper => '"', :format => :html5, :id_delimiter => nil
|
15
|
+
filter :Debugger, :debug, :prefix => 'after html'
|
11
16
|
filter :MultiFlattener
|
12
17
|
filter :StaticMerger
|
13
18
|
filter :DynamicInliner
|
data/lib/slim/filter.rb
CHANGED
@@ -1,48 +1,19 @@
|
|
1
1
|
module Slim
|
2
2
|
# Base class for Temple filters used in Slim
|
3
3
|
# @api private
|
4
|
-
class Filter
|
5
|
-
|
4
|
+
class Filter < Temple::Filter
|
5
|
+
temple_dispatch :slim
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
def initialize(options = {})
|
10
|
-
@options = DEFAULT_OPTIONS.merge(options)
|
11
|
-
end
|
12
|
-
|
13
|
-
def compile(exp)
|
14
|
-
if exp[0] == :slim
|
15
|
-
_, type, *args = exp
|
16
|
-
else
|
17
|
-
type, *args = exp
|
18
|
-
end
|
19
|
-
|
20
|
-
if respond_to?("on_#{type}")
|
21
|
-
send("on_#{type}", *args)
|
22
|
-
else
|
23
|
-
exp
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
def on_control(code, content)
|
28
|
-
[:slim, :control, code, compile(content)]
|
7
|
+
def on_slim_control(code, content)
|
8
|
+
[:slim, :control, code, compile!(content)]
|
29
9
|
end
|
30
10
|
|
31
|
-
def
|
32
|
-
[:slim, :
|
11
|
+
def on_slim_output(code, escape, content)
|
12
|
+
[:slim, :output, code, escape, compile!(content)]
|
33
13
|
end
|
34
14
|
|
35
|
-
def
|
36
|
-
[:
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
# Simple filter which prints Temple expression
|
41
|
-
# @api private
|
42
|
-
class Debugger < Filter
|
43
|
-
def compile(exp)
|
44
|
-
puts exp.inspect
|
45
|
-
exp
|
15
|
+
def on_slim_tag(name, attrs, closed, content)
|
16
|
+
[:slim, :tag, name, attrs, closed, compile!(content)]
|
46
17
|
end
|
47
18
|
end
|
48
19
|
end
|
data/lib/slim/parser.rb
CHANGED
@@ -2,6 +2,8 @@ module Slim
|
|
2
2
|
# Parses Slim code and transforms it to a Temple expression
|
3
3
|
# @api private
|
4
4
|
class Parser
|
5
|
+
include Temple::Mixins::Options
|
6
|
+
|
5
7
|
class SyntaxError < StandardError
|
6
8
|
attr_reader :error, :file, :line, :lineno, :column
|
7
9
|
|
@@ -22,11 +24,11 @@ module Slim
|
|
22
24
|
end
|
23
25
|
end
|
24
26
|
|
25
|
-
|
27
|
+
default_options[:tabsize] = 4
|
26
28
|
|
27
29
|
def initialize(options = {})
|
28
|
-
|
29
|
-
@tab
|
30
|
+
super
|
31
|
+
@tab = ' ' * @options[:tabsize]
|
30
32
|
end
|
31
33
|
|
32
34
|
# Compile string to Temple expression
|
@@ -72,8 +74,8 @@ module Slim
|
|
72
74
|
str.each_line do |line|
|
73
75
|
lineno += 1
|
74
76
|
|
75
|
-
# Remove the newline at the
|
76
|
-
line.
|
77
|
+
# Remove the newline at the end
|
78
|
+
line.chomp!
|
77
79
|
|
78
80
|
# Handle broken lines
|
79
81
|
if broken_line
|
@@ -177,24 +179,20 @@ module Slim
|
|
177
179
|
block << [:slim, :text, line.sub(/^( )/, '')]
|
178
180
|
text_indent = block_indent + ($1 ? 2 : 1)
|
179
181
|
end
|
180
|
-
when
|
181
|
-
# Found a
|
182
|
-
|
183
|
-
# First of all we need to push a exp into the stack. Anything
|
184
|
-
# indented deeper will be pushed into this exp. We'll include the
|
185
|
-
# same exp in the current-stack, which makes sure that it'll be
|
186
|
-
# included in the generated code.
|
182
|
+
when ?-
|
183
|
+
# Found a code block.
|
184
|
+
# We expect the line to be broken or the next line to be indented.
|
187
185
|
block = [:multi]
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
186
|
+
broken_line = line[1..-1].strip
|
187
|
+
stacks.last << [:slim, :control, broken_line, block]
|
188
|
+
stacks << block
|
189
|
+
when ?=
|
190
|
+
# Found a output bloc
|
191
|
+
# We expect the line to be broken or the next line to be indented.
|
192
|
+
block = [:multi]
|
193
|
+
escape = line[1] != ?=
|
194
|
+
broken_line = escape ? line[1..-1].strip : line[2..-1].strip
|
195
|
+
stacks.last << [:slim, :output, escape, broken_line, block]
|
198
196
|
stacks << block
|
199
197
|
when ?!
|
200
198
|
# Found a directive (currently only used for doctypes)
|
@@ -297,12 +295,12 @@ module Slim
|
|
297
295
|
if line[0, 1] == delimiter
|
298
296
|
line.slice!(0)
|
299
297
|
else
|
300
|
-
syntax_error! "Expected closing
|
298
|
+
syntax_error! "Expected closing delimiter #{delimiter}", orig_line, lineno, orig_line.size - line.size
|
301
299
|
end
|
302
300
|
end
|
303
301
|
|
304
302
|
content = [:multi]
|
305
|
-
tag = [:slim, :tag, tag, attributes, content]
|
303
|
+
tag = [:slim, :tag, tag, attributes, false, content]
|
306
304
|
|
307
305
|
if line =~ /^\s*=(=?)/
|
308
306
|
# Handle output code
|
@@ -310,6 +308,10 @@ module Slim
|
|
310
308
|
broken_line = $'.strip
|
311
309
|
content << [:slim, :output, $1 != '=', broken_line, block]
|
312
310
|
[tag, block, broken_line, nil]
|
311
|
+
elsif line =~ /^\s*\//
|
312
|
+
# Closed tag
|
313
|
+
tag[4] = true
|
314
|
+
[tag, block, nil, nil]
|
313
315
|
elsif !line.empty?
|
314
316
|
# Handle text content
|
315
317
|
content << [:slim, :text, line.sub(/^( )/, '')]
|
@@ -362,7 +364,7 @@ module Slim
|
|
362
364
|
|
363
365
|
# A little helper for raising exceptions.
|
364
366
|
def syntax_error!(message, *args)
|
365
|
-
raise SyntaxError.new(message,
|
367
|
+
raise SyntaxError.new(message, options[:file], *args)
|
366
368
|
end
|
367
369
|
end
|
368
370
|
end
|
data/lib/slim/template.rb
CHANGED
@@ -1,37 +1,8 @@
|
|
1
1
|
module Slim
|
2
2
|
# Tilt template implementation for Slim
|
3
3
|
# @api public
|
4
|
-
class Template <
|
5
|
-
|
6
|
-
#
|
7
|
-
# Called immediately after template data is loaded.
|
8
|
-
#
|
9
|
-
# @return [void]
|
10
|
-
def prepare
|
11
|
-
@src = Engine.new(options.merge(:file => eval_file)).compile(data)
|
12
|
-
end
|
13
|
-
|
14
|
-
# Process the template and return the result.
|
15
|
-
#
|
16
|
-
# Template executationis guaranteed to be performed in the scope object with the locals
|
17
|
-
# specified and with support for yielding to the block.
|
18
|
-
#
|
19
|
-
# @param [Object] scope Scope object where the code is evaluated
|
20
|
-
# @param [Hash] locals Local variables
|
21
|
-
# @yield Block given to the template code
|
22
|
-
# @return [String] Evaluated template
|
23
|
-
def evaluate(scope, locals, &block)
|
24
|
-
scope.instance_eval { extend Slim::Helpers } if options[:helpers]
|
25
|
-
super
|
26
|
-
end
|
27
|
-
|
28
|
-
# A string containing the (Ruby) source code for the template.
|
29
|
-
#
|
30
|
-
# @param [Hash] locals Local variables
|
31
|
-
# @return [String] Compiled template ruby code
|
32
|
-
def precompiled_template(locals)
|
33
|
-
@src
|
34
|
-
end
|
4
|
+
class Template < Temple::Template
|
5
|
+
engine Slim::Engine
|
35
6
|
end
|
36
7
|
|
37
8
|
Tilt.register 'slim', Template
|
data/lib/slim/version.rb
CHANGED
data/slim.gemspec
CHANGED
@@ -20,8 +20,7 @@ Gem::Specification.new do |s|
|
|
20
20
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
21
21
|
s.require_paths = ["lib"]
|
22
22
|
|
23
|
-
s.add_runtime_dependency(%q<
|
24
|
-
s.add_runtime_dependency(%q<temple>, ["~> 0.1.3"])
|
23
|
+
s.add_runtime_dependency(%q<temple>, ["~> 0.1.4"])
|
25
24
|
s.add_runtime_dependency(%q<tilt>, ["~> 1.1"])
|
26
25
|
s.add_development_dependency(%q<rake>, [">= 0.8.7"])
|
27
26
|
s.add_development_dependency(%q<haml>, [">= 0"])
|
data/test/helper.rb
CHANGED
@@ -2,36 +2,40 @@
|
|
2
2
|
|
3
3
|
require 'rubygems'
|
4
4
|
require 'minitest/unit'
|
5
|
+
require 'slim'
|
5
6
|
|
6
7
|
MiniTest::Unit.autorun
|
7
8
|
|
8
|
-
require File.dirname(__FILE__) + '/../lib/slim'
|
9
|
-
|
10
9
|
class TestSlim < MiniTest::Unit::TestCase
|
11
10
|
def setup
|
12
11
|
@env = Env.new
|
12
|
+
# Slim::Filter::DEFAULT_OPTIONS[:debug] = true
|
13
13
|
end
|
14
14
|
|
15
15
|
def teardown
|
16
16
|
String.send(:undef_method, :html_safe?) if String.method_defined?(:html_safe?)
|
17
17
|
String.send(:undef_method, :html_safe) if String.method_defined?(:html_safe)
|
18
18
|
Object.send(:undef_method, :html_safe?) if Object.method_defined?(:html_safe?)
|
19
|
-
|
19
|
+
Temple::Filters::EscapeHTML.default_options.delete(:use_html_safe)
|
20
|
+
end
|
21
|
+
|
22
|
+
def render(source, options = {}, &block)
|
23
|
+
Slim::Template.new(options[:file], options) { source }.render(@env, &block)
|
20
24
|
end
|
21
25
|
|
22
|
-
def assert_html(expected, source, options = {})
|
23
|
-
assert_equal expected,
|
26
|
+
def assert_html(expected, source, options = {}, &block)
|
27
|
+
assert_equal expected, render(source, options, &block)
|
24
28
|
end
|
25
29
|
|
26
30
|
def assert_syntax_error(message, source, options = {})
|
27
|
-
|
31
|
+
render(source, options)
|
28
32
|
raise 'Syntax error expected'
|
29
33
|
rescue Slim::Parser::SyntaxError => ex
|
30
34
|
assert_equal message, ex.message
|
31
35
|
end
|
32
36
|
|
33
37
|
def assert_ruby_error(error, from, source, options = {})
|
34
|
-
|
38
|
+
render(source, options)
|
35
39
|
raise 'Ruby error expected'
|
36
40
|
rescue error => ex
|
37
41
|
ex.backtrace[0] =~ /^(.*?:\d+):/
|
@@ -39,15 +43,28 @@ class TestSlim < MiniTest::Unit::TestCase
|
|
39
43
|
end
|
40
44
|
|
41
45
|
def assert_ruby_syntax_error(from, source, options = {})
|
42
|
-
|
46
|
+
render(source, options)
|
43
47
|
raise 'Ruby syntax error expected'
|
44
48
|
rescue SyntaxError => ex
|
45
49
|
ex.message =~ /^(.*?:\d+):/
|
46
50
|
assert_equal from, $1
|
47
51
|
end
|
52
|
+
|
53
|
+
def assert_runtime_error(message, source, options = {})
|
54
|
+
render(source, options)
|
55
|
+
raise Exception, 'Runtime error expected'
|
56
|
+
rescue RuntimeError => ex
|
57
|
+
assert_equal message, ex.message
|
58
|
+
end
|
48
59
|
end
|
49
60
|
|
50
61
|
class Env
|
62
|
+
attr_reader :var
|
63
|
+
|
64
|
+
def initialize
|
65
|
+
@var = 'instance'
|
66
|
+
end
|
67
|
+
|
51
68
|
def id_helper
|
52
69
|
"notice"
|
53
70
|
end
|
@@ -60,6 +77,16 @@ class Env
|
|
60
77
|
show
|
61
78
|
end
|
62
79
|
|
80
|
+
def define_macro(name, &block)
|
81
|
+
@macro ||= {}
|
82
|
+
@macro[name.to_s] = block
|
83
|
+
''
|
84
|
+
end
|
85
|
+
|
86
|
+
def call_macro(name, *args)
|
87
|
+
@macro[name.to_s].call(*args)
|
88
|
+
end
|
89
|
+
|
63
90
|
def hello_world(text = "Hello World from @env", opts = {})
|
64
91
|
text << opts.to_a * " " if opts.any?
|
65
92
|
if block_given?
|
@@ -15,12 +15,37 @@ p
|
|
15
15
|
source = %q{
|
16
16
|
p
|
17
17
|
= hello_world "Hello Ruby!" do
|
18
|
-
= hello_world "Hello from within a block!
|
18
|
+
= hello_world "Hello from within a block!"
|
19
19
|
}
|
20
20
|
|
21
|
-
assert_html '<p>Hello Ruby! Hello from within a block!
|
21
|
+
assert_html '<p>Hello Ruby! Hello from within a block! Hello Ruby!</p>', source
|
22
22
|
end
|
23
23
|
|
24
|
+
def test_render_with_output_code_within_block_2
|
25
|
+
source = %q{
|
26
|
+
p
|
27
|
+
= hello_world "Hello Ruby!" do
|
28
|
+
= hello_world "Hello from within a block!" do
|
29
|
+
= hello_world "And another one!"
|
30
|
+
}
|
31
|
+
|
32
|
+
assert_html '<p>Hello Ruby! Hello from within a block! And another one! Hello from within a block! Hello Ruby!</p>', source
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_output_block_with_arguments
|
36
|
+
source = %q{
|
37
|
+
p
|
38
|
+
= define_macro :person do |first_name, last_name|
|
39
|
+
.first_name = first_name
|
40
|
+
.last_name = last_name
|
41
|
+
== call_macro :person, 'John', 'Doe'
|
42
|
+
== call_macro :person, 'Max', 'Mustermann'
|
43
|
+
}
|
44
|
+
|
45
|
+
assert_html '<p><div class="first_name">John</div><div class="last_name">Doe</div><div class="first_name">Max</div><div class="last_name">Mustermann</div></p>', source
|
46
|
+
end
|
47
|
+
|
48
|
+
|
24
49
|
def test_render_with_control_code_loop
|
25
50
|
source = %q{
|
26
51
|
p
|
@@ -30,4 +55,14 @@ p
|
|
30
55
|
|
31
56
|
assert_html '<p>Hey!Hey!Hey!</p>', source
|
32
57
|
end
|
58
|
+
|
59
|
+
def test_captured_code_block_with_conditional
|
60
|
+
source = %q{
|
61
|
+
= hello_world "Hello Ruby!" do
|
62
|
+
- if true
|
63
|
+
| Hello from within a block!
|
64
|
+
}
|
65
|
+
|
66
|
+
assert_html 'Hello Ruby! Hello from within a block! Hello Ruby!', source
|
67
|
+
end
|
33
68
|
end
|
@@ -47,7 +47,7 @@ p = "<strong>Hello World\\n, meet \\"Slim\\"</strong>."
|
|
47
47
|
|
48
48
|
def test_render_with_global_html_safe_false
|
49
49
|
String.send(:define_method, :html_safe?) { false }
|
50
|
-
|
50
|
+
Temple::Filters::EscapeHTML.default_options[:use_html_safe] = false
|
51
51
|
|
52
52
|
source = %q{
|
53
53
|
p = "<strong>Hello World\\n, meet \\"Slim\\"</strong>."
|
@@ -58,7 +58,7 @@ p = "<strong>Hello World\\n, meet \\"Slim\\"</strong>."
|
|
58
58
|
|
59
59
|
def test_render_with_global_html_safe_true
|
60
60
|
String.send(:define_method, :html_safe?) { true }
|
61
|
-
|
61
|
+
Temple::Filters::EscapeHTML.default_options[:use_html_safe] = true
|
62
62
|
|
63
63
|
source = %q{
|
64
64
|
p = "<strong>Hello World\\n, meet \\"Slim\\"</strong>."
|