temple 0.1.3 → 0.1.4
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -0
- data/README.md +17 -18
- data/Rakefile +17 -12
- data/lib/temple.rb +19 -14
- data/lib/temple/engine.rb +13 -18
- data/lib/temple/erb/engine.rb +13 -0
- data/lib/temple/erb/parser.rb +38 -0
- data/lib/temple/erb/template.rb +7 -0
- data/lib/temple/erb/trimming.rb +33 -0
- data/lib/temple/filter.rb +7 -0
- data/lib/temple/filters/debugger.rb +15 -0
- data/lib/temple/filters/dynamic_inliner.rb +11 -19
- data/lib/temple/filters/escape_html.rb +27 -0
- data/lib/temple/filters/multi_flattener.rb +5 -13
- data/lib/temple/filters/static_merger.rb +6 -14
- data/lib/temple/{core.rb → generators.rb} +78 -37
- data/lib/temple/html/fast.rb +79 -127
- data/lib/temple/html/pretty.rb +75 -0
- data/lib/temple/mixins.rb +66 -0
- data/lib/temple/template.rb +35 -0
- data/lib/temple/utils.rb +51 -15
- data/lib/temple/version.rb +3 -0
- data/temple.gemspec +8 -13
- data/test/filters/test_dynamic_inliner.rb +42 -58
- data/test/filters/test_escape_html.rb +32 -0
- data/test/filters/test_multi_flattener.rb +33 -0
- data/test/filters/test_static_merger.rb +21 -23
- data/test/helper.rb +11 -17
- data/test/html/test_fast.rb +153 -0
- data/test/test_erb.rb +68 -0
- data/test/test_generator.rb +69 -76
- data/test/test_utils.rb +28 -0
- metadata +45 -16
- data/lib/temple/engines/erb.rb +0 -93
- data/lib/temple/generator.rb +0 -76
- data/lib/temple/parsers/erb.rb +0 -83
- data/test/engines/hello.erb +0 -4
- data/test/engines/test_erb.rb +0 -495
- data/test/engines/test_erb_m17n.rb +0 -132
- data/test/test_temple.rb +0 -28
@@ -0,0 +1,75 @@
|
|
1
|
+
module Temple
|
2
|
+
module HTML
|
3
|
+
class Pretty < Fast
|
4
|
+
set_default_options :indent => ' ',
|
5
|
+
:pretty => true
|
6
|
+
|
7
|
+
INDENT_TAGS = %w(base div doctype form head html img input li link meta ol
|
8
|
+
script style table tbody td th thead title tr ul).freeze
|
9
|
+
|
10
|
+
def initialize(opts = {})
|
11
|
+
super
|
12
|
+
@last = nil
|
13
|
+
@stack = []
|
14
|
+
end
|
15
|
+
|
16
|
+
def on_static(content)
|
17
|
+
if options[:pretty]
|
18
|
+
@last = nil
|
19
|
+
[:static, content.gsub("\n", indent)]
|
20
|
+
else
|
21
|
+
[:static, content]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def on_dynamic(content)
|
26
|
+
if options[:pretty]
|
27
|
+
@last = nil
|
28
|
+
[:dynamic, %{(#{content}).to_s.gsub("\n", #{indent.inspect})}]
|
29
|
+
else
|
30
|
+
[:dynamic, content]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def on_html_doctype(type)
|
35
|
+
@last = 'doctype'
|
36
|
+
super
|
37
|
+
end
|
38
|
+
|
39
|
+
def on_html_comment(content)
|
40
|
+
return super if !options[:pretty]
|
41
|
+
[:multi, [:static, indent], super]
|
42
|
+
end
|
43
|
+
|
44
|
+
def on_html_tag(name, attrs, closed, content)
|
45
|
+
return super if !options[:pretty]
|
46
|
+
|
47
|
+
closed ||= options[:autoclose].include?(name)
|
48
|
+
raise "Closed tag #{name} has content" if closed && !empty_exp?(content)
|
49
|
+
|
50
|
+
result = [:multi, [:static, "#{tag_indent(name)}<#{name}"], compile!(attrs)]
|
51
|
+
result << [:static, ' /'] if closed && xhtml?
|
52
|
+
result << [:static, '>']
|
53
|
+
|
54
|
+
@stack << name
|
55
|
+
@last = name
|
56
|
+
result << compile!(content)
|
57
|
+
@stack.pop
|
58
|
+
|
59
|
+
result << [:static, "#{tag_indent(name)}</#{name}>"] if !closed
|
60
|
+
@last = name
|
61
|
+
result
|
62
|
+
end
|
63
|
+
|
64
|
+
# Return indentation if not in pre tag
|
65
|
+
def indent
|
66
|
+
@stack.include?('pre') ? '' : ("\n" + ((options[:indent] || '') * @stack.size))
|
67
|
+
end
|
68
|
+
|
69
|
+
# Return indentation before tag
|
70
|
+
def tag_indent(name)
|
71
|
+
@last && (INDENT_TAGS.include?(@last) || INDENT_TAGS.include?(name)) ? indent : ''
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module Temple
|
2
|
+
module Mixins
|
3
|
+
module Dispatcher
|
4
|
+
def self.included(base)
|
5
|
+
base.class_eval { extend ClassMethods }
|
6
|
+
end
|
7
|
+
|
8
|
+
def compile(exp)
|
9
|
+
compile!(exp)
|
10
|
+
end
|
11
|
+
|
12
|
+
def compile!(exp)
|
13
|
+
type, *args = exp
|
14
|
+
if respond_to?("on_#{type}")
|
15
|
+
send("on_#{type}", *args)
|
16
|
+
else
|
17
|
+
exp
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def on_multi(*exps)
|
22
|
+
[:multi, *exps.map {|exp| compile!(exp) }]
|
23
|
+
end
|
24
|
+
|
25
|
+
def on_capture(name, exp)
|
26
|
+
[:capture, name, compile!(exp)]
|
27
|
+
end
|
28
|
+
|
29
|
+
module ClassMethods
|
30
|
+
def temple_dispatch(*bases)
|
31
|
+
bases.each do |base|
|
32
|
+
class_eval %{def on_#{base}(type, *args)
|
33
|
+
if respond_to?("on_" #{base.to_s.inspect} "_\#{type}")
|
34
|
+
send("on_" #{base.to_s.inspect} "_\#{type}", *args)
|
35
|
+
else
|
36
|
+
[:#{base}, type, *args]
|
37
|
+
end
|
38
|
+
end}
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
module Options
|
45
|
+
def self.included(base)
|
46
|
+
base.class_eval { extend ClassMethods }
|
47
|
+
end
|
48
|
+
|
49
|
+
attr_reader :options
|
50
|
+
|
51
|
+
def initialize(options = {})
|
52
|
+
@options = self.class.default_options.merge(options)
|
53
|
+
end
|
54
|
+
|
55
|
+
module ClassMethods
|
56
|
+
def set_default_options(opts)
|
57
|
+
default_options.merge!(opts)
|
58
|
+
end
|
59
|
+
|
60
|
+
def default_options(opts = nil)
|
61
|
+
@default_options ||= superclass.respond_to?(:default_options) ? superclass.default_options.dup : {}
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'tilt'
|
2
|
+
|
3
|
+
module Temple
|
4
|
+
# Tilt template implementation for Temple
|
5
|
+
class Template < Tilt::Template
|
6
|
+
class << self
|
7
|
+
def engine(engine = nil)
|
8
|
+
if engine
|
9
|
+
@engine = engine
|
10
|
+
elsif @engine
|
11
|
+
@engine
|
12
|
+
else
|
13
|
+
raise 'No engine configured'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# Prepare Temple template
|
19
|
+
#
|
20
|
+
# Called immediately after template data is loaded.
|
21
|
+
#
|
22
|
+
# @return [void]
|
23
|
+
def prepare
|
24
|
+
@src = self.class.engine.new(options.merge(:file => eval_file)).compile(data)
|
25
|
+
end
|
26
|
+
|
27
|
+
# A string containing the (Ruby) source code for the template.
|
28
|
+
#
|
29
|
+
# @param [Hash] locals Local variables
|
30
|
+
# @return [String] Compiled template ruby code
|
31
|
+
def precompiled_template(locals = {})
|
32
|
+
@src
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/temple/utils.rb
CHANGED
@@ -1,26 +1,62 @@
|
|
1
|
-
require 'irb/ruby-lex'
|
2
|
-
require 'stringio'
|
3
|
-
|
4
1
|
module Temple
|
5
2
|
module Utils
|
6
3
|
extend self
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
4
|
+
|
5
|
+
# Returns an escaped copy of `html`.
|
6
|
+
# Strings which are declared as html_safe are not escaped.
|
7
|
+
#
|
8
|
+
# @param html [String] The string to escape
|
9
|
+
# @return [String] The escaped string
|
10
|
+
# @api public
|
11
|
+
def escape_html_safe(html)
|
12
|
+
html.html_safe? ? html : escape_html(html)
|
13
|
+
end
|
14
|
+
|
15
|
+
if defined?(EscapeUtils)
|
16
|
+
# Returns an escaped copy of `html`.
|
17
|
+
#
|
18
|
+
# @param html [String] The string to escape
|
19
|
+
# @return [String] The escaped string
|
20
|
+
# @api public
|
21
|
+
def escape_html(html)
|
22
|
+
EscapeUtils.escape_html(html.to_s)
|
23
|
+
end
|
24
|
+
elsif RUBY_VERSION > '1.9'
|
25
|
+
# Used by escape_html
|
26
|
+
# @api private
|
27
|
+
ESCAPE_HTML = {
|
28
|
+
'&' => '&',
|
29
|
+
'"' => '"',
|
30
|
+
'<' => '<',
|
31
|
+
'>' => '>',
|
32
|
+
'/' => '/',
|
33
|
+
}.freeze
|
34
|
+
|
35
|
+
# Returns an escaped copy of `html`.
|
36
|
+
#
|
37
|
+
# @param html [String] The string to escape
|
38
|
+
# @return [String] The escaped string
|
39
|
+
# @api public
|
40
|
+
def escape_html(html)
|
41
|
+
html.to_s.gsub(/[&\"<>\/]/, ESCAPE_HTML)
|
42
|
+
end
|
43
|
+
else
|
44
|
+
# Returns an escaped copy of `html`.
|
45
|
+
#
|
46
|
+
# @param html [String] The string to escape
|
47
|
+
# @return [String] The escaped string
|
48
|
+
# @api public
|
49
|
+
def escape_html(html)
|
50
|
+
html.to_s.gsub(/&/n, '&').gsub(/\"/n, '"').gsub(/>/n, '>').gsub(/</n, '<').gsub(/\//, '/')
|
51
|
+
end
|
18
52
|
end
|
19
53
|
|
20
54
|
def empty_exp?(exp)
|
21
55
|
case exp[0]
|
22
56
|
when :multi
|
23
|
-
exp[1..-1].all? {
|
57
|
+
exp[1..-1].all? {|e| empty_exp?(e) }
|
58
|
+
when :newline
|
59
|
+
true
|
24
60
|
else
|
25
61
|
false
|
26
62
|
end
|
data/temple.gemspec
CHANGED
@@ -1,26 +1,21 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
|
+
require File.dirname(__FILE__) + "/lib/temple/version"
|
2
3
|
|
3
4
|
Gem::Specification.new do |s|
|
4
5
|
s.name = %q{temple}
|
5
|
-
s.version =
|
6
|
+
s.version = Temple::VERSION
|
6
7
|
|
7
|
-
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
8
|
s.authors = ["Magnus Holm"]
|
9
|
-
s.date = %q{2010-
|
9
|
+
s.date = %q{2010-11-03}
|
10
10
|
s.email = %q{judofyr@gmail.com}
|
11
|
-
s.files = [".yardopts", "LICENSE", "README.md", "Rakefile", "lib/temple.rb", "lib/temple/core.rb", "lib/temple/engine.rb", "lib/temple/engines/erb.rb", "lib/temple/filters/dynamic_inliner.rb", "lib/temple/filters/multi_flattener.rb", "lib/temple/filters/static_merger.rb", "lib/temple/generator.rb", "lib/temple/html/fast.rb", "lib/temple/parsers/erb.rb", "lib/temple/utils.rb", "temple.gemspec", "test/engines/hello.erb", "test/engines/test_erb.rb", "test/engines/test_erb_m17n.rb", "test/filters/test_dynamic_inliner.rb", "test/filters/test_static_merger.rb", "test/helper.rb", "test/test_generator.rb", "test/test_temple.rb"]
|
12
11
|
s.homepage = %q{http://dojo.rubyforge.org/}
|
13
12
|
s.require_paths = ["lib"]
|
14
13
|
s.rubygems_version = %q{1.3.6}
|
15
|
-
s.summary = %q{Template compilation framework in
|
14
|
+
s.summary = %q{Template compilation framework in Ruby}
|
16
15
|
|
17
|
-
|
18
|
-
|
19
|
-
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
19
|
|
21
|
-
|
22
|
-
else
|
23
|
-
end
|
24
|
-
else
|
25
|
-
end
|
20
|
+
s.add_development_dependency('bacon')
|
26
21
|
end
|
@@ -1,76 +1,66 @@
|
|
1
|
-
require
|
1
|
+
require 'helper'
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
describe Temple::Filters::DynamicInliner do
|
4
|
+
before do
|
5
|
+
@filter = Temple::Filters::DynamicInliner.new
|
6
|
+
end
|
7
|
+
|
8
|
+
it 'should compile several statics into dynamic' do
|
9
|
+
@filter.compile([:multi,
|
6
10
|
[:static, "Hello "],
|
7
11
|
[:static, "World\n "],
|
8
12
|
[:static, "Have a nice day"]
|
9
|
-
])
|
10
|
-
|
11
|
-
assert_equal([:multi,
|
12
|
-
[:dynamic, '"Hello World\n Have a nice day"']
|
13
|
-
], exp)
|
13
|
+
]).should.equal [:multi, [:dynamic, '"Hello World\n Have a nice day"']]
|
14
14
|
end
|
15
15
|
|
16
|
-
|
17
|
-
|
16
|
+
it 'should compile several dynamics into dynamic' do
|
17
|
+
@filter.compile([:multi,
|
18
18
|
[:dynamic, "@hello"],
|
19
19
|
[:dynamic, "@world"],
|
20
20
|
[:dynamic, "@yeah"]
|
21
|
-
])
|
22
|
-
|
23
|
-
assert_equal([:multi,
|
24
|
-
[:dynamic, '"#{@hello}#{@world}#{@yeah}"']
|
25
|
-
], exp)
|
21
|
+
]).should.equal [:multi, [:dynamic, '"#{@hello}#{@world}#{@yeah}"']]
|
26
22
|
end
|
27
|
-
|
28
|
-
|
29
|
-
|
23
|
+
|
24
|
+
it 'should compile static and dynamic into dynamic' do
|
25
|
+
@filter.compile([:multi,
|
30
26
|
[:static, "Hello"],
|
31
27
|
[:dynamic, "@world"],
|
32
28
|
[:dynamic, "@yeah"],
|
33
29
|
[:static, "Nice"]
|
34
|
-
])
|
35
|
-
|
36
|
-
assert_equal([:multi,
|
37
|
-
[:dynamic, '"Hello#{@world}#{@yeah}Nice"']
|
38
|
-
], exp)
|
30
|
+
]).should.equal [:multi, [:dynamic, '"Hello#{@world}#{@yeah}Nice"']]
|
39
31
|
end
|
40
|
-
|
41
|
-
|
32
|
+
|
33
|
+
it 'should merge statics and dynamics around a block' do
|
42
34
|
exp = @filter.compile([:multi,
|
43
35
|
[:static, "Hello "],
|
44
36
|
[:dynamic, "@world"],
|
45
37
|
[:block, "Oh yeah"],
|
46
38
|
[:dynamic, "@yeah"],
|
47
39
|
[:static, "Once more"]
|
48
|
-
])
|
49
|
-
|
50
|
-
assert_equal([:multi,
|
40
|
+
]).should.equal [:multi,
|
51
41
|
[:dynamic, '"Hello #{@world}"'],
|
52
42
|
[:block, "Oh yeah"],
|
53
43
|
[:dynamic, '"#{@yeah}Once more"']
|
54
|
-
]
|
44
|
+
]
|
55
45
|
end
|
56
|
-
|
57
|
-
|
46
|
+
|
47
|
+
it 'should keep blocks intact' do
|
58
48
|
exp = [:multi, [:block, 'foo']]
|
59
|
-
|
49
|
+
@filter.compile(exp).should.equal exp
|
60
50
|
end
|
61
|
-
|
62
|
-
|
51
|
+
|
52
|
+
it 'should keep single statics intact' do
|
63
53
|
exp = [:multi, [:static, 'foo']]
|
64
|
-
|
54
|
+
@filter.compile(exp).should.equal exp
|
65
55
|
end
|
66
56
|
|
67
|
-
|
57
|
+
it 'should keep single dynamic intact' do
|
68
58
|
exp = [:multi, [:dynamic, 'foo']]
|
69
|
-
|
59
|
+
@filter.compile(exp).should.equal exp
|
70
60
|
end
|
71
|
-
|
72
|
-
|
73
|
-
|
61
|
+
|
62
|
+
it 'should inline inside multi' do
|
63
|
+
@filter.compile([:multi,
|
74
64
|
[:static, "Hello "],
|
75
65
|
[:dynamic, "@world"],
|
76
66
|
[:multi,
|
@@ -78,39 +68,33 @@ class TestTempleFiltersDynamicInliner < TestFilter(:DynamicInliner)
|
|
78
68
|
[:dynamic, "@world"]],
|
79
69
|
[:static, "Hello "],
|
80
70
|
[:dynamic, "@world"]
|
81
|
-
])
|
82
|
-
|
83
|
-
assert_equal([:multi,
|
71
|
+
]).should.equal [:multi,
|
84
72
|
[:dynamic, '"Hello #{@world}"'],
|
85
73
|
[:multi, [:dynamic, '"Hello #{@world}"']],
|
86
74
|
[:dynamic, '"Hello #{@world}"']
|
87
|
-
]
|
75
|
+
]
|
88
76
|
end
|
89
|
-
|
90
|
-
|
77
|
+
|
78
|
+
it 'should merge across newlines' do
|
91
79
|
exp = @filter.compile([:multi,
|
92
80
|
[:static, "Hello \n"],
|
93
81
|
[:newline],
|
94
82
|
[:dynamic, "@world"],
|
95
83
|
[:newline]
|
96
|
-
])
|
97
|
-
|
98
|
-
assert_equal([:multi,
|
84
|
+
]).should.equal [:multi,
|
99
85
|
[:dynamic, ['"Hello \n"', '"#{@world}"', '""'].join("\\\n")]
|
100
|
-
]
|
86
|
+
]
|
101
87
|
end
|
102
|
-
|
103
|
-
|
104
|
-
|
88
|
+
|
89
|
+
it 'should compile static followed by newline' do
|
90
|
+
@filter.compile([:multi,
|
105
91
|
[:static, "Hello \n"],
|
106
92
|
[:newline],
|
107
93
|
[:block, "world"]
|
108
|
-
])
|
109
|
-
|
110
|
-
assert_equal([:multi,
|
94
|
+
]).should.equal [:multi,
|
111
95
|
[:static, "Hello \n"],
|
112
96
|
[:newline],
|
113
97
|
[:block, "world"]
|
114
|
-
]
|
98
|
+
]
|
115
99
|
end
|
116
100
|
end
|