temple 0.1.1 → 0.1.2

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.
@@ -1,5 +1,5 @@
1
1
  module Temple
2
- VERSION = "0.1.1"
2
+ VERSION = "0.1.2"
3
3
 
4
4
  autoload :Core, 'temple/core'
5
5
  autoload :Engine, 'temple/engine'
@@ -18,6 +18,9 @@ module Temple
18
18
  autoload :MultiFlattener, 'temple/filters/multi_flattener'
19
19
  autoload :StaticMerger, 'temple/filters/static_merger'
20
20
  autoload :DynamicInliner, 'temple/filters/dynamic_inliner'
21
- autoload :Escapable, 'temple/filters/escapable'
21
+ end
22
+
23
+ module HTML
24
+ autoload :Fast, 'temple/html/fast'
22
25
  end
23
26
  end
@@ -25,8 +25,6 @@ module Temple
25
25
  # === [:static, string]
26
26
  #
27
27
  # Static indicates that the given string should be appended to the result.
28
- # Every \n will be also cause a newline in the generated code. \r\n on the
29
- # other hand, only causes a newline in the result.
30
28
  #
31
29
  # Example:
32
30
  #
@@ -35,18 +33,13 @@ module Temple
35
33
  # _buf << "Hello World"
36
34
  #
37
35
  # [:static, "Hello \n World"]
38
- # # is the same as:
39
- # _buf << "Hello
40
- # World"
41
- #
42
- # [:static, "Hello \r\n World"]
43
36
  # # is the same as
44
37
  # _buf << "Hello\nWorld"
45
38
  #
46
39
  # === [:dynamic, ruby]
47
40
  #
48
41
  # Dynamic indicates that the given Ruby code should be evaluated and then
49
- # appended to the result. Any \n causes a newline in the generated code.
42
+ # appended to the result.
50
43
  #
51
44
  # The Ruby code must be a complete expression in the sense that you can pass
52
45
  # it to eval() and it would not raise SyntaxError.
@@ -129,4 +122,4 @@ module Temple
129
122
  end
130
123
  end
131
124
  end
132
- end
125
+ end
@@ -47,7 +47,7 @@ module Temple
47
47
 
48
48
  def initialize(options = {})
49
49
  @chain = self.class.filters.map do |filter, args, blk|
50
- opt = args.last.is_a?(Hash) ? args.pop : {}
50
+ opt = args.last.is_a?(Hash) ? args.last : {}
51
51
  opt = args.inject(opt) do |memo, ele|
52
52
  memo[ele] = options[ele] if options.has_key?(ele)
53
53
  memo
@@ -61,4 +61,4 @@ module Temple
61
61
  @chain.inject(thing) { |m, e| e.compile(m) }
62
62
  end
63
63
  end
64
- end
64
+ end
@@ -4,19 +4,22 @@ module Temple
4
4
  def initialize(options = {})
5
5
  @options = {}
6
6
  end
7
-
7
+
8
8
  def compile(exp)
9
- return exp unless exp.first == :multi
9
+ exp.first == :multi ? on_multi(*exp[1..-1]) : exp
10
+ end
11
+
12
+ def on_multi(*exps)
10
13
  # If the multi contains a single element, just return the element
11
- return compile(exp[1]) if exp.length == 2
14
+ return compile(exps.first) if exps.length == 1
12
15
  result = [:multi]
13
16
 
14
- exp[1..-1].each do |e|
15
- e = compile(e)
16
- if e.first == :multi
17
- result.concat(e[1..-1])
17
+ exps.each do |exp|
18
+ exp = compile(exp)
19
+ if exp.first == :multi
20
+ result.concat(exp[1..-1])
18
21
  else
19
- result << e
22
+ result << exp
20
23
  end
21
24
  end
22
25
 
@@ -24,4 +27,4 @@ module Temple
24
27
  end
25
28
  end
26
29
  end
27
- end
30
+ end
@@ -26,15 +26,7 @@ module Temple
26
26
  end
27
27
 
28
28
  def self.to_ruby(str)
