slim 0.9.2 → 0.9.3

Sign up to get free protection for your applications and to get access to all the features.
data/extra/test.slim ADDED
@@ -0,0 +1,49 @@
1
+ doctype html
2
+ html
3
+ head
4
+ title Slim Test
5
+ meta name="keywords" content="slim, syntax"
6
+
7
+ javascript:
8
+ $(function() {
9
+ alert('Hello World');
10
+ });
11
+
12
+ haml:
13
+ #someid.someclass{:this => 'test'} Content in haml
14
+
15
+ erb:
16
+ <%= some_method(@request) %>
17
+
18
+ body
19
+ / comment block
20
+ with multiple lines
21
+ This is another line
22
+ h1 = @page_title
23
+ p#notice.message
24
+ | Welcome to the the syntax test.
25
+ This file is to exercise the various markup.
26
+ This is another line
27
+ - unless @users.empty?
28
+ table
29
+ - for user in users do
30
+ tr
31
+ td.user id=some_ruby('ere', @rme) data-test="some text #{with @ruby}" = @post.name
32
+ - else
33
+ p There are no users.
34
+
35
+ / Single comment line
36
+ #content Hello #{@user.name}! Welcome to the test page!
37
+ Try out Slim!
38
+
39
+ = function_with_many_parameters(:a, @variable, :option => 1)
40
+
41
+ p.text
42
+ ' Another text block
43
+ with multiple lines
44
+
45
+ = link_to('Test', @site)
46
+
47
+ .text#footer
48
+ ' Footer text block
49
+ with multiple lines
data/lib/slim/command.rb CHANGED
@@ -52,10 +52,6 @@ module Slim
52
52
  @options[:pretty] = true
53
53
  end
54
54
 
55
- opts.on('-d', '--debug', :NONE, 'Debugging output') do
56
- @options[:debug] = true
57
- end
58
-
59
55
  opts.on_tail('-h', '--help', 'Show this message') do
60
56
  puts opts
61
57
  exit
@@ -91,14 +87,12 @@ module Slim
91
87
  :pretty => @options[:pretty],
92
88
  :sections => @options[:sections],
93
89
  :disable_capture => @options[:rails],
94
- :debug => @options[:debug],
95
90
  :generator => @options[:rails] ?
96
91
  Temple::Generators::RailsOutputBuffer :
97
92
  Temple::Generators::ArrayBuffer).call(@options[:input].read))
98
93
  else
99
94
  @options[:output].puts(Slim::Template.new(@options[:file],
100
95
  :pretty => @options[:pretty],
101
- :debug => @options[:debug],
102
96
  :sections => @options[:sections]) { @options[:input].read }.render)
103
97
  end
104
98
  end
data/lib/slim/compiler.rb CHANGED
@@ -2,7 +2,7 @@ module Slim
2
2
  # Compiles Slim expressions into Temple::HTML expressions.
3
3
  # @api private
4
4
  class Compiler < Filter
5
- set_default_options :auto_escape => true
5
+ set_default_options :bool_attrs => %w(selected)
6
6
 
7
7
  # Handle control expression `[:slim, :control, code, content]`
8
8
  #
@@ -11,16 +11,17 @@ module Slim
11
11
  # @return [Array] Compiled temple expression
12
12
  def on_slim_control(code, content)
13
13
  [:multi,
14
- [:block, code],
14
+ [:code, code],
15
15
  compile(content)]
16
16
  end
17
17
 
18
- # Handle comment expression `[:slim, :comment, content]`
18
+ # Handle conditional comment expression
19
+ # `[:slim, :conditional_comment, conditional, content]`
19
20
  #
20
21
  # @param [Array] content Temple expression
21
22
  # @return [Array] Compiled temple expression
22
- def on_slim_comment(content)
23
- [:html, :comment, compile(content)]
23
+ def on_slim_condcomment(condition, content)
24
+ [:multi, [:static, "<!--[#{condition}]>"], compile(content), [:static, '<![endif]-->']]
24
25
  end
25
26
 
26
27
  # Handle output expression `[:slim, :output, escape, code, content]`
@@ -31,42 +32,28 @@ module Slim
31
32
  # @return [Array] Compiled temple expression
32
33
  def on_slim_output(escape, code, content)
33
34
  if empty_exp?(content)
34
- [:multi, [:escape, escape && options[:auto_escape], [:dynamic, code]], content]
35
+ [:multi, [:escape, escape, [:dynamic, code]], content]
35
36
  else
