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.
@@ -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>."