skim 0.8.6 → 0.9.0

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/History.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # master
2
2
 
3
+ # 0.9.0
4
+
5
+ * Now uses/requires slim 2.0.0
6
+ * Now requires Ruby 1.9.3+
7
+
3
8
  # 0.8.6
4
9
 
5
10
  * Restore compatibility with slim 1.2.2
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- Skim [ ![Build status](http://travis-ci.org/jfirebaugh/skim.png) ](http://travis-ci.org/jfirebaugh/skim)
1
+ Skim [ ![Build status](https://travis-ci.org/jfirebaugh/skim.png) ](https://travis-ci.org/jfirebaugh/skim)
2
2
  ====
3
3
 
4
4
  Take the fat out of your client-side templates with Skim. Skim is the [Slim](http://slim-lang.com/) templating engine
@@ -9,17 +9,23 @@ Sprockets-based asset pipeline.
9
9
 
10
10
  `gem install skim`, or add `skim` to your `Gemfile` in the `:assets` group:
11
11
 
12
- group :assets do
13
- gem 'skim'
14
- end
12
+ ```ruby
13
+ group :assets do
14
+ gem 'skim'
15
+ end
16
+ ```
15
17
 
16
18
  Create template files with the extension `.jst.skim`. For example, `test.jst.skim`:
17
19
 
18
- p Hello #{@world}!
20
+ ```jade
21
+ p Hello #{@world}!
22
+ ```
19
23
 
20
24
  In your JavaScript or CoffeeScript, render the result, passing a context object:
21
25
 
22
- $("body").html(JST["test"]({world: "World"}));
26
+ ```js
27
+ $("body").html(JST["test"]({world: "World"}));
28
+ ```
23
29
 
24
30
  Order up a skinny latte and enjoy!
25
31
 
@@ -66,20 +72,26 @@ escaping. In addition, the special `safe` method on the context object tells Ski
66
72
  being escaped. You can use this in conjunction with `escape` context method to selectively sanitize parts of the string.
67
73
  For example, given the template:
68
74
 
69
- = @linkTo(@project)
75
+ ```jade
76
+ = @linkTo(@project)
77
+ ```
70
78
 
71
79
  you could render it with the following context:
72
80
 
73
- JST["my_template"]
74
- project: { id: 4, name: "Crate & Barrel" }
75
- linkTo: (project) ->
76
- url = "/projects/#{project.id}"
77
- name = @escape project.name
78
- @safe "<a href='#{url}'>#{name}</a>"
81
+ ```coffeescript
82
+ JST["my_template"]
83
+ project: { id: 4, name: "Crate & Barrel" }
84
+ linkTo: (project) ->
85
+ url = "/projects/#{project.id}"
86
+ name = @escape project.name
87
+ @safe "<a href='#{url}'>#{name}</a>"
88
+ ```
79
89
 
80
90
  to produce:
81
91
 
82
- <a href='/projects/4'>Crate &amp; Barrel</a>
92
+ ```html
93
+ <a href='/projects/4'>Crate &amp; Barrel</a>
94
+ ```
83
95
 
84
96
  ## The Skim asset
85
97
 
@@ -89,11 +101,15 @@ and setting Skim's `:use_asset` option to true.
89
101
 
90
102
  In Rails, this can be done by adding the following to `application.js`:
91
103
 
92
- //= require skim
104
+ ```js
105
+ //= require skim
106
+ ```
93
107
 
94
108
  And the following in an initializer:
95
109
 
96
- Skim::Engine.default_options[:use_asset] = true
110
+ ```ruby
111
+ Skim::Engine.default_options[:use_asset] = true
112
+ ```
97
113
 
98
114
  # License (MIT)
99
115
 
@@ -2,8 +2,9 @@ require "temple"
2
2
  require "temple/coffee_script"
3
3
 
4
4
  require "slim"
5
- require "skim/compiler"
6
- require "skim/sections"
5
+ require "skim/controls"
6
+ require "skim/code_attributes"
7
+ require "skim/interpolation"
7
8
  require "skim/engine"
8
9
  require "skim/template"
9
10
  require "skim/version"
@@ -0,0 +1,66 @@
1
+ module Skim
2
+ class CodeAttributes < Slim::Filter
3
+ define_options :merge_attrs
4
+
5
+ # Handle attributes expression `[:html, :attrs, *attrs]`
6
+ #
7
+ # @param [Array] attrs Array of temple expressions
8
+ # @return [Array] Compiled temple expression
9
+ def on_html_attrs(*attrs)
10
+ [:multi, *attrs.map {|a| compile(a) }]
11
+ end
12
+
13
+ # Handle attribute expression `[:html, :attr, name, value]`
14
+ #
15
+ # @param [String] name Attribute name
16
+ # @param [Array] value Value expression
17
+ # @return [Array] Compiled temple expression
18
+ def on_html_attr(name, value)
19
+ if value[0] == :slim && value[1] == :attrvalue && !options[:merge_attrs][name]
20
+ # We handle the attribute as a boolean attribute
21
+ escape, code = value[2], value[3]
22
+ case code
23
+ when 'true'
24
+ [:html, :attr, name, [:static, name]]
25
+ when 'false', 'null'
26
+ [:multi]
27
+ else
28
+ tmp = unique_name
29
+ [:multi,
30
+ [:code, "#{tmp} = #{code}"],
31
+ [:case, tmp,
32
+ ['true', [:html, :attr, name, [:static, name]]],
33
+ ['false, null', [:multi]],
34
+ [:else, [:html, :attr, name, [:escape, escape, [:dynamic, tmp]]]]]]
35
+ end
36
+ else
37
+ # Attribute with merging
38
+ @attr = name
39
+ return super
40
+ end
41
+ end
42
+
43
+ # Handle attribute expression `[:slim, :attrvalue, escape, code]`
44
+ #
45
+ # @param [Boolean] escape Escape html
46
+ # @param [String] code Ruby code
47
+ # @return [Array] Compiled temple expression
48
+ def on_slim_attrvalue(escape, code)
49
+ # We perform attribute merging on Array values
50
+ if delimiter = options[:merge_attrs][@attr]
51
+ tmp = unique_name
52
+ [:multi,
53
+ [:code, "#{tmp} = #{code}"],
54
+ [:if, "@isArray(#{tmp})",
55
+ [:multi,
56
+ [:code, "#{tmp} = @flatten(#{tmp})"],
57
+ [:code, "#{tmp} = item.toString() for item in #{tmp} when item"],
58
+ [:code, "#{tmp} = item for item in #{tmp} when item.length > 0"],
59
+ [:escape, escape, [:dynamic, "#{tmp}.join(#{delimiter.inspect})"]]],
60
+ [:escape, escape, [:dynamic, tmp]]]]
61
+ else
62
+ [:escape, escape, [:dynamic, code]]
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,9 @@
1
+ module Skim
2
+ class Controls < Slim::Controls
3
+ def on_slim_control(code, content)
4
+ [:multi,
5
+ [:code, code],
6
+ [:indent, compile(content)]]
7
+ end
8
+ end
9
+ end
@@ -1,27 +1,38 @@
1
1
  module Skim
2
+ # Skim engine which transforms slim code to executable ruby code
3
+ # @api public
2
4
  class Engine < Temple::Engine
3
- set_default_options :pretty => false,
4
- :sort_attrs => true,
5
- :attr_wrapper => '"',
6
- :attr_delimiter => {'class' => ' '},
7
- :remove_empty_attrs => true,
8
- :generator => Temple::CoffeeScript::Generator,
9
- :default_tag => 'div',
10
- :use_asset => false
5
+ # This overwrites some Temple default options or sets default options for Slim specific filters.
6
+ # It is recommended to set the default settings only once in the code and avoid duplication. Only use
7
+ # `define_options` when you have to override some default settings.
8
+ define_options :pretty => false,
9
+ :sort_attrs => true,
10
+ :attr_quote => '"',
11
+ :merge_attrs => {'class' => ' '},
12
+ :encoding => 'utf-8',
13
+ :generator => Temple::CoffeeScript::Generator,
14
+ :default_tag => 'div',
15
+ :use_asset => false
11
16
 
12
- use Slim::Parser, :file, :tabsize, :encoding, :default_tag
13
- use Slim::EmbeddedEngine, :enable_engines, :disable_engines, :pretty
14
- use Slim::Interpolation
15
- use Skim::Sections, :sections
16
- use Skim::Compiler, :disable_capture, :attr_delimiter
17
- use Temple::CoffeeScript::AttributeMerger, :attr_delimiter
18
- use Temple::HTML::AttributeSorter
19
- use Temple::CoffeeScript::AttributeRemover
20
- use Temple::HTML::Fast, :format, :attr_wrapper
21
- use Temple::CoffeeScript::Filters::Escapable, :disable_escape
17
+ filter :Encoding, :encoding
18
+ filter :RemoveBOM
19
+ use Slim::Parser, :file, :tabsize, :shortcut, :default_tag
20
+ use Slim::Embedded, :enable_engines, :disable_engines, :pretty
21
+ use Skim::Interpolation
22
+ use Slim::Splat::Filter, :merge_attrs, :attr_quote, :sort_attrs, :default_tag, :hyphen_attrs
23
+ use Skim::Controls, :disable_capture
24
+ html :AttributeSorter, :sort_attrs
25
+ html :AttributeMerger, :merge_attrs
26
+ use Skim::CodeAttributes, :merge_attrs
27
+ use(:AttributeRemover) { Temple::CoffeeScript::AttributeRemover.new(:remove_empty_attrs => options[:merge_attrs].keys)}
28
+ html :Pretty, :format, :attr_quote, :pretty, :indent, :js_wrapper
29
+ use Temple::HTML::Fast, :format, :attr_quote
30
+ use Temple::CoffeeScript::Filters::Escapable, :use_html_safe, :disable_escape
22
31
  use Temple::CoffeeScript::Filters::ControlFlow
23
32
  filter :MultiFlattener
24
33
  use(:Optimizer) { Temple::Filters::StaticMerger.new }
25
- use(:Generator) { options[:generator].new(options) }
34
+ use :Generator do
35
+ options[:generator].new(options.to_hash.reject {|k,v| !options[:generator].default_options.valid_keys.include?(k) })
36
+ end
26
37
  end
27
38
  end
@@ -0,0 +1,53 @@
1
+ module Skim
2
+ # Perform interpolation of #{var_name} in the
3
+ # expressions `[:slim, :interpolate, string]`.
4
+ #
5
+ # @api private
6
+ class Interpolation < Slim::Filter
7
+ # Handle interpolate expression `[:slim, :interpolate, string]`
8
+ #
9
+ # @param [String] string Static interpolate
10
+ # @return [Array] Compiled temple expression
11
+ def on_slim_interpolate(string)
12
+ # Interpolate variables in text (#{variable}).
13
+ # Split the text into multiple dynamic and static parts.
14
+ block = [:multi]
15
+ begin
16
+ case string
17
+ when /\A\\#\{/
18
+ # Escaped interpolation
19
+ block << [:dynamic, "'\#{'"]
20
+ string = $'
21
+ when /\A#\{/
22
+ # Interpolation
23
+ string, code = parse_expression($')
24
+ escape = code !~ /\A\{.*\}\Z/
25
+ block << [:slim, :output, escape, escape ? code : code[1..-2], [:multi]]
26
+ when /\A([#\\]|[^#\\]*)/
27
+ # Static text
28
+ block << [:static, $&]
29
+ string = $'
30
+ end
31
+ end until string.empty?
32
+ block
33
+ end
34
+
35
+ protected
36
+
37
+ def parse_expression(string)
38
+ count, i = 1, 0
39
+ while i < string.size && count != 0
40
+ if string[i] == ?{
41
+ count += 1
42
+ elsif string[i] == ?}
43
+ count -= 1
44
+ end
45
+ i += 1
46
+ end
47
+
48
+ raise(Temple::FilterError, "Text interpolation: Expected closing }") if count != 0
49
+
50
+ return string[i..-1], string[0, i-1]
51
+ end
52
+ end
53
+ end
@@ -7,15 +7,18 @@ module Skim
7
7
  self.default_mime_type = "application/javascript"
8
8
 
9
9
  def coffee_script_src
10
- engine = self.class.build_engine({
10
+
11
+ engine = Engine.new(options.merge({
11
12
  :streaming => false, # Overwrite option: No streaming support in Tilt
12
13
  :file => eval_file,
13
- :indent => 2 }, options)
14
- <<SRC
14
+ :indent => 2
15
+ }))
16
+ src = engine.call(data)
17
+ <<-SRC
15
18
  #{self.class.skim_src unless engine.options[:use_asset]}
16
19
  return (context = {}) ->
17
20
  Skim.withContext.call {}, context, ->
18
- #{engine.call(data)}
21
+ #{src}
19
22
  SRC
20
23
  end
21
24
 
@@ -1,3 +1,3 @@
1
1
  module Skim
2
- VERSION = "0.8.6"
2
+ VERSION = "0.9.0"
3
3
  end
@@ -3,7 +3,7 @@ module Temple
3
3
  class AttributeMerger < Filter
4
4
  include Temple::HTML::Dispatcher
5
5
 
6
- default_options[:attr_delimiter] = {'id' => '_', 'class' => ' '}
6
+ define_options :merge_attrs => {'id' => '_', 'class' => ' '}
7
7
 
8
8
  def on_html_attrs(*attrs)
9
9
  names = []
@@ -12,7 +12,7 @@ module Temple
12
12
  attrs.each do |html, attr, name, value|
13
13
  raise(InvalidExpression, 'Attribute is not a html attr') if html != :html || attr != :attr
14
14
  name = name.to_s
15
- if delimiter = options[:attr_delimiter][name]
15
+ if delimiter = options[:merge_attrs][name]
16
16
  if current = result[name]
17
17
  current << [:static, delimiter] << value
18
18
  else
@@ -1,19 +1,16 @@
1
1
  module Temple
2
2
  module CoffeeScript
3
- class AttributeRemover < Filter
3
+ class AttributeRemover < Temple::HTML::AttributeRemover
4
4
  include Temple::HTML::Dispatcher
5
5
 
6
- default_options[:remove_empty_attrs] = true
7
-
8
- def on_html_attrs(*attrs)
9
- [:multi, *(options[:remove_empty_attrs] ?
10
- attrs.map {|attr| compile(attr) } : attrs)]
11
- end
6
+ define_options :remove_empty_attrs => %w(id class)
12
7
 
13
8
  def on_html_attr(name, value)
9
+ return super unless options[:remove_empty_attrs].include?(name.to_s)
10
+
14
11
  if empty_exp?(value)
15
12
  value
16
- elsif contains_static?(value)
13
+ elsif contains_nonempty_static?(value)
17
14
  [:html, :attr, name, value]
18
15
  else
19
16
  tmp = unique_name
@@ -3,12 +3,14 @@ module Temple
3
3
  module Filters
4
4
  class Escapable < Filter
5
5
  # Activate the usage of html_safe? if it is available (for Rails 3 for example)
6
- set_default_options :use_html_safe => ''.respond_to?(:html_safe?),
7
- :disable_escape => false
6
+ define_options :escape_code,
7
+ :use_html_safe => ''.respond_to?(:html_safe?),
8
+ :disable_escape => false
8
9
 
9
10
  def initialize(opts = {})
10
11
  super
11
- @escape_code = "::Temple::Utils.escape_html#{options[:use_html_safe] ? '_safe' : ''}((%s))"
12
+ @escape_code = options[:escape_code] ||
13
+ "::Temple::Utils.escape_html#{options[:use_html_safe] ? '_safe' : ''}((%s))"
12
14
  @escaper = eval("proc {|v| #{@escape_code % 'v'} }")
13
15
  @escape = false
14
16
  end
@@ -1,13 +1,12 @@
1
- require "multi_json"
1
+ require "json"
2
2
 
3
3
  module Temple
4
4
  module CoffeeScript
5
5
  class Generator < Temple::Generator
6
- default_options[:indent] = 0
6
+ define_options :indent => 0
7
7
 
8
8
  def call(exp)
9
9
  @indent = options[:indent]
10
-
11
10
  compile [:multi,
12
11
  [:code, "#{buffer} = []"],
13
12
  exp,
@@ -19,7 +18,7 @@ module Temple
19
18
  end
20
19
 
21
20
  def on_static(text)
22
- concat(MultiJson.encode(text))
21
+ concat(JSON.generate(text, :quirks_mode => true))
23
22
  end
24
23
 
25
24
  def on_dynamic(code)
@@ -17,15 +17,14 @@ Sprockets-based asset pipeline.}
17
17
  gem.require_paths = ["lib"]
18
18
  gem.version = Skim::VERSION
19
19
 
20
- gem.add_dependency "slim", "<= 1.2.2"
21
- gem.add_dependency "temple", "<= 0.4.1"
20
+ gem.add_dependency "slim", "~> 2.0.0"
22
21
  gem.add_dependency "coffee-script"
23
22
  gem.add_dependency "coffee-script-source", ">= 1.2.0"
24
- gem.add_dependency "multi_json"
25
23
  gem.add_dependency "sprockets"
26
24
 
27
25
  gem.add_development_dependency "rake"
28
26
  gem.add_development_dependency "execjs"
29
27
  gem.add_development_dependency "minitest-reporters", "~> 0.10"
30
28
  gem.add_development_dependency "therubyracer"
29
+ gem.add_development_dependency "libv8", "3.11.8.3"
31
30
  end
@@ -1,7 +1,6 @@
1
1
  require "rubygems"
2
2
  require "minitest/unit"
3
3
  require "minitest/reporters"
4
- require "temple"; Temple::Filter # Trigger #13
5
4
  require "skim"
6
5
  require "coffee_script"
7
6
  require "execjs"
@@ -28,11 +27,11 @@ class TestSkim < MiniTest::Unit::TestCase
28
27
  end
29
28
 
30
29
  def context(options)
31
- case context = options[:context]
30
+ case context = options.delete(:context)
32
31
  when String
33
32
  context
34
33
  when Hash
35
- MultiJson.encode(context)
34
+ JSON.dump(context)
36
35
  else
37
36
  "new Context()"
38
37
  end
@@ -52,6 +51,7 @@ class TestSkim < MiniTest::Unit::TestCase
52
51
  if Skim::Engine.default_options[:use_asset]
53
52
  code.unshift skim_source
54
53
  end
54
+
55
55
  context = ExecJS.compile(code.join(";"))
56
56
  context.call("evaluate")
57
57
  end
@@ -6,7 +6,7 @@ class TestSkimCodeEscaping < TestSkim
6
6
  p = @evil_method()
7
7
  }
8
8
 
9
- assert_html '<p>&lt;script&gt;do_something_evil();&lt;&#47;script&gt;</p>', source
9
+ assert_html '<p>&lt;script&gt;do_something_evil();&lt;/script&gt;</p>', source
10
10
  end
11
11
 
12
12
  def test_render_unsafe
@@ -14,7 +14,7 @@ p = @evil_method()
14
14
  p = "<strong>Hello World\\n, meet \\"Skim\\"</strong>."
15
15
  }
16
16
 
17
- assert_html "<p>&lt;strong&gt;Hello World\n, meet \&quot;Skim\&quot;&lt;&#47;strong&gt;.</p>", source
17
+ assert_html "<p>&lt;strong&gt;Hello World\n, meet \&quot;Skim\&quot;&lt;/strong&gt;.</p>", source
18
18
  end
19
19
 
20
20
  def test_render_safe
@@ -31,7 +31,7 @@ p = @safe("<strong>Hello World\\n, meet \\"Skim\\"</strong>.")
31
31
  == "<p>World</p>"
32
32
  }
33
33
 
34
- assert_html "&lt;p&gt;Hello&lt;&#47;p&gt;<p>World</p>", source
34
+ assert_html "&lt;p&gt;Hello&lt;/p&gt;<p>World</p>", source
35
35
  end
36
36
 
37
37
  def test_render_with_disable_escape_true
@@ -68,10 +68,10 @@ p id=@var
68
68
 
69
69
  def test_method_call_in_attribute_without_quotes
70
70
  source = %q{
71
- form action=@action_path("page", "save") method='post'
71
+ form action=@action_path('page', 'save') method='post'
72
72
  }
73
73
 
74
- assert_html '<form action="&#47;action-page-save" method="post"></form>', source
74
+ assert_html '<form action="/action-page-save" method="post"></form>', source
75
75
  end
76
76
 
77
77
  def test_ruby_attribute_with_unbalanced_delimiters
@@ -79,28 +79,20 @@ form action=@action_path("page", "save") method='post'
79
79
  div crazy=@action_path('[') id="crazy_delimiters"
80
80
  }
81
81
 
82
- assert_html '<div crazy="&#47;action-[" id="crazy_delimiters"></div>', source
82
+ assert_html '<div crazy="/action-[" id="crazy_delimiters"></div>', source
83
83
  end
84
84
 
85
85
  def test_method_call_in_delimited_attribute_without_quotes
86
86
  source = %q{
87
- form(action=@action_path("page", "save") method='post')
87
+ form(action=@action_path('page', 'save') method='post')
88
88
  }
89
89
 
90
- assert_html '<form action="&#47;action-page-save" method="post"></form>', source
90
+ assert_html '<form action="/action-page-save" method="post"></form>', source
91
91
  end
92
92
 
93
93
  def test_method_call_in_delimited_attribute_without_quotes2
94
94
  source = %q{
95
- form(method='post' action=@action_path("page", "save"))
96
- }
97
-
98
- assert_html '<form action="&#47;action-page-save" method="post"></form>', source
99
- end
100
-
101
- def test_bypassing_escape_in_attribute
102
- source = %q{
103
- form action==@action_path("page", "save") method='post'
95
+ form(method='post' action=@action_path('page', 'save'))
104
96
  }
105
97
 
106
98
  assert_html '<form action="/action-page-save" method="post"></form>', source
@@ -124,7 +116,7 @@ p(id=@hash()['a']) Test it
124
116
 
125
117
  def test_hash_call_in_attribute_with_ruby_evaluation
126
118
  source = %q{
127
- p id={@hash()['a'] + @hash()['a']} Test it
119
+ p id=(@hash()['a'] + @hash()['a']) Test it
128
120
  }
129
121
 
130
122
  assert_html '<p id="The letter aThe letter a">Test it</p>', source
@@ -148,23 +140,15 @@ p[id=(@hash()['a'] + @hash()['a'])] Test it
148
140
 
149
141
  def test_hash_call_in_delimited_attribute_with_ruby_evaluation_3
150
142
  source = %q{
151
- p(id=[@hash()['a'] + @hash()['a']]) Test it
152
- }
153
-
154
- assert_html '<p id="The letter aThe letter a">Test it</p>', source
155
- end
156
-
157
- def test_hash_call_in_delimited_attribute_with_ruby_evaluation_4
158
- source = %q{
159
- p(id=[@hash()['a'] + @hash()['a']] class=[@hash()['a']]) Test it
143
+ p(id=(@hash()['a'] + @hash()['a']) class=@hash()['a']) Test it
160
144
  }
161
145
 
162
146
  assert_html '<p class="The letter a" id="The letter aThe letter a">Test it</p>', source
163
147
  end
164
148
 
165
- def test_hash_call_in_delimited_attribute_with_ruby_evaluation_5
149
+ def test_hash_call_in_delimited_attribute_with_ruby_evaluation_4_
166
150
  source = %q{
167
- p(id=@hash()['a'] class=[@hash()['a']]) Test it
151
+ p(id=@hash()['a'] class=@hash()['a']) Test it
168
152
  }
169
153
 
170
154
  assert_html '<p class="The letter a" id="The letter a">Test it</p>', source
@@ -185,101 +169,4 @@ p = @output_number()
185
169
 
186
170
  assert_html '<p>1337</p>', source
187
171
  end
188
-
189
- def test_ternary_operation_in_attribute
190
- source = %q{
191
- p id="#{(if false then 'notshown' else 'shown')}" = @output_number()
192
- }
193
-
194
- assert_html '<p id="shown">1337</p>', source
195
- end
196
-
197
- def test_class_attribute_merging
198
- source = %{
199
- .alpha class="beta" Test it
200
- }
201
- assert_html '<div class="alpha beta">Test it</div>', source
202
- end
203
-
204
- def test_class_attribute_merging_with_null
205
- skip "pending"
206
-
207
- source = %{
208
- .alpha class="beta" class=null class="gamma" Test it
209
- }
210
- assert_html '<div class="alpha beta gamma">Test it</div>', source
211
- end
212
-
213
- def test_id_attribute_merging
214
- source = %{
215
- #alpha id="beta" Test it
216
- }
217
- assert_html '<div id="alpha_beta">Test it</div>', source, :attr_delimiter => {'class' => ' ', 'id' => '_' }
218
- end
219
-
220
- def test_id_attribute_merging2
221
- source = %{
222
- #alpha id="beta" Test it
223
- }
224
- assert_html '<div id="alpha-beta">Test it</div>', source, :attr_delimiter => {'class' => ' ', 'id' => '-' }
225
- end
226
-
227
- def test_boolean_attribute_false
228
- source = %{
229
- option selected=false Text
230
- }
231
-
232
- assert_html '<option>Text</option>', source
233
- end
234
-
235
- def test_boolean_attribute_true
236
- source = %{
237
- option selected=true Text
238
- }
239
-
240
- assert_html '<option selected="selected">Text</option>', source
241
- end
242
-
243
- def test_boolean_attribute_dynamic
244
- source = %{
245
- option selected=@method_which_returns_true() Text
246
- }
247
-
248
- assert_html '<option selected="selected">Text</option>', source
249
- end
250
-
251
- def test_boolean_attribute_null
252
- source = %{
253
- option selected=null Text
254
- }
255
-
256
- assert_html '<option>Text</option>', source
257
- end
258
-
259
- def test_boolean_attribute_string2
260
- source = %{
261
- option selected="selected" Text
262
- }
263
-
264
- assert_html '<option selected="selected">Text</option>', source
265
- end
266
-
267
- def test_boolean_attribute_shortcut
268
- source = %{
269
- option(class="clazz" selected) Text
270
- option(selected class="clazz") Text
271
- }
272
-
273
- assert_html '<option class="clazz" selected="selected">Text</option><option class="clazz" selected="selected">Text</option>', source
274
- end
275
-
276
- def test_array_attribute
277
- skip "pending"
278
-
279
- source = %{
280
- .alpha class="beta" class=['gamma', null, 'delta', [true, false]]
281
- }
282
-
283
- assert_html '<div class="alpha beta gamma delta true false"></div>', source
284
- end
285
172
  end
@@ -15,7 +15,6 @@ html
15
15
  end
16
16
 
17
17
  def test_html_tag_with_text_and_empty_line
18
- # Keep the trailing space behind "body "!
19
18
  source = %q{
20
19
  p Hello
21
20
 
@@ -74,10 +73,21 @@ h1#title This is my title
74
73
  def test_render_with_overwritten_default_tag
75
74
  source = %q{
76
75
  #notice.hello.world
76
+ = @hello_world()
77
+ }
78
+
79
+ assert_html '<section class="hello world" id="notice">Hello World from @env</section>', source, :default_tag => 'section'
80
+ end
81
+
82
+ def test_render_with_custom_shortcut
83
+ source = %q{
84
+ #notice.hello.world@test
85
+ = @hello_world()
86
+ @abc
77
87
  = @hello_world()
78
88
  }
79
89
 
80
- assert_html '<section class="hello world" id="notice">Hello World from @env</section>', source, :default_tag => 'section'
90
+ assert_html '<div class="hello world" id="notice" role="test">Hello World from @env</div><section role="abc">Hello World from @env</section>', source, :shortcut => {'#' => {:attr => 'id'}, '.' => {:attr => 'class'}, '@' => {:tag => 'section', :attr => 'role'}}
81
91
  end
82
92
 
83
93
  def test_render_with_text_block
@@ -215,7 +225,7 @@ p class='underscored_class_name' = @output_number()
215
225
  }
216
226
 
217
227
  assert_html '<p class="underscored_class_name">1337</p>', source
218
- end
228
+ end
219
229
 
220
230
  def test_nonstandard_attributes
221
231
  source = %q{
@@ -273,24 +283,6 @@ p(id="marvin" class="martian" data-info="Illudium Q-36")= @output_number()
273
283
  assert_html '<p class="martian" data-info="Illudium Q-36" id="marvin">1337</p>', source
274
284
  end
275
285
 
276
- def test_static_empty_attribute
277
- source = %q{
278
- p(id="marvin" class="" data-info="Illudium Q-36")= @output_number()
279
- }
280
-
281
- assert_html '<p class="" data-info="Illudium Q-36" id="marvin">1337</p>', source
282
- end
283
-
284
- def test_dynamic_empty_attribute
285
- skip "pending"
286
-
287
- source = %q{
288
- p(id="marvin" class=null other_empty=("") data-info="Illudium Q-36")= @output_number()
289
- }
290
-
291
- assert_html '<p data-info="Illudium Q-36" id="marvin">1337</p>', source
292
- end
293
-
294
286
  def test_closed_tag
295
287
  source = %q{
296
288
  closed/
@@ -354,7 +346,7 @@ p World
354
346
  def test_render_with_html_conditional_and_method_output
355
347
  source = %q{
356
348
  /[ if IE ]
357
- = @message 'hello'
349
+ = @message('hello')
358
350
  }
359
351
 
360
352
  assert_html "<!--[if IE]>hello<![endif]-->", source
@@ -366,7 +358,7 @@ p<id="marvin"
366
358
  class="martian"
367
359
  data-info="Illudium Q-36"> = @output_number()
368
360
  }
369
- Slim::Parser::DELIMITERS.each do |k,v|
361
+ Slim::Parser::DELIMS.each do |k,v|
370
362
  str = source.sub('<',k).sub('>',v)
371
363
  assert_html '<p class="martian" data-info="Illudium Q-36" id="marvin">1337</p>', str
372
364
  end
@@ -378,7 +370,7 @@ p<id="marvin"
378
370
  class="martian"
379
371
  data-info="Illudium Q-36"> THE space modulator
380
372
  }
381
- Slim::Parser::DELIMITERS.each do |k,v|
373
+ Slim::Parser::DELIMS.each do |k,v|
382
374
  str = source.sub('<',k).sub('>',v)
383
375
  assert_html '<p class="martian" data-info="Illudium Q-36" id="marvin">THE space modulator</p>', str
384
376
  end
@@ -391,7 +383,7 @@ p<id="marvin"
391
383
  data-info="Illudium Q-36">
392
384
  | THE space modulator
393
385
  }
394
- Slim::Parser::DELIMITERS.each do |k,v|
386
+ Slim::Parser::DELIMS.each do |k,v|
395
387
  str = source.sub('<',k).sub('>',v)
396
388
  assert_html '<p class="martian" data-info="Illudium Q-36" id="marvin">THE space modulator</p>', str
397
389
  end
@@ -404,7 +396,7 @@ p<id=@id_helper()
404
396
  data-info="Illudium Q-36">
405
397
  | THE space modulator
406
398
  }
407
- Slim::Parser::DELIMITERS.each do |k,v|
399
+ Slim::Parser::DELIMS.each do |k,v|
408
400
  str = source.sub('<',k).sub('>',v)
409
401
  assert_html '<p class="martian" data-info="Illudium Q-36" id="notice">THE space modulator</p>', str
410
402
  end
@@ -418,7 +410,7 @@ p<id=@id_helper()
418
410
  span.emphasis THE
419
411
  | space modulator
420
412
  }
421
- Slim::Parser::DELIMITERS.each do |k,v|
413
+ Slim::Parser::DELIMS.each do |k,v|
422
414
  str = source.sub('<',k).sub('>',v)
423
415
  assert_html '<p class="martian" data-info="Illudium Q-36" id="notice"><span class="emphasis">THE</span> space modulator</p>', str
424
416
  end
@@ -431,7 +423,7 @@ li< id="myid"
431
423
  data-info="myinfo">
432
424
  a href="link" My Link
433
425
  }
