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/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
- # Dispatch on_slim_*
11
- temple_dispatch :slim
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
- def on_slim_comment(content)
18
- [:slim, :comment, compile(content)]
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
@@ -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
@@ -1,14 +1,14 @@
1
1
  module Slim
2
2
  # Perform interpolation of #{var_name} in the
3
- # expressions `[:slim, :text, string]`.
3
+ # expressions `[:slim, :interpolate, string]`.
4
4
  #
5
5
  # @api private
6
6
  class Interpolation < Filter
7
- # Handle text expression `[:slim, :text, string]`
7
+ # Handle interpolate expression `[:slim, :interpolate, string]`
8
8
  #
9
- # @param [String] string Static text
9
+ # @param [String] string Static interpolate
10
10
  # @return [Array] Compiled temple expression
11
- def on_slim_text(string)
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 << [:slim, :output, escape, escape ? code : code[1..-2], [:multi]]
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
- str.force_encoding(options[:encoding]) if options[:encoding] && str.respond_to?(:force_encoding)
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
- if !in_comment
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, :text, newline + (' ' * offset) + line]
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 ch == ?'
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
- line.slice!(0)
182
- [:slim, :comment, block]
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
- in_comment = ch == ?/
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
- if !in_comment && !line.strip.empty?
191
- block << [:slim, :text, line.sub(/^( )/, '')]
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 = [:slim, :embedded, $1]
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 = [:slim, :attrs]
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
- key = $1
313
+ name = $1
300
314
  line = $'
301
315
  if line =~ QUOTED_VALUE_REGEX
302
316
  # Value is quoted (static)
303
317
  line = $'
304
- attributes << [key, [:slim, :text, $1[1..-2]]]
318
+ attributes << [:html, :attr, name, [:slim, :interpolate, $1[1..-2]]]
305
319
  else
306
320
  # Value is ruby code
307
- line, value = parse_ruby_attribute(orig_line, line, lineno, delimiter)
308
- attributes << [key, [:slim, :output, true, value, [:multi]]]
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
- if !delimiter.empty?
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 = [:slim, :tag, tag, attributes, false, content]
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[4] = true
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, :text, line.sub(/^( )/, '')]
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 if !stack.empty?
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
- # A little helper for raising exceptions.
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
- [:block, "_slimdict = #{dictionary}"],
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 = tmp_var(:section)
47
+ tmp = unique_name
41
48
  [:multi,
42
- [:block, "#{tmp} = #{access name}"],
43
- [:block, "if !#{tmp} || #{tmp}.respond_to?(:empty) && #{tmp}.empty?"],
44
- compile(content),
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 = tmp_var(:dict), tmp_var(:dict)
56
+ tmp1, tmp2 = unique_name, unique_name
51
57
 
52
- [:multi,
53
- [:block, "if #{tmp1} = #{access name}"],
54
- [:block, "if #{tmp1} == true"],
55
- content,
56
- [:block, 'else'],
57
- # Wrap map in array because maps implement each
58
- [:block, "#{tmp1} = [#{tmp1}] if #{tmp1}.respond_to?(:has_key?) || !#{tmp1}.respond_to?(:map)"],
59
- [:block, "#{tmp2} = _slimdict"],
60
- [:block, "#{tmp1}.each do |_slimdict|"],
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, :register_as => :slim)
10
-
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
- RailsTemplate.set_default_options :generator => Temple::Generators::RailsOutputBuffer,
15
- :disable_capture => true
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
@@ -1,3 +1,5 @@
1
1
  module Slim
2
- VERSION = '0.9.2'
2
+ # Slim version string
3
+ # @api public
4
+ VERSION = '0.9.3'
3
5
  end
data/lib/slim/wrapper.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  module Slim
2
2
  # For logic-less mode, objects can be encased in the Wrapper class.
3
+ # @api private
3
4
  class Wrapper
4
5
  attr_reader :value, :parent
5
6
 
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 test_render_with_auto_escape_true
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 "&lt;p&gt;Hello&lt;&#47;p&gt;<p>World</p>", source
43
43
  end
44
44
 
45
- def test_render_with_auto_escape_false
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, :auto_escape => false
51
+ assert_html "<p>Hello</p><p>World</p>", source, :disable_escape => true
52
52
  end
53
53
  end