skim 0.8.6 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
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