434
- Slim::Parser::DELIMITERS.each do |k,v|
426
+ Slim::Parser::DELIMS.each do |k,v|
435
427
  str = source.sub('<',k).sub('>',v)
436
428
  assert_html '<li class="myclass" data-info="myinfo" id="myid"><a href="link">My Link</a></li>', str
437
429
  end
@@ -470,4 +462,17 @@ input[value=@succ_x()]
470
462
  assert_html %{<input value="1" /><input value="2" />}, source
471
463
  end
472
464
 
465
+ def test_html_line_indicator
466
+ source = %q{
467
+ <html>
468
+ head
469
+ meta name="keywords" content=@hello_world()
470
+ - if true
471
+ <p>#{@hello_world()}</p>
472
+ span = @hello_world()
473
+ </html>
474
+ }
475
+
476
+ assert_html '<html><head><meta content="Hello World from @env" name="keywords" /></head><p>Hello World from @env</p><span>Hello World from @env</span></html>', source
477
+ end
473
478
  end
@@ -40,9 +40,10 @@ p #{@hello_world()}
40
40
  def test_escape_interpolation
41
41
  source = %q{
42
42
  p \\#{@hello_world()}
43
+ p text1 \\#{@hello_world()} text2
43
44
  }
44
45
 
45
- assert_html '<p>#{@hello_world()}</p>', source
46
+ assert_html '<p>#{@hello_world()}</p><p>text1 #{@hello_world()} text2</p>', source
46
47
  end
