temple 0.0.1
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/README +7 -0
- data/Rakefile +20 -0
- data/VERSION +1 -0
- data/lib/temple.rb +19 -0
- data/lib/temple/core.rb +84 -0
- data/lib/temple/engine.rb +42 -0
- data/lib/temple/filters/dynamic_inliner.rb +62 -0
- data/lib/temple/filters/escapable.rb +25 -0
- data/lib/temple/filters/mustache.rb +70 -0
- data/lib/temple/filters/static_merger.rb +41 -0
- data/lib/temple/generator.rb +32 -0
- data/lib/temple/parsers/erb.rb +28 -0
- data/lib/temple/parsers/mustache.rb +68 -0
- data/spec/dynamic_inliner_spec.rb +79 -0
- data/spec/escapable_spec.rb +24 -0
- data/spec/spec_helper.rb +15 -0
- data/spec/static_merger_spec.rb +27 -0
- data/spec/temple_spec.rb +5 -0
- data/temple.gemspec +61 -0
- metadata +77 -0
data/README
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
= Temple, a template compilation framework for Ruby
|
2
|
+
|
3
|
+
I just wrote a 7521px long blog post about Temple, and I'm way too tired to
|
4
|
+
write anything sensible here.
|
5
|
+
|
6
|
+
* Read http://judofyr.net/posts/temple.html
|
7
|
+
* Join http://groups.google.com/group/guardians-of-the-temple
|
data/Rakefile
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
task :default => :spec
|
2
|
+
require 'spec/rake/spectask'
|
3
|
+
Spec::Rake::SpecTask.new {|t| t.spec_opts = ['--color']}
|
4
|
+
|
5
|
+
|
6
|
+
begin
|
7
|
+
project = 'temple'
|
8
|
+
require 'jeweler'
|
9
|
+
Jeweler::Tasks.new do |gem|
|
10
|
+
gem.name = project
|
11
|
+
gem.summary = "Template compilation framework in Ruby"
|
12
|
+
gem.email = "judofyr@gmail.com"
|
13
|
+
gem.homepage = "http://github.com/judofyr/#{project}"
|
14
|
+
gem.authors = ["Magnus Holm"]
|
15
|
+
end
|
16
|
+
|
17
|
+
Jeweler::GemcutterTasks.new
|
18
|
+
rescue LoadError
|
19
|
+
puts "Jeweler, or one of its dependencies, is not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
20
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
data/lib/temple.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
module Temple
|
2
|
+
VERSION = File.read( File.join(File.dirname(__FILE__),'..','VERSION') ).strip.split('.').map{|i|i.to_i}
|
3
|
+
|
4
|
+
autoload :Core, 'temple/core'
|
5
|
+
autoload :Engine, 'temple/engine'
|
6
|
+
autoload :Generator, 'temple/generator'
|
7
|
+
|
8
|
+
module Parsers
|
9
|
+
autoload :ERB, 'temple/parsers/erb'
|
10
|
+
autoload :Mustache, 'temple/parsers/mustache'
|
11
|
+
end
|
12
|
+
|
13
|
+
module Filters
|
14
|
+
autoload :Mustache, 'temple/filters/mustache'
|
15
|
+
autoload :StaticMerger, 'temple/filters/static_merger'
|
16
|
+
autoload :DynamicInliner, 'temple/filters/dynamic_inliner'
|
17
|
+
autoload :Escapable, 'temple/filters/escapable'
|
18
|
+
end
|
19
|
+
end
|
data/lib/temple/core.rb
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
module Temple
|
2
|
+
module Core
|
3
|
+
# _buf = []
|
4
|
+
# _buf << "static"
|
5
|
+
# _buf << dynamic
|
6
|
+
# block do
|
7
|
+
# _buf << "more static"
|
8
|
+
# end
|
9
|
+
# _buf.join
|
10
|
+
class ArrayBuffer < Generator
|
11
|
+
def preamble; buffer " = []\n" end
|
12
|
+
def postamble; buffer ".join" end
|
13
|
+
|
14
|
+
def on_static(text)
|
15
|
+
buffer " << #{text.inspect}\n"
|
16
|
+
end
|
17
|
+
|
18
|
+
def on_dynamic(code)
|
19
|
+
buffer " << (#{code})\n"
|
20
|
+
end
|
21
|
+
|
22
|
+
def on_block(code)
|
23
|
+
code + "\n"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Just like ArrayBuffer, but doesn't call #join on the array.
|
28
|
+
class Array < ArrayBuffer
|
29
|
+
def postamble; buffer; end
|
30
|
+
end
|
31
|
+
|
32
|
+
# _buf = ''
|
33
|
+
# _buf << "static"
|
34
|
+
# _buf << dynamic.to_s
|
35
|
+
# block do
|
36
|
+
# _buf << "more static"
|
37
|
+
# end
|
38
|
+
# _buf
|
39
|
+
class StringBuffer < ArrayBuffer
|
40
|
+
def preamble; buffer " = ''\n" end
|
41
|
+
def postamble; buffer end
|
42
|
+
|
43
|
+
def on_dynamic(code)
|
44
|
+
buffer " << (#{code}).to_s\n"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Compiles into a single double-quoted string.
|
49
|
+
#
|
50
|
+
# This doesn't make so much sense when you have blocks, so it's
|
51
|
+
# kinda funky. You probably want to use Filters::DynamicInliner instead.
|
52
|
+
# For now, check out the source for #on_multi.
|
53
|
+
class Interpolation < Generator
|
54
|
+
def preamble; '"' end
|
55
|
+
def postamble; '"' end
|
56
|
+
|
57
|
+
def on_static(text)
|
58
|
+
text.inspect[1..-2]
|
59
|
+
end
|
60
|
+
|
61
|
+
def on_dynamic(code)
|
62
|
+
'#{%s}' % code
|
63
|
+
end
|
64
|
+
|
65
|
+
def on_multi(*exps)
|
66
|
+
if exps.detect { |exp| exp[0] == :block }
|
67
|
+
'#{%s}' % exps.map do |exp|
|
68
|
+
if exp[0] == :block
|
69
|
+
exp[1]
|
70
|
+
else
|
71
|
+
compile(exp)
|
72
|
+
end
|
73
|
+
end.join
|
74
|
+
else
|
75
|
+
super
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def on_block(code)
|
80
|
+
'#{%s;nil}' % code
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Temple
|
2
|
+
# An engine is simply a chain of compilers (that includes both parsers,
|
3
|
+
# filters and generators).
|
4
|
+
#
|
5
|
+
# class ERB < Temple::Engine
|
6
|
+
# parser :ERB # shortcut for use Temple::Parsers::ERB
|
7
|
+
# filter :Escapable # use Temple::Filters::Escapable
|
8
|
+
# filter :DynamicInliner # use Temple::Filters::DynamicInliner
|
9
|
+
# generator :ArrayBuffer # use Temple::Core::ArrayBuffer
|
10
|
+
# end
|
11
|
+
class Engine
|
12
|
+
def self.filters
|
13
|
+
@filters ||= []
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.use(filter, *args, &blk)
|
17
|
+
filters << [filter, args, blk]
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.parser(parser, *args, &blk)
|
21
|
+
use(Temple::Parsers.const_get(parser), *args, &blk)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.filter(filter, *args, &blk)
|
25
|
+
use(Temple::Filters.const_get(filter), *args, &blk)
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.generator(compiler, *args, &blk)
|
29
|
+
use(Temple::Core.const_get(compiler), *args, &blk)
|
30
|
+
end
|
31
|
+
|
32
|
+
def initialize(options = {})
|
33
|
+
@chain = self.class.filters.map do |filter, args, blk|
|
34
|
+
filter.new(*args, &blk)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def compile(thing)
|
39
|
+
@chain.inject(thing) { |m, e| e.compile(m) }
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Temple
|
2
|
+
module Filters
|
3
|
+
# Inlines several static/dynamic into a single dynamic.
|
4
|
+
class DynamicInliner
|
5
|
+
def compile(exp)
|
6
|
+
exp.first == :multi ? on_multi(*exp[1..-1]) : exp
|
7
|
+
end
|
8
|
+
|
9
|
+
def on_multi(*exps)
|
10
|
+
res = [:multi]
|
11
|
+
curr = nil
|
12
|
+
prev = nil
|
13
|
+
state = :looking
|
14
|
+
|
15
|
+
# We add a noop because we need to do some cleanup at the end too.
|
16
|
+
(exps + [:noop]).each do |exp|
|
17
|
+
head, arg = exp
|
18
|
+
|
19
|
+
if head == :dynamic || head == :static
|
20
|
+
case state
|
21
|
+
when :looking
|
22
|
+
# Found a single static/dynamic. We don't want to turn this
|
23
|
+
# into a dynamic yet. Instead we store it, and if we find
|
24
|
+
# another one, we add both then.
|
25
|
+
state = :single
|
26
|
+
prev = exp
|
27
|
+
curr = [:dynamic, '"' + send(head, arg)]
|
28
|
+
when :single
|
29
|
+
# Yes! We found another one. Append the content to the current
|
30
|
+
# dynamic and add it to the result.
|
31
|
+
curr[1] << send(head, arg)
|
32
|
+
res << curr
|
33
|
+
state = :several
|
34
|
+
when :several
|
35
|
+
# Yet another dynamic/single. Just add it now.
|
36
|
+
curr[1] << send(head, arg)
|
37
|
+
end
|
38
|
+
else
|
39
|
+
# We need to add the closing quote.
|
40
|
+
curr[1] << '"' unless state == :looking
|
41
|
+
# If we found a single exp last time, let's add it.
|
42
|
+
res << prev if state == :single
|
43
|
+
# Compile the current exp (unless it's the noop)
|
44
|
+
res << compile(exp) unless head == :noop
|
45
|
+
# Now we're looking for more!
|
46
|
+
state = :looking
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
res
|
51
|
+
end
|
52
|
+
|
53
|
+
def static(str)
|
54
|
+
str.inspect[1..-2]
|
55
|
+
end
|
56
|
+
|
57
|
+
def dynamic(str)
|
58
|
+
'#{%s}' % str
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Temple
|
2
|
+
module Filters
|
3
|
+
class Escapable
|
4
|
+
def initialize(options = {})
|
5
|
+
@escaper = options[:escaper] || 'CGI.escapeHTML(%s.to_s)'
|
6
|
+
end
|
7
|
+
|
8
|
+
def compile(exp)
|
9
|
+
return exp if !exp.is_a?(Enumerable) || exp.is_a?(String)
|
10
|
+
|
11
|
+
if exp[0] == :static && is_escape?(exp[1])
|
12
|
+
[:static, eval(@escaper % exp[1][1].inspect)]
|
13
|
+
elsif is_escape?(exp)
|
14
|
+
@escaper % exp[1]
|
15
|
+
else
|
16
|
+
exp.map { |e| compile(e) }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def is_escape?(exp)
|
21
|
+
exp.respond_to?(:[]) && exp[0] == :escape
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module Temple
|
2
|
+
module Filters
|
3
|
+
# A Mustache filter which compiles Mustache-nodes into Core and Escapable.
|
4
|
+
# It's currently built for the Interpolation generator, but works with the
|
5
|
+
# others too.
|
6
|
+
class Mustache
|
7
|
+
def initialize
|
8
|
+
@tmpid = 0
|
9
|
+
end
|
10
|
+
|
11
|
+
def tmpid
|
12
|
+
@tmpid += 1
|
13
|
+
end
|
14
|
+
|
15
|
+
def compile(exp)
|
16
|
+
case exp.first
|
17
|
+
when :mustache
|
18
|
+
send("on_#{exp[1]}", *exp[2..-1])
|
19
|
+
when :multi
|
20
|
+
[:multi, *exp[1..-1].map { |e| compile(e) }]
|
21
|
+
else
|
22
|
+
exp
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def on_evar(name)
|
27
|
+
exp = on_var(name)
|
28
|
+
exp[1] = [:escape, exp[1]]
|
29
|
+
exp
|
30
|
+
end
|
31
|
+
|
32
|
+
def on_var(name)
|
33
|
+
[:dynamic, "ctx[#{name.inspect}]"]
|
34
|
+
end
|
35
|
+
|
36
|
+
def on_section(name, content)
|
37
|
+
res = [:multi]
|
38
|
+
code = compile(content)
|
39
|
+
ctxtmp = "ctx#{tmpid}"
|
40
|
+
|
41
|
+
block = <<-EOF
|
42
|
+
if v = ctx[#{name.inspect}]
|
43
|
+
v = [v] if v.is_a?(Hash) # shortcut when passed a single hash
|
44
|
+
if v.respond_to?(:each)
|
45
|
+
#{ctxtmp} = ctx.dup
|
46
|
+
begin
|
47
|
+
r = v.map { |h| ctx.update(h); CODE }.join
|
48
|
+
rescue TypeError => e
|
49
|
+
raise TypeError,
|
50
|
+
"All elements in {{#{name.to_s}}} are not hashes!"
|
51
|
+
end
|
52
|
+
ctx.replace(#{ctxtmp})
|
53
|
+
r
|
54
|
+
else
|
55
|
+
CODE
|
56
|
+
end
|
57
|
+
end
|
58
|
+
EOF
|
59
|
+
|
60
|
+
block.split("CODE").each do |str|
|
61
|
+
res << [:block, str]
|
62
|
+
res << code
|
63
|
+
end
|
64
|
+
|
65
|
+
res.pop
|
66
|
+
res
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Temple
|
2
|
+
module Filters
|
3
|
+
# Merges several statics into a single static. Example:
|
4
|
+
#
|
5
|
+
# [:multi,
|
6
|
+
# [:static, "Hello "],
|
7
|
+
# [:static, "World!"]]
|
8
|
+
#
|
9
|
+
# Compiles to:
|
10
|
+
#
|
11
|
+
# [:multi,
|
12
|
+
# [:static, "Hello World!"]]
|
13
|
+
class StaticMerger
|
14
|
+
def compile(exp)
|
15
|
+
exp.first == :multi ? on_multi(*exp[1..-1]) : exp
|
16
|
+
end
|
17
|
+
|
18
|
+
def on_multi(*exps)
|
19
|
+
res = [:multi]
|
20
|
+
curr = nil
|
21
|
+
state = :looking
|
22
|
+
|
23
|
+
exps.each do |exp|
|
24
|
+
if exp.first == :static
|
25
|
+
if state == :looking
|
26
|
+
res << [:static, (curr = exp[1].dup)]
|
27
|
+
state = :static
|
28
|
+
else
|
29
|
+
curr << exp[1]
|
30
|
+
end
|
31
|
+
else
|
32
|
+
res << compile(exp)
|
33
|
+
state = :looking
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
res
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Temple
|
2
|
+
class Generator
|
3
|
+
DEFAULT_OPTIONS = {
|
4
|
+
:buffer => "_buf"
|
5
|
+
}
|
6
|
+
|
7
|
+
def initialize(options = {})
|
8
|
+
@options = DEFAULT_OPTIONS.merge(options)
|
9
|
+
end
|
10
|
+
|
11
|
+
def compile(exp)
|
12
|
+
preamble + compile_part(exp) + postamble
|
13
|
+
end
|
14
|
+
|
15
|
+
def compile_part(exp)
|
16
|
+
send("on_#{exp.first}", *exp[1..-1])
|
17
|
+
end
|
18
|
+
|
19
|
+
def buffer(str = '')
|
20
|
+
@options[:buffer] + str
|
21
|
+
end
|
22
|
+
|
23
|
+
# Sensible defaults
|
24
|
+
|
25
|
+
def preamble; '' end
|
26
|
+
def postamble; '' end
|
27
|
+
|
28
|
+
def on_multi(*exp)
|
29
|
+
exp.map { |e| compile_part(e) }.join
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Temple
|
2
|
+
module Parsers
|
3
|
+
# A dead simple ERB parser which compiles directly to Core.
|
4
|
+
# It's crappy, but it works for now.
|
5
|
+
class ERB
|
6
|
+
def compile(src)
|
7
|
+
result = [:multi]
|
8
|
+
while src =~ /<%(.*?)%>/
|
9
|
+
result << [:static, $`]
|
10
|
+
text = $1[1..-1].strip
|
11
|
+
case $1[0]
|
12
|
+
when ?#
|
13
|
+
next
|
14
|
+
when ?=
|
15
|
+
text = [:escape, text] if $1[1] == ?=
|
16
|
+
head = :dynamic
|
17
|
+
else
|
18
|
+
head = :block
|
19
|
+
end
|
20
|
+
result << [head, text]
|
21
|
+
src = $'
|
22
|
+
end
|
23
|
+
result << [:static, src]
|
24
|
+
result
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module Temple
|
2
|
+
module Parsers
|
3
|
+
# A Mustache parser which compiles into Mustache-nodes:
|
4
|
+
#
|
5
|
+
# {{ name }} # => [:mustache, :evar, :name]
|
6
|
+
# {{{ name }}} # => [:mustache, :var, :name]
|
7
|
+
# {{#name}} code {{/name}} # => [:mustache, :section, :name, code]
|
8
|
+
# {{> simple }} # => [:mustache, :partial, :simple]
|
9
|
+
class Mustache
|
10
|
+
attr_accessor :otag, :ctag
|
11
|
+
|
12
|
+
def compile(src)
|
13
|
+
res = [:multi]
|
14
|
+
while src =~ /#{otag}\#([^\}]*)#{ctag}\s*(.+?)#{otag}\/\1#{ctag}\s*/m
|
15
|
+
# $` = The string to the left of the last successful match
|
16
|
+
res << compile_tags($`)
|
17
|
+
name = $1.strip.to_sym
|
18
|
+
code = compile($2)
|
19
|
+
res << [:mustache, :section, name, code]
|
20
|
+
# $' = The string to the right of the last successful match
|
21
|
+
src = $'
|
22
|
+
end
|
23
|
+
res << compile_tags(src)
|
24
|
+
end
|
25
|
+
|
26
|
+
def compile_tags(src)
|
27
|
+
res = [:multi]
|
28
|
+
while src =~ /#{otag}(#|=|!|<|>|\{)?(.+?)\1?#{ctag}+/
|
29
|
+
res << [:static, $`]
|
30
|
+
case $1
|
31
|
+
when '#'
|
32
|
+
raise "Unclosed section"
|
33
|
+
when '!'
|
34
|
+
# ignore comments
|
35
|
+
when '='
|
36
|
+
self.otag, self.ctag = $2.strip.split(' ', 2)
|
37
|
+
when '>', '<'
|
38
|
+
res << [:mustache, :partial, $2.strip.to_sym]
|
39
|
+
when '{'
|
40
|
+
res << [:mustache, :var, $2.strip.to_sym]
|
41
|
+
else
|
42
|
+
res << [:mustache, :evar, $2.strip.to_sym]
|
43
|
+
end
|
44
|
+
src = $'
|
45
|
+
end
|
46
|
+
res << [:static, src]
|
47
|
+
end
|
48
|
+
|
49
|
+
# {{ - opening tag delimiter
|
50
|
+
def otag
|
51
|
+
@otag ||= Regexp.escape('{{')
|
52
|
+
end
|
53
|
+
|
54
|
+
def otag=(tag)
|
55
|
+
@otag = Regexp.escape(tag)
|
56
|
+
end
|
57
|
+
|
58
|
+
# }} - closing tag delimiter
|
59
|
+
def ctag
|
60
|
+
@ctag ||= Regexp.escape('}}')
|
61
|
+
end
|
62
|
+
|
63
|
+
def ctag=(tag)
|
64
|
+
@ctag = Regexp.escape(tag)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
|
+
|
3
|
+
describe_filter :DynamicInliner do
|
4
|
+
it "should merge several statics into dynamic" do
|
5
|
+
@filter.compile([:multi,
|
6
|
+
[:static, "Hello "],
|
7
|
+
[:static, "World\n "],
|
8
|
+
[:static, "Have a nice day"]
|
9
|
+
]).should == [:multi,
|
10
|
+
[:dynamic, '"Hello World\n Have a nice day"']
|
11
|
+
]
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should merge several dynamics into a single dynamic" do
|
15
|
+
@filter.compile([:multi,
|
16
|
+
[:dynamic, "@hello"],
|
17
|
+
[:dynamic, "@world"],
|
18
|
+
[:dynamic, "@yeah"]
|
19
|
+
]).should == [:multi,
|
20
|
+
[:dynamic, '"#{@hello}#{@world}#{@yeah}"']
|
21
|
+
]
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should merge static+dynamic into dynamic" do
|
25
|
+
@filter.compile([:multi,
|
26
|
+
[:static, "Hello"],
|
27
|
+
[:dynamic, "@world"],
|
28
|
+
[:dynamic, "@yeah"],
|
29
|
+
[:static, "Nice"]
|
30
|
+
]).should == [:multi,
|
31
|
+
[:dynamic, '"Hello#{@world}#{@yeah}Nice"']
|
32
|
+
]
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should merge static+dynamic around blocks" do
|
36
|
+
@filter.compile([:multi,
|
37
|
+
[:static, "Hello "],
|
38
|
+
[:dynamic, "@world"],
|
39
|
+
[:block, "Oh yeah"],
|
40
|
+
[:dynamic, "@yeah"],
|
41
|
+
[:static, "Once more"]
|
42
|
+
]).should == [:multi,
|
43
|
+
[:dynamic, '"Hello #{@world}"'],
|
44
|
+
[:block, "Oh yeah"],
|
45
|
+
[:dynamic, '"#{@yeah}Once more"']
|
46
|
+
]
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should keep blocks intact" do
|
50
|
+
exp = [:multi, [:block, 'foo']]
|
51
|
+
@filter.compile(exp).should == exp
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should keep single static intact" do
|
55
|
+
exp = [:multi, [:static, 'foo']]
|
56
|
+
@filter.compile(exp).should == exp
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should keep single dynamic intact" do
|
60
|
+
exp = [:multi, [:dynamic, 'foo']]
|
61
|
+
@filter.compile(exp).should == exp
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should inline inside multi" do
|
65
|
+
@filter.compile([:multi,
|
66
|
+
[:static, "Hello "],
|
67
|
+
[:dynamic, "@world"],
|
68
|
+
[:multi,
|
69
|
+
[:static, "Hello "],
|
70
|
+
[:dynamic, "@world"]],
|
71
|
+
[:static, "Hello "],
|
72
|
+
[:dynamic, "@world"]
|
73
|
+
]).should == [:multi,
|
74
|
+
[:dynamic, '"Hello #{@world}"'],
|
75
|
+
[:multi, [:dynamic, '"Hello #{@world}"']],
|
76
|
+
[:dynamic, '"Hello #{@world}"']
|
77
|
+
]
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
|
+
require 'cgi'
|
3
|
+
|
4
|
+
describe_filter :Escapable do
|
5
|
+
it "should escape everywhere" do
|
6
|
+
@filter.compile([:multi,
|
7
|
+
[:dynamic, [:escape, "@hello"]],
|
8
|
+
[:block, [:escape, "@world"]]
|
9
|
+
]).should == [:multi,
|
10
|
+
[:dynamic, "CGI.escapeHTML(@hello.to_s)"],
|
11
|
+
[:block, "CGI.escapeHTML(@world.to_s)"]
|
12
|
+
]
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should automatically escape static content" do
|
16
|
+
@filter.compile([:multi,
|
17
|
+
[:static, [:escape, "<hello>"]],
|
18
|
+
[:block, [:escape, "@world"]]
|
19
|
+
]).should == [:multi,
|
20
|
+
[:static, "<hello>"],
|
21
|
+
[:block, "CGI.escapeHTML(@world.to_s)"]
|
22
|
+
]
|
23
|
+
end
|
24
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
$LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'temple'
|
5
|
+
|
6
|
+
def describe_filter(name, &blk)
|
7
|
+
klass = Temple::Filters.const_get(name)
|
8
|
+
describe(klass) do
|
9
|
+
before do
|
10
|
+
@filter = klass.new
|
11
|
+
end
|
12
|
+
|
13
|
+
instance_eval(&blk)
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
|
+
|
3
|
+
describe_filter :StaticMerger do
|
4
|
+
it "should merge several statics" do
|
5
|
+
@filter.compile([:multi,
|
6
|
+
[:static, "Hello "],
|
7
|
+
[:static, "World, "],
|
8
|
+
[:static, "Good night"]
|
9
|
+
]).should == [:multi,
|
10
|
+
[:static, "Hello World, Good night"]
|
11
|
+
]
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should merge several statics around block" do
|
15
|
+
@filter.compile([:multi,
|
16
|
+
[:static, "Hello "],
|
17
|
+
[:static, "World!"],
|
18
|
+
[:block, "123"],
|
19
|
+
[:static, "Good night, "],
|
20
|
+
[:static, "everybody"]
|
21
|
+
]).should == [:multi,
|
22
|
+
[:static, "Hello World!"],
|
23
|
+
[:block, "123"],
|
24
|
+
[:static, "Good night, everybody"]
|
25
|
+
]
|
26
|
+
end
|
27
|
+
end
|
data/spec/temple_spec.rb
ADDED
data/temple.gemspec
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{temple}
|
8
|
+
s.version = "0.0.1"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Magnus Holm"]
|
12
|
+
s.date = %q{2009-12-15}
|
13
|
+
s.email = %q{judofyr@gmail.com}
|
14
|
+
s.extra_rdoc_files = [
|
15
|
+
"README"
|
16
|
+
]
|
17
|
+
s.files = [
|
18
|
+
"README",
|
19
|
+
"Rakefile",
|
20
|
+
"VERSION",
|
21
|
+
"lib/temple.rb",
|
22
|
+
"lib/temple/core.rb",
|
23
|
+
"lib/temple/engine.rb",
|
24
|
+
"lib/temple/filters/dynamic_inliner.rb",
|
25
|
+
"lib/temple/filters/escapable.rb",
|
26
|
+
"lib/temple/filters/mustache.rb",
|
27
|
+
"lib/temple/filters/static_merger.rb",
|
28
|
+
"lib/temple/generator.rb",
|
29
|
+
"lib/temple/parsers/erb.rb",
|
30
|
+
"lib/temple/parsers/mustache.rb",
|
31
|
+
"spec/dynamic_inliner_spec.rb",
|
32
|
+
"spec/escapable_spec.rb",
|
33
|
+
"spec/spec_helper.rb",
|
34
|
+
"spec/static_merger_spec.rb",
|
35
|
+
"spec/temple_spec.rb",
|
36
|
+
"temple.gemspec"
|
37
|
+
]
|
38
|
+
s.homepage = %q{http://github.com/judofyr/temple}
|
39
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
40
|
+
s.require_paths = ["lib"]
|
41
|
+
s.rubygems_version = %q{1.3.5}
|
42
|
+
s.summary = %q{Template compilation framework in Ruby}
|
43
|
+
s.test_files = [
|
44
|
+
"spec/spec_helper.rb",
|
45
|
+
"spec/static_merger_spec.rb",
|
46
|
+
"spec/escapable_spec.rb",
|
47
|
+
"spec/dynamic_inliner_spec.rb",
|
48
|
+
"spec/temple_spec.rb"
|
49
|
+
]
|
50
|
+
|
51
|
+
if s.respond_to? :specification_version then
|
52
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
53
|
+
s.specification_version = 3
|
54
|
+
|
55
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
56
|
+
else
|
57
|
+
end
|
58
|
+
else
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
metadata
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: temple
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Magnus Holm
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-12-15 00:00:00 +01:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description:
|
17
|
+
email: judofyr@gmail.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- README
|
24
|
+
files:
|
25
|
+
- README
|
26
|
+
- Rakefile
|
27
|
+
- VERSION
|
28
|
+
- lib/temple.rb
|
29
|
+
- lib/temple/core.rb
|
30
|
+
- lib/temple/engine.rb
|
31
|
+
- lib/temple/filters/dynamic_inliner.rb
|
32
|
+
- lib/temple/filters/escapable.rb
|
33
|
+
- lib/temple/filters/mustache.rb
|
34
|
+
- lib/temple/filters/static_merger.rb
|
35
|
+
- lib/temple/generator.rb
|
36
|
+
- lib/temple/parsers/erb.rb
|
37
|
+
- lib/temple/parsers/mustache.rb
|
38
|
+
- spec/dynamic_inliner_spec.rb
|
39
|
+
- spec/escapable_spec.rb
|
40
|
+
- spec/spec_helper.rb
|
41
|
+
- spec/static_merger_spec.rb
|
42
|
+
- spec/temple_spec.rb
|
43
|
+
- temple.gemspec
|
44
|
+
has_rdoc: true
|
45
|
+
homepage: http://github.com/judofyr/temple
|
46
|
+
licenses: []
|
47
|
+
|
48
|
+
post_install_message:
|
49
|
+
rdoc_options:
|
50
|
+
- --charset=UTF-8
|
51
|
+
require_paths:
|
52
|
+
- lib
|
53
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: "0"
|
58
|
+
version:
|
59
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: "0"
|
64
|
+
version:
|
65
|
+
requirements: []
|
66
|
+
|
67
|
+
rubyforge_project:
|
68
|
+
rubygems_version: 1.3.5
|
69
|
+
signing_key:
|
70
|
+
specification_version: 3
|
71
|
+
summary: Template compilation framework in Ruby
|
72
|
+
test_files:
|
73
|
+
- spec/spec_helper.rb
|
74
|
+
- spec/static_merger_spec.rb
|
75
|
+
- spec/escapable_spec.rb
|
76
|
+
- spec/dynamic_inliner_spec.rb
|
77
|
+
- spec/temple_spec.rb
|