36
- on_slim_output_block(escape, code, content)
37
- end
38
- end
39
-
40
- # Handle output expression `[:slim, :output, escape, code, content]`
41
- # if content is not empty.
42
- #
43
- # @param [Boolean] escape Escape html
44
- # @param [String] code Ruby code
45
- # @param [Array] content Temple expression
46
- # @return [Array] Compiled temple expression
47
- def on_slim_output_block(escape, code, content)
48
- tmp = tmp_var(:output)
49
-
50
- [:multi,
51
- # Capture the result of the code in a variable. We can't do
52
- # `[:dynamic, code]` because it's probably not a complete
53
- # expression (which is a requirement for Temple).
54
- [:block, "#{tmp} = #{code}"],
37
+ tmp = unique_name
55
38
 
56
- # Capture the content of a block in a separate buffer. This means
57
- # that `yield` will not output the content to the current buffer,
58
- # but rather return the output.
59
- #
60
- # The capturing can be disabled with the option :disable_capture.
61
- # Output code in the block writes directly to the output buffer then.
62
- # Rails handles this by replacing the output buffer for helpers (with_output_buffer - braindead!).
63
- options[:disable_capture] ? compile(content) : [:capture, tmp_var(:output), compile(content)],
39
+ [:multi,
40
+ # Capture the result of the code in a variable. We can't do
41
+ # `[:dynamic, code]` because it's probably not a complete
42
+ # expression (which is a requirement for Temple).
43
+ [:block, "#{tmp} = #{code}",
64
44
 
65
- # Close the block.
66
- [:block, 'end'],
45
+ # Capture the content of a block in a separate buffer. This means
46
+ # that `yield` will not output the content to the current buffer,
47
+ # but rather return the output.
48
+ #
49
+ # The capturing can be disabled with the option :disable_capture.
50
+ # Output code in the block writes directly to the output buffer then.
51
+ # Rails handles this by replacing the output buffer for helpers.
52
+ options[:disable_capture] ? compile(content) : [:capture, unique_name, compile(content)]],
67
53
 
68
- # Output the content.
69
- on_slim_output(escape, tmp, [:multi])]
54
+ # Output the content.
55
+ [:escape, escape, [:dynamic, tmp]]]
56
+ end
70
57
  end
71
58
 
72
59
  # Handle directive expression `[:slim, :directive, type, args]`
@@ -82,22 +69,24 @@ module Slim
82
69
  end
83
70
  end
84
71
 
85
- # Handle tag expression `[:slim, :tag, name, attrs, closed, content]`
86
- #
87
- # @param [String] name Tag name
88
- # @param [Array] attrs Attributes
89
- # @param [Array] content Temple expression
90
- # @return [Array] Compiled temple expression
91
- def on_slim_tag(name, attrs, closed, content)
92
- [:html, :tag, name, compile(attrs), closed, compile(content)]
93
- end
94
-
95
- # Handle tag attributes expression `[:slim, :attrs, *attrs]`
72
+ # Handle attribute expression `[:slim, :attr, escape, code]`
96
73
  #
97
- # @param [Array] attrs Attributes
74
+ # @param [Boolean] escape Escape html
75
+ # @param [String] code Ruby code
98
76
  # @return [Array] Compiled temple expression
99
- def on_slim_attrs(*attrs)
100
- [:html, :staticattrs, *attrs.map {|k, v| [k.to_s, compile(v)] }]
77
+ def on_slim_attr(name, escape, code)
78
+ if options[:bool_attrs].include?(name)
79
+ escape = false
80
+ value = [:dynamic, "(#{code}) ? #{name.inspect} : nil"]
81
+ elsif delimiter = options[:attr_delimiter][name]
82
+ tmp = unique_name
83
+ value = [:multi,
84
+ [:code, "#{tmp} = #{code}"],
85
+ [:dynamic, "#{tmp}.respond_to?(:join) ? #{tmp}.flatten.compact.join(#{delimiter.inspect}) : #{tmp}"]]
86
+ else
87
+ value = [:dynamic, code]
88
+ end
89
+ [:html, :attr, name, [:escape, escape, value]]
101
90
  end
102
91
  end
103
92
  end
@@ -7,96 +7,124 @@ module Slim
7
7
  class << self
8
8
  attr_reader :engines
9
9
 
