slim 1.3.2 → 1.3.3

Sign up to get free protection for your applications and to get access to all the features.
data/.yardopts CHANGED
@@ -1,2 +1,3 @@
1
- --title Slim
2
- --files CHANGES,LICENSE
1
+ --markup-provider redcarpet
2
+ --markup markdown
3
+ - README.md CHANGES LICENSE test/literate/TESTS.md
data/CHANGES CHANGED
@@ -1,3 +1,15 @@
1
+ 1.3.3
2
+
3
+ * Attribute handling made consistent:
4
+ - Splat attributes, static and dynamic attributes are now all handled the same
5
+ - Merged attributes are removed if empty (in the default configuration this is only "class")
6
+ - Dynamic attributes which are not merged are checked for false or nil and removed if this is the case
7
+ - Dynamic attributes which are not merged are checked for true and generated as attribute-name="attribute-name"
8
+ * Rename class BooleanAttributes to CodeAttributes
9
+ * Add literate test suite (still incomplete), run with `rake test:literate`
10
+ * Remove UTF BOM when parsing Slim code
11
+ * Fixed issue #303
12
+
1
13
  1.3.2
2
14
 
3
15
  * Fix boolean attributes #299
data/README.md CHANGED
@@ -119,9 +119,9 @@ You can also embed html in the text line
119
119
  - articles.each do |a|
120
120
  | <tr><td>#{a.name}</td><td>#{a.description}</td></tr>
121
121
 
122
- ### Text with trailing space `'`
122
+ ### Text with trailing white space `'`
123
123
 
124
- The single quote tells Slim to copy the line (similar to `|`), but makes sure that a single trailing space is appended.
124
+ The single quote tells Slim to copy the line (similar to `|`), but makes sure that a single trailing white space is appended.
125
125
 
126
126
  ### Inline html `<` (HTML style)
127
127
 
@@ -148,7 +148,7 @@ If your ruby code needs to use multiple lines, append a backslash `\` at the end
148
148
  - if articles.empty?
149
149
  | No inventory
150
150
 
151
- ### Dynamic output `=`
151
+ ### Output `=`
152
152
 
153
153
  The equal sign tells Slim it's a Ruby call that produces output to add to the buffer. If your ruby code needs to use multiple lines, append a backslash `\` at the end of the lines, for example:
154
154
 
@@ -158,7 +158,7 @@ The equal sign tells Slim it's a Ruby call that produces output to add to the bu
158
158
 
159
159
  ### Output with trailing white space `='`
160
160
 
161
- Same as the single equal sign (`=`), except that it adds a trailing whitespace.
161
+ Same as the single equal sign (`=`), except that it adds a trailing white space.
162
162
 
163
163
  ### Output without HTML escaping `==`
164
164
 
@@ -166,7 +166,7 @@ Same as the single equal sign (`=`), but does not go through the `escape_html` m
166
166
 
167
167
  ### Output without HTML escaping and trailing ws `=='`
168
168
 
169
- Same as the double equal sign (`==`), except that it adds a trailing whitespace.
169
+ Same as the double equal sign (`==`), except that it adds a trailing white space.
170
170
 
171
171
  ### Code comment `/`
172
172
 
@@ -321,7 +321,6 @@ you can use the characters `{...}`, `(...)`, `[...]` to wrap the attributes.
321
321
  h1(id="logo") = page_logo
322
322
  h2[id="tagline" class="small tagline"] = page_tagline
323
323
 
324
-
325
324
  If you wrap the attributes, you can spread them across multiple lines:
326
325
 