47
48
 
48
49
  def test_complex_interpolation
@@ -58,7 +59,7 @@ p Message: #{@message('hello', "user #{@output_number()}")}
58
59
  | #{@evil_method()}
59
60
  }
60
61
 
61
- assert_html '&lt;script&gt;do_something_evil();&lt;&#47;script&gt;', source
62
+ assert_html '&lt;script&gt;do_something_evil();&lt;/script&gt;', source
62
63
  end
63
64
 
64
65
  def test_interpolation_without_escaping
@@ -73,6 +74,6 @@ p Message: #{@message('hello', "user #{@output_number()}")}
73
74
  source = %q{
74
75
  | #{(@evil_method())}
75
76
  }
76
- assert_html '&lt;script&gt;do_something_evil();&lt;&#47;script&gt;', source
77
+ assert_html '&lt;script&gt;do_something_evil();&lt;/script&gt;', source
77
78
  end
78
79
  end
@@ -22,6 +22,17 @@ this.Skim =
22
22
  result.skimSafe = true
23
23
  result
24
24
 
25
+ context.isArray = Array.isArray || ( value ) -> return {}.toString.call( value ) is '[object Array]'
26
+
27
+ context.flatten = flatten = (array) ->
28
+ flattened = []
29
+ for element in array
30
+ if element instanceof Array
31
+ flattened = flattened.concat flatten element
32
+ else
33
+ flattened.push element
34
+ flattened
35
+
25
36
  context.escape ||= @escape || (string) ->