10
+ # Register embedded engine
11
+ #
12
+ # @param [String] name Name of the engine
13
+ # @param [Class] klass Engine class
14
+ # @param option_filter List of options to pass to engine.
15
+ # Last argument can be default option hash.
10
16
  def register(name, klass, *option_filter)
11
17
  local_options = Hash === option_filter.last ? option_filter.pop : nil
12
18
  @engines[name.to_s] = [klass, option_filter, local_options]
13
19
  end
14
20
  end
15
21
 
22
+ def on_slim_embedded(name, body)
23
+ new_engine(name).on_slim_embedded(name, body)
24
+ end
25
+
26
+ protected
27
+
16
28
  def new_engine(name)
17
29
  name = name.to_s
18
30
  raise "Embedded engine #{name} is disabled" if (options[:enable_engines] && !options[:enable_engines].include?(name)) ||
19
31
  (options[:disable_engines] && options[:disable_engines].include?(name))
20
32
  engine, option_filter, local_options = self.class.engines[name] || raise("Embedded engine #{name} not found")
21
33
  filtered_options = Hash[*option_filter.select {|k| options.include?(k) }.map {|k| [k, options[k]] }.flatten]
22
- engine.new(Temple::Utils::ImmutableHash.new(local_options, filtered_options))
34
+ engine.new(Temple::ImmutableHash.new(local_options, filtered_options))
23
35
  end
24
36
 
25
- def on_slim_embedded(name, *body)
26
- new_engine(name).on_slim_embedded(name, *body)
37
+ def collect_text(body)
38
+ body[1..-1].inject('') do |text, exp|
39
+ exp[0] == :slim && exp[1] == :interpolate ? (text << exp[2]) : text
40
+ end
27
41
  end
28
42
 
29
- def collect_text(body)
30
- body.inject('') do |text, exp|
31
- text << exp[2] if exp[0] == :slim && exp[1] == :text
32
- text
43
+ def collect_newlines(body)
44
+ body[1..-1].inject([:multi]) do |multi, exp|
45
+ exp[0] == :newline ? (multi << exp) : multi
33
46
  end
34
47
  end
35
48
 
49
+ # Basic tilt engine
36
50
  class TiltEngine < EmbeddedEngine
37
- def on_slim_embedded(engine, *body)
38
- text = collect_text(body)
51
+ def on_slim_embedded(engine, body)
39
52
  engine = Tilt[engine] || raise("Tilt engine #{engine} is not available.")
40
- tilt_render(engine, text)
53
+ [:multi, render(engine, collect_text(body)), collect_newlines(body)]
41
54
  end
55
+ end
56
+
57
+ # Tilt-based static template (evaluated at compile-time)
58
+ class StaticTiltEngine < TiltEngine
59
+ protected
42
60
 
43
- def tilt_render(engine, text)
44
- # Static template
61
+ def render(engine, text)
45
62
  [:static, engine.new { text }.render]
46
63
  end
47
64
  end
48
65
 
49
- class SassEngine < TiltEngine
50
- def tilt_render(engine, text)
51
- text = engine.new(:style => (options[:pretty] ? :expanded : :compressed)) { text }.render
66
+ # Sass engine which supports :pretty option
67
+ class SassEngine < StaticTiltEngine
68
+ protected
69
+
70
+ def render(engine, text)
71
+ text = engine.new(:style => (options[:pretty] ? :expanded : :compressed), :cache => false) { text }.render
52
72
  text.chomp!
53
73
  [:static, options[:pretty] ? "\n#{text}\n" : text]
54
74
  end
55
75
  end
56
76
 
57
- class DynamicTiltEngine < TiltEngine
77
+ # Tilt-based engine which is fully dynamically evaluated during runtime (Slow and uncached)
78
+ class DynamicTiltEngine < StaticTiltEngine
79
+ protected
80
+
58
81
  # Code to collect local variables
59
82
  COLLECT_LOCALS = %q{eval('{' + local_variables.select {|v| v[0] != ?_ }.map {|v| ":#{v}=>#{v}" }.join(',') + '}')}
60
83
 
61
- def tilt_render(engine, text)
62
- # Fully dynamic evaluation of the template during runtime (Slow and uncached)
84
+ def render(engine, text)
63
85
  [:dynamic, "#{engine.name}.new { #{text.inspect} }.render(self, #{COLLECT_LOCALS})"]
64
86
  end
65
87
  end
66
88
 
