slim 0.7.0 → 0.7.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -10,7 +10,7 @@ module Slim
10
10
  #
11
11
  # @api private
12
12
  class EndInserter < Filter
13
- ELSE_REGEX = /^(else|elsif|when|end)\b/
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 prev_indent
26
- # Two control code in a row. If this one is *not*
27
- # an else block, we should close the previous one.
28
- append_end(result) if exp[2] !~ ELSE_REGEX
29
- prev_indent = exp[2].match(END_REGEX).nil?
30
- else
31
- # Indent if the control code contains something.
32
- prev_indent = !empty_exp?(exp[3])
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.
@@ -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, :use_html_safe
9
- #use Slim::Debugger
10
- use Temple::HTML::Fast, :format, :attr_wrapper => '"', :format => :html5
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
@@ -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
- include Temple::Utils
4
+ class Filter < Temple::Filter
5
+ temple_dispatch :slim
6
6
 
7
- DEFAULT_OPTIONS = {}
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 on_tag(name, attrs, content)
32
- [:slim, :tag, name, attrs, compile(content)]
11
+ def on_slim_output(code, escape, content)
12
+ [:slim, :output, code, escape, compile!(content)]
33
13
  end
34
14
 
35
- def on_multi(*exps)
36
- [:multi, *exps.map { |exp| compile(exp) }]
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
@@ -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
- attr_reader :options
27
+ default_options[:tabsize] = 4
26
28
 
27
29
  def initialize(options = {})
28
- @options = options
29
- @tab = ' ' * (options[:tabsize] || 4)
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 ned
76
- line.chop!
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 potential code block.
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
- if line[1] == ?=
189
- broken_line = line[2..-1].strip
190
- stacks.last << [:slim, :output, false, broken_line, block]
191
- elsif line[0] == ?=
192
- broken_line = line[1..-1].strip
193
- stacks.last << [:slim, :output, true, broken_line, block]
194
- else
195
- broken_line = line[1..-1].strip
196
- stacks.last << [:slim, :control, broken_line, block]
197
- end
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 attribute delimiter #{delimiter}", orig_line, lineno, orig_line.size - line.size
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, @options[:file], *args)
367
+ raise SyntaxError.new(message, options[:file], *args)
366
368
  end
367
369
  end
368
370
  end
@@ -1,37 +1,8 @@
1
1
  module Slim
2
2
  # Tilt template implementation for Slim
3
3
  # @api public
4
- class Template < Tilt::Template
5
- # Prepare Slim template
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
@@ -1,3 +1,3 @@
1
1
  module Slim
2
- VERSION = "0.7.0"
2
+ VERSION = "0.7.1"
3
3
  end
@@ -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<escape_utils>, [">= 0.1.9"]) unless RUBY_PLATFORM == "java"
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"])
@@ -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
- Slim::Filter::DEFAULT_OPTIONS.delete(:use_html_safe)
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, Slim::Template.new(options[:file], options) { source }.render(@env)
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
- Slim::Template.new(options[:file], options) { source }.render(@env)
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
- Slim::Template.new(options[:file], options) { source }.render(@env)
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
- Slim::Template.new(options[:file], options) { source }.render(@env)
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! Hello Ruby!</p>', source
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
- Slim::Filter::DEFAULT_OPTIONS[:use_html_safe] = false
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
- Slim::Filter::DEFAULT_OPTIONS[:use_html_safe] = true
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>."