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
data/.yardopts CHANGED
@@ -1 +1,2 @@
1
- --title Temple
1
+ --title Temple
2
+ --files EXPRESSIONS.md,CHANGES,LICENSE
data/CHANGES ADDED
@@ -0,0 +1,63 @@
1
+ master
2
+
3
+ * Compiled expression dispatching
4
+ * Method temple_dispatch is obsolete
5
+ * EscapeHTML renamed to Escapable
6
+ * Control flow filter added
7
+ * HTML filter: Tag and attribute expressions changed
8
+ * Expression grammar added
9
+ * Expression validator added
10
+ * Debugger filter removed (Validator is better replacement)
11
+
12
+ 0.2.0
13
+
14
+ * Add mutable/immutable hashes for option inheritance
15
+ * Rails template support added
16
+ * Rename Filter#compile to Filter#call
17
+ * Engine chain reconfiguration (append, prepend, replace, ...)
18
+ * HTML filter: Don't output empty attributes
19
+ * Escape expression changed [:escape, true/false, Expression]
20
+
21
+ 0.1.8
22
+
23
+ * HTML filter: Support :format => :html (alias for :html5)
24
+
25
+ 0.1.7
26
+
27
+ * HTML::Pretty indents dynamic content only if it doesn't contain
28
+ preformatted tags
29
+
30
+ 0.1.6
31
+
32
+ * Flexible chain building
33
+
34
+ 0.1.5
35
+
36
+ * Default options for engines
37
+
38
+ 0.1.4
39
+
40
+ * HTML::Pretty added
41
+ * Tilt-based template class added
42
+ * Escaping filter added
43
+ * Filter base class added
44
+ * Fix capturing (Issue #15)
45
+
46
+ 0.1.3
47
+
48
+ * Close issue #10
49
+ * Refactoring
50
+
51
+ 0.1.2
52
+
53
+ * Add HTML filter
54
+ * Remove Escapable filter
55
+ * Add method for checking if expression is empty
56
+
57
+ 0.1.1
58
+
59
+ * Test added
60
+
61
+ 0.1.0
62
+
63
+ * Initial release
data/EXPRESSIONS.md ADDED
@@ -0,0 +1,250 @@
1
+ Temple expression documentation
2
+ ===============================
3
+
4
+ Temple uses S-expressions to represent the parsed template code. The S-expressions
5
+ are passed from filter to filter until the generator. The generator transforms
6
+ the S-expression to a ruby code string. See the {file:README.md README} for an introduction.
7
+
8
+ In this document we documented all the expressions which are used by Temple. There is also
9
+ a formal grammar which can validate expressions.
10
+
11
+ The Core Abstraction
12
+ --------------------
13
+
14
+ The core abstraction is what every template evetually should be compiled
15
+ to. Currently it consists of six types:
16
+ multi, static, dynamic, code, newline and capture.
17
+
18
+ When compiling, there's two different strings we'll have to think about.
19
+ First we have the generated code. This is what your engine (from Temple's
20
+ point of view) spits out. If you construct this carefully enough, you can
21
+ make exceptions report correct line numbers, which is very convenient.
22
+
23
+ Then there's the result. This is what your engine (from the user's point
24
+ of view) spits out. It's what happens if you evaluate the generated code.
25
+
26
+ ### [:multi, *sexp]
27
+
28
+ Multi is what glues everything together. It's simply a sexp which combines
29
+ several others sexps:
30
+
31
+ [:multi,
32
+ [:static, "Hello "],
33
+ [:dynamic, "@world"]]
34
+
35
+ ### [:static, string]
36
+
37
+ Static indicates that the given string should be appended to the result.
38
+
39
+ Example:
40
+
41
+ [:static, "Hello World"]
42
+ is the same as:
43
+ _buf << "Hello World"
44
+
45
+ [:static, "Hello \n World"]
46
+ is the same as
47
+ _buf << "Hello\nWorld"
48
+
49
+ ### [:dynamic, ruby]
50
+
51
+ Dynamic indicates that the given Ruby code should be evaluated and then
52
+ appended to the result.
53
+
54
+ The Ruby code must be a complete expression in the sense that you can pass
55
+ it to eval() and it would not raise SyntaxError.
56
+
57
+ Example:
58
+
59
+ [:dynamic, 'Math::PI * r**2']
60
+
61
+ ### [:code, ruby]
62
+
63
+ Code indicates that the given Ruby code should be evaluated, and may
64
+ change the control flow. Any \n causes a newline in the generated code.
65
+
66
+ Example:
67
+
68
+ [:code, 'area = Math::PI * r**2']
69
+
70
+ ### [:newline]
71
+
72
+ Newline causes a newline in the generated code, but not in the result.
73
+
74
+ ### [:capture, variable_name, sexp]
75
+
76
+ Evaluates the Sexp using the rules above, but instead of appending to the
77
+ result, it sets the content to the variable given.
78
+
79
+ Example:
80
+
81
+ [:multi,
82
+ [:static, "Some content"],
83
+ [:capture, "foo", [:static, "More content"]],
84
+ [:dynamic, "foo.downcase"]]
85
+ is the same as:
86
+ _buf << "Some content"
87
+ foo = "More content"
88
+ _buf << foo.downcase
89
+
90
+ Control flow abstraction
91
+ ------------------------
92
+
93
+ Control flow abstractions can be used to write common ruby control flow constructs.
94
+ These expressions are compiled to [:code, ruby] by Temple::Filters::ControlFlow
95
+
96
+ ### [:if, condition, if-sexp, optional-else-sexp]
97
+
98
+ Example:
99
+
100
+ [:if,
101
+ "1+1 == 2",
102
+ [:static, "Yes"],
103
+ [:static, "No"]]
104
+ is the same as:
105
+ if 1+1 == 2
106
+ _buf << "Yes"
107
+ else
108
+ _buf << "No"
109
+ end
110
+
111
+ ### [:block, ruby, sexp]
112
+
113
+ Example:
114
+
115
+ [:block,
116
+ '10.times do',
117
+ [:static, 'Hello']]
118
+ is the same as:
119
+ 10.times do
120
+ _buf << 'Hello'
121
+ end
122
+
123
+ ### [:case, argument, [condition, sexp], [condition, sexp], ...]
124
+
125
+ Example:
126
+
127
+ [:case,
128
+ 'value',
129
+ ["1", "value is 1"],
130
+ ["2", "value is 2"],
131
+ [:else, "don't know"]]
132
+ is the same as:
133
+ case value
134
+ when 1
135
+ _buf << "value is 1"
136
+ when 2
137
+ _buf << "value is 2"
138
+ else
139
+ _buf << "don't know"
140
+ end
141
+
142
+ ### [:cond, [condition, sexp], [condition, sexp], ...]
143
+
144
+ [:cond,
145
+ ["a", "a is true"],
146
+ ["b", "b is true"],
147
+ [:else, "a and b are false"]]
148
+ is the same as:
149
+ case
150
+ when a
151
+ _buf << "a is true"
152
+ when b
153
+ _buf << "b is true"
154
+ else
155
+ _buf << "a and b are false"
156
+ end
157
+
158
+ Escape abstraction
159
+ ------------------
160
+
161
+ The Escape abstraction is processed by Temple::Filters::Escapable.
162
+
163
+ ### [:escape, bool, sexp]
164
+
165
+ The boolean flag switches escaping on or off for the content sexp. Dynamic and static
166
+ expressions are manipulated.
167
+
168
+ Example:
169
+
170
+ [:escape, true,
171
+ [:multi,
172
+ [:dynamic, "code"],
173
+ [:static, "<"],
174
+ [:escape, false, [:static, ">"]]]]
175
+ is transformed to
176
+ [:multi,
177
+ [:dynamic, 'escape_html(code)'],
178
+ [:static, '&lt;'],
179
+ [:static, '>']]
180
+
181
+ HTML abstraction
182
+ ----------------
183
+
184
+ The HTML abstraction is processed by the html filters (Temple::HTML::Fast and Temple::HTML::Pretty).
185
+
186
+ ### [:html, :doctype, string]
187
+
188
+ Example:
189
+ [:html, :doctype, '5']
190
+ generates
191
+ <!DOCTYPE html5>
192
+
193
+ Supported doctypes:
194
+
195
+ <table>
196
+ <tr><td><b>Name</b></td><td><b>Generated doctype</b></td></tr>
197
+ <tr><td>1.1</td><td>&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"&gt;</td></tr>
198
+ <tr><td>html, 5</td><td>&lt;!DOCTYPE html></td></tr>
199
+ <tr><td>strict</td><td>&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"&gt;</td></tr>
200
+ <tr><td>frameset</td><td>&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd"&gt;</td></tr>
201
+ <tr><td>mobile</td><td>&lt;!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.2//EN" "http://www.openmobilealliance.org/tech/DTD/xhtml-mobile12.dtd"&gt;</td></tr>
202
+ <tr><td>basic</td><td>&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML Basic 1.1//EN" "http://www.w3.org/TR/xhtml-basic/xhtml-basic11.dtd"&gt;</td></tr>
203
+ <tr><td>transitional</td><td>&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;</td></tr>
204
+ </table>
205
+
206
+ ### [:html, :comment, sexp]
207
+
208
+ Example:
209
+ [:html, :comment, [:static, 'comment']]
210
+ generates:
211
+ <!--comment-->
212
+
213
+ ### [:html, :tag, identifier, attributes, optional-sexp]
214
+
215
+ HTML tag abstraction. Identifier can be a String or a Symbol. If the optional content Sexp is omitted
216
+ the tag is closed (e.g. <br/> <img/>). The tag is also closed if the content Sexp is empty
217
+ (consists only of :multi and :newline expressions) and the tag is registered as auto-closing.
218
+
219
+ Example:
220
+ [:html, :tag, 'img', [:html, :attrs, [:html, :attr, 'src', 'image.png']]]
221
+ [:html, :tag, 'p', [:multi], [:static, 'Content']]
222
+ generates:
223
+ <img src="image.png"/>
224
+ <p>Content</p>
225
+
226
+ ### [:html, :attrs, attributes]
227
+
228
+ List of html attributes [:html, :attr, identifier, sexp]
229
+
230
+ ### [:html, :attr, identifier, sexp]
231
+
232
+ HTML attribute abstraction. Identifier can be a String or a Symbol.
233
+
234
+ Formal grammar
235
+ --------------
236
+
237
+ Validate expressions with Temple::Grammar.match? and Temple::Grammar.validate!
238
+
239
+ Temple::Grammar.match? [:multi, [:static, 'Valid Temple Expression']]
240
+ Temple::Grammar.validate! [:multi, 'Invalid Temple Expression']
241
+
242
+ The formal grammar is given in a Ruby DSL similar to EBNF and should be easy to understand if you know EBNF. Repeated tokens
243
+ are given by appending ?, * or + as in regular expressions.
244
+
245
+ * ? means zero or one occurence
246
+ * \* means zero or more occurences
247
+ * \+ means one or more occurences
248
+
249
+ <!-- Find a better way to include the grammar -->
250
+ <script src="http://gist-it.appspot.com/github/judofyr/temple/raw/master/lib/temple/grammar.rb"></script>
data/README.md CHANGED
@@ -12,13 +12,15 @@ we'll try to do our best. In fact, it doesn't have to be related to Temple at
12
12
  all. As long as it has something to do with template languages, we're