26
37
  return '' unless string?
27
38
  return string if string.skimSafe
@@ -30,6 +41,5 @@ this.Skim =
30
41
  .replace(/</g, '&lt;')
31
42
  .replace(/>/g, '&gt;')
32
43
  .replace(/"/g, '&quot;')
33
- .replace(/\//g,'&#47;')
34
44
 
35
45
  block.call(context)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: skim
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.6
4
+ version: 0.9.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,40 +9,24 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-09-25 00:00:00.000000000 Z
12
+ date: 2013-06-27 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: slim
16
16
  requirement: !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
- - - <=
20
- - !ruby/object:Gem::Version
21
- version: 1.2.2
22
- type: :runtime
23
- prerelease: false
24
- version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
- requirements:
27
- - - <=
28
- - !ruby/object:Gem::Version
29
- version: 1.2.2
30
- - !ruby/object:Gem::Dependency
31
- name: temple
32
- requirement: !ruby/object:Gem::Requirement
33
- none: false
34
- requirements:
35
- - - <=
19
+ - - ~>
36
20
  - !ruby/object:Gem::Version
37
- version: 0.4.1
21
+ version: 2.0.0
38
22
  type: :runtime
39
23
  prerelease: false
40
24
  version_requirements: !ruby/object:Gem::Requirement
41
25
  none: false
42
26
  requirements:
43
- - - <=
27
+ - - ~>
44
28
  - !ruby/object:Gem::Version
45
- version: 0.4.1
29
+ version: 2.0.0
46
30
  - !ruby/object:Gem::Dependency
47
31
  name: coffee-script
48
32
  requirement: !ruby/object:Gem::Requirement
@@ -75,22 +59,6 @@ dependencies:
75
59
  - - ! '>='
76
60
  - !ruby/object:Gem::Version
77
61
  version: 1.2.0
78
- - !ruby/object:Gem::Dependency
79
- name: multi_json
80
- requirement: !ruby/object:Gem::Requirement
81
- none: false
82
- requirements:
83
- - - ! '>='
84
- - !ruby/object:Gem::Version
85
- version: '0'
86
- type: :runtime
87
- prerelease: false
88
- version_requirements: !ruby/object:Gem::Requirement
89
- none: false
90
- requirements:
91
- - - ! '>='
92
- - !ruby/object:Gem::Version
93
- version: '0'
94
62
  - !ruby/object:Gem::Dependency
95
63
  name: sprockets
96
64
  requirement: !ruby/object:Gem::Requirement
@@ -171,6 +139,22 @@ dependencies:
171
139
  - - ! '>='
172
140
  - !ruby/object:Gem::Version
173
141
  version: '0'
142
+ - !ruby/object:Gem::Dependency
143
+ name: libv8
144
+ requirement: !ruby/object:Gem::Requirement
145
+ none: false
146
+ requirements:
147
+ - - '='
148
+ - !ruby/object:Gem::Version
149
+ version: 3.11.8.3
150
+ type: :development
151
+ prerelease: false
152
+ version_requirements: !ruby/object:Gem::Requirement
153
+ none: false
154
+ requirements:
155
+ - - '='
156
+ - !ruby/object:Gem::Version
157
+ version: 3.11.8.3
174
158
  description: Fat-free client-side templates with Slim and CoffeeScript
175
159
  email:
176
160
  - john.firebaugh@gmail.com
@@ -186,10 +170,11 @@ files:
186
170
  - README.md
187
171
  - Rakefile
188
172
  - lib/skim.rb
189
- - lib/skim/compiler.rb
173
+ - lib/skim/code_attributes.rb
174
+ - lib/skim/controls.rb
190
175
  - lib/skim/engine.rb
176
+ - lib/skim/interpolation.rb
191
177
  - lib/skim/rails.rb
192
- - lib/skim/sections.rb
193
178
  - lib/skim/template.rb
194
179
  - lib/skim/version.rb
195
180
  - lib/temple/coffee_script.rb
@@ -213,7 +198,6 @@ files:
213
198
  - test/test_code_structure.rb
214
199
  - test/test_html_escaping.rb
215
200
  - test/test_html_structure.rb
216
- - test/test_sections.rb
217
201
  - test/test_skim_template.rb
218
202
  - test/test_text_interpolation.rb
219
203
  - vendor/assets/javascripts/skim.js.coffee
@@ -229,21 +213,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
229
213
  - - ! '>='
230
214
  - !ruby/object:Gem::Version
231
215
  version: '0'
232
- segments:
233
- - 0
234
- hash: -2623561313365634861
235
216
  required_rubygems_version: !ruby/object:Gem::Requirement
236
217
  none: false
237
218
  requirements:
238
219
  - - ! '>='
239
220
  - !ruby/object:Gem::Version
240
221
  version: '0'
241
- segments:
242
- - 0
243
- hash: -2623561313365634861
244
222
  requirements: []
245
223
  rubyforge_project:
246
- rubygems_version: 1.8.24
224
+ rubygems_version: 1.8.23
247
225
  signing_key:
248
226
  specification_version: 3
249
227
  summary: Take the fat out of your client-side templates with Skim. Skim is the Slim
@@ -262,6 +240,5 @@ test_files:
262
240
  - test/test_code_structure.rb
263
241
  - test/test_html_escaping.rb
264
242
  - test/test_html_structure.rb
265
- - test/test_sections.rb
266
243
  - test/test_skim_template.rb
267
244
  - test/test_text_interpolation.rb
@@ -1,36 +0,0 @@
1
- module Skim
2
- class Compiler < Slim::Compiler
3
- def on_slim_control(code, content)
4
- [:multi,
5
- [:code, code],
6
- [:indent, compile(content)]]
7
- end
8
-
9
- def on_slim_attr(name, escape, code)
10
- value = case code
11
- when 'true'
12
- escape = false
13
- [:static, name]
14
- when 'false', 'null'
15
- escape = false
16
- [:multi]
17
- else
18
- tmp = unique_name
19
- [:multi,
20
- [:code, "#{tmp} = #{code}"],
21
- [:case, tmp,
22
- ['true', [:static, name]],
23
- ['false, null', [:multi]],
24
- [:else,
25
- [:dynamic,
26
- #if delimiter = options[:attr_delimiter][name]
27
- # "#{tmp}.respond_to?(:join) ? #{tmp}.flatten.compact.join(#{delimiter.inspect}) : #{tmp}"
28
- #else
29
- tmp
30
- #end
31
- ]]]]
32
- end
33
- [:html, :attr, name, [:escape, escape, value]]
34
- end
35
- end
36
- end
@@ -1,32 +0,0 @@
1
- require "multi_json"
2
-
3
- module Skim
4
- class Sections < Slim::Sections
5
- def call(exp)
6
- if options[:sections]
7
- compile(exp)
8
- else
9
- exp
10
- end
11
- end
12
-
13
- protected
14
-
15
- def on_slim_inverted_section(name, content)
16
- [:if, "not #{access(name)}", compile(content)]
17
- end
18
-
19
- def on_slim_section(name, content)
20
- tmp1, tmp2 = unique_name, unique_name
21
- [:if, "#{tmp1} = #{access(name)}",
22
- [:block, "for #{tmp2} in #{tmp1}",
23
- [:block, "Skim.withContext.call @, #{tmp2}, ->", compile(content)]]]
24
- end
25
-
26
- private
27
-
28
- def access(name)
29
- "Skim.access.call(@, #{MultiJson.encode(name.to_s)})"
30
- end
31
- end
32
- end
@@ -1,157 +0,0 @@
1
- require 'helper'
2
-
3
- class TestSkimLogicLess < TestSkim
4
- def test_sections
5
- source = %q{
6
- p
7
- - person
8
- .name = name
9
- }
10
-
11
- hash = {
12
- :person => [
13
- { :name => 'Joe', },
14
- { :name => 'Jack', }
15
- ]
16
- }
17
-
18
- assert_html '<p><div class="name">Joe</div><div class="name">Jack</div></p>', source, :context => hash, :sections => true
19
- end
20
-
21
- def test_flag_section
22
- source = %q{
23
- p
24
- - show_person
25
- - person
26
- .name = name
27
- - show_person
28
- | shown
29
- }
30
-
31
- hash = {
32
- :show_person => true,
33
- :person => [
34
- { :name => 'Joe', },
35
- { :name => 'Jack', }
36
- ]
37
- }
38
-
39
- assert_html '<p><div class="name">Joe</div><div class="name">Jack</div>shown</p>', source, :context => hash, :sections => true
40
- end
41
-
42
- def test_inverted_section
43
- source = %q{
44
- p
45
- - person
46
- .name = name
47
- -! person
48
- | No person
49
- - !person
50
- | No person 2
51
- }
52
-
53
- hash = {}
54
-
55
- assert_html '<p>No person No person 2</p>', source, :context => hash, :sections => true
56
- end
57
-
58
- def test_output_with_content
59
- source = %{
60
- p = method_with_block do
61
- block
62
- }
63
- assert_runtime_error 'Output statements with content are forbidden in sections mode', source, :sections => true
64
- end
65
-
66
- def test_sections2
67
- source = %q{
68
- p
69
- - person
70
- .name = name
71
- }
72
- assert_html '<p><div class="name">Joe</div><div class="name">Jack</div></p>', source, :sections => true
73
- end
74
-
75
- def test_with_array
76
- source = %q{
77
- ul
78
- - people_with_locations
79
- li = name
80
- li = city
81
- }
82
- assert_html '<ul><li>Andy</li><li>Atlanta</li><li>Fred</li><li>Melbourne</li><li>Daniel</li><li>Karlsruhe</li></ul>', source, :sections => true
83
- end
84
-
85
- def test_method
86
- source = %q{
87
- a href=output_number Link
88
- }
89
- assert_html '<a href="1337">Link</a>', source, :sections => true
90
- end
91
-
92
- SECTION = <<-SRC
93
- - test
94
- | Test
95
- SRC
96
-
97
- INVERTED_SECTION = <<-SRC
98
- -! test
99
- | Test
100
- SRC
101
-
102
- def test_undefined_section
103
- assert_html "", SECTION, :context => {}, :sections => true
104
- end
105
-
106
- def test_null_section
107
- assert_html "", SECTION, :context => {:test => nil}, :sections => true
108
- end
109
-
110
- def test_false_section
111
- assert_html "", SECTION, :context => {:test => false}, :sections => true
112
- end
113
-
114
- def test_empty_string_section
115
- assert_html "Test", SECTION, :context => {:test => ""}, :sections => true
116
- end
117
-
118
- def test_empty_array_section
119
- assert_html "", SECTION, :context => {:test => []}, :sections => true
120
- end
121
-
122
- def test_empty_object_section
123
- assert_html "Test", SECTION, :context => {:test => {}}, :sections => true
124
- end
125
-
126
- def test_undefined_inverted_section
127
- assert_html "Test", INVERTED_SECTION, :context => {}, :sections => true
128
- end
129
-
130
- def test_null_inverted_section
131
- assert_html "Test", INVERTED_SECTION, :context => {:test => nil}, :sections => true
132
- end
133
-
134
- def test_false_inverted_section
135
- assert_html "Test", INVERTED_SECTION, :context => {:test => false}, :sections => true
136
- end
137
-
138
- def test_empty_string_inverted_section
139
- assert_html "", INVERTED_SECTION, :context => {:test => ""}, :sections => true
140
- end
141
-
142
- def test_empty_array_inverted_section
143
- assert_html "Test", INVERTED_SECTION, :context => {:test => []}, :sections => true
144
- end
145
-
146
- def test_empty_object_inverted_section
147
- assert_html "", INVERTED_SECTION, :context => {:test => {}}, :sections => true
148
- end
149
-
150
- def test_doesnt_modify_context
151
- source = %q{
152
- - constant_object
153
- = constant_object_size
154
- }
155
- assert_html '2', source, :sections => true
156
- end
157
- end