67
- class PrecompiledTiltEngine < TiltEngine
68
- def tilt_render(engine, text)
69
- # Wrap precompiled code in proc, local variables from out the proc are accessible
89
+ # Tilt-based engine which is precompiled
90
+ class PrecompiledTiltEngine < StaticTiltEngine
91
+ protected
92
+
93
+ def render(engine, text)
70
94
  # WARNING: This is a bit of a hack. Tilt::Engine#precompiled is protected
71
- precompiled = engine.new { text }.send(:precompiled, {}).first
72
- [:dynamic, "proc { #{precompiled} }.call"]
95
+ [:dynamic, engine.new { text }.send(:precompiled, {}).first]
73
96
  end
74
97
  end
75
98
 
76
- class InterpolateTiltEngine < TiltEngine
77
- def tilt_render(engine, text)
78
- # Static template with interpolated ruby code
79
- [:slim, :text, engine.new { text }.render]
99
+ # Static template with interpolated ruby code
100
+ class InterpolateTiltEngine < StaticTiltEngine
101
+ protected
102
+
103
+ def render(engine, text)
104
+ [:slim, :interpolate, engine.new { text }.render]
80
105
  end
81
106
  end
82
107
 
108
+ # ERB engine (uses the Temple ERB implementation)
83
109
  class ERBEngine < EmbeddedEngine
84
- def on_slim_embedded(engine, *body)
85
- text = collect_text(body)
86
- Temple::ERB::Parser.new(:auto_escape => true).call(text)
110
+ def on_slim_embedded(engine, body)
111
+ Temple::ERB::Parser.new.call(collect_text(body))
87
112
  end
88
113
  end
89
114
 
115
+ # Tag wrapper engine
116
+ # Generates a html tag and wraps another engine (specified via :engine option)
90
117
  class TagEngine < EmbeddedEngine
91
- def on_slim_embedded(engine, *body)
92
- content = options[:engine] ? options[:engine].new(options).on_slim_embedded(engine, *body) : [:multi, *body]
93
- [:slim, :tag, options[:tag], [:slim, :attrs, *options[:attributes].map {|k, v| [k, [:static, v]] }], false, content]
118
+ def on_slim_embedded(engine, body)
119
+ content = options[:engine] ? options[:engine].new(options).on_slim_embedded(engine, body) : [:multi, body]
120
+ [:html, :tag, options[:tag], [:html, :attrs, *options[:attributes].map {|k, v| [:html, :attr, k, [:static, v]] }], content]
94
121
  end
95
122
  end
96
123
 
124
+ # Embeds ruby code
97
125
  class RubyEngine < EmbeddedEngine
98
- def on_slim_embedded(engine, *body)
99
- [:block, collect_text(body)]
126
+ def on_slim_embedded(engine, body)
127
+ [:code, "\n" + collect_text(body)]
100
128
  end
101
129
  end
102
130
 
@@ -107,10 +135,10 @@ module Slim
107
135
  register :creole, InterpolateTiltEngine
108
136
 
109
137
  # These engines are executed at compile time
110
- register :coffee, TagEngine, :tag => 'script', :attributes => { :type => 'text/javascript' }, :engine => TiltEngine
111
- register :less, TagEngine, :tag => 'style', :attributes => { :type => 'text/css' }, :engine => TiltEngine
112
- register :sass, TagEngine, :pretty, :tag => 'style', :attributes => { :type => 'text/css' }, :engine => SassEngine
113
- register :scss, TagEngine, :pretty, :tag => 'style', :attributes => { :type => 'text/css' }, :engine => SassEngine
138
+ register :coffee, TagEngine, :tag => :script, :attributes => { :type => 'text/javascript' }, :engine => StaticTiltEngine
139
+ register :less, TagEngine, :tag => :style, :attributes => { :type => 'text/css' }, :engine => StaticTiltEngine
140
+ register :sass, TagEngine, :pretty, :tag => :style, :attributes => { :type => 'text/css' }, :engine => SassEngine
141
+ register :scss, TagEngine, :pretty, :tag => :style, :attributes => { :type => 'text/css' }, :engine => SassEngine
114
142
 
115
143
  # These engines are precompiled, code is embedded
116
144
  register :erb, ERBEngine
@@ -124,8 +152,8 @@ module Slim
124
152
  register :markaby, DynamicTiltEngine
125
153
 
126
154
  # Embedded javascript/css