13
13
  interested: <http://groups.google.com/group/guardians-of-the-temple>.
14
14
 
15
- Meta
16
- ----
17
-
18
- * Home: <http://github.com/judofyr/temple>
19
- * Bugs: <http://github.com/judofyr/temple/issues>
20
- * List: <http://groups.google.com/group/guardians-of-the-temple>
15
+ Links
16
+ -----
21
17
 
18
+ * Source: <http://github.com/judofyr/temple>
19
+ * Bugs: <http://github.com/judofyr/temple/issues>
20
+ * List: <http://groups.google.com/group/guardians-of-the-temple>
21
+ * API documentation:
22
+ * Latest Gem: <http://rubydoc.info/gems/temple/frames>
23
+ * GitHub master: <http://rubydoc.info/github/judofyr/temple/master/frames>
22
24
 
23
25
  Overview
24
26
  --------
@@ -27,7 +29,7 @@ Temple is built on a theory that every template consists of three elements:
27
29
 
28
30
  * Static text
29
31
  * Dynamic text (pieces of Ruby which are evaluated and sent to the client)
30
- * Blocks (pieces of Ruby which are evaluated and *not* sent to the client, but
32
+ * Codes (pieces of Ruby which are evaluated and *not* sent to the client, but
31
33
  might change the control flow).
32
34
 
33
35
  The goal of a template engine is to take the template and eventually compile
@@ -37,9 +39,9 @@ it into *the core abstraction*:
37
39
  [:static, "Hello "],
38
40
  [:dynamic, "@user.name"],
39
41
  [:static, "!\n"],
40
- [:block, "if @user.birthday == Date.today"],
42
+ [:code, "if @user.birthday == Date.today"],
41
43
  [:static, "Happy birthday!"],
42
- [:block, "end"]]
44
+ [:code, "end"]]
43
45
 
