skim 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +18 -0
- data/.travis.yml +3 -0
- data/Gemfile +2 -0
- data/LICENSE +22 -0
- data/README.md +125 -0
- data/Rakefile +9 -0
- data/lib/skim/compiler.rb +36 -0
- data/lib/skim/engine.rb +24 -0
- data/lib/skim/rails.rb +4 -0
- data/lib/skim/sections.rb +32 -0
- data/lib/skim/template.rb +31 -0
- data/lib/skim/version.rb +3 -0
- data/lib/skim.rb +15 -0
- data/lib/temple/coffee_script/filters/attribute_merger.rb +33 -0
- data/lib/temple/coffee_script/filters/attribute_remover.rb +28 -0
- data/lib/temple/coffee_script/filters/control_flow.rb +37 -0
- data/lib/temple/coffee_script/filters/escapable.rb +34 -0
- data/lib/temple/coffee_script/filters.rb +4 -0
- data/lib/temple/coffee_script/generators.rb +53 -0
- data/lib/temple/coffee_script.rb +3 -0
- data/lib/temple/mixins/indent_dispatcher.rb +13 -0
- data/skim.gemspec +30 -0
- data/test/context.coffee +82 -0
- data/test/helper.rb +92 -0
- data/test/test_attribute_merger.rb +23 -0
- data/test/test_code_blocks.rb +88 -0
- data/test/test_code_escaping.rb +69 -0
- data/test/test_code_evaluation.rb +285 -0
- data/test/test_code_output.rb +143 -0
- data/test/test_code_structure.rb +84 -0
- data/test/test_html_escaping.rb +48 -0
- data/test/test_html_structure.rb +473 -0
- data/test/test_sections.rb +157 -0
- data/test/test_skim_template.rb +7 -0
- data/test/test_text_interpolation.rb +78 -0
- data/vendor/assets/javascripts/skim.js.coffee +35 -0
- metadata +195 -0
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 John Firebaugh
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,125 @@
|
|
1
|
+
Skim
|
2
|
+
====
|
3
|
+
|
4
|
+
Take the fat out of your client-side templates with Skim. Skim is the [Slim](http://slim-lang.com/) templating engine
|
5
|
+
with embedded CoffeeScript. It compiles to JavaScript templates (`.jst`), which can then be served by Rails or any other
|
6
|
+
Sprockets-based asset pipeline.
|
7
|
+
|
8
|
+
# Usage
|
9
|
+
|
10
|
+
`gem install skim`, or add `skim` to your `Gemfile` in the `:assets` group:
|
11
|
+
|
12
|
+
group :assets do
|
13
|
+
gem 'skim'
|
14
|
+
end
|
15
|
+
|
16
|
+
Create template files with the extension `.jst.skim`. For example, `test.jst.skim`:
|
17
|
+
|
18
|
+
p Hello #{@world}!
|
19
|
+
|
20
|
+
In your JavaScript or CoffeeScript, render the result, passing a context object:
|
21
|
+
|
22
|
+
$("body").html(JST["test"]({world: "World"}));
|
23
|
+
|
24
|
+
Order up a skinny latte and enjoy!
|
25
|
+
|
26
|
+
# Caveats
|
27
|
+
|
28
|
+
Skim is an early proof-of-concept. Some Slim features are still missing:
|
29
|
+
|
30
|
+
* Skim does not currently support embedded engines. Being a client-side templating languages, it will only be able to
|
31
|
+
support embedded engines with a client-side implementation.
|
32
|
+
* Skim does not currently support HTML pretty-printing (Slim's `:pretty` option). This is low priority, as
|
33
|
+
pretty-printing is even less important client-side than server-side.
|
34
|
+
* Skim does not currently support backslash line continuations.
|
35
|
+
|
36
|
+
# Language reference
|
37
|
+
|
38
|
+
Skim supports the following Slim language features:
|
39
|
+
|
40
|
+
* doctype declarations (`doctype`)
|
41
|
+
* HTML Comments (`/!`) and conditional comments (`/[...]`)
|
42
|
+
* static content (same line and nested)
|
43
|
+
* dynamic content, escaped and not (`=` and `==`)
|
44
|
+
* control logic (`-`)
|
45
|
+
* string interpolation, escaped and not (`#{}` and `#{{}}`)
|
46
|
+
* id and class attribute shortcuts (`#` and `.`)
|
47
|
+
* attribute and attribute value wrappers
|
48
|
+
* logic-less (sections) mode
|
49
|
+
|
50
|
+
Several Coffee/JavaScript considerations are specific to Skim:
|
51
|
+
|
52
|
+
* When interpolating the results of evaluating code, Skim will replace `null` and `undefined` results with an empty
|
53
|
+
string.
|
54
|
+
* You will typically want to use the fat arrow `=>` function definition to create callbacks, to preserve the binding of
|
55
|
+
`this` analogously to how `self` behaves in a Ruby block.
|
56
|
+
|
57
|
+
## The context object
|
58
|
+
|
59
|
+
The context object you pass to the compiled template function becomes the value of this inside your template. You can
|
60
|
+
use CoffeeScript's `@` sigil to easily access properties and call helper methods on the context object.
|
61
|
+
|
62
|
+
## Escaping and unescaping
|
63
|
+
|
64
|
+
Like Slim, Skim escapes dynamic output by default, and it supports the same `==` and `#{{}}` syntaxes for bypassing
|
65
|
+
escaping. In addition, the special `safe` method on the context object tells Skim that the string can be output without
|
66
|
+
being escaped. You can use this in conjunction with `escape` context method to selectively sanitize parts of the string.
|
67
|
+
For example, given the template:
|
68
|
+
|
69
|
+
= @linkTo(@project)
|
70
|
+
|
71
|
+
you could render it with the following context:
|
72
|
+
|
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>"
|
79
|
+
|
80
|
+
to produce:
|
81
|
+
|
82
|
+
<a href='/projects/4'>Crate & Barrel</a>
|
83
|
+
|
84
|
+
## The Skim asset
|
85
|
+
|
86
|
+
By default, all you need to do to start using Skim is add it to your Gemfile. Skim will embed a small amount of
|
87
|
+
supporting code in each generated template asset. You can remove this duplication by manually including Skim's asset,
|
88
|
+
and setting Skim's `:use_asset` option to true.
|
89
|
+
|
90
|
+
In Rails, this can be done by adding the following to `application.js`:
|
91
|
+
|
92
|
+
//= require skim
|
93
|
+
|
94
|
+
And the following in an initializer:
|
95
|
+
|
96
|
+
Skim::Engine.default_options[:use_asset] = true
|
97
|
+
|
98
|
+
# License (MIT)
|
99
|
+
|
100
|
+
Copyright (c) 2012 John Firebaugh
|
101
|
+
|
102
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
103
|
+
a copy of this software and associated documentation files (the
|
104
|
+
"Software"), to deal in the Software without restriction, including
|
105
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
106
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
107
|
+
permit persons to whom the Software is furnished to do so, subject to
|
108
|
+
the following conditions:
|
109
|
+
|
110
|
+
The above copyright notice and this permission notice shall be
|
111
|
+
included in all copies or substantial portions of the Software.
|
112
|
+
|
113
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
114
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
115
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
116
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
117
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
118
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
119
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
120
|
+
|
121
|
+
# Special Thanks
|
122
|
+
|
123
|
+
* Andrew Stone, for Slim
|
124
|
+
* Magnus Holm, for Temple
|
125
|
+
* Daniel Mendler, for maintenance of both
|
data/Rakefile
ADDED
@@ -0,0 +1,36 @@
|
|
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
|
data/lib/skim/engine.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
module Skim
|
2
|
+
class Engine < Temple::Engine
|
3
|
+
set_default_options :pretty => false,
|
4
|
+
:attr_wrapper => '"',
|
5
|
+
:attr_delimiter => {'class' => ' '},
|
6
|
+
:generator => Temple::CoffeeScript::Generator,
|
7
|
+
:use_asset => false
|
8
|
+
|
9
|
+
use Slim::Parser, :file, :tabsize, :encoding, :default_tag
|
10
|
+
use Slim::EmbeddedEngine, :enable_engines, :disable_engines, :pretty
|
11
|
+
use Slim::Interpolation
|
12
|
+
use Skim::Sections, :sections
|
13
|
+
use Skim::Compiler, :disable_capture, :attr_delimiter
|
14
|
+
use Temple::CoffeeScript::AttributeMerger, :attr_delimiter
|
15
|
+
use Temple::HTML::AttributeSorter
|
16
|
+
use Temple::CoffeeScript::AttributeRemover
|
17
|
+
use Temple::HTML::Fast, :format, :attr_wrapper
|
18
|
+
use Temple::CoffeeScript::Filters::Escapable, :disable_escape
|
19
|
+
use Temple::CoffeeScript::Filters::ControlFlow
|
20
|
+
filter :MultiFlattener
|
21
|
+
use(:Optimizer) { Temple::Filters::StaticMerger.new }
|
22
|
+
use(:Generator) { options[:generator].new(options) }
|
23
|
+
end
|
24
|
+
end
|
data/lib/skim/rails.rb
ADDED
@@ -0,0 +1,32 @@
|
|
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
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Skim
|
2
|
+
Template = Temple::Templates::Tilt(Skim::Engine, :register_as => :skim)
|
3
|
+
|
4
|
+
class Template
|
5
|
+
def coffee_script_src
|
6
|
+
engine = self.class.build_engine({
|
7
|
+
:streaming => false, # Overwrite option: No streaming support in Tilt
|
8
|
+
:file => eval_file,
|
9
|
+
:indent => 2 }, options)
|
10
|
+
<<SRC
|
11
|
+
return (context = {}) ->
|
12
|
+
#{self.class.skim_src unless engine.options[:use_asset]}
|
13
|
+
Skim.withContext.call {}, context, ->
|
14
|
+
#{engine.call(data)}
|
15
|
+
SRC
|
16
|
+
end
|
17
|
+
|
18
|
+
def prepare
|
19
|
+
@src = CoffeeScript.compile(coffee_script_src)
|
20
|
+
end
|
21
|
+
|
22
|
+
def evaluate(scope, locals, &block)
|
23
|
+
precompiled_template
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.skim_src
|
27
|
+
@@skim_src ||=
|
28
|
+
File.read(File.expand_path("../../../vendor/assets/javascripts/skim.js.coffee", __FILE__))
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/skim/version.rb
ADDED
data/lib/skim.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require "temple"
|
2
|
+
require "temple/coffee_script"
|
3
|
+
|
4
|
+
require "slim"
|
5
|
+
require "skim/compiler"
|
6
|
+
require "skim/sections"
|
7
|
+
require "skim/engine"
|
8
|
+
require "skim/template"
|
9
|
+
require "skim/version"
|
10
|
+
|
11
|
+
require "skim/rails" if Object.const_defined?(:Rails)
|
12
|
+
|
13
|
+
require "sprockets"
|
14
|
+
Sprockets::Engines # force autoload
|
15
|
+
Sprockets.register_engine ".skim", Skim::Template
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Temple
|
2
|
+
module CoffeeScript
|
3
|
+
class AttributeMerger < Filter
|
4
|
+
include Temple::HTML::Dispatcher
|
5
|
+
|
6
|
+
default_options[:attr_delimiter] = {'id' => '_', 'class' => ' '}
|
7
|
+
|
8
|
+
def on_html_attrs(*attrs)
|
9
|
+
names = []
|
10
|
+
result = {}
|
11
|
+
|
12
|
+
attrs.each do |html, attr, name, value|
|
13
|
+
raise(InvalidExpression, 'Attribute is not a html attr') if html != :html || attr != :attr
|
14
|
+
name = name.to_s
|
15
|
+
if delimiter = options[:attr_delimiter][name]
|
16
|
+
if current = result[name]
|
17
|
+
current << [:static, delimiter] << value
|
18
|
+
else
|
19
|
+
result[name] = [:multi, value]
|
20
|
+
names << name
|
21
|
+
end
|
22
|
+
else
|
23
|
+
raise "Multiple #{name} attributes specified" if result[name]
|
24
|
+
result[name] = value
|
25
|
+
names << name
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
[:html, :attrs, *names.map {|name| [:html, :attr, name, result[name]]}]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Temple
|
2
|
+
module CoffeeScript
|
3
|
+
class AttributeRemover < Filter
|
4
|
+
include Temple::HTML::Dispatcher
|
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
|
12
|
+
|
13
|
+
def on_html_attr(name, value)
|
14
|
+
if empty_exp?(value)
|
15
|
+
value
|
16
|
+
elsif contains_static?(value)
|
17
|
+
[:html, :attr, name, value]
|
18
|
+
else
|
19
|
+
tmp = unique_name
|
20
|
+
[:multi,
|
21
|
+
[:capture, tmp, compile(value)],
|
22
|
+
[:if, "#{tmp}.length > 0",
|
23
|
+
[:html, :attr, name, [:dynamic, tmp]]]]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Temple
|
2
|
+
module CoffeeScript
|
3
|
+
module Filters
|
4
|
+
class ControlFlow < Filter
|
5
|
+
def on_if(condition, yes, no = nil)
|
6
|
+
result = [:multi, [:code, "if #{condition}"], [:indent, compile(yes)]]
|
7
|
+
while no && no.first == :if
|
8
|
+
result << [:code, "else if #{no[1]}"] << [:indent, compile(no[2])]
|
9
|
+
no = no[3]
|
10
|
+
end
|
11
|
+
result << [:code, 'else'] << [:indent, compile(no)] if no
|
12
|
+
result
|
13
|
+
end
|
14
|
+
|
15
|
+
def on_case(arg, *cases)
|
16
|
+
result = [:multi, [:code, arg ? "switch (#{arg})" : 'switch'], [:indent, [:multi]]]
|
17
|
+
cases.map do |c|
|
18
|
+
condition, *exps = c
|
19
|
+
result[2][1] << [:code, condition == :else ? 'else' : "when #{condition}"]
|
20
|
+
exps.each {|e| result[2][1] << [:indent, compile(e)] }
|
21
|
+
end
|
22
|
+
result
|
23
|
+
end
|
24
|
+
|
25
|
+
def on_cond(*cases)
|
26
|
+
on_case(nil, *cases)
|
27
|
+
end
|
28
|
+
|
29
|
+
def on_block(code, exp)
|
30
|
+
[:multi,
|
31
|
+
[:code, code],
|
32
|
+
[:indent, compile(exp)]]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Temple
|
2
|
+
module CoffeeScript
|
3
|
+
module Filters
|
4
|
+
class Escapable < Filter
|
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
|
8
|
+
|
9
|
+
def initialize(opts = {})
|
10
|
+
super
|
11
|
+
@escape_code = "::Temple::Utils.escape_html#{options[:use_html_safe] ? '_safe' : ''}((%s))"
|
12
|
+
@escaper = eval("proc {|v| #{@escape_code % 'v'} }")
|
13
|
+
@escape = false
|
14
|
+
end
|
15
|
+
|
16
|
+
def on_escape(flag, exp)
|
17
|
+
old = @escape
|
18
|
+
@escape = flag && !options[:disable_escape]
|
19
|
+
compile(exp)
|
20
|
+
ensure
|
21
|
+
@escape = old
|
22
|
+
end
|
23
|
+
|
24
|
+
def on_static(value)
|
25
|
+
[:static, @escape ? @escaper[value] : value]
|
26
|
+
end
|
27
|
+
|
28
|
+
def on_dynamic(value)
|
29
|
+
[:dynamic, @escape ? "@escape(#{value})" : value]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require "multi_json"
|
2
|
+
|
3
|
+
module Temple
|
4
|
+
module CoffeeScript
|
5
|
+
class Generator < Temple::Generator
|
6
|
+
default_options[:indent] = 0
|
7
|
+
|
8
|
+
def call(exp)
|
9
|
+
@indent = options[:indent]
|
10
|
+
|
11
|
+
compile [:multi,
|
12
|
+
[:code, "#{buffer} = []"],
|
13
|
+
exp,
|
14
|
+
[:code, "#{buffer}.join('')"]]
|
15
|
+
end
|
16
|
+
|
17
|
+
def on_multi(*exp)
|
18
|
+
exp.map {|e| compile(e) }.join("\n")
|
19
|
+
end
|
20
|
+
|
21
|
+
def on_static(text)
|
22
|
+
concat(MultiJson.encode(text))
|
23
|
+
end
|
24
|
+
|
25
|
+
def on_dynamic(code)
|
26
|
+
concat(code)
|
27
|
+
end
|
28
|
+
|
29
|
+
def on_code(code)
|
30
|
+
indent(code)
|
31
|
+
end
|
32
|
+
|
33
|
+
def on_indent(exp)
|
34
|
+
@indent += 1
|
35
|
+
compile(exp)
|
36
|
+
ensure
|
37
|
+
@indent -= 1
|
38
|
+
end
|
39
|
+
|
40
|
+
def on_capture(name, exp)
|
41
|
+
self.class.new(:buffer => name, :indent => @indent).call(exp)
|
42
|
+
end
|
43
|
+
|
44
|
+
def concat(str)
|
45
|
+
indent("#{buffer}.push(#{str})")
|
46
|
+
end
|
47
|
+
|
48
|
+
def indent(str, indent = @indent)
|
49
|
+
" " * indent + str
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
data/skim.gemspec
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/skim/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["John Firebaugh"]
|
6
|
+
gem.email = ["john.firebaugh@gmail.com"]
|
7
|
+
gem.description = %q{Fat-free client-side templates with Slim and CoffeeScript}
|
8
|
+
gem.summary = %q{Take the fat out of your client-side templates with Skim. Skim is the Slim templating engine
|
9
|
+
with embedded CoffeeScript. It compiles to JavaScript templates (.jst), which can then be served by Rails or any other
|
10
|
+
Sprockets-based asset pipeline.}
|
11
|
+
gem.homepage = ""
|
12
|
+
|
13
|
+
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
14
|
+
gem.files = `git ls-files`.split("\n")
|
15
|
+
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
16
|
+
gem.name = "skim"
|
17
|
+
gem.require_paths = ["lib"]
|
18
|
+
gem.version = Skim::VERSION
|
19
|
+
|
20
|
+
gem.add_dependency "slim", "~> 1.1.0"
|
21
|
+
gem.add_dependency "temple", "~> 0.4.0"
|
22
|
+
gem.add_dependency "coffee-script"
|
23
|
+
gem.add_dependency "multi_json"
|
24
|
+
gem.add_dependency "sprockets"
|
25
|
+
|
26
|
+
gem.add_development_dependency "rake"
|
27
|
+
gem.add_development_dependency "execjs"
|
28
|
+
gem.add_development_dependency "minitest-reporters"
|
29
|
+
gem.add_development_dependency "therubyracer"
|
30
|
+
end
|
data/test/context.coffee
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
class Context
|
2
|
+
constructor: ->
|
3
|
+
@var = 'instance'
|
4
|
+
@x = 0
|
5
|
+
|
6
|
+
id_helper: ->
|
7
|
+
"notice"
|
8
|
+
|
9
|
+
hash: ->
|
10
|
+
a: 'The letter a', b: 'The letter b'
|
11
|
+
|
12
|
+
show_first: (show = false) ->
|
13
|
+
show
|
14
|
+
|
15
|
+
define_macro: (name, block) ->
|
16
|
+
@macro ||= {}
|
17
|
+
@macro[name] = block
|
18
|
+
''
|
19
|
+
|
20
|
+
call_macro: (name, args...) ->
|
21
|
+
@macro[name](args...)
|
22
|
+
|
23
|
+
hello_world: (text = "Hello World from @env", opts = {}) ->
|
24
|
+
text + ("#{key} #{value}" for key, value of opts).join(" ")
|
25
|
+
|
26
|
+
callback: (text, block) ->
|
27
|
+
if block
|
28
|
+
"#{text} #{block()} #{text}"
|
29
|
+
else
|
30
|
+
text
|
31
|
+
|
32
|
+
message: (args...) ->
|
33
|
+
args.join(' ')
|
34
|
+
|
35
|
+
action_path: (args...) ->
|
36
|
+
"/action-#{args.join('-')}"
|
37
|
+
|
38
|
+
in_keyword: ->
|
39
|
+
"starts with keyword"
|
40
|
+
|
41
|
+
evil_method: ->
|
42
|
+
"<script>do_something_evil();</script>"
|
43
|
+
|
44
|
+
method_which_returns_true: ->
|
45
|
+
true
|
46
|
+
|
47
|
+
output_number: ->
|
48
|
+
1337
|
49
|
+
|
50
|
+
succ_x: ->
|
51
|
+
@x = @x + 1
|
52
|
+
|
53
|
+
person: ->
|
54
|
+
[{name: 'Joe'}, {name: 'Jack'}]
|
55
|
+
|
56
|
+
people: ->
|
57
|
+
['Andy', 'Fred', 'Daniel'].map (n) -> new Person(n)
|
58
|
+
|
59
|
+
cities: ->
|
60
|
+
['Atlanta', 'Melbourne', 'Karlsruhe']
|
61
|
+
|
62
|
+
people_with_locations: ->
|
63
|
+
@people().map (p, i) =>
|
64
|
+
p.location = new Location(@cities()[i])
|
65
|
+
p
|
66
|
+
|
67
|
+
constant_object: ->
|
68
|
+
@_constant_object ||= {a: 1, b: 2}
|
69
|
+
|
70
|
+
constant_object_size: ->
|
71
|
+
i = 0
|
72
|
+
i++ for k, v of @constant_object()
|
73
|
+
i
|
74
|
+
|
75
|
+
class Person
|
76
|
+
constructor: (@_name) ->
|
77
|
+
name: => @_name
|
78
|
+
city: => @location.city()
|
79
|
+
|
80
|
+
class Location
|
81
|
+
constructor: (@_city) ->
|
82
|
+
city: => @_city
|