feedtools 0.2.26 → 0.2.27

Sign up to get free protection for your applications and to get access to all the features.
Files changed (166) hide show
  1. data/CHANGELOG +232 -216
  2. data/db/migration.rb +2 -0
  3. data/db/schema.mysql.sql +2 -0
  4. data/db/schema.postgresql.sql +3 -1
  5. data/db/schema.sqlite.sql +3 -1
  6. data/lib/feed_tools.rb +37 -14
  7. data/lib/feed_tools/database_feed_cache.rb +13 -2
  8. data/lib/feed_tools/feed.rb +430 -104
  9. data/lib/feed_tools/feed_item.rb +533 -268
  10. data/lib/feed_tools/helpers/generic_helper.rb +1 -1
  11. data/lib/feed_tools/helpers/html_helper.rb +78 -116
  12. data/lib/feed_tools/helpers/retrieval_helper.rb +33 -3
  13. data/lib/feed_tools/helpers/uri_helper.rb +46 -54
  14. data/lib/feed_tools/monkey_patch.rb +27 -1
  15. data/lib/feed_tools/vendor/html5/History.txt +10 -0
  16. data/lib/feed_tools/vendor/html5/Manifest.txt +117 -0
  17. data/lib/feed_tools/vendor/html5/README +45 -0
  18. data/lib/feed_tools/vendor/html5/Rakefile.rb +33 -0
  19. data/lib/feed_tools/vendor/html5/bin/html5 +217 -0
  20. data/lib/feed_tools/vendor/html5/lib/core_ext/string.rb +17 -0
  21. data/lib/feed_tools/vendor/html5/lib/html5.rb +13 -0
  22. data/lib/feed_tools/vendor/html5/lib/html5/constants.rb +1046 -0
  23. data/lib/feed_tools/vendor/html5/lib/html5/filters/base.rb +10 -0
  24. data/lib/feed_tools/vendor/html5/lib/html5/filters/inject_meta_charset.rb +82 -0
  25. data/lib/feed_tools/vendor/html5/lib/html5/filters/iso639codes.rb +752 -0
  26. data/lib/feed_tools/vendor/html5/lib/html5/filters/optionaltags.rb +198 -0
  27. data/lib/feed_tools/vendor/html5/lib/html5/filters/rfc2046.rb +30 -0
  28. data/lib/feed_tools/vendor/html5/lib/html5/filters/rfc3987.rb +89 -0
  29. data/lib/feed_tools/vendor/html5/lib/html5/filters/sanitizer.rb +15 -0
  30. data/lib/feed_tools/vendor/html5/lib/html5/filters/validator.rb +830 -0
  31. data/lib/feed_tools/vendor/html5/lib/html5/filters/whitespace.rb +36 -0
  32. data/lib/feed_tools/vendor/html5/lib/html5/html5parser.rb +248 -0
  33. data/lib/feed_tools/vendor/html5/lib/html5/html5parser/after_body_phase.rb +46 -0
  34. data/lib/feed_tools/vendor/html5/lib/html5/html5parser/after_frameset_phase.rb +33 -0
  35. data/lib/feed_tools/vendor/html5/lib/html5/html5parser/after_head_phase.rb +50 -0
  36. data/lib/feed_tools/vendor/html5/lib/html5/html5parser/before_head_phase.rb +41 -0
  37. data/lib/feed_tools/vendor/html5/lib/html5/html5parser/in_body_phase.rb +613 -0
  38. data/lib/feed_tools/vendor/html5/lib/html5/html5parser/in_caption_phase.rb +69 -0
  39. data/lib/feed_tools/vendor/html5/lib/html5/html5parser/in_cell_phase.rb +78 -0
  40. data/lib/feed_tools/vendor/html5/lib/html5/html5parser/in_column_group_phase.rb +55 -0
  41. data/lib/feed_tools/vendor/html5/lib/html5/html5parser/in_frameset_phase.rb +57 -0
  42. data/lib/feed_tools/vendor/html5/lib/html5/html5parser/in_head_phase.rb +138 -0
  43. data/lib/feed_tools/vendor/html5/lib/html5/html5parser/in_row_phase.rb +89 -0
  44. data/lib/feed_tools/vendor/html5/lib/html5/html5parser/in_select_phase.rb +85 -0
  45. data/lib/feed_tools/vendor/html5/lib/html5/html5parser/in_table_body_phase.rb +86 -0
  46. data/lib/feed_tools/vendor/html5/lib/html5/html5parser/in_table_phase.rb +115 -0
  47. data/lib/feed_tools/vendor/html5/lib/html5/html5parser/initial_phase.rb +133 -0
  48. data/lib/feed_tools/vendor/html5/lib/html5/html5parser/phase.rb +154 -0
  49. data/lib/feed_tools/vendor/html5/lib/html5/html5parser/root_element_phase.rb +41 -0
  50. data/lib/feed_tools/vendor/html5/lib/html5/html5parser/trailing_end_phase.rb +35 -0
  51. data/lib/feed_tools/vendor/html5/lib/html5/inputstream.rb +648 -0
  52. data/lib/feed_tools/vendor/html5/lib/html5/liberalxmlparser.rb +158 -0
  53. data/lib/feed_tools/vendor/html5/lib/html5/sanitizer.rb +188 -0
  54. data/lib/feed_tools/vendor/html5/lib/html5/serializer.rb +2 -0
  55. data/lib/feed_tools/vendor/html5/lib/html5/serializer/htmlserializer.rb +179 -0
  56. data/lib/feed_tools/vendor/html5/lib/html5/serializer/xhtmlserializer.rb +20 -0
  57. data/lib/feed_tools/vendor/html5/lib/html5/sniffer.rb +45 -0
  58. data/lib/feed_tools/vendor/html5/lib/html5/tokenizer.rb +966 -0
  59. data/lib/feed_tools/vendor/html5/lib/html5/treebuilders.rb +24 -0
  60. data/lib/feed_tools/vendor/html5/lib/html5/treebuilders/base.rb +334 -0
  61. data/lib/feed_tools/vendor/html5/lib/html5/treebuilders/hpricot.rb +231 -0
  62. data/lib/feed_tools/vendor/html5/lib/html5/treebuilders/rexml.rb +209 -0
  63. data/lib/feed_tools/vendor/html5/lib/html5/treebuilders/simpletree.rb +185 -0
  64. data/lib/feed_tools/vendor/html5/lib/html5/treewalkers.rb +26 -0
  65. data/lib/feed_tools/vendor/html5/lib/html5/treewalkers/base.rb +162 -0
  66. data/lib/feed_tools/vendor/html5/lib/html5/treewalkers/hpricot.rb +48 -0
  67. data/lib/feed_tools/vendor/html5/lib/html5/treewalkers/rexml.rb +48 -0
  68. data/lib/feed_tools/vendor/html5/lib/html5/treewalkers/simpletree.rb +48 -0
  69. data/lib/feed_tools/vendor/html5/lib/html5/version.rb +3 -0
  70. data/lib/feed_tools/vendor/html5/testdata/encoding/chardet/test_big5.txt +51 -0
  71. data/lib/feed_tools/vendor/html5/testdata/encoding/test-yahoo-jp.dat +10 -0
  72. data/lib/feed_tools/vendor/html5/testdata/encoding/tests1.dat +394 -0
  73. data/lib/feed_tools/vendor/html5/testdata/encoding/tests2.dat +81 -0
  74. data/lib/feed_tools/vendor/html5/testdata/sanitizer/tests1.dat +416 -0
  75. data/lib/feed_tools/vendor/html5/testdata/serializer/core.test +104 -0
  76. data/lib/feed_tools/vendor/html5/testdata/serializer/injectmeta.test +65 -0
  77. data/lib/feed_tools/vendor/html5/testdata/serializer/optionaltags.test +900 -0
  78. data/lib/feed_tools/vendor/html5/testdata/serializer/options.test +60 -0
  79. data/lib/feed_tools/vendor/html5/testdata/serializer/whitespace.test +51 -0
  80. data/lib/feed_tools/vendor/html5/testdata/sites/google-results.htm +1 -0
  81. data/lib/feed_tools/vendor/html5/testdata/sites/python-ref-import.htm +1 -0
  82. data/lib/feed_tools/vendor/html5/testdata/sites/web-apps-old.htm +1 -0
  83. data/lib/feed_tools/vendor/html5/testdata/sites/web-apps.htm +34275 -0
  84. data/lib/feed_tools/vendor/html5/testdata/sniffer/htmlOrFeed.json +43 -0
  85. data/lib/feed_tools/vendor/html5/testdata/tokenizer/contentModelFlags.test +48 -0
  86. data/lib/feed_tools/vendor/html5/testdata/tokenizer/entities.test +2339 -0
  87. data/lib/feed_tools/vendor/html5/testdata/tokenizer/escapeFlag.test +21 -0
  88. data/lib/feed_tools/vendor/html5/testdata/tokenizer/test1.test +172 -0
  89. data/lib/feed_tools/vendor/html5/testdata/tokenizer/test2.test +129 -0
  90. data/lib/feed_tools/vendor/html5/testdata/tokenizer/test3.test +367 -0
  91. data/lib/feed_tools/vendor/html5/testdata/tokenizer/test4.test +198 -0
  92. data/lib/feed_tools/vendor/html5/testdata/tree-construction/tests1.dat +1950 -0
  93. data/lib/feed_tools/vendor/html5/testdata/tree-construction/tests2.dat +773 -0
  94. data/lib/feed_tools/vendor/html5/testdata/tree-construction/tests3.dat +270 -0
  95. data/lib/feed_tools/vendor/html5/testdata/tree-construction/tests4.dat +60 -0
  96. data/lib/feed_tools/vendor/html5/testdata/tree-construction/tests5.dat +175 -0
  97. data/lib/feed_tools/vendor/html5/testdata/tree-construction/tests6.dat +196 -0
  98. data/lib/feed_tools/vendor/html5/testdata/validator/attributes.test +1035 -0
  99. data/lib/feed_tools/vendor/html5/testdata/validator/base-href-attribute.test +787 -0
  100. data/lib/feed_tools/vendor/html5/testdata/validator/base-target-attribute.test +35 -0
  101. data/lib/feed_tools/vendor/html5/testdata/validator/blockquote-cite-attribute.test +7 -0
  102. data/lib/feed_tools/vendor/html5/testdata/validator/classattribute.test +152 -0
  103. data/lib/feed_tools/vendor/html5/testdata/validator/contenteditableattribute.test +59 -0
  104. data/lib/feed_tools/vendor/html5/testdata/validator/contextmenuattribute.test +115 -0
  105. data/lib/feed_tools/vendor/html5/testdata/validator/dirattribute.test +59 -0
  106. data/lib/feed_tools/vendor/html5/testdata/validator/draggableattribute.test +63 -0
  107. data/lib/feed_tools/vendor/html5/testdata/validator/html-xmlns-attribute.test +23 -0
  108. data/lib/feed_tools/vendor/html5/testdata/validator/idattribute.test +115 -0
  109. data/lib/feed_tools/vendor/html5/testdata/validator/inputattributes.test +2795 -0
  110. data/lib/feed_tools/vendor/html5/testdata/validator/irrelevantattribute.test +63 -0
  111. data/lib/feed_tools/vendor/html5/testdata/validator/langattribute.test +5579 -0
  112. data/lib/feed_tools/vendor/html5/testdata/validator/li-value-attribute.test +7 -0
  113. data/lib/feed_tools/vendor/html5/testdata/validator/link-href-attribute.test +7 -0
  114. data/lib/feed_tools/vendor/html5/testdata/validator/link-hreflang-attribute.test +7 -0
  115. data/lib/feed_tools/vendor/html5/testdata/validator/link-rel-attribute.test +271 -0
  116. data/lib/feed_tools/vendor/html5/testdata/validator/ol-start-attribute.test +7 -0
  117. data/lib/feed_tools/vendor/html5/testdata/validator/starttags.test +375 -0
  118. data/lib/feed_tools/vendor/html5/testdata/validator/style-scoped-attribute.test +7 -0
  119. data/lib/feed_tools/vendor/html5/testdata/validator/tabindexattribute.test +79 -0
  120. data/lib/feed_tools/vendor/html5/tests/preamble.rb +72 -0
  121. data/lib/feed_tools/vendor/html5/tests/test_encoding.rb +35 -0
  122. data/lib/feed_tools/vendor/html5/tests/test_lxp.rb +279 -0
  123. data/lib/feed_tools/vendor/html5/tests/test_parser.rb +68 -0
  124. data/lib/feed_tools/vendor/html5/tests/test_sanitizer.rb +142 -0
  125. data/lib/feed_tools/vendor/html5/tests/test_serializer.rb +68 -0
  126. data/lib/feed_tools/vendor/html5/tests/test_sniffer.rb +27 -0
  127. data/lib/feed_tools/vendor/html5/tests/test_stream.rb +62 -0
  128. data/lib/feed_tools/vendor/html5/tests/test_tokenizer.rb +94 -0
  129. data/lib/feed_tools/vendor/html5/tests/test_treewalkers.rb +135 -0
  130. data/lib/feed_tools/vendor/html5/tests/test_validator.rb +31 -0
  131. data/lib/feed_tools/vendor/html5/tests/tokenizer_test_parser.rb +63 -0
  132. data/lib/feed_tools/vendor/uri.rb +781 -0
  133. data/lib/feed_tools/version.rb +1 -1
  134. data/rakefile +27 -6
  135. data/test/unit/atom_test.rb +298 -210
  136. data/test/unit/helper_test.rb +7 -12
  137. data/test/unit/rdf_test.rb +51 -1
  138. data/test/unit/rss_test.rb +13 -3
  139. metadata +239 -116
  140. data/lib/feed_tools/vendor/htree.rb +0 -97
  141. data/lib/feed_tools/vendor/htree/container.rb +0 -10
  142. data/lib/feed_tools/vendor/htree/context.rb +0 -67
  143. data/lib/feed_tools/vendor/htree/display.rb +0 -27
  144. data/lib/feed_tools/vendor/htree/doc.rb +0 -149
  145. data/lib/feed_tools/vendor/htree/elem.rb +0 -262
  146. data/lib/feed_tools/vendor/htree/encoder.rb +0 -163
  147. data/lib/feed_tools/vendor/htree/equality.rb +0 -218
  148. data/lib/feed_tools/vendor/htree/extract_text.rb +0 -37
  149. data/lib/feed_tools/vendor/htree/fstr.rb +0 -33
  150. data/lib/feed_tools/vendor/htree/gencode.rb +0 -97
  151. data/lib/feed_tools/vendor/htree/htmlinfo.rb +0 -672
  152. data/lib/feed_tools/vendor/htree/inspect.rb +0 -108
  153. data/lib/feed_tools/vendor/htree/leaf.rb +0 -94
  154. data/lib/feed_tools/vendor/htree/loc.rb +0 -367
  155. data/lib/feed_tools/vendor/htree/modules.rb +0 -48
  156. data/lib/feed_tools/vendor/htree/name.rb +0 -124
  157. data/lib/feed_tools/vendor/htree/output.rb +0 -207
  158. data/lib/feed_tools/vendor/htree/parse.rb +0 -409
  159. data/lib/feed_tools/vendor/htree/raw_string.rb +0 -124
  160. data/lib/feed_tools/vendor/htree/regexp-util.rb +0 -15
  161. data/lib/feed_tools/vendor/htree/rexml.rb +0 -130
  162. data/lib/feed_tools/vendor/htree/scan.rb +0 -166
  163. data/lib/feed_tools/vendor/htree/tag.rb +0 -111
  164. data/lib/feed_tools/vendor/htree/template.rb +0 -909
  165. data/lib/feed_tools/vendor/htree/text.rb +0 -115
  166. data/lib/feed_tools/vendor/htree/traverse.rb +0 -465