127
- register :javascript, TagEngine, :tag => 'script', :attributes => { :type => 'text/javascript' }
128
- register :css, TagEngine, :tag => 'style', :attributes => { :type => 'text/css' }
155
+ register :javascript, TagEngine, :tag => :script, :attributes => { :type => 'text/javascript' }
156
+ register :css, TagEngine, :tag => :style, :attributes => { :type => 'text/css' }
129
157
 
130
158
  # Embedded ruby code
131
159
  register :ruby, RubyEngine
@@ -14,6 +14,8 @@ module Slim
14
14
  END_REGEX = /^end\b/
15
15
 
16
16
  # Handle multi expression `[:multi, *exps]`
17
+ #
18
+ # @return [Array] Corrected Temple expression with ends inserted
17
19
  def on_multi(*exps)
18
20
  result = [:multi]
19
21
  # This variable is true if the previous line was
@@ -46,12 +48,12 @@ module Slim
46
48
 
47
49
  private
48
50
 
49
- # Appends an end.
51
+ # Appends an end
50
52
  def append_end(result)
51
- result << [:block, 'end']
53
+ result << [:code, 'end']
52
54
  end
53
55
 
54
- # Checks if an expression is a Slim control code.
56
+ # Checks if an expression is a Slim control code
55
57
  def control?(exp)
56
58
  exp[0] == :slim && exp[1] == :control
57
59
  end
data/lib/slim/engine.rb CHANGED
@@ -11,7 +11,7 @@ module Slim
11
11
  set_default_options :pretty => false,
12
12
  :attr_wrapper => '"',
13
13
  :format => :html5,
14
- :id_delimiter => nil,
14
+ :attr_delimiter => {'class' => ' '},
15
15
  :generator => Temple::Generators::ArrayBuffer
16
16
 
17
17
  #
@@ -28,12 +28,12 @@ module Slim
28
28
  # String | :dictionary | "self" | Name of dictionary variable in sections mode
29
29
  # Symbol | :dictionary_access | :wrapped | Access mode of dictionary variable (:wrapped, :symbol, :string)
30
30
  # Boolean | :disable_capture | false (true in Rails) | Disable capturing in blocks (blocks write to the default buffer then)
31
- # Boolean | :auto_escape | true | Enable automatic escaping of strings
32
- # Boolean | :use_html_safe | false (true in Rails) | Use String#html_safe? from ActiveSupport (Works together with :auto_escape)
33
- # Boolean | :debug | false | Enable debug outputs (Temple internals)
31
+ # Boolean | :disable_escape | false | Disable automatic escaping of strings
32
+ # Boolean | :use_html_safe | false (true in Rails) | Use String#html_safe? from ActiveSupport (Works together with :disable_escape)
34
33
  # Symbol | :format | :html5 | HTML output format
35
34
  # String | :attr_wrapper | '"' | Character to wrap attributes in html (can be ' or ")
36
- # String | :id_delimiter | '_' | Joining character used if multiple html ids are supplied (e.g. #id1#id2)
35
+ # Hash | :attr_delimiter | {'class' => ' '} | Joining character used if multiple html attributes are supplied (e.g. id1_id2)
36
+ # String list | :bool_attrs | %w(selected) | List of boolean attributes
37
37
  # Boolean | :pretty | false | Pretty html indenting (This is slower!)
38
38
  # Class | :generator | ArrayBuffer/RailsOutputBuffer | Temple code generator (default generator generates array buffer)
39
39
  #
@@ -58,14 +58,12 @@ module Slim
58
58
  use Slim::Interpolation
59
59
  use Slim::Sections, :sections, :dictionary, :dictionary_access
60
60
  use Slim::EndInserter
61
- use Slim::Compiler, :disable_capture, :auto_escape
62
- filter :EscapeHTML, :use_html_safe
63
- filter :Debugger, :debug, :debug_prefix => 'After Slim'
64
- use Temple::HTML::Pretty, :format, :attr_wrapper, :id_delimiter, :pretty
61
+ use Slim::Compiler, :disable_capture, :attr_delimiter, :bool_attrs
62
+ use Temple::HTML::Pretty, :format, :attr_wrapper, :attr_delimiter, :pretty
63
+ filter :Escapable, :use_html_safe, :disable_escape
64
+ filter :ControlFlow
65
65
  filter :MultiFlattener
66
- filter :StaticMerger
67
66
  filter :DynamicInliner
68
- filter :Debugger, :debug, :debug_prefix => 'Optimized code'
69
67
  use(:Generator) {|exp| options[:generator].new(options).call(exp) }
70
68
  end
71
69
  end