slim 1.3.2 → 1.3.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/.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