44
46
  Then you can apply some optimizations, feed it to Temple and it generates fast
45
47
  Ruby code for you:
@@ -71,7 +73,7 @@ Some examples:
71
73
  [:static, "Hello "],
72
74
  [:dynamic, "@world"]]
73
75
 
74
- [:html, :tag, "em", "Hey hey"]
76
+ [:html, :tag, "em", [:html, :attrs], [:static, "Hey hey"]]
75
77
 
76
78
  *NOTE:* SexpProcessor, a library written by Ryan Davis, includes a `Sexp`
77
79
  class. While you can use this class (since it's a subclass of Array), it's not
@@ -91,7 +93,7 @@ So what's an abstraction? An abstraction is when you introduce a new types:
91
93
  [:static, "<strong>Use the force</strong>"]
92
94
 
93
95
  # You use:
94
- [:html, :tag, "strong", [:static, "Use the force"]]
96
+ [:html, :tag, "strong", [:html, :attrs], [:static, "Use the force"]]
95
97
 
96
98
  ### Why are abstractions so important?
97
99
 
@@ -111,6 +113,7 @@ continue to use the HTML abstraction. Maybe you even want to write your
111
113
  compiler in another language? Sexps are easily serialized and if you don't
112
114
  mind working across processes, it's not a problem at all.
113
115
 
116
+ All abstractions used by Temple are documented in {file:EXPRESSIONS.md EXPRESSIONS}.
114
117
 
115
118
  Compilers
116
119
  ---------
@@ -214,12 +217,21 @@ Rails.
214
217
  Tilt.new('example.ext').render # => Render a file
215
218
  MyTemplate.new { "String" }.render # => Render a string
216
219
 
217
-
218
220
  Installation
219
221
  ------------
220
222
 
223
+ You need Ruby 1.8.7 or Ruby 1.9.2 to work with Temple. Temple is published as a Ruby Gem which can be installed
224
+ as following:
225
+
221
226
  $ gem install temple
222
227
 
228
+ Engines using Temple
229
+ --------------------
230
+
231
+ * Temple ERB example implementation (Temple::ERB::Template)
232
+ * [Slim](http://github.com/stonean/slim)
233
+ * [Sal](http://github.com/stonean/sal.rb)
234
+ * [Temple-Mustache (Example implementation)](https://github.com/minad/temple-mustache)
223
235
 
224
236
  Acknowledgements
225
237
  ----------------
data/lib/temple.rb CHANGED
@@ -1,31 +1,47 @@
1
1
  require 'temple/version'
2
2
 
3
3
  module Temple
4
- autoload :Generator, 'temple/generators'
5
- autoload :Generators, 'temple/generators'
6
- autoload :Engine, 'temple/engine'
7
- autoload :Utils, 'temple/utils'
8
- autoload :Mixins, 'temple/mixins'
9
- autoload :Filter, 'temple/filter'
10
- autoload :Templates, 'temple/templates'
4
+ autoload :InvalidExpression, 'temple/generators'
5
+ autoload :Generator, 'temple/generators'
6
+ autoload :Generators, 'temple/generators'
7
+ autoload :Engine, 'temple/engine'
8
+ autoload :Utils, 'temple/utils'
9
+ autoload :Filter, 'temple/filter'
10
+ autoload :Templates, 'temple/templates'
11
+ autoload :Grammar, 'temple/grammar'
12
+ autoload :ImmutableHash, 'temple/hash'
13
+ autoload :MutableHash, 'temple/hash'
14
+
15
+ module Mixins
16
+ autoload :Dispatcher, 'temple/mixins/dispatcher'
17
+ autoload :EngineDSL, 'temple/mixins/engine_dsl'
18
+ autoload :GrammarDSL, 'temple/mixins/grammar_dsl'
19
+ autoload :Options, 'temple/mixins/options'
20
+ autoload :DefaultOptions, 'temple/mixins/options'
21
+ autoload :Template, 'temple/mixins/template'
22
+ end
11
23
 
12
24
  module ERB
13
- autoload :Engine, 'temple/erb/engine'
14
- autoload :Parser, 'temple/erb/parser'
15
- autoload :Trimming, 'temple/erb/trimming'
25
+ autoload :Engine, 'temple/erb/engine'
26
+ autoload :Parser, 'temple/erb/parser'
27
+ autoload :Trimming, 'temple/erb/trimming'
28
+ autoload :Template, 'temple/erb/template'
16
29
  end
17
30
 
18
31
  module Filters
19
- autoload :MultiFlattener, 'temple/filters/multi_flattener'
20
- autoload :StaticMerger, 'temple/filters/static_merger'
21
- autoload :DynamicInliner, 'temple/filters/dynamic_inliner'
22
- autoload :EscapeHTML, 'temple/filters/escape_html'
23
- autoload :Debugger, 'temple/filters/debugger'
32
+ autoload :ControlFlow, 'temple/filters/control_flow'
33
+ autoload :MultiFlattener, 'temple/filters/multi_flattener'
34
+ autoload :StaticMerger, 'temple/filters/static_merger'
35
+ autoload :DynamicInliner, 'temple/filters/dynamic_inliner'
36
+ autoload :Escapable, 'temple/filters/escapable'
37
+ autoload :Eraser, 'temple/filters/eraser'
38
+ autoload :Validator, 'temple/filters/validator'
24
39
  end
25
40
 
26
41
  module HTML
27
- autoload :Dispatcher, 'temple/html/dispatcher'
28
- autoload :Fast, 'temple/html/fast'
29
- autoload :Pretty, 'temple/html/pretty'
42
+ autoload :Dispatcher, 'temple/html/dispatcher'
43
+ autoload :Filter, 'temple/html/filter'
44
+ autoload :Fast, 'temple/html/fast'
45
+ autoload :Pretty, 'temple/html/pretty'
30
46
  end
31
47
  end