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.
@@ -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
- LITERAL_TOKENS = [RubyToken::TkSTRING, RubyToken::TkDSTRING]
9
-
10
- def literal_string?(str)
11
- lexer = RubyLex.new
12
- lexer.set_input(StringIO.new(str.strip))
13
-
14
- # The first token has to be a string.
15
- LITERAL_TOKENS.include?(lexer.token.class) and
16
- # That has to be the only token.
17
- lexer.token.nil?
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
+ '&' => '&amp;',
29
+ '"' => '&quot;',
30
+ '<' => '&lt;',
31
+ '>' => '&gt;',
32
+ '/' => '&#47;',
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, '&amp;').gsub(/\"/n, '&quot;').gsub(/>/n, '&gt;').gsub(/</n, '&lt;').gsub(/\//, '&#47;')
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? { |e| e[0] == :newline }
57
+ exp[1..-1].all? {|e| empty_exp?(e) }
58
+ when :newline
59
+ true
24
60
  else
25
61
  false
26
62
  end
@@ -0,0 +1,3 @@
1
+ module Temple
2
+ VERSION = '0.1.4'
3
+ 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 = "0.1.3"
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-01-23}
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 RUby}
14
+ s.summary = %q{Template compilation framework in Ruby}
16
15
 
17
- if s.respond_to? :specification_version then
18
- current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
19
- s.specification_version = 3
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
- if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
22
- else
23
- end
24
- else
25
- end
20
+ s.add_development_dependency('bacon')
26
21
  end
@@ -1,76 +1,66 @@
1
- require File.dirname(__FILE__) + '/../helper'
1
+ require 'helper'
2
2
 
3
- class TestTempleFiltersDynamicInliner < TestFilter(:DynamicInliner)
4
- def test_several_statics_into_dynamic
5
- exp = @filter.compile([:multi,
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
- def test_several_dynamics_into_dynamic
17
- exp = @filter.compile([:multi,
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
- def test_static_and_dynamic_into_dynamic
29
- exp = @filter.compile([:multi,
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
- def test_static_and_dynamic_around_blocks
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
- ], exp)
44
+ ]
55
45
  end
56
-
57
- def test_keep_blocks_intact
46
+
47
+ it 'should keep blocks intact' do
58
48
  exp = [:multi, [:block, 'foo']]
59
- assert_equal(exp, @filter.compile(exp))
49
+ @filter.compile(exp).should.equal exp
60
50
  end
61
-
62
- def test_keep_single_static_intact
51
+
52
+ it 'should keep single statics intact' do
63
53
  exp = [:multi, [:static, 'foo']]
64
- assert_equal(exp, @filter.compile(exp))
54
+ @filter.compile(exp).should.equal exp
65
55
  end
66
56
 
67
- def test_keep_single_dynamic_intact
57
+ it 'should keep single dynamic intact' do
68
58
  exp = [:multi, [:dynamic, 'foo']]
69
- assert_equal(exp, @filter.compile(exp))
59
+ @filter.compile(exp).should.equal exp
70
60
  end
71
-
72
- def test_inline_inside_multi
73
- exp = @filter.compile([:multi,
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
- ], exp)
75
+ ]
88
76
  end
89
-
90
- def test_merge_across_newlines
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
- ], exp)
86
+ ]
101
87
  end
102
-
103
- def test_static_followed_by_newline
104
- exp = @filter.compile([:multi,
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
- ], exp)
98
+ ]
115
99
  end
116
100
  end