temple 0.1.3 → 0.1.4
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/.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
|