29
- str.inspect.gsub(/(\\r)?\\n/m) do |str|
30
- if $`[-1] == ?\\
31
- str
32
- elsif $1
33
- "\\n"
34
- else
35
- "\n"
36
- end
37
- end
29
+ str.inspect
38
30
  end
39
31
 
40
32
  def to_ruby(str)
@@ -83,4 +75,4 @@ module Temple
83
75
  @options[:buffer] = prev_buffer
84
76
  end
85
77
  end
86
- end
78
+ end
@@ -0,0 +1,159 @@
1
+ module Temple
2
+ module HTML
3
+ class Fast
4
+ DEFAULT_OPTIONS = {
5
+ :format => :xhtml,
6
+ :attr_wrapper => "'",
7
+ :autoclose => %w[meta img link br hr input area param col base]
8
+ }
9
+
10
+ def initialize(options = {})
11
+ @options = DEFAULT_OPTIONS.merge(options)
12
+
13
+ unless [:xhtml, :html4, :html5].include?(@options[:format])
14
+ raise "Invalid format #{@options[:format].inspect}"
15
+ end
16
+
17
+ end
18
+
19
+ def xhtml?
20
+ @options[:format] == :xhtml
21
+ end
22
+
23
+ def html?
24
+ html5? or html4?
25
+ end
26
+
27
+ def html5?
28
+ @options[:format] == :html5
29
+ end
30
+
31
+ def html4?
32
+ @options[:format] == :html4
33
+ end
34
+
35
+ def compile(exp)
36
+ case exp[0]
37
+ when :multi, :capture
38
+ send("on_#{exp[0]}", *exp[1..-1])
39
+ when :html
40
+ send("on_#{exp[1]}", *exp[2..-1])
41
+ else
42
+ exp
43
+ end
44
+ end
45
+
46
+ def on_multi(*exp)
47
+ [:multi, *exp.map { |e| compile(e) }]
48
+ end
49
+
50
+ def on_doctype(type)
51
+ trailing_newlines = type[/(\A|[^\r])(\n+)\Z/, 2].to_s
52
+
53
+ text = type.to_s.downcase.strip
54
+ if text.index("xml") == 0
55
+ if html?
56
+ return [:multi].concat([[:newline]] * trailing_newlines.size)
57
+ end
58
+
59
+ wrapper = @options[:attr_wrapper]
60
+ str = "<?xml version=#{wrapper}1.0#{wrapper} encoding=#{wrapper}#{text.split(' ')[1] || "utf-8"}#{wrapper} ?>"
61
+ end
62
+
63
+ str = "<!DOCTYPE html>" if html5?
64
+
65
+ str ||= if xhtml?
66
+ case text
67
+ when /^1\.1/; '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">'
68
+ when /^5/; '<!DOCTYPE html>'
69
+ when "strict"; '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">'
70
+ when "frameset"; '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">'
71
+ when "mobile"; '<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.2//EN" "http://www.openmobilealliance.org/tech/DTD/xhtml-mobile12.dtd">'
72
+ when "basic"; '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML Basic 1.1//EN" "http://www.w3.org/TR/xhtml-basic/xhtml-basic11.dtd">'
73
+ else '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'
74
+ end
75
+ elsif html4?
76
+ case text
77
+ when "strict"; '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">'
78
+ when "frameset"; '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">'
79
+ else '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">'
80
+ end
81
+ end
82
+
83
+ str << trailing_newlines
84
+ [:static, str]
85
+ end
86
+
87
+ def on_comment(content)
88
+ [:multi,
89
+ [:static, "<!--"],
90
+ compile(content),
91
+ [:static, "-->"]]
92
+ end
93
+
94
+ def on_tag(name, attrs, content)
95
+ ac = @options[:autoclose].include?(name)
96
+ result = [:multi]
97
+ result << [:static, "<#{name}"]
98
+ result << compile(attrs)
99
+ result << [:static, " /"] if ac && xhtml?
100
+ result << [:static, ">"]
101
+ result << compile(content)
102
+ result << [:static, "</#{name}>"] if !ac
103
+ result
104
+ end
105
+
106
+ def on_attrs(*exp)
107
+ if exp.all? { |e| attr_easily_compilable?(e) }
108
+ [:multi, *merge_basicattrs(exp).map { |e| compile(e) }]
109
+ else
110
+ raise "[:html, :attrs] currently only support basicattrs"
111
+ end
112
+ end
113
+
114
+ def attr_easily_compilable?(exp)
115
+ exp[1] == :basicattr and
116
+ exp[2][0] == :static
117
+ end
118
+
119
+ def merge_basicattrs(attrs)
120
+ result = []
121
+ position = {}
122
+
123
+ attrs.each do |(html, type, (name_type, name), value)|
124
+ if pos = position[name]
125
+ case name
126
+ when 'class', 'id'
127
+ value = [:multi,
128
+ result[pos].last, # previous value
129
+ [:static, (name == 'class' ? ' ' : '_')], # delimiter
130
+ value] # new value
131
+ end
132
+
133
+ result[pos] = [name, value]
134
+ else
135
+ position[name] = result.size
136
+ result << [name, value]
137
+ end
138
+ end
139
+
140
+ final = []
141
+ result.each_with_index do |(name, value), index|
142
+ final << [:html, :basicattr, [:static, name], value]
143
+ end
144
+ final
145
+ end
146
+
147
+ def on_basicattr(name, value)
148
+ [:multi,
149
+ [:static, " "],
150
+ name,
151
+ [:static, "="],
152
+ [:static, @options[:attr_wrapper]],
153
+ value,
154
+ [:static, @options[:attr_wrapper]]]
155
+ end
156
+ end
157
+ end
158
+ end
159
+
@@ -25,7 +25,7 @@ module Temple
25
25
  if scanner.stag.nil?
26
26
  case token
27
27
  when Compiler::PercentLine
28
- result << [:static, content] if content.size > 0
28
+ append_static(result, content) if content.size > 0
29
29
  content = ''
30
30
  result << [:block, token.to_s.strip]
31
31
  result << [:newline]
@@ -35,12 +35,12 @@ module Temple
35
35
  scanner.stag = token
36
36
  when "\n"
37
37
  content << "\n"
38
- result << [:static, content]
38
+ append_static(result, content)
39
39
  content = ''
40
40
  when '<%%'
41
41
  result << [:static, '<%']
42
42
  else
43
- result << [:static, token]
43
+ append_static(result, token)
44
44
  end
45
45
  else
46
46
  case token
@@ -71,6 +71,13 @@ module Temple
71
71
 
72
72
  result
73
73
  end
74
+
75
+ def append_static(result, content)
76
+ result << [:static, content]
77
+ content.count("\n").times do
78
+ result << [:newline]
79
+ end
80
+ end
74
81
  end
75
82
  end
76
- end
83
+ end
@@ -16,5 +16,14 @@ module Temple
16
16
  # That has to be the only token.
17
17
  lexer.token.nil?
18
18
  end
19
+
20
+ def empty_exp?(exp)
21
+ case exp[0]
22
+ when :multi
23
+ exp[1..-1].all? { |e| e[0] == :newline }
24
+ else
25
+ false
26
+ end
27
+ end
19
28
  end
20
- end
29
+ end
@@ -2,13 +2,13 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{temple}
5
- s.version = "0.1.1"
5
+ s.version = "0.1.2"
6
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
9
  s.date = %q{2010-01-23}
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/escapable.rb", "lib/temple/filters/multi_flattener.rb", "lib/temple/filters/static_merger.rb", "lib/temple/generator.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_escapable.rb", "test/filters/test_static_merger.rb", "test/helper.rb", "test/test_generator.rb", "test/test_temple.rb"]
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
12
  s.homepage = %q{http://dojo.rubyforge.org/}
13
13
  s.require_paths = ["lib"]
14
14
  s.rubygems_version = %q{1.3.6}
@@ -9,7 +9,7 @@ class TestTempleFiltersDynamicInliner < TestFilter(:DynamicInliner)
9
9
  ])
10
10
 
11
11
  assert_equal([:multi,
12
- [:dynamic, "\"Hello World\n Have a nice day\""]
12
+ [:dynamic, '"Hello World\n Have a nice day"']
13
13
  ], exp)
14
14
  end
15
15
 
@@ -96,7 +96,7 @@ class TestTempleFiltersDynamicInliner < TestFilter(:DynamicInliner)
96
96
  ])
97
97
 
98
98
  assert_equal([:multi,
99
- [:dynamic, ["\"Hello \n\"", '"#{@world}"', '""'].join("\\\n")],
99
+ [:dynamic, ['"Hello \n"', '"#{@world}"', '""'].join("\\\n")]
100
100
  ], exp)
101
101
  end
102
102
 
@@ -113,4 +113,4 @@ class TestTempleFiltersDynamicInliner < TestFilter(:DynamicInliner)
113
113
  [:block, "world"]
114
114
  ], exp)
115
115
  end
116
- end
116
+ end
@@ -93,30 +93,8 @@ class TestTempleGenerator < Test::Unit::TestCase
93
93
 
94
94
  def test_to_ruby
95
95
  simple = Simple.new
96
-
97
- {
98
- "Hello" => 'Hello',
99
- "Hello\r\nWorld" => 'Hello\nWorld',
100
- "Hello\nWorld" => %w|Hello World|,
101
- "Hello\n\r\n\nWorld" => %w|Hello \n World|,
102
- "\r\n\nHelloWorld\n" => %w|\n HelloWorld .|,
103
- "\nHelloWorld\r\n" => %w|. HelloWorld\n|,
104
- }.
105
- each do |actual, expected|
106
- if expected.is_a?(Array)
107
- expected = expected.map do |x|
108
- if x == "."
109
- # Use the dot so we can easily match a newline
110
- # at the end or the beginning.
111
- ""
112
- else
113
- x
114
- end
115
- end.join("\n")
116
- end
117
-
118
- expected = '"' + expected + '"'
119
- assert_equal(expected, simple.to_ruby(actual))
120
- end
96
+
97
+ assert_equal('"Hello"', simple.to_ruby("Hello"))
98
+ assert_equal('"Hello\nWorld"', simple.to_ruby("Hello\nWorld"))
121
99
  end
122
- end
100
+ end
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 1
8
- - 1
9
- version: 0.1.1
8
+ - 2
9
+ version: 0.1.2
10
10
  platform: ruby
11
11
  authors:
12
12
  - Magnus Holm
@@ -36,10 +36,10 @@ files:
36
36
  - lib/temple/engine.rb
37
37
  - lib/temple/engines/erb.rb
38
38
  - lib/temple/filters/dynamic_inliner.rb
39
- - lib/temple/filters/escapable.rb
40
39
  - lib/temple/filters/multi_flattener.rb
41
40
  - lib/temple/filters/static_merger.rb
42
41
  - lib/temple/generator.rb
42
+ - lib/temple/html/fast.rb
43
43
  - lib/temple/parsers/erb.rb
44
44
  - lib/temple/utils.rb
45
45
  - temple.gemspec
@@ -47,7 +47,6 @@ files:
47
47
  - test/engines/test_erb.rb
48
48
  - test/engines/test_erb_m17n.rb
49
49
  - test/filters/test_dynamic_inliner.rb
50
- - test/filters/test_escapable.rb
51
50
  - test/filters/test_static_merger.rb
52
51
  - test/helper.rb
53
52
  - test/test_generator.rb
@@ -1,32 +0,0 @@
1
- require 'cgi'
2
-
3
- module Temple
4
- module Filters
5
- class Escapable
6
- def initialize(options = {})
7
- @escaper = options[:escaper] || 'CGI.escapeHTML((%s).to_s)'
8
- end
9
-
10
- def compile(exp)
11
- return exp if !exp.is_a?(Enumerable) || exp.is_a?(String)
12
-
13
- if is_escape?(exp)
14
- case exp[1][0]
15
- when :static
16
- [:static, eval(@escaper % exp[1][1].inspect)]
17
- when :dynamic, :block
18
- [exp[1][0], @escaper % exp[1][1]]
19
- else
20
- raise "Escapable can only handle :static, :dynamic and :block for the moment."
21
- end
22
- else
23
- exp.map { |e| compile(e) }
24
- end
25
- end
26
-
27
- def is_escape?(exp)
28
- exp.respond_to?(:[]) && exp[0] == :escape
29
- end
30
- end
31
- end
32
- end
@@ -1,28 +0,0 @@
1
- require File.dirname(__FILE__) + '/../helper'
2
-
3
- class TestTempleFiltersEscapable < TestFilter(:Escapable)
4
- def test_escape
5
- exp = @filter.compile([:multi,
6
- [:escape, [:dynamic, "@hello"]],
7
- [:escape, [:block, "@world"]]
8
- ])
9
-
10
- assert_equal([:multi,
11
- [:dynamic, "CGI.escapeHTML((@hello).to_s)"],
12
- [:block, "CGI.escapeHTML((@world).to_s)"]
13
- ], exp)
14
- end
15
-
16
- def test_escape_static_content
17
- exp = @filter.compile([:multi,
18
- [:escape, [:static, "<hello>"]],
19
- [:escape, [:block, "@world"]]
20
- ])
21
-
22
- assert_equal([:multi,
23
- [:static, "&lt;hello&gt;"],
24
- [:block, "CGI.escapeHTML((@world).to_s)"]
25
- ], exp)
26
- end
27
- end
28
-