327
326
  h2[id="tagline"
@@ -337,7 +336,7 @@ You can use text interpolation in the quoted attributes:
337
336
 
338
337
  a href="http://#{url}" Goto the #{url}
339
338
 
340
- The attribute value will be escaped by default. Use == if you want to disable escaping in the attribute.
339
+ The attribute value will be escaped if the option `:escape_quoted_attrs` is set. Use == if you want to disable escaping in the attribute.
341
340
 
342
341
  a href=="&amp;"
343
342
 
@@ -370,17 +369,44 @@ as booleans. If you use the attribut wrapper you can omit the attribute assigmen
370
369
  input type="text" disabled=false
371
370
  input type="text" disabled=nil
372
371
 
372
+ #### Attribute merging
373
+
374
+ You can configure attributes to be merged if multiple are given (See option `:attr_delimiter`). In the default configuration
375
+ this is done for class attributes with the white space as delimiter.
376
+
377
+ a.menu class="highlight" href="http://slim-lang.com/" Slim-lang.com
378
+
379
+ This renders as
380
+
381
+ <a class="menu highlight" href="http://slim-lang.com/">Slim-lang.com</a>
382
+
383
+ You can also use an `Array` as attribute value and the array elements will be merged using the delimiter.
384
+
385
+ a class=["menu","highlight"]
386
+ a class=:menu,:highlight
387
+
373
388
  #### Splat attributes `*`
374
389
 
375
390
  The splat shortcut allows you turn a hash in to attribute/value pairs
376
391
 
377
392
  .card*{'data-url'=>place_path(place), 'data-id'=>place.id} = place.name
393
+
394
+ renders as
395
+
396
+ <div class="card" data-id="1234" data-url="/place/1234">Slim's house</div>
397
+
398
+ You can also use methods or instance variables which return a hash as shown here:
399
+
378
400
  .card *method_which_returns_hash = place.name
379
401
  .card *@hash_instance_variable = place.name
380
402
 
403
+ The hash attributes which support attribute merging (see Slim option `:attr_delimiter`) can be given as an `Array`
404
+
405
+ .first *{:class => [:second, :third]} Text
406
+
381
407
  renders as
382
408
 
383
- <div class="card" data-id="1234" data-url="/place/1234">Slim's house</div>
409
+ div class="first second third"
384
410
 
385
411
  #### ID shortcut `#` and class shortcut `.`
386
412
 
@@ -408,7 +434,23 @@ This is the same as
408
434
 
409
435
  You can define custom shortcuts (Similar to `#` for id and `.` for class).
410
436
 
411
- In this example we add `@` to create a shortcut for the role attribute.
437
+ In this example we add `&` to create a shortcut for the input elements with type attribute.
438
+
439
+ Slim::Engine.set_default_options :shortcut => {'&' => 'input type', '#' => 'id', '.' => 'class'}
440
+
441
+ We can use it in Slim code like this
442
+
443
+ &text name="user"
444
+ &password name="pw"
445
+ &submit
446
+
447
+ which renders to
448
+
449
+ <input type="text" name="user" />
450
+ <input type="password" name="pw" />
451
+ <input type="submit" />
452
+
453
+ In another example we add `@` to create a shortcut for the role attribute.
412
454
 
413
455
  Slim::Engine.set_default_options :shortcut => {'@' => 'role', '#' => 'id', '.' => 'class'}
414
456
 
@@ -528,7 +570,7 @@ There are a lot of them but the good thing is, that Slim checks the configuratio
528
570
  <thead style="font-weight:bold"><tr><td>Type</td><td>Name</td><td>Default</td><td>Purpose</td></tr></thead>
529
571
  <tbody>
530
572
  <tr><td>String</td><td>:file</td><td>nil</td><td>Name of parsed file, set automatically by Slim::Template</td></tr>
531
- <tr><td>Integer</td><td>:tabsize</td><td>4</td><td>Number of whitespaces per tab (used by the parser)</td></tr>
573
+ <tr><td>Integer</td><td>:tabsize</td><td>4</td><td>Number of white spaces per tab (used by the parser)</td></tr>
532
574
  <tr><td>String</td><td>:encoding</td><td>"utf-8"</td><td>Set encoding of template</td></tr>
533
575
  <tr><td>String</td><td>:default_tag</td><td>"div"</td><td>Default tag to be used if tag name is omitted</td></tr>
534
576
  <tr><td>Hash</td><td>:shortcut</td><td>\{'.' => 'class', '#' => 'id'}</td><td>Attribute shortcuts</td></tr>
@@ -792,6 +834,7 @@ There are plugins for various text editors (including the most important ones -
792
834
  * [Emacs](https://github.com/minad/emacs-slim)
793
835
  * [Textmate / Sublime Text](https://github.com/fredwu/ruby-slim-tmbundle)
794
836
  * [Espresso text editor](https://github.com/CiiDub/Slim-Sugar)
837
+ * [Coda](https://github.com/nwalton3/Coda-2-Slim.mode)
795
838
 
796
839
  ### Template Converters (HAML, ERB, ...)
797
840
 
@@ -875,6 +918,8 @@ overhead added by the Temple framework compared to ERB.
875
918
  Slim provides an extensive test-suite based on minitest. You can run the tests
876
919
  with 'rake test' and the rails integration tests with 'rake test:rails'.
877
920
 
921
+ We are currently experimenting with human-readable literate tests which are written as markdown files: {file:test/literate/TESTS.md TESTS.md}
922
+
878
923
  Travis-CI is used for continous integration testing: {http://travis-ci.org/#!/stonean/slim}
879
924
 
880
925
  Slim is working well on all major Ruby implementations:
@@ -934,6 +979,7 @@ Syntax highlighting:
934
979
  * [Emacs](https://github.com/minad/emacs-slim)
935
980
  * [Textmate / Sublime Text](https://github.com/fredwu/ruby-slim-tmbundle)
936
981
  * [Espresso text editor](https://github.com/CiiDub/Slim-Sugar)
982
+ * [Coda](https://github.com/nwalton3/Coda-2-Slim.mode)
937
983
 
938
984
  Template Converters (HAML, ERB, ...):
939
985
 
data/Rakefile CHANGED
@@ -14,7 +14,7 @@ end
14
14
  task 'test' => %w(test:core_and_plugins)
15
15
 
16
16
  namespace 'test' do
17
- task 'core_and_plugins' => %w(core logic_less translator)
17
+ task 'core_and_plugins' => %w(core literate logic_less translator)
18
18
 
19
19
  Rake::TestTask.new('core') do |t|
20
20
  t.libs << 'lib' << 'test/core'
@@ -22,6 +22,12 @@ namespace 'test' do
22
22
  t.verbose = true
23
23
  end
24
24
 
25
+ Rake::TestTask.new('literate') do |t|
26
+ t.libs << 'lib' << 'test/literate'
27
+ t.test_files = FileList['test/literate/run.rb']
28
+ t.verbose = true
29
+ end
30
+
25
31
  Rake::TestTask.new('logic_less') do |t|
26
32
  t.libs << 'lib' << 'test/core'
27
33
  t.test_files = FileList['test/logic_less/test_*.rb']
data/lib/slim.rb CHANGED
@@ -6,7 +6,7 @@ require 'slim/embedded_engine'
6
6
  require 'slim/interpolation'
7
7
  require 'slim/control_structures'
8
8
  require 'slim/splat_attributes'
9
- require 'slim/boolean_attributes'
9
+ require 'slim/code_attributes'
10
10
  require 'slim/engine'
11
11
  require 'slim/template'
12
12
  require 'slim/version'
@@ -0,0 +1,67 @@
1
+ module Slim
2
+ # @api private
3
+ class CodeAttributes < Filter
4
+ define_options :attr_delimiter
5
+
6
+ # Handle attributes expression `[:html, :attrs, *attrs]`
7
+ #
8
+ # @param [Array] attrs Array of temple expressions
9
+ # @return [Array] Compiled temple expression
10
+ def on_html_attrs(*attrs)
11
+ [:multi, *attrs.map {|a| compile(a) }]
12
+ end
13
+
14
+ # Handle attribute expression `[:slim, :attr, escape, code]`
15
+ #
16
+ # @param [String] name Attribute name
17
+ # @param [Array] value Value expression
18
+ # @return [Array] Compiled temple expression
19
+ def on_html_attr(name, value)
20
+ unless value[0] == :slim && value[1] == :attrvalue && !options[:attr_delimiter][name]
21
+ # We perform merging on the attribute
22
+ @attr = name
23
+ return super
24
+ end
25
+
26
+ # We handle the attribute as a boolean attribute
27
+ escape, code = value[2], value[3]
28
+ case code
29
+ when 'true'
30
+ [:html, :attr, name, [:static, name]]
31
+ when 'false', 'nil'
32
+ [:multi]
33
+ else
34
+ tmp = unique_name
35
+ [:multi,
36
+ [:code, "#{tmp} = #{code}"],
37
+ [:case, tmp,
38
+ ['true', [:html, :attr, name, [:static, name]]],
39
+ ['false, nil', [:multi]],
40
+ [:else, [:html, :attr, name, [:escape, escape, [:dynamic, tmp]]]]]]
41
+ end
42
+ end
43
+
44
+ # Handle attribute expression `[:slim, :attrvalue, escape, code]`
45
+ #
46
+ # @param [Boolean] escape Escape html
47
+ # @param [String] code Ruby code
48
+ # @return [Array] Compiled temple expression
49
+ def on_slim_attrvalue(escape, code)
50
+ # We perform attribute merging on Array values
51
+ if delimiter = options[:attr_delimiter][@attr]
52
+ tmp = unique_name
53
+ [:multi,
54
+ [:code, "#{tmp} = #{code}"],
55
+ [:if, "Array === #{tmp}",
56
+ [:multi,
57
+ [:code, "#{tmp}.flatten!"],
58
+ [:code, "#{tmp}.map!(&:to_s)"],
59
+ [:code, "#{tmp}.reject!(&:empty?)"],
60
+ [:escape, escape, [:dynamic, "#{tmp}.join(#{delimiter.inspect})"]]],
61
+ [:escape, escape, [:dynamic, tmp]]]]
62
+ else
63
+ [:escape, escape, [:dynamic, code]]
64
+ end
65
+ end
66
+ end
67
+ end
data/lib/slim/engine.rb CHANGED
@@ -23,7 +23,8 @@ module Slim
23
23
  use Slim::SplatAttributes, :attr_delimiter, :attr_wrapper, :sort_attrs, :default_tag
24
24
  html :AttributeSorter, :sort_attrs
25
25
  html :AttributeMerger, :attr_delimiter
26
- use Slim::BooleanAttributes, :attr_delimiter
26
+ use Slim::CodeAttributes, :attr_delimiter
27
+ use(:AttributeRemover) { Temple::HTML::AttributeRemover.new(:remove_empty_attrs => options[:attr_delimiter].keys) }
27
28
  html :Pretty, :format, :attr_wrapper, :pretty, :indent
28
29
  filter :Escapable, :use_html_safe, :disable_escape
29
30
  filter :ControlFlow
data/lib/slim/parser.rb CHANGED
@@ -55,14 +55,7 @@ module Slim
55
55
  # @param [String] str Slim code
56
56
  # @return [Array] Temple expression representing the code]]
57
57
  def call(str)
58
- # Set string encoding if option is set
59
- if options[:encoding] && str.respond_to?(:encoding)
60
- old_enc = str.encoding
61
- str = str.dup if str.frozen?
62
- str.force_encoding(options[:encoding])
63
- # Fall back to old encoding if new encoding is invalid
64
- str.force_encoding(old_enc) unless str.valid_encoding?
65
- end
58
+ str = remove_bom(set_encoding(str))
66
59
 
67
60
  result = [:multi]
68
61
  reset(str.split(/\r?\n/), [result])
@@ -86,6 +79,31 @@ module Slim
86
79
  QUOTED_ATTR_REGEX = /#{ATTR_NAME}=(=?)("|')/
87
80
  CODE_ATTR_REGEX = /#{ATTR_NAME}=(=?)/
88
81
 
82
+ # Set string encoding if option is set
83
+ def set_encoding(s)
84
+ if options[:encoding] && s.respond_to?(:encoding)
85
+ old_enc = s.encoding
86
+ s = s.dup if s.frozen?
87
+ s.force_encoding(options[:encoding])
88
+ # Fall back to old encoding if new encoding is invalid
89
+ s.force_encoding(old_enc) unless s.valid_encoding?
90
+ end
91
+ s
92
+ end
93
+
94
+ # Remove unicode byte order mark from string
95
+ def remove_bom(s)
96
+ if s.respond_to?(:encoding)
97
+ if s.encoding.name =~ /^UTF-(8|16|32)(BE|LE)?/
98
+ s.gsub(Regexp.new("\\A\uFEFF".encode(s.encoding.name)), '')
99
+ else
100
+ s
101
+ end
102
+ else
103
+ s.gsub(/\A\xEF\xBB\xBF/, '')
104
+ end
105
+ end
106
+
89
107
  def reset(lines = nil, stacks = nil)
90
108
  # Since you can indent however you like in Slim, we need to keep a list
91
109
  # of how deeply indented you are. For instance, in a template like this:
@@ -347,7 +365,7 @@ module Slim
347
365
  end
348
366
 
349
367
  if delimiter
350
- boolean_attr_regex = /#{ATTR_NAME}(?=(\s|#{Regexp.escape delimiter}))/
368
+ boolean_attr_regex = /#{ATTR_NAME}(?=(\s|#{Regexp.escape delimiter}|\Z))/
351
369
  end_regex = /\A\s*#{Regexp.escape delimiter}/
352
370
  end
353
371
 
@@ -79,30 +79,31 @@ module Slim
79
79
  end
80
80
  end
81
81
 
82
- merger << [:block, "#{hash}.each do |#{name},#{value}|",
82
+ merger << [:block, "#{hash}.keys.each do |#{name}|",
83
83
  [:multi,
84
- [:code, "#{value}.flatten!"],
85
- [:if, "#{value}.size > 1 && !#{@attr_delimiter}[#{name}]",
86
- [:code, %{raise("Multiple #\{#{name}\} attributes specified")}]],
87
- [:code, "#{value}.compact!"],
88
- [:case, "#{value}.size",
89
- ['0',
90
- [:code, "#{hash}[#{name}] = nil"]],
91
- ['1',
84
+ [:code, "#{value} = #{hash}[#{name}]"],
85
+ [:if, "#{@attr_delimiter}[#{name}]",
86
+ [:multi,
87
+ [:code, "#{value}.flatten!"],
88
+ [:code, "#{value}.map!(&:to_s)"],
89
+ [:code, "#{value}.reject!(&:empty?)"],
90
+ [:if, "#{value}.empty?",
91
+ [:code, "#{hash}.delete(#{name})"],
92
+ [:code, "#{hash}[#{name}] = #{value}.join(#{@attr_delimiter}[#{name}].to_s)"]]],
93
+ [:multi,
94
+ [:if, "#{value}.size > 1",
95
+ [:code, %{raise("Multiple #\{#{name}\} attributes specified")}]],
92
96
  [:case, "#{value}.first",
93
97
  ['true', [:code, "#{hash}[#{name}] = #{name}"]],
94
- ['false, nil', [:code, "#{hash}[#{name}] = nil"]],
95
- [:else, [:code, "#{hash}[#{name}] = #{value}.first"]]]],
96
- [:else,
97
- [:code, "#{hash}[#{name}] = #{value}.join(#{@attr_delimiter}[#{name}].to_s)"]]]]]
98
+ ['false, nil', [:code, "#{hash}.delete(#{name})"]],
99
+ [:else, [:code, "#{hash}[#{name}] = #{value}.first"]]]]]]]
98
100
 
99
- attr = [:if, value,
100
- [:multi,
101
- [:static, ' '],
102
- [:dynamic, name],
103
- [:static, "=#{options[:attr_wrapper]}"],
104
- [:escape, true, [:dynamic, value]],
105
- [:static, options[:attr_wrapper]]]]
101
+ attr = [:multi,
102
+ [:static, ' '],
103
+ [:dynamic, name],
104
+ [:static, "=#{options[:attr_wrapper]}"],
105
+ [:escape, true, [:dynamic, value]],
106
+ [:static, options[:attr_wrapper]]]
106
107
  enumerator = options[:sort_attrs] ? "#{hash}.sort_by {|#{name},#{value}| #{name} }" : hash
107
108
  formatter = [:block, "#{enumerator}.each do |#{name},#{value}|", attr]
108
109
 
data/lib/slim/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module Slim
2
2
  # Slim version string
3
3
  # @api public
4
- VERSION = '1.3.2'
4
+ VERSION = '1.3.3'
5
5
  end
data/slim.gemspec CHANGED
@@ -11,15 +11,13 @@ Gem::Specification.new do |s|
11
11
  s.summary = 'Slim is a template language.'
12
12
  s.description = 'Slim is a template language whose goal is reduce the syntax to the essential parts without becoming cryptic.'
13
13
  s.homepage = 'http://slim-lang.com/'
14
- s.extra_rdoc_files = %w(README.md LICENSE CHANGES)
15
- s.rdoc_options = %w(--charset=UTF-8)
16
14
  s.rubyforge_project = s.name
17
15
 
18
16
  s.files = `git ls-files`.split("\n")
19
17
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
18
  s.require_paths = %w(lib)
21
19
 
22
- s.add_runtime_dependency('temple', ['~> 0.5.0'])
20
+ s.add_runtime_dependency('temple', ['~> 0.5.5'])
23
21
  s.add_runtime_dependency('tilt', ['~> 1.3.3'])
24
22
 
25
23
  s.add_development_dependency('rake', ['>= 0.8.7'])
@@ -12,4 +12,10 @@ class TestSlimEncoding < TestSlim
12
12
  result = "\xFF\xFF"
13
13
  assert_html result, source
14
14
  end
15
+
16
+ def test_bom
17
+ source = "\xEF\xBB\xBFh1 Hello World!"
18
+ result = '<h1>Hello World!</h1>'
19
+ assert_html result, source
20
+ end
15
21
  end
@@ -99,15 +99,15 @@ option(selected class="clazz") Text
99
99
  assert_html '<option class="clazz" selected="selected">Text</option><option class="clazz" selected="selected">Text</option>', source
100
100
  end
101
101
 
102
- def test_array_attribute
102
+ def test_array_attribute_merging
103
103
  source = %{
104
- .alpha class="beta" class=[:gamma, nil, :delta, [true, false]]
104
+ .alpha class="beta" class=[[""], :gamma, nil, :delta, [true, false]]
105
+ .alpha class=:beta,:gamma
105
106
  }
106
107
 
107
- assert_html '<div class="alpha beta gamma delta true false"></div>', source
108
+ assert_html '<div class="alpha beta gamma delta true false"></div><div class="alpha beta gamma"></div>', source
108
109
  end
109
110
 
110
-
111
111
  def test_shortcut_splat
112
112
  source = %q{
113
113
  *hash This is my title
@@ -213,6 +213,22 @@ a class=true
213
213
  a class=false
214
214
  }
215
215
 
216
- assert_html '<a class="true false"></a><a class="false true"></a><a class="class"></a><a></a>', source
216
+ assert_html '<a class="true false"></a><a class="false true"></a><a class="true"></a><a class="false"></a>', source
217
+ end
218
+
219
+ def test_static_empty_attribute
220
+ source = %q{
221
+ p(id="marvin" name="" class="" data-info="Illudium Q-36")= output_number
222
+ }
223
+
224
+ assert_html '<p data-info="Illudium Q-36" id="marvin" name="">1337</p>', source
225
+ end
226
+
227
+ def test_dynamic_empty_attribute
228
+ source = %q{
229
+ p(id="marvin" class=nil nonempty=("".to_s) data-info="Illudium Q-36")= output_number
230
+ }
231
+
232
+ assert_html '<p data-info="Illudium Q-36" id="marvin" nonempty="">1337</p>', source
217
233
  end
218
234
  end