@@ -1,909 +0,0 @@
1
- # = Template Engine
2
- #
3
- # == Template Syntax
4
- #
5
- # The htree template engine converts HTML and some data to XHTML.
6
- # A template directive is described as special HTML attribute which name
7
- # begins with underscore.
8
- #
9
- # The template directives are listed as follows.
10
- #
11
- # - <elem \_attr_<i>name</i>="<i>expr</i>">content</elem>
12
- # - <elem _text="<i>expr</i>">dummy-content</elem>
13
- # - <elem _text><i>expr</i></elem>
14
- # - <elem _if="<i>expr</i>" _else="<i>mod.name(args)</i>">then-content</elem>
15
- # - <elem _iter="<i>expr.meth(args)//vars</i>">content</elem>
16
- # - <elem _iter_content="<i>expr.meth(args)//vars</i>">content</elem>
17
- # - <elem _call="<i>mod.name(args)</i>">dummy-content</elem>
18
- # - <elem _template="<i>name(vars)</i>">body</elem>
19
- #
20
- # === Template Semantics
21
- #
22
- # - attribute substitution
23
- # - <elem \_attr_<i>name</i>="<i>expr</i>">content</elem>
24
- #
25
- # \_attr_<i>name</i> is used for a dynamic attribute.
26
- #
27
- # <elem _attr_xxx="..."/>
28
- # -> <elem xxx="..."/>
29
- #
30
- # It is expanded to <i>name</i>="content".
31
- # The content is generated by evaluating _expr_.
32
- # Usually you don't need to care escaping: &, <, > and " are automatically escaped.
33
- # If you need to output character references,
34
- # the value of _expr_ should be an object which have a +rcdata+ method such as an HTree::Text.
35
- # If the value has a +rcdata+ method,
36
- # it is called and the result is used as the content with escaping <, > and ".
37
- #
38
- # \_attr_<i>name</i> can be used multiple times in single element.
39
- #
40
- # - text substitution
41
- # - <elem _text="<i>expr</i>">dummy-content</elem>
42
- # - <elem _text><i>expr</i></elem>
43
- #
44
- # _text substitutes content of the element by the string
45
- # evaluated from _expr_.
46
- # _expr_ is described in the attribute value or the content of the element.
47
- #
48
- # If a result of _expr_ have &, < and/or >, they are automatically escaped.
49
- # If you need to output character references,
50
- # the value of _expr_ should be an object which have a +rcdata+ method such as an HTree::Text.
51
- # If the value has a +rcdata+ method,
52
- # it is called and the result is used as the content with escaping < and >.
53
- #
54
- # If the element is span or div, and there is no other attributes,
55
- # no tags are produced.
56
- #
57
- # <elem _text="...">dummy-content</elem>
58
- # -> <elem>...</elem>
59
- #
60
- # - conditional
61
- # - <elem _if="<i>expr</i>">then-content</elem>
62
- # - <elem _if="<i>expr</i>" _else="<i>name(args)</i>">then-content</elem>
63
- #
64
- # _if is used for conditional.
65
- #
66
- # If <i>expr</i> is evaluated to true, it expands as follows
67
- # regardless of existence of _else.
68
- #
69
- # <elem _if="<i>expr</i>">then-content</elem>
70
- # -> <elem>then-content</elem>
71
- #
72
- # If <i>expr</i> is evaluated to false, it expands using _else.
73
- # If _else is not given, it expands to empty.
74
- # If _else is given, it expands as follows.
75
- #
76
- # <elem _if="<i>expr</i>" _else="<i>name(args)</i>">then-content</elem>
77
- # -> <elem _call="<i>name(args)</i>">then-content</elem>
78
- # -> see _call for further expansion.
79
- #
80
- # It is expanded to <elem>then-content</elem> if _expr_ is evaluated to
81
- # a true value.
82
- # Otherwise, it is replaced by other template specified by _else attribute.
83
- # If _else attribute is not given, it just replaced by empty.
84
- #
85
- # - iteration
86
- # - <elem _iter="<i>expr.meth(args)//vars</i>">content</elem>
87
- # - <elem _iter_content="<i>expr.meth(args)//vars</i>">content</elem>
88
- #
89
- # _iter and _iter_content is used for iteration.
90
- # _iter iterates the element itself but _iter_content iterates the content.
91
- #
92
- # <outer _iter="..."><inner/></outer>
93
- # -> <outer><inner/></outer><outer><inner/></outer>...
94
- #
95
- # <outer _iter_content="..."><inner/></outer>
96
- # -> <outer><inner/><inner/>...</outer>
97
- #
98
- # <i>expr.meth(args)</i> specifies iterator method call.
99
- # It is actually called with a block.
100
- # The block have block parameters <i>vars</i>.
101
- # <i>vars</i> must be variables separated by comma.
102
- #
103
- # - template call
104
- # - <elem _call="<i>name(args)</i>">dummy-content</elem>
105
- # - <elem _call="<i>mod.name(args)</i>">dummy-content</elem>
106
- #
107
- # _call is used to expand a template function.
108
- # The template function is defined by _template.
109
- #
110
- # <d _template="m">...</d>
111
- # <c _call="m">...</c>
112
- # -> <d>...</d>
113
- #
114
- # A local template can be called as follows:
115
- #
116
- # HTree.expand_template{<<'End'}
117
- # <a _template=ruby_talk(num)
118
- # _attr_href='"http://ruby-talk.org/#{num}"'
119
- # >[ruby-talk:<span _text=num>nnn</span>]</a>
120
- # Ruby 1.8.0 is released at <span _call=ruby_talk(77946) />.
121
- # Ruby 1.8.1 is released at <span _call=ruby_talk(88814) />.
122
- # End
123
- #
124
- # <i>mod</i> should be the result of HTree.compile_template.
125
- #
126
- # M = HTree.compile_template(<<'End')
127
- # <a _template=ruby_talk(num)
128
- # _attr_href='"http://ruby-talk.org/#{num}"'
129
- # >[ruby-talk:<span _text=num>nnn</span>]</a>
130
- # End
131
- # HTree.expand_template{<<'End'}
132
- # <html>
133
- # Ruby 1.8.0 is released at <span _call=M.ruby_talk(77946) />.
134
- # Ruby 1.8.1 is released at <span _call=M.ruby_talk(88814) />.
135
- # </html>
136
- # End
137
- #
138
- # The module can included.
139
- # In such case, the template function can be called without <i>mod.</i>
140
- # prefix.
141
- #
142
- # include HTree.compile_template(<<'End')
143
- # <a _template=ruby_talk(num)
144
- # _attr_href='"http://ruby-talk.org/#{num}"'
145
- # >[ruby-talk:<span _text=num>nnn</span>]</a>
146
- # End
147
- # HTree.expand_template{<<'End'}
148
- # <html>
149
- # Ruby 1.8.0 is released at <span _call=ruby_talk(77946) />.
150
- # Ruby 1.8.1 is released at <span _call=ruby_talk(88814) />.
151
- # </html>
152
- # End
153
- #
154
- # - template definition
155
- # - <elem _template="<i>name(vars)</i>">body</elem>
156
- #
157
- # _template defines a template function which is usable by _call.
158
- #
159
- # When a template is compiled to a module by HTree.compile_template,
160
- # the module have a module function for each template function
161
- # defined by outermost _template attribute.
162
- #
163
- # === White Space Handling
164
- #
165
- # The htree template engine strips whitespace text nodes in a template
166
- # except under HTML pre element.
167
- #
168
- # For example the white space text node between two spans in following template is stripped.
169
- #
170
- # <span _text="'a'"/> <span _text="'b'"/> -> "ab"
171
- #
172
- # Character entity references are not stripped.
173
- #
174
- # <span _text="'a'"/>&#32;<span _text="'b'"/> -> "a&#32;b"
175
- #
176
- # Text nodes generated by _text is not stripped.
177
- #
178
- # <span _text="'a'"/><span _text="' '"> </span><span _text="'b'"/> -> "a b"
179
- #
180
- # == Method Summary
181
- #
182
- # - HTree.expand_template(<i>template_pathname</i>) -> $stdout
183
- # - HTree.expand_template(<i>template_pathname</i>, <i>obj</i>) -> $stdout
184
- # - HTree.expand_template(<i>template_pathname</i>, <i>obj</i>, <i>out</i>) -> <i>out</i>
185
- # - HTree.expand_template(<i>template_pathname</i>, <i>obj</i>, <i>out</i>, <i>encoding</i>) -> <i>out</i>
186
- #
187
- # - HTree.expand_template{<i>template_string</i>} -> $stdout
188
- # - HTree.expand_template(<i>out</i>) {<i>template_string</i>} -> <i>out</i>
189
- # - HTree.expand_template(<i>out</i>, <i>encoding</i>) {<i>template_string</i>} -> <i>out</i>
190
- #
191
- # - HTree.compile_template(<i>template_string</i>) -> Module
192
- # - HTree{<i>template_string</i>} -> HTree::Doc
193
- # - HTree(<i>html_string</i>) -> HTree::Doc
194
- #
195
- # == Design Decision on Design/Logic Separation
196
- #
197
- # HTree template engine doesn't force you to separate design and logic.
198
- # Any logic (Ruby code) can be embedded in design (HTML).
199
- #
200
- # However the template engine cares the separation by logic refactorings.
201
- # The logic is easy to move between a template and an application.
202
- # For example, following tangled template
203
- #
204
- # tmpl.html:
205
- # <html>
206
- # <head>
207
- # <title _text="very-complex-ruby-code">dummy</title>
208
- # </head>
209
- # ...
210
- # </html>
211
- #
212
- # app.rb:
213
- # HTree.expand_template('tmpl.html', obj)
214
- #
215
- # can be refactored as follows.
216
- #
217
- # tmpl.html:
218
- # <html>
219
- # <head>
220
- # <title _text="title">dummy</title>
221
- # </head>
222
- # ...
223
- # </html>
224
- #
225
- # app.rb:
226
- # def obj.title
227
- # very-complex-ruby-code
228
- # end
229
- # HTree.expand_template('tmpl.html', obj)
230
- #
231
- # In general, any expression in a template can be refactored to an application
232
- # by extracting it as a method.
233
- # In JSP, this is difficult especially for a code fragment of an iteration.
234
- #
235
- # Also HTree encourages to separate business logic (Ruby code in an application)
236
- # and presentation logic (Ruby code in a template).
237
- # For example, presentation logic to color table rows stripe
238
- # can be embedded in a template.
239
- # It doesn't need to tangle an application.
240
- #
241
-
242
- # :stopdoc:
243
- module HTree # :nodoc:
244
- EmptyBindingObject = Object.new
245
- end
246
- def (HTree::EmptyBindingObject).empty_binding
247
- binding
248
- end
249
-
250
- require 'htree/parse'
251
- require 'htree/gencode'
252
- require 'htree/equality'
253
- require 'htree/traverse'
254
-
255
- # call-seq:
256
- # HTree.expand_template(template_pathname, obj=nil, out=$stdout, encoding=internal_encoding) -> out
257
- # HTree.expand_template(out=$stdout, encoding=internal_encoding) { template_string } -> out
258
- #
259
- # <code>HTree.expand_template</code> expands a template.
260
- #
261
- # The arguments should be specified as follows.
262
- # All argument except <i>pathname</i> are optional.
263
- #
264
- # - HTree.expand_template(<i>pathname</i>, <i>obj</i>, <i>out</i>, <i>encoding</i>) -> <i>out</i>
265
- # - HTree.expand_template(<i>out</i>, <i>encoding</i>) {<i>template_string</i>} -> <i>out</i>
266
- #
267
- # The template is specified by a file or a string.
268
- # If a block is not given, the first argument represent a template pathname.
269
- # Otherwise, the block is yielded and its value is interpreted as a template
270
- # string.
271
- # So it can be called as follows in simplest case.
272
- #
273
- # - HTree.expand_template(<i>template_pathname</i>)
274
- # - HTree.expand_template{<i>template_string</i>}
275
- #
276
- # Ruby expressions in the template file specified by _template_pathname_ are
277
- # evaluated in the context of the optional second argument <i>obj</i> as follows.
278
- # I.e. the pseudo variable self in the expressions is bound to <i>obj</i>.
279
- #
280
- # HTree.expand_template(template_pathname, obj)
281
- #
282
- # Ruby expressions in the template_string are evaluated
283
- # in the context of the caller of HTree.expand_template.
284
- # (binding information is specified by the block.)
285
- # I.e. they can access local variables etc.
286
- # We recommend to specify template_string as a literal string without
287
- # interpolation because dynamically generated string may break lexical scope.
288
- #
289
- # HTree.expand_template has two more optional arguments:
290
- # <i>out</i>, <i>encoding</i>.
291
- #
292
- # <i>out</i> specifies output target.
293
- # It should have <tt><<</tt> method: IO and String for example.
294
- # If it is not specified, $stdout is used.
295
- #
296
- # <i>encoding</i> specifies output character encoding.
297
- # If it is not specified, internal encoding is used.
298
- #
299
- # HTree.expand_template returns <i>out</i> or $stdout if <i>out</i> is not
300
- # specified.
301
- #
302
- def HTree.expand_template(*args, &block)
303
- if block
304
- template = block.call
305
- binding = block
306
- else
307
- pathname = args.shift
308
- obj = args.shift
309
- if pathname.respond_to? :read
310
- template = pathname.read.untaint
311
- if template.respond_to? :charset
312
- template = Iconv.conv(HTree::Encoder.internal_charset, template.charset, template)
313
- end
314
- else
315
- template = File.read(pathname).untaint
316
- end
317
- Thread.current[:htree_expand_template_obj] = obj
318
- binding = eval(<<-'End',
319
- Thread.current[:htree_expand_template_obj].class.class_eval <<-'EE'
320
- Thread.current[:htree_expand_template_obj].instance_eval { binding }
321
- EE
322
- End
323
- HTree::EmptyBindingObject.empty_binding)
324
- Thread.current[:htree_expand_template_obj] = nil
325
- end
326
-
327
- out = args.shift || $stdout
328
- encoding = args.shift || HTree::Encoder.internal_charset
329
- if !args.empty?
330
- raise ArgumentError, "wrong number of arguments"
331
- end
332
- HTree::TemplateCompiler.new.expand_template(template, out, encoding, binding)
333
- end
334
-
335
- # call-seq:
336
- # HTree(html_string) -> doc
337
- # HTree{template_string} -> doc
338
- #
339
- # <code>HTree(<i>html_string</i>)</code> parses <i>html_string</i>.
340
- # <code>HTree{<i>template_string</i>}</code> parses <i>template_string</i> and expand it as a template.
341
- # Ruby expressions in <i>template_string</i> is evaluated in the scope of the caller.
342
- #
343
- # <code>HTree()</code> and <code>HTree{}</code> returns a tree as an instance of HTree::Doc.
344
- def HTree(html_string=nil, &block)
345
- if block_given?
346
- raise ArgumentError, "both argument and block given." if html_string
347
- template = block.call
348
- HTree.parse(HTree::TemplateCompiler.new.expand_fragment_template(template, '', HTree::Encoder.internal_charset, block))
349
- else
350
- HTree.parse(html_string)
351
- end
352
- end
353
-
354
- # call-seq:
355
- # HTree.compile_template(template_string) -> module
356
- #
357
- # <code>HTree.compile_template(<i>template_string</i>)</code> compiles
358
- # <i>template_string</i> as a template.
359
- #
360
- # HTree.compile_template returns a module.
361
- # The module has module functions for each templates defined in
362
- # <i>template_string</i>.
363
- # The returned module can be used for +include+.
364
- #
365
- # M = HTree.compile_template(<<'End')
366
- # <p _template=birthday(subj,t)>
367
- # <span _text=subj />'s birthday is <span _text="t.strftime('%B %dth %Y')"/>.
368
- # </p>
369
- # End
370
- # M.birthday('Ruby', Time.utc(1993, 2, 24)).display_xml
371
- # # <p>Ruby's birthday is February 24th 1993.</p>
372
- #
373
- # The module function takes arguments specifies by a <code>_template</code>
374
- # attribute and returns a tree represented as HTree::Node.
375
- #
376
- def HTree.compile_template(template_string)
377
- code = HTree::TemplateCompiler.new.compile_template(template_string)
378
- Thread.current[:htree_compile_template_code] = code
379
- mod = eval(<<-'End',
380
- eval(Thread.current[:htree_compile_template_code])
381
- End
382
- HTree::EmptyBindingObject.empty_binding)
383
- Thread.current[:htree_compile_template_code] = nil
384
- mod
385
- end
386
-
387
- class HTree::TemplateCompiler # :nodoc:
388
- IGNORABLE_ELEMENTS = {
389
- 'span' => true,
390
- 'div' => true,
391
- '{http://www.w3.org/1999/xhtml}span' => true,
392
- '{http://www.w3.org/1999/xhtml}div' => true,
393
- }
394
-
395
- def initialize
396
- @gensym_id = 0
397
- end
398
-
399
- def gensym(suffix='')
400
- @gensym_id += 1
401
- "g#{@gensym_id}#{suffix}"
402
- end
403
-
404
- def parse_template(template)
405
- strip_whitespaces(HTree.parse(template))
406
- end
407
-
408
- WhiteSpacePreservingElements = {
409
- '{http://www.w3.org/1999/xhtml}pre' => true
410
- }
411
-
412
- def strip_whitespaces(template)
413
- case template
414
- when HTree::Doc
415
- HTree::Doc.new(*template.children.map {|c| strip_whitespaces(c) }.compact)
416
- when HTree::Elem, HTree::Doc
417
- return template if WhiteSpacePreservingElements[template.name]
418
- subst = {}
419
- template.children.each_with_index {|c, i|
420
- subst[i] = strip_whitespaces(c)
421
- }
422
- template.subst_subnode(subst)
423
- when HTree::Text
424
- if /\A[ \t\r\n]*\z/ =~ template.rcdata
425
- nil
426
- else
427
- template
428
- end
429
- else
430
- template
431
- end
432
- end
433
-
434
- def expand_template(template, out, encoding, binding)
435
- template = parse_template(template)
436
- outvar = gensym('out')
437
- contextvar = gensym('top_context')
438
- code = <<"End"
439
- #{outvar} = HTree::Encoder.new(#{encoding.dump})
440
- #{contextvar} = HTree::DefaultContext
441
- #{compile_body(outvar, contextvar, template, false)}\
442
- #{outvar}.finish_with_xmldecl
443
- End
444
- result = eval(code, binding)
445
- out << result
446
- out
447
- end
448
-
449
- def expand_fragment_template(template, out, encoding, binding)
450
- template = parse_template(template)
451
- outvar = gensym('out')
452
- contextvar = gensym('top_context')
453
- code = <<"End"
454
- #{outvar} = HTree::Encoder.new(#{encoding.dump})
455
- #{contextvar} = HTree::DefaultContext
456
- #{compile_body(outvar, contextvar, template, false)}\
457
- #{outvar}.finish
458
- End
459
- result = eval(code, binding)
460
- out << result
461
- out
462
- end
463
-
464
- def compile_template(src)
465
- srcdoc = parse_template(src)
466
- templates = []
467
- body = extract_templates(srcdoc, templates, true)
468
- methods = []
469
- templates.each {|name_args, node|
470
- methods << compile_global_template(name_args, node)
471
- }
472
- <<"End"
473
- require 'htree/encoder'
474
- require 'htree/context'
475
- Module.new.module_eval <<'EE'
476
- module_function
477
- #{methods.join('').chomp}
478
- self
479
- EE
480
- End
481
- end
482
-
483
- def template_attribute?(name)
484
- /\A_/ =~ name.local_name
485
- end
486
-
487
- def extract_templates(node, templates, is_toplevel)
488
- case node
489
- when HTree::Doc
490
- subst = {}
491
- node.children.each_with_index {|n, i|
492
- subst[i] = extract_templates(n, templates, is_toplevel)
493
- }
494
- node.subst_subnode(subst)
495
- when HTree::Elem
496
- ht_attrs, rest_attrs = node.attributes.partition {|name, text| template_attribute? name }
497
- if ht_attrs.empty?
498
- subst = {}
499
- node.children.each_with_index {|n, i|
500
- subst[i] = extract_templates(n, templates, is_toplevel)
501
- }
502
- node.subst_subnode(subst)
503
- else
504
- ht_attrs.each {|htname, text|
505
- if htname.universal_name == '_template'
506
- name_fargs = text.to_s
507
- templates << [name_fargs, node.subst_subnode('_template' => nil)]
508
- return nil
509
- end
510
- }
511
- if is_toplevel
512
- raise HTree::Error, "unexpected template attributes in toplevel: #{ht_attrs.inspect}"
513
- else
514
- node
515
- end
516
- end
517
- else
518
- node
519
- end
520
- end
521
-
522
- ID_PAT = /[a-z][a-z0-9_]*/
523
- NAME_FARGS_PAT = /(#{ID_PAT})(?:\(\s*(|#{ID_PAT}\s*(?:,\s*#{ID_PAT}\s*)*)\))?/
524
- def compile_global_template(name_fargs, node)
525
- unless /\A#{NAME_FARGS_PAT}\z/o =~ name_fargs
526
- raise HTree::Error, "invalid template declaration: #{name_fargs}"
527
- end
528
- name = $1
529
- fargs = $2 ? $2.scan(ID_PAT) : []
530
-
531
- outvar = gensym('out')
532
- contextvar = gensym('top_context')
533
- args2 = [outvar, contextvar, *fargs]
534
-
535
- <<"End"
536
- def #{name}(#{fargs.join(',')})
537
- HTree.parse(_xml_#{name}(#{fargs.join(',')}))
538
- end
539
- def _xml_#{name}(#{fargs.join(',')})
540
- #{outvar} = HTree::Encoder.new(HTree::Encoder.internal_charset)
541
- #{contextvar} = HTree::DefaultContext
542
- _ht_#{name}(#{args2.join(',')})
543
- #{outvar}.finish
544
- end
545
- def _ht_#{name}(#{args2.join(',')})
546
- #{compile_body(outvar, contextvar, node, false)}\
547
- end
548
- public :_ht_#{name}
549
- End
550
- end
551
-
552
- def compile_local_template(name_fargs, node, local_templates)
553
- unless /\A#{NAME_FARGS_PAT}\z/o =~ name_fargs
554
- raise HTree::Error, "invalid template declaration: #{name_fargs}"
555
- end
556
- name = $1
557
- fargs = $2 ? $2.scan(ID_PAT) : []
558
-
559
- outvar = gensym('out')
560
- contextvar = gensym('top_context')
561
- args2 = [outvar, contextvar, *fargs]
562
-
563
- <<"End"
564
- #{name} = lambda {|#{args2.join(',')}|
565
- #{compile_body(outvar, contextvar, node, false, local_templates)}\
566
- }
567
- End
568
- end
569
-
570
- def compile_body(outvar, contextvar, node, is_toplevel, local_templates={})
571
- if node.elem? && IGNORABLE_ELEMENTS[node.name] && node.attributes.empty?
572
- node = TemplateNode.new(node.children)
573
- else
574
- node = TemplateNode.new(node)
575
- end
576
- generate_logic_node([:content], node, local_templates).generate_xml_output_code(outvar, contextvar)
577
- end
578
-
579
- def compile_node(node, local_templates)
580
- case node
581
- when HTree::Doc
582
- TemplateNode.new(node.children.map {|n| compile_node(n, local_templates) })
583
- when HTree::Elem
584
- ht_attrs = node.attributes.find_all {|name, text| template_attribute? name }
585
- ht_attrs = ht_attrs.sort_by {|htname, text| htname.universal_name }
586
- ignore_tag = false
587
- unless ht_attrs.empty?
588
- attr_mod = {}
589
- ht_attrs.each {|htname, text|
590
- attr_mod[htname] = nil
591
- if /\A_attr_/ =~ htname.local_name
592
- attr_mod[TemplateAttrName.new(htname.namespace_prefix, htname.namespace_uri, $')] = text
593
- end
594
- }
595
- ht_attrs.reject! {|htname, text| /\A_attr_/ =~ htname.local_name }
596
- node = node.subst_subnode(attr_mod)
597
- ignore_tag = IGNORABLE_ELEMENTS[node.name] && node.attributes.empty?
598
- end
599
- ht_names = ht_attrs.map {|htname, text| htname.universal_name }
600
- ht_vals = ht_attrs.map {|htname, text| text.to_s }
601
- case ht_names
602
- when []
603
- generate_logic_node([:tag, [:content]], node, local_templates)
604
- when ['_text'] # <n _text="expr" /> or <n _text>expr</n>
605
- if ht_vals[0] != '_text' # xxx: attribute value is really omitted?
606
- expr = ht_vals[0]
607
- else
608
- children = node.children
609
- if children.length != 1
610
- raise HTree::Error, "_text expression has #{children.length} nodes"
611
- end
612
- if !children[0].text?
613
- raise HTree::Error, "_text expression is not text: #{children[0].class}"
614
- end
615
- expr = children[0].to_s
616
- end
617
- if /\A\s*'((?:[^'\\]|\\[\0-\377])*)'\s*\z/ =~ expr
618
- # if expr is just a constant string literal, use it as a literal text.
619
- # This saves dynamic evaluation of <span _text="' '"/>
620
- # xxx: handle "..." as well if it has no #{}.
621
- HTree::Text.new($1.gsub(/\\([\0-\377])/, '\1'))
622
- else
623
- generate_logic_node(compile_dynamic_text(ignore_tag, expr), node, local_templates)
624
- end
625
- when ['_if'] # <n _if="expr" >...</n>
626
- generate_logic_node(compile_if(ignore_tag, ht_vals[0], nil), node, local_templates)
627
- when ['_else', '_if'] # <n _if="expr" _else="expr.meth(args)" >...</n>
628
- generate_logic_node(compile_if(ignore_tag, ht_vals[1], ht_vals[0]), node, local_templates)
629
- when ['_call'] # <n _call="recv.meth(args)" />
630
- generate_logic_node(compile_call(ignore_tag, ht_vals[0]), node, local_templates)
631
- when ['_iter'] # <n _iter="expr.meth(args)//fargs" >...</n>
632
- generate_logic_node(compile_iter(ignore_tag, ht_vals[0]), node, local_templates)
633
- when ['_iter_content'] # <n _iter_content="expr.meth(args)//fargs" >...</n>
634
- generate_logic_node(compile_iter_content(ignore_tag, ht_vals[0]), node, local_templates)
635
- else
636
- raise HTree::Error, "unexpected template attributes: #{ht_attrs.inspect}"
637
- end
638
- else
639
- return node
640
- end
641
- end
642
-
643
- def valid_syntax?(code)
644
- begin
645
- eval("BEGIN {return true}\n#{code.untaint}")
646
- rescue SyntaxError
647
- raise SyntaxError, "invalid code: #{code}"
648
- end
649
- end
650
-
651
- def check_syntax(code)
652
- unless valid_syntax?(code)
653
- raise HTree::Error, "invalid ruby code: #{code}"
654
- end
655
- end
656
-
657
- def compile_dynamic_text(ignore_tag, expr)
658
- check_syntax(expr)
659
- logic = [:text, expr]
660
- logic = [:tag, logic] unless ignore_tag
661
- logic
662
- end
663
-
664
- def compile_if(ignore_tag, expr, else_call)
665
- check_syntax(expr)
666
- then_logic = [:content]
667
- unless ignore_tag
668
- then_logic = [:tag, then_logic]
669
- end
670
- else_logic = nil
671
- if else_call
672
- else_logic = compile_call(true, else_call)
673
- end
674
- [:if, expr, then_logic, else_logic]
675
- end
676
-
677
- def split_args(spec)
678
- return spec, '' if /\)\z/ !~ spec
679
- i = spec.length - 1
680
- nest = 0
681
- begin
682
- raise HTree::Error, "unmatched paren: #{spec}" if i < 0
683
- case spec[i]
684
- when ?\)
685
- nest += 1
686
- when ?\(
687
- nest -= 1
688
- end
689
- i -= 1
690
- end while nest != 0
691
- i += 1
692
- return spec[0, i], spec[(i+1)...-1]
693
- end
694
-
695
- def compile_call(ignore_tag, spec)
696
- # spec : [recv.]meth[(args)]
697
- spec = spec.strip
698
- spec, args = split_args(spec)
699
- unless /#{ID_PAT}\z/o =~ spec
700
- raise HTree::Error, "invalid _call: #{spec}"
701
- end
702
- meth = $&
703
- spec = $`
704
- if /\A\s*\z/ =~ spec
705
- recv = nil
706
- elsif /\A\s*(.*)\.\z/ =~ spec
707
- recv = $1
708
- else
709
- raise HTree::Error, "invalid _call: #{spec}"
710
- end
711
- if recv
712
- check_syntax(recv)
713
- check_syntax("#{recv}.#{meth}(#{args})")
714
- end
715
- check_syntax("#{meth}(#{args})")
716
- [:call, recv, meth, args]
717
- end
718
-
719
- def compile_iter(ignore_tag, spec)
720
- # spec: <n _iter="expr.meth[(args)]//fargs" >...</n>
721
- spec = spec.strip
722
- unless %r{\s*//\s*(#{ID_PAT}\s*(?:,\s*#{ID_PAT}\s*)*)?\z}o =~ spec
723
- raise HTree::Error, "invalid block arguments for _iter: #{spec}"
724
- end
725
- call = $`.strip
726
- fargs = $1 ? $1.strip : ''
727
- check_syntax("#{call} {|#{fargs}| }")
728
- logic = [:content]
729
- unless ignore_tag
730
- logic = [:tag, logic]
731
- end
732
- [:iter, call, fargs, logic]
733
- end
734
-
735
- def compile_iter_content(ignore_tag, spec)
736
- # spec: <n _iter_content="expr.meth[(args)]//fargs" >...</n>
737
- spec = spec.strip
738
- unless %r{\s*//\s*(#{ID_PAT}\s*(?:,\s*#{ID_PAT}\s*)*)?\z}o =~ spec
739
- raise HTree::Error, "invalid block arguments for _iter: #{spec}"
740
- end
741
- call = $`.strip
742
- fargs = $1 ? $1.strip : ''
743
- check_syntax("#{call} {|#{fargs}| }")
744
- logic = [:content]
745
- logic = [:iter, call, fargs, logic]
746
- unless ignore_tag
747
- logic = [:tag, logic]
748
- end
749
- logic
750
- end
751
-
752
- def generate_logic_node(logic, node, local_templates)
753
- # logic ::= [:if, expr, then_logic, else_logic]
754
- # | [:iter, call, fargs, logic]
755
- # | [:tag, logic]
756
- # | [:text, expr]
757
- # | [:call, expr, meth, args]
758
- # | [:content]
759
- # | [:empty]
760
- case logic.first
761
- when :empty
762
- nil
763
- when :content
764
- subtemplates = []
765
- children = []
766
- node.children.each {|c|
767
- children << extract_templates(c, subtemplates, false)
768
- }
769
- if subtemplates.empty?
770
- TemplateNode.new(node.children.map {|n|
771
- compile_node(n, local_templates)
772
- })
773
- else
774
- local_templates = local_templates.dup
775
- decl = ''
776
- subtemplates.each {|sub_name_args, sub_node|
777
- sub_name = sub_name_args[ID_PAT]
778
- local_templates[sub_name] = sub_name
779
- decl << "#{sub_name} = "
780
- }
781
- decl << "nil\n"
782
- defs = []
783
- subtemplates.each {|sub_name_args, sub_node|
784
- defs << lambda {|out, context|
785
- out.output_logic_line compile_local_template(sub_name_args, sub_node, local_templates)
786
- }
787
- }
788
- TemplateNode.new(
789
- lambda {|out, context| out.output_logic_line decl },
790
- defs,
791
- children.map {|n| compile_node(n, local_templates) }
792
- )
793
- end
794
- when :text
795
- _, expr = logic
796
- TemplateNode.new(lambda {|out, context| out.output_dynamic_text expr })
797
- when :tag
798
- _, rest_logic = logic
799
- if rest_logic == [:content] && node.empty_element?
800
- node
801
- else
802
- subst = {}
803
- node.children.each_index {|i| subst[i] = nil }
804
- subst[0] = TemplateNode.new(generate_logic_node(rest_logic, node, local_templates))
805
- node.subst_subnode(subst)
806
- end
807
- when :if
808
- _, expr, then_logic, else_logic = logic
809
- children = [
810
- lambda {|out, context| out.output_logic_line "if (#{expr})" },
811
- generate_logic_node(then_logic, node, local_templates)
812
- ]
813
- if else_logic
814
- children.concat [
815
- lambda {|out, context| out.output_logic_line "else" },
816
- generate_logic_node(else_logic, node, local_templates)
817
- ]
818
- end
819
- children <<
820
- lambda {|out, context| out.output_logic_line "end" }
821
- TemplateNode.new(*children)
822
- when :iter
823
- _, call, fargs, rest_logic = logic
824
- TemplateNode.new(
825
- lambda {|out, context| out.output_logic_line "#{call} {|#{fargs}|" },
826
- generate_logic_node(rest_logic, node, local_templates),
827
- lambda {|out, context| out.output_logic_line "}" }
828
- )
829
- when :call
830
- _, recv, meth, args = logic
831
- TemplateNode.new(
832
- lambda {|out, context|
833
- as = [out.outvar, ", "]
834
- ns = context.namespaces.reject {|k, v| HTree::Context::DefaultNamespaces[k] == v }
835
- if ns.empty?
836
- as << out.contextvar
837
- else
838
- as << "#{out.contextvar}.subst_namespaces("
839
- sep = ''
840
- ns.each {|k, v|
841
- as << sep << (k ? k.dump : "nil") << '=>' << v.dump
842
- sep = ', '
843
- }
844
- as << ")"
845
- end
846
- unless args.empty?
847
- as << ", " << args
848
- end
849
- if recv
850
- out.output_logic_line "(#{recv})._ht_#{meth}(#{as.join('')})"
851
- elsif local_templates.include? meth
852
- out.output_logic_line "#{meth}.call(#{as.join('')})"
853
- else
854
- out.output_logic_line "_ht_#{meth}(#{as.join('')})"
855
- end
856
- }
857
- )
858
- else
859
- raise Exception, "[bug] invalid logic: #{logic.inspect}"
860
- end
861
- end
862
-
863
- class HTree::GenCode # :nodoc:
864
- def output_dynamic_text(expr)
865
- flush_buffer
866
- @code << "#{@outvar}.output_dynamic_text((#{expr}))\n"
867
- end
868
-
869
- def output_dynamic_attvalue(expr)
870
- flush_buffer
871
- @code << "#{@outvar}.output_dynamic_attvalue((#{expr}))\n"
872
- end
873
-
874
- def output_logic_line(line)
875
- flush_buffer
876
- @code << line << "\n"
877
- end
878
- end
879
-
880
- class TemplateNode # :nodoc:
881
- include HTree::Node
882
-
883
- def initialize(*children)
884
- @children = children.flatten.compact
885
- end
886
- attr_reader :children
887
-
888
- def output(out, context)
889
- @children.each {|c|
890
- if c.respond_to? :call
891
- c.call(out, context)
892
- else
893
- c.output(out, context)
894
- end
895
- }
896
- end
897
- end
898
-
899
- class TemplateAttrName < HTree::Name # :nodoc:
900
- def output_attribute(text, out, context)
901
- output(out, context)
902
- out.output_string '="'
903
- out.output_dynamic_attvalue(text.to_s)
904
- out.output_string '"'
905
- end
906
- end
907
-
908
- end
909
- # :startdoc: