temple 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. data/.yardopts +2 -1
  2. data/CHANGES +63 -0
  3. data/EXPRESSIONS.md +250 -0
  4. data/README.md +24 -12
  5. data/lib/temple.rb +34 -18
  6. data/lib/temple/engine.rb +11 -7
  7. data/lib/temple/erb/engine.rb +5 -3
  8. data/lib/temple/erb/parser.rb +5 -2
  9. data/lib/temple/erb/template.rb +11 -0
  10. data/lib/temple/erb/trimming.rb +9 -1
  11. data/lib/temple/filter.rb +2 -0
  12. data/lib/temple/filters/control_flow.rb +43 -0
  13. data/lib/temple/filters/dynamic_inliner.rb +29 -32
  14. data/lib/temple/filters/eraser.rb +22 -0
  15. data/lib/temple/filters/escapable.rb +39 -0
  16. data/lib/temple/filters/multi_flattener.rb +4 -1
  17. data/lib/temple/filters/static_merger.rb +11 -10
  18. data/lib/temple/filters/validator.rb +15 -0
  19. data/lib/temple/generators.rb +41 -100
  20. data/lib/temple/grammar.rb +56 -0
  21. data/lib/temple/hash.rb +48 -0
  22. data/lib/temple/html/dispatcher.rb +10 -4
  23. data/lib/temple/html/fast.rb +50 -38
  24. data/lib/temple/html/filter.rb +8 -0
  25. data/lib/temple/html/pretty.rb +25 -14
  26. data/lib/temple/mixins/dispatcher.rb +103 -0
  27. data/lib/temple/{mixins.rb → mixins/engine_dsl.rb} +10 -95
  28. data/lib/temple/mixins/grammar_dsl.rb +166 -0
  29. data/lib/temple/mixins/options.rb +28 -0
  30. data/lib/temple/mixins/template.rb +25 -0
  31. data/lib/temple/templates.rb +2 -0
  32. data/lib/temple/utils.rb +11 -57
  33. data/lib/temple/version.rb +1 -1
  34. data/test/filters/test_control_flow.rb +92 -0
  35. data/test/filters/test_dynamic_inliner.rb +7 -7
  36. data/test/filters/test_eraser.rb +55 -0
  37. data/test/filters/{test_escape_html.rb → test_escapable.rb} +13 -6
  38. data/test/filters/test_multi_flattener.rb +1 -1
  39. data/test/filters/test_static_merger.rb +3 -3
  40. data/test/helper.rb +8 -0
  41. data/test/html/test_fast.rb +42 -57
  42. data/test/html/test_pretty.rb +10 -7
  43. data/test/mixins/test_dispatcher.rb +31 -0
  44. data/test/mixins/test_grammar_dsl.rb +86 -0
  45. data/test/test_engine.rb +73 -57
  46. data/test/test_erb.rb +0 -7
  47. data/test/test_filter.rb +26 -0
  48. data/test/test_generator.rb +57 -36
  49. data/test/test_grammar.rb +52 -0
  50. data/test/test_hash.rb +39 -0
  51. data/test/test_utils.rb +11 -38
  52. metadata +34 -10
  53. data/lib/temple/filters/debugger.rb +0 -26
  54. data/lib/temple/filters/escape_html.rb +0 -33
@@ -6,10 +6,10 @@ describe Temple::HTML::Pretty do
6
6
  end
7
7
 
8
8
  it 'should indent nested tags' do
9
- @html.call([:html, :tag, 'div', [:multi], false,
10
- [:html, :tag, 'p', [:multi], false, [:multi, [:static, 'text'], [:dynamic, 'code']]]
9
+ @html.call([:html, :tag, 'div', [:multi],
10
+ [:html, :tag, 'p', [:multi], [:multi, [:static, 'text'], [:dynamic, 'code']]]
11
11
  ]).should.equal [:multi,
12
- [:block, "_temple_pre_tags = /<pre|<textarea/"],
12
+ [:code, "_temple_html_pretty1 = /<code|<pre|<textarea/"],
13
13
  [:multi,
14
14
  [:static, "<div"],
15
15
  [:multi],
@@ -20,17 +20,20 @@ describe Temple::HTML::Pretty do
20
20
  [:static, ">"],
21
21
  [:multi,
22
22
  [:static, "text"],
23
- [:dynamic, 'Temple::Utils.indent((code), "\n ", _temple_pre_tags)']],
23
+ [:multi,
24
+ [:code, "_temple_html_pretty2 = (code).to_s"],
25
+ [:code, '_temple_html_pretty2.gsub!("\n", "\n ") if _temple_html_pretty1 !~ _temple_html_pretty2'],
26
+ [:dynamic, "_temple_html_pretty2"]]],
24
27
  [:static, "</p>"]],
25
28
  [:static, "\n</div>"]]]
26
29
  end
27
30
 
28
31
 
29
32
  it 'should not indent preformatted tags' do
30
- @html.call([:html, :tag, 'pre', [:multi], false,
31
- [:html, :tag, 'p', [:multi], false, [:static, 'text']]
33
+ @html.call([:html, :tag, 'pre', [:multi],
34
+ [:html, :tag, 'p', [:multi], [:static, 'text']]
32
35
  ]).should.equal [:multi,
33
- [:block, "_temple_pre_tags = /<pre|<textarea/"],
36
+ [:code, "_temple_html_pretty1 = /<code|<pre|<textarea/"],
34
37
  [:multi,
35
38
  [:static, "<pre"],
36
39
  [:multi],
@@ -0,0 +1,31 @@
1
+ require 'helper'
2
+
3
+ class FilterWithDispatcherMixin
4
+ include Temple::Mixins::Dispatcher
5
+
6
+ def on_test(arg)
7
+ [:on_test, arg]
8
+ end
9
+
10
+ def on_second_test(arg)
11
+ [:on_second_test, arg]
12
+ end
13
+ end
14
+
15
+ describe Temple::Mixins::Dispatcher do
16
+ before do
17
+ @filter = FilterWithDispatcherMixin.new
18
+ end
19
+
20
+ it 'should return unhandled expressions' do
21
+ @filter.call([:unhandled]).should.equal [:unhandled]
22
+ end
23
+
24
+ it 'should dispatch first level' do
25
+ @filter.call([:test, 42]).should.equal [:on_test, 42]
26
+ end
27
+
28
+ it 'should dispatch second level' do
29
+ @filter.call([:second, :test, 42]).should.equal [:on_second_test, 42]
30
+ end
31
+ end
@@ -0,0 +1,86 @@
1
+ require 'helper'
2
+
3
+ module BasicGrammar
4
+ extend Temple::Mixins::GrammarDSL
5
+
6
+ Expression <<
7
+ Symbol |
8
+ Answer |
9
+ [:zero_or_more, 'Expression*'] |
10
+ [:one_or_more, 'Expression+'] |
11
+ [:zero_or_one, 'Expression?'] |
12
+ [:bool, Bool] |
13
+ nil
14
+
15
+ Bool <<
16
+ true | false
17
+
18
+ Answer <<
19
+ Value(42)
20
+
21
+ end
22
+
23
+ module ExtendedGrammar
24
+ extend BasicGrammar
25
+
26
+ Expression << [:extended, Expression]
27
+ end
28
+
29
+ describe Temple::Mixins::GrammarDSL do
30
+ it 'should support class types' do
31
+ BasicGrammar.should.match :symbol
32
+ BasicGrammar.should.not.match [:symbol]
33
+ BasicGrammar.should.not.match 'string'
34
+ BasicGrammar.should.not.match ['string']
35
+ end
36
+
37
+ it 'should support value types' do
38
+ BasicGrammar.should.match 42
39
+ BasicGrammar.should.not.match 43
40
+ end
41
+
42
+ it 'should support nesting' do
43
+ BasicGrammar.should.match [:zero_or_more, [:zero_or_more]]
44
+ end
45
+
46
+ it 'should support *' do
47
+ BasicGrammar.should.match [:zero_or_more]
48
+ BasicGrammar.should.match [:zero_or_more, nil, 42]
49
+ end
50
+
51
+ it 'should support +' do
52
+ BasicGrammar.should.not.match [:one_or_more]
53
+ BasicGrammar.should.match [:one_or_more, 42]
54
+ BasicGrammar.should.match [:one_or_more, 42, nil]
55
+ end
56
+
57
+ it 'should support ?' do
58
+ BasicGrammar.should.not.match [:zero_or_one, nil, 42]
59
+ BasicGrammar.should.match [:zero_or_one]
60
+ BasicGrammar.should.match [:zero_or_one, 42]
61
+ end
62
+
63
+ it 'should support extended grammars' do
64
+ ExtendedGrammar.should.match [:extended, [:extended, 42]]
65
+ BasicGrammar.should.not.match [:zero_or_more, [:extended, nil]]
66
+ BasicGrammar.should.not.match [:extended, [:extended, 42]]
67
+ end
68
+
69
+ it 'should have validate!' do
70
+ grammar_validate BasicGrammar,
71
+ [:zero_or_more, [:zero_or_more, [:unknown]]],
72
+ "BasicGrammar::Expression did not match\n[:unknown]\n"
73
+
74
+ grammar_validate BasicGrammar,
75
+ [:zero_or_more, [:one_or_more]],
76
+ "BasicGrammar::Expression did not match\n[:one_or_more]\n"
77
+
78
+ grammar_validate BasicGrammar,
79
+ [:zero_or_more, 123, [:unknown]],
80
+ "BasicGrammar::Expression did not match\n123\n"
81
+
82
+ grammar_validate BasicGrammar,
83
+ [:bool, 123],
84
+ "BasicGrammar::Bool did not match\n123\n"
85
+ end
86
+ end
data/test/test_engine.rb CHANGED
@@ -50,86 +50,94 @@ describe Temple::Engine do
50
50
  end
51
51
 
52
52
  it 'should instantiate chain' do
53
- e = TestEngine.new
54
- e.chain[0].should.be.instance_of Method
55
- e.chain[1].should.be.instance_of Method
56
- e.chain[2].should.be.instance_of Method
57
- e.chain[3].should.be.instance_of Temple::HTML::Pretty
58
- e.chain[4].should.be.instance_of Temple::Filters::MultiFlattener
59
- e.chain[5].should.be.instance_of Temple::Generators::ArrayBuffer
60
- e.chain[6].should.be.instance_of OtherCallable
53
+ call_chain = TestEngine.new.send(:call_chain)
54
+ call_chain[0].should.be.instance_of Method
55
+ call_chain[1].should.be.instance_of Method
56
+ call_chain[2].should.be.instance_of Method
57
+ call_chain[3].should.be.instance_of Temple::HTML::Pretty
58
+ call_chain[4].should.be.instance_of Temple::Filters::MultiFlattener
59
+ call_chain[5].should.be.instance_of Temple::Generators::ArrayBuffer
60
+ call_chain[6].should.be.instance_of OtherCallable
61
61
  end
62
62
 
63
63
  it 'should have #append' do
64
- e = TestEngine.new do |engine|
65
- engine.append :MyFilter3 do |exp|
66
- exp
67
- end
68
- engine.chain.size.should.equal 8
69
- engine.chain[7].first.should.equal :MyFilter3
70
- engine.chain[7].size.should.equal 2
71
- engine.chain[7].last.should.be.instance_of Method
64
+ engine = TestEngine.new
65
+ call_chain = engine.send(:call_chain)
66
+ call_chain.size.should.equal 7
67
+
68
+ engine.append :MyFilter3 do |exp|
69
+ exp
72
70
  end
73
- e.chain.size.should.equal 8
74
- e.chain[7].should.be.instance_of Method
71
+
72
+ TestEngine.chain.size.should.equal 7
73
+ engine.chain.size.should.equal 8
74
+ engine.chain[7].first.should.equal :MyFilter3
75
+ engine.chain[7].size.should.equal 2
76
+ engine.chain[7].last.should.be.instance_of Method
77
+
78
+ call_chain = engine.send(:call_chain)
79
+ call_chain.size.should.equal 8
80
+ call_chain[7].should.be.instance_of Method
75
81
  end
76
82
 
77
83
  it 'should have #prepend' do
78
- e = TestEngine.new do |engine|
79
- engine.prepend :MyFilter0 do |exp|
80
- exp
81
- end
82
- engine.chain.size.should.equal 8
83
- engine.chain[0].first.should.equal :MyFilter0
84
- engine.chain[0].size.should.equal 2
85
- engine.chain[0].last.should.be.instance_of Method
86
- engine.chain[1].first.should.equal :Parser
84
+ engine = TestEngine.new
85
+ call_chain = engine.send(:call_chain)
86
+ call_chain.size.should.equal 7
87
+
88
+ engine.prepend :MyFilter0 do |exp|
89
+ exp
87
90
  end
88
- e.chain.size.should.equal 8
89
- e.chain[0].should.be.instance_of Method
91
+
92
+ TestEngine.chain.size.should.equal 7
93
+ engine.chain.size.should.equal 8
94
+ engine.chain[0].first.should.equal :MyFilter0
95
+ engine.chain[0].size.should.equal 2
96
+ engine.chain[0].last.should.be.instance_of Method
97
+ engine.chain[1].first.should.equal :Parser
98
+
99
+ call_chain = engine.send(:call_chain)
100
+ call_chain.size.should.equal 8
101
+ call_chain[0].should.be.instance_of Method
90
102
  end
91
103
 
92
104
  it 'should have #after' do
93
- e = TestEngine.new do |engine|
94
- engine.after :Parser, :MyFilter0 do |exp|
95
- exp
96
- end
97
- engine.chain.size.should.equal 8
98
- engine.chain[0].first.should.equal :Parser
99
- engine.chain[1].first.should.equal :MyFilter0
100
- engine.chain[2].first.should.equal :MyFilter1
105
+ engine = TestEngine.new
106
+ engine.after :Parser, :MyFilter0 do |exp|
107
+ exp
101
108
  end
109
+ engine.chain.size.should.equal 8
110
+ engine.chain[0].first.should.equal :Parser
111
+ engine.chain[1].first.should.equal :MyFilter0
112
+ engine.chain[2].first.should.equal :MyFilter1
102
113
  end
103
114
 
104
115
  it 'should have #before' do
105
- e = TestEngine.new do |engine|
106
- engine.before :MyFilter1, :MyFilter0 do |exp|
107
- exp
108
- end
109
- engine.chain.size.should.equal 8
110
- engine.chain[0].first.should.equal :Parser
111
- engine.chain[1].first.should.equal :MyFilter0
112
- engine.chain[2].first.should.equal :MyFilter1
116
+ engine = TestEngine.new
117
+ engine.before :MyFilter1, :MyFilter0 do |exp|
118
+ exp
113
119
  end
120
+ engine.chain.size.should.equal 8
121
+ engine.chain[0].first.should.equal :Parser
122
+ engine.chain[1].first.should.equal :MyFilter0
123
+ engine.chain[2].first.should.equal :MyFilter1
114
124
  end
115
125
 
116
126
  it 'should have #remove' do
117
- e = TestEngine.new do |engine|
118
- engine.remove :MyFilter1
119
- engine.chain.size.should.equal 6
120
- engine.chain[0].first.should.equal :Parser
121
- engine.chain[1].first.should.equal :MyFilter2
122
- end
127
+ engine = TestEngine.new
128
+ engine.remove :MyFilter1
129
+ engine.chain.size.should.equal 6
130
+ engine.chain[0].first.should.equal :Parser
131
+ engine.chain[1].first.should.equal :MyFilter2
123
132
  end
124
133
 
125
134
  it 'should have #replace' do
126
- e = TestEngine.new do |engine|
127
- engine.before :Parser, :MyParser do |exp|
128
- exp
129
- end
130
- engine.chain.size.should.equal 8
131
- engine.chain[0].first.should.equal :MyParser
135
+ engine = TestEngine.new
136
+ engine.before :Parser, :MyParser do |exp|
137
+ exp
132
138
  end
139
+ engine.chain.size.should.equal 8
140
+ engine.chain[0].first.should.equal :MyParser
133
141
  end
134
142
 
135
143
  it 'should work with inheritance' do
@@ -141,4 +149,12 @@ describe Temple::Engine do
141
149
  inherited_engine.chain.size.should.equal 8
142
150
  TestEngine.chain.size.should.equal 7
143
151
  end
152
+
153
+ it 'should support chain option' do
154
+ engine = TestEngine.new(:chain => proc {|e| e.remove :MyFilter1 })
155
+ TestEngine.chain.size.should.equal 7
156
+ engine.chain.size.should.equal 6
157
+ engine.chain[0].first.should.equal :Parser
158
+ engine.chain[1].first.should.equal :MyFilter2
159
+ end
144
160
  end
data/test/test_erb.rb CHANGED
@@ -2,14 +2,7 @@ require 'helper'
2
2
  require 'erb'
3
3
  require 'tilt'
4
4
 
5
- class Bacon::Context
6
- def erb(src, options = {})
7
- Temple::Templates::Tilt(Temple::ERB::Engine).new(options) { src }.render
8
- end
9
- end
10
-
11
5
  describe Temple::ERB::Engine do
12
-
13
6
  it 'should compile erb' do
14
7
  src = %q{
15
8
  %% hi
@@ -0,0 +1,26 @@
1
+ require 'helper'
2
+
3
+ class SimpleFilter < Temple::Filter
4
+ def on_test(arg)
5
+ [:on_test, arg]
6
+ end
7
+ end
8
+
9
+ describe Temple::Filter do
10
+ it 'should support options' do
11
+ Temple::Filter.should.respond_to :default_options
12
+ Temple::Filter.should.respond_to :set_default_options
13
+ Temple::Filter.new.options.should.be.instance_of Temple::ImmutableHash
14
+ Temple::Filter.new(:key => 3).options[:key].should.equal 3
15
+ end
16
+
17
+ it 'should implement call' do
18
+ Temple::Filter.new.call([:exp]).should.equal [:exp]
19
+ end
20
+
21
+ it 'should process expressions' do
22
+ filter = SimpleFilter.new
23
+ filter.call([:unhandled]).should.equal [:unhandled]
24
+ filter.call([:test, 42]).should.equal [:on_test, 42]
25
+ end
26
+ end
@@ -17,8 +17,8 @@ class SimpleGenerator < Temple::Generator
17
17
  concat "D:#{s}"
18
18
  end
19
19
 
20
- def on_block(s)
21
- "B:#{s}"
20
+ def on_code(s)
21
+ "C:#{s}"
22
22
  end
23
23
  end
24
24
 
@@ -26,69 +26,90 @@ describe Temple::Generator do
26
26
  it 'should compile simple expressions' do
27
27
  gen = SimpleGenerator.new
28
28
 
29
- gen.call([:static, "test"]).should.match(/ << \(S:test\)/)
30
- gen.call([:dynamic, "test"]).should.match(/ << \(D:test\)/)
31
- gen.call([:block, "test"]).should.match(/B:test/)
29
+ gen.call([:static, 'test']).should.equal '_buf = BUFFER; _buf << (S:test); _buf'
30
+ gen.call([:dynamic, 'test']).should.equal '_buf = BUFFER; _buf << (D:test); _buf'
31
+ gen.call([:code, 'test']).should.equal '_buf = BUFFER; C:test; _buf'
32
32
  end
33
33
 
34
34
  it 'should compile multi expression' do
35
35
  gen = SimpleGenerator.new(:buffer => "VAR")
36
- str = gen.call([:multi,
36
+ gen.call([:multi,
37
37
  [:static, "static"],
38
38
  [:dynamic, "dynamic"],
39
- [:block, "block"]
40
- ])
41
-
42
- str.should.match(/VAR = BUFFER/)
43
- str.should.match(/VAR << \(S:static\)/)
44
- str.should.match(/VAR << \(D:dynamic\)/)
45
- str.should.match(/ B:block /)
39
+ [:code, "code"]
40
+ ]).should.equal 'VAR = BUFFER; VAR << (S:static); VAR << (D:dynamic); C:code; VAR'
46
41
  end
47
42
 
48
43
  it 'should compile capture' do
49
44
  gen = SimpleGenerator.new(:buffer => "VAR", :capture_generator => SimpleGenerator)
50
- str = gen.call([:capture, "foo", [:static, "test"]])
51
-
52
- str.should.match(/foo = BUFFER/)
53
- str.should.match(/foo << \(S:test\)/)
54
- str.should.match(/VAR\Z/)
45
+ gen.call([:capture, "foo",
46
+ [:static, "test"]
47
+ ]).should.equal 'VAR = BUFFER; foo = BUFFER; foo << (S:test); foo; VAR'
55
48
  end
56
49
 
57
50
  it 'should compile capture with multi' do
58
51
  gen = SimpleGenerator.new(:buffer => "VAR", :capture_generator => SimpleGenerator)
59
- str = gen.call([:multi,
52
+ gen.call([:multi,
60
53
  [:static, "before"],
61
54
 
62
55
  [:capture, "foo", [:multi,
63
56
  [:static, "static"],
64
57
  [:dynamic, "dynamic"],
65
- [:block, "block"]]],
58
+ [:code, "code"]]],
66
59
 
67
60
  [:static, "after"]
68
- ])
69
-
70
- str.should.match(/VAR << \(S:before\)/)
71
- str.should.match( /foo = BUFFER/)
72
- str.should.match( /foo << \(S:static\)/)
73
- str.should.match( /foo << \(D:dynamic\)/)
74
- str.should.match( / B:block /)
75
- str.should.match(/VAR << \(S:after\)/)
76
- str.should.match(/VAR\Z/)
61
+ ]).should.equal 'VAR = BUFFER; VAR << (S:before); foo = BUFFER; foo << (S:static); ' +
62
+ 'foo << (D:dynamic); C:code; foo; VAR << (S:after); VAR'
77
63
  end
78
64
 
79
65
  it 'should compile newlines' do
80
66
  gen = SimpleGenerator.new(:buffer => "VAR")
81
- str = gen.call([:multi,
67
+ gen.call([:multi,
82
68
  [:static, "static"],
83
69
  [:newline],
84
70
  [:dynamic, "dynamic"],
85
71
  [:newline],
86
- [:block, "block"]
87
- ])
72
+ [:code, "code"]
73
+ ]).should.equal "VAR = BUFFER; VAR << (S:static); \n; " +
74
+ "VAR << (D:dynamic); \n; C:code; VAR"
75
+ end
76
+ end
88
77
 
89
- lines = str.split("\n")
90
- lines[0].should.match(/VAR << \(S:static\)/)
91
- lines[1].should.match(/VAR << \(D:dynamic\)/)
92
- lines[2].should.match(/ B:block /)
78
+ describe Temple::Generators::Array do
79
+ it 'should compile simple expressions' do
80
+ gen = Temple::Generators::Array.new
81
+ gen.call([:static, 'test']).should.equal '_buf = []; _buf << ("test"); _buf'
82
+ gen.call([:dynamic, 'test']).should.equal '_buf = []; _buf << (test); _buf'
83
+ gen.call([:code, 'test']).should.equal '_buf = []; test; _buf'
84
+ end
85
+ end
86
+
87
+ describe Temple::Generators::ArrayBuffer do
88
+ it 'should compile simple expressions' do
89
+ gen = Temple::Generators::ArrayBuffer.new
90
+ gen.call([:static, 'test']).should.equal '_buf = []; _buf << ("test"); _buf = _buf.join'
91
+ gen.call([:dynamic, 'test']).should.equal '_buf = []; _buf << (test); _buf = _buf.join'
92
+ gen.call([:code, 'test']).should.equal '_buf = []; test; _buf = _buf.join'
93
+ end
94
+ end
95
+
96
+ describe Temple::Generators::StringBuffer do
97
+ it 'should compile simple expressions' do
98
+ gen = Temple::Generators::StringBuffer.new
99
+ gen.call([:static, 'test']).should.equal '_buf = \'\'; _buf << ("test"); _buf'
100
+ gen.call([:dynamic, 'test']).should.equal '_buf = \'\'; _buf << ((test).to_s); _buf'
101
+ gen.call([:code, 'test']).should.equal '_buf = \'\'; test; _buf'
102
+ end
103
+ end
104
+
105
+ describe Temple::Generators::RailsOutputBuffer do
106
+ it 'should compile simple expressions' do
107
+ gen = Temple::Generators::RailsOutputBuffer.new
108
+ gen.call([:static, 'test']).should.equal '@output_buffer = ActionView::OutputBuffer.new; ' +
109
+ '@output_buffer.safe_concat(("test")); @output_buffer'
110
+ gen.call([:dynamic, 'test']).should.equal '@output_buffer = ActionView::OutputBuffer.new; ' +
111
+ '@output_buffer.safe_concat(((test).to_s)); @output_buffer'
112
+ gen.call([:code, 'test']).should.equal '@output_buffer = ActionView::OutputBuffer.new; ' +
113
+ 'test; @output_buffer'
93
114
  end
94
115
  end