drnic-haml 2.3.0

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.
Files changed (190) hide show
  1. data/.yardopts +5 -0
  2. data/CONTRIBUTING +4 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.md +347 -0
  5. data/REVISION +1 -0
  6. data/Rakefile +371 -0
  7. data/VERSION +1 -0
  8. data/VERSION_NAME +1 -0
  9. data/bin/css2sass +7 -0
  10. data/bin/haml +9 -0
  11. data/bin/html2haml +7 -0
  12. data/bin/sass +8 -0
  13. data/extra/haml-mode.el +663 -0
  14. data/extra/sass-mode.el +205 -0
  15. data/extra/update_watch.rb +13 -0
  16. data/init.rb +8 -0
  17. data/lib/haml.rb +40 -0
  18. data/lib/haml/buffer.rb +307 -0
  19. data/lib/haml/engine.rb +301 -0
  20. data/lib/haml/error.rb +22 -0
  21. data/lib/haml/exec.rb +470 -0
  22. data/lib/haml/filters.rb +341 -0
  23. data/lib/haml/helpers.rb +560 -0
  24. data/lib/haml/helpers/action_view_extensions.rb +40 -0
  25. data/lib/haml/helpers/action_view_mods.rb +176 -0
  26. data/lib/haml/herb.rb +96 -0
  27. data/lib/haml/html.rb +308 -0
  28. data/lib/haml/precompiler.rb +997 -0
  29. data/lib/haml/shared.rb +78 -0
  30. data/lib/haml/template.rb +51 -0
  31. data/lib/haml/template/patch.rb +58 -0
  32. data/lib/haml/template/plugin.rb +71 -0
  33. data/lib/haml/util.rb +244 -0
  34. data/lib/haml/version.rb +64 -0
  35. data/lib/sass.rb +24 -0
  36. data/lib/sass/css.rb +423 -0
  37. data/lib/sass/engine.rb +491 -0
  38. data/lib/sass/environment.rb +79 -0
  39. data/lib/sass/error.rb +162 -0
  40. data/lib/sass/files.rb +133 -0
  41. data/lib/sass/plugin.rb +170 -0
  42. data/lib/sass/plugin/merb.rb +57 -0
  43. data/lib/sass/plugin/rails.rb +23 -0
  44. data/lib/sass/repl.rb +58 -0
  45. data/lib/sass/script.rb +55 -0
  46. data/lib/sass/script/bool.rb +17 -0
  47. data/lib/sass/script/color.rb +183 -0
  48. data/lib/sass/script/funcall.rb +50 -0
  49. data/lib/sass/script/functions.rb +199 -0
  50. data/lib/sass/script/lexer.rb +191 -0
  51. data/lib/sass/script/literal.rb +177 -0
  52. data/lib/sass/script/node.rb +14 -0
  53. data/lib/sass/script/number.rb +381 -0
  54. data/lib/sass/script/operation.rb +45 -0
  55. data/lib/sass/script/parser.rb +222 -0
  56. data/lib/sass/script/string.rb +12 -0
  57. data/lib/sass/script/unary_operation.rb +34 -0
  58. data/lib/sass/script/variable.rb +31 -0
  59. data/lib/sass/tree/comment_node.rb +84 -0
  60. data/lib/sass/tree/debug_node.rb +30 -0
  61. data/lib/sass/tree/directive_node.rb +70 -0
  62. data/lib/sass/tree/for_node.rb +48 -0
  63. data/lib/sass/tree/if_node.rb +54 -0
  64. data/lib/sass/tree/import_node.rb +69 -0
  65. data/lib/sass/tree/mixin_def_node.rb +29 -0
  66. data/lib/sass/tree/mixin_node.rb +48 -0
  67. data/lib/sass/tree/node.rb +252 -0
  68. data/lib/sass/tree/prop_node.rb +106 -0
  69. data/lib/sass/tree/root_node.rb +56 -0
  70. data/lib/sass/tree/rule_node.rb +220 -0
  71. data/lib/sass/tree/variable_node.rb +34 -0
  72. data/lib/sass/tree/while_node.rb +31 -0
  73. data/rails/init.rb +1 -0
  74. data/test/benchmark.rb +99 -0
  75. data/test/haml/engine_test.rb +1129 -0
  76. data/test/haml/helper_test.rb +282 -0
  77. data/test/haml/html2haml_test.rb +258 -0
  78. data/test/haml/markaby/standard.mab +52 -0
  79. data/test/haml/mocks/article.rb +6 -0
  80. data/test/haml/results/content_for_layout.xhtml +12 -0
  81. data/test/haml/results/eval_suppressed.xhtml +9 -0
  82. data/test/haml/results/filters.xhtml +62 -0
  83. data/test/haml/results/helpers.xhtml +93 -0
  84. data/test/haml/results/helpful.xhtml +10 -0
  85. data/test/haml/results/just_stuff.xhtml +68 -0
  86. data/test/haml/results/list.xhtml +12 -0
  87. data/test/haml/results/nuke_inner_whitespace.xhtml +40 -0
  88. data/test/haml/results/nuke_outer_whitespace.xhtml +148 -0
  89. data/test/haml/results/original_engine.xhtml +20 -0
  90. data/test/haml/results/partial_layout.xhtml +5 -0
  91. data/test/haml/results/partials.xhtml +21 -0
  92. data/test/haml/results/render_layout.xhtml +3 -0
  93. data/test/haml/results/silent_script.xhtml +74 -0
  94. data/test/haml/results/standard.xhtml +162 -0
  95. data/test/haml/results/tag_parsing.xhtml +23 -0
  96. data/test/haml/results/very_basic.xhtml +5 -0
  97. data/test/haml/results/whitespace_handling.xhtml +89 -0
  98. data/test/haml/rhtml/_av_partial_1.rhtml +12 -0
  99. data/test/haml/rhtml/_av_partial_2.rhtml +8 -0
  100. data/test/haml/rhtml/action_view.rhtml +62 -0
  101. data/test/haml/rhtml/standard.rhtml +54 -0
  102. data/test/haml/spec_test.rb +44 -0
  103. data/test/haml/template_test.rb +217 -0
  104. data/test/haml/templates/_av_partial_1.haml +9 -0
  105. data/test/haml/templates/_av_partial_1_ugly.haml +9 -0
  106. data/test/haml/templates/_av_partial_2.haml +5 -0
  107. data/test/haml/templates/_av_partial_2_ugly.haml +5 -0
  108. data/test/haml/templates/_layout.erb +3 -0
  109. data/test/haml/templates/_layout_for_partial.haml +3 -0
  110. data/test/haml/templates/_partial.haml +8 -0
  111. data/test/haml/templates/_text_area.haml +3 -0
  112. data/test/haml/templates/action_view.haml +47 -0
  113. data/test/haml/templates/action_view_ugly.haml +47 -0
  114. data/test/haml/templates/breakage.haml +8 -0
  115. data/test/haml/templates/content_for_layout.haml +8 -0
  116. data/test/haml/templates/eval_suppressed.haml +11 -0
  117. data/test/haml/templates/filters.haml +66 -0
  118. data/test/haml/templates/helpers.haml +95 -0
  119. data/test/haml/templates/helpful.haml +11 -0
  120. data/test/haml/templates/just_stuff.haml +83 -0
  121. data/test/haml/templates/list.haml +12 -0
  122. data/test/haml/templates/nuke_inner_whitespace.haml +32 -0
  123. data/test/haml/templates/nuke_outer_whitespace.haml +144 -0
  124. data/test/haml/templates/original_engine.haml +17 -0
  125. data/test/haml/templates/partial_layout.haml +3 -0
  126. data/test/haml/templates/partialize.haml +1 -0
  127. data/test/haml/templates/partials.haml +12 -0
  128. data/test/haml/templates/render_layout.haml +2 -0
  129. data/test/haml/templates/silent_script.haml +40 -0
  130. data/test/haml/templates/standard.haml +42 -0
  131. data/test/haml/templates/standard_ugly.haml +42 -0
  132. data/test/haml/templates/tag_parsing.haml +21 -0
  133. data/test/haml/templates/very_basic.haml +4 -0
  134. data/test/haml/templates/whitespace_handling.haml +87 -0
  135. data/test/haml/util_test.rb +92 -0
  136. data/test/linked_rails.rb +12 -0
  137. data/test/sass/css2sass_test.rb +294 -0
  138. data/test/sass/engine_test.rb +956 -0
  139. data/test/sass/functions_test.rb +126 -0
  140. data/test/sass/more_results/more1.css +9 -0
  141. data/test/sass/more_results/more1_with_line_comments.css +26 -0
  142. data/test/sass/more_results/more_import.css +29 -0
  143. data/test/sass/more_templates/_more_partial.sass +2 -0
  144. data/test/sass/more_templates/more1.sass +23 -0
  145. data/test/sass/more_templates/more_import.sass +11 -0
  146. data/test/sass/plugin_test.rb +229 -0
  147. data/test/sass/results/alt.css +4 -0
  148. data/test/sass/results/basic.css +9 -0
  149. data/test/sass/results/compact.css +5 -0
  150. data/test/sass/results/complex.css +87 -0
  151. data/test/sass/results/compressed.css +1 -0
  152. data/test/sass/results/expanded.css +19 -0
  153. data/test/sass/results/import.css +29 -0
  154. data/test/sass/results/line_numbers.css +49 -0
  155. data/test/sass/results/mixins.css +95 -0
  156. data/test/sass/results/multiline.css +24 -0
  157. data/test/sass/results/nested.css +22 -0
  158. data/test/sass/results/parent_ref.css +13 -0
  159. data/test/sass/results/script.css +16 -0
  160. data/test/sass/results/subdir/nested_subdir/nested_subdir.css +1 -0
  161. data/test/sass/results/subdir/subdir.css +3 -0
  162. data/test/sass/results/units.css +11 -0
  163. data/test/sass/script_test.rb +261 -0
  164. data/test/sass/templates/_partial.sass +2 -0
  165. data/test/sass/templates/alt.sass +16 -0
  166. data/test/sass/templates/basic.sass +23 -0
  167. data/test/sass/templates/bork1.sass +2 -0
  168. data/test/sass/templates/bork2.sass +2 -0
  169. data/test/sass/templates/bork3.sass +2 -0
  170. data/test/sass/templates/compact.sass +17 -0
  171. data/test/sass/templates/complex.sass +307 -0
  172. data/test/sass/templates/compressed.sass +15 -0
  173. data/test/sass/templates/expanded.sass +17 -0
  174. data/test/sass/templates/import.sass +11 -0
  175. data/test/sass/templates/importee.sass +19 -0
  176. data/test/sass/templates/line_numbers.sass +13 -0
  177. data/test/sass/templates/mixins.sass +76 -0
  178. data/test/sass/templates/multiline.sass +20 -0
  179. data/test/sass/templates/nested.sass +25 -0
  180. data/test/sass/templates/nested_bork1.sass +2 -0
  181. data/test/sass/templates/nested_bork2.sass +2 -0
  182. data/test/sass/templates/nested_bork3.sass +2 -0
  183. data/test/sass/templates/parent_ref.sass +25 -0
  184. data/test/sass/templates/script.sass +101 -0
  185. data/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +2 -0
  186. data/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +3 -0
  187. data/test/sass/templates/subdir/subdir.sass +6 -0
  188. data/test/sass/templates/units.sass +11 -0
  189. data/test/test_helper.rb +44 -0
  190. metadata +298 -0
@@ -0,0 +1,341 @@
1
+ module Haml
2
+ # The module containing the default Haml filters,
3
+ # as well as the base module, {Haml::Filters::Base}.
4
+ #
5
+ # @see Haml::Filters::Base
6
+ module Filters
7
+ # @return [Hash<String, Haml::Filters::Base>] a hash of filter names to classes
8
+ def self.defined
9
+ @defined ||= {}
10
+ end
11
+
12
+ # The base module for Haml filters.
13
+ # User-defined filters should be modules including this module.
14
+ # The name of the filter is taken by downcasing the module name.
15
+ # For instance, if the module is named `FooBar`, the filter will be `:foobar`.
16
+ #
17
+ # A user-defined filter should override either \{#render} or {\#compile}.
18
+ # \{#render} is the most common.
19
+ # It takes a string, the filter source,
20
+ # and returns another string, the result of the filter.
21
+ # For example, the following will define a filter named `:sass`:
22
+ #
23
+ # module Haml::Filters::Sass
24
+ # include Haml::Filters::Base
25
+ #
26
+ # def render(text)
27
+ # ::Sass::Engine.new(text).render
28
+ # end
29
+ # end
30
+ #
31
+ # For details on overriding \{#compile}, see its documentation.
32
+ #
33
+ # Note that filters overriding \{#render} automatically support `#{}`
34
+ # for interpolating Ruby code.
35
+ # Those overriding \{#compile} will need to add such support manually
36
+ # if it's desired.
37
+ module Base
38
+ # This method is automatically called when {Base} is included in a module.
39
+ # It automatically defines a filter
40
+ # with the downcased name of that module.
41
+ # For example, if the module is named `FooBar`, the filter will be `:foobar`.
42
+ #
43
+ # @param base [Module, Class] The module that this is included in
44
+ def self.included(base)
45
+ Filters.defined[base.name.split("::").last.downcase] = base
46
+ base.extend(base)
47
+ end
48
+
49
+ # Takes the source text that should be passed to the filter
50
+ # and returns the result of running the filter on that string.
51
+ #
52
+ # This should be overridden in most individual filter modules
53
+ # to render text with the given filter.
54
+ # If \{#compile} is overridden, however, \{#render} doesn't need to be.
55
+ #
56
+ # @param text [String] The source text for the filter to process
57
+ # @return [String] The filtered result
58
+ # @raise [Haml::Error] if it's not overridden
59
+ def render(text)
60
+ raise Error.new("#{self.inspect}#render not defined!")
61
+ end
62
+
63
+ # Same as \{#render}, but takes a {Haml::Engine} options hash as well.
64
+ # It's only safe to rely on options made available in {Haml::Engine#options\_for\_buffer}.
65
+ #
66
+ # @see #render
67
+ # @param text [String] The source text for the filter to process
68
+ # @return [String] The filtered result
69
+ # @raise [Haml::Error] if it or \{#render} isn't overridden
70
+ def render_with_options(text, options)
71
+ render(text)
72
+ end
73
+
74
+ # Same as \{#compile}, but requires the necessary files first.
75
+ # *This is used by {Haml::Engine} and is not intended to be overridden or used elsewhere.*
76
+ #
77
+ # @see #compile
78
+ def internal_compile(*args)
79
+ resolve_lazy_requires
80
+ compile(*args)
81
+ end
82
+
83
+ # This should be overridden when a filter needs to have access to the Haml evaluation context.
84
+ # Rather than applying a filter to a string at compile-time,
85
+ # \{#compile} uses the {Haml::Precompiler} instance to compile the string to Ruby code
86
+ # that will be executed in the context of the active Haml template.
87
+ #
88
+ # Warning: the {Haml::Precompiler} interface is neither well-documented
89
+ # nor guaranteed to be stable.
90
+ # If you want to make use of it, you'll probably need to look at the source code
91
+ # and should test your filter when upgrading to new Haml versions.
92
+ #
93
+ # @param precompiler [Haml::Precompiler] The precompiler instance
94
+ # @param text [String] The text of the filter
95
+ # @raise [Haml::Error] if none of \{#compile}, \{#render}, and \{#render_with_options} are overridden
96
+ def compile(precompiler, text)
97
+ resolve_lazy_requires
98
+ filter = self
99
+ precompiler.instance_eval do
100
+ if contains_interpolation?(text)
101
+ return if options[:suppress_eval]
102
+
103
+ push_script <<RUBY, :escape_html => false
104
+ find_and_preserve(#{filter.inspect}.render_with_options(#{unescape_interpolation(text)}, _hamlout.options))
105
+ RUBY
106
+ return
107
+ end
108
+
109
+ rendered = Haml::Helpers::find_and_preserve(filter.render_with_options(text, precompiler.options), precompiler.options[:preserve])
110
+
111
+ if !options[:ugly]
112
+ push_text(rendered.rstrip.gsub("\n", "\n#{' ' * @output_tabs}"))
113
+ else
114
+ push_text(rendered.rstrip)
115
+ end
116
+ end
117
+ end
118
+
119
+ # This becomes a class method of modules that include {Base}.
120
+ # It allows the module to specify one or more Ruby files
121
+ # that Haml should try to require when compiling the filter.
122
+ #
123
+ # The first file specified is tried first, then the second, etc.
124
+ # If none are found, the compilation throws an exception.
125
+ #
126
+ # For example:
127
+ #
128
+ # module Haml::Filters::Markdown
129
+ # lazy_require 'rdiscount', 'peg_markdown', 'maruku', 'bluecloth'
130
+ #
131
+ # ...
132
+ # end
133
+ #
134
+ # @param reqs [Array<String>] The requires to run
135
+ def lazy_require(*reqs)
136
+ @lazy_requires = reqs
137
+ end
138
+
139
+ private
140
+
141
+ def resolve_lazy_requires
142
+ return unless @lazy_requires
143
+
144
+ @lazy_requires[0...-1].each do |req|
145
+ begin
146
+ @required = req
147
+ require @required
148
+ return
149
+ rescue LoadError; end # RCov doesn't see this, but it is run
150
+ end
151
+
152
+ begin
153
+ @required = @lazy_requires[-1]
154
+ require @required
155
+ rescue LoadError => e
156
+ classname = self.name.match(/\w+$/)[0]
157
+
158
+ if @lazy_requires.size == 1
159
+ raise Error.new("Can't run #{classname} filter; required file '#{@lazy_requires.first}' not found")
160
+ else
161
+ raise Error.new("Can't run #{classname} filter; required #{@lazy_requires.map { |r| "'#{r}'" }.join(' or ')}, but none were found")
162
+ end
163
+ end
164
+ end
165
+ end
166
+ end
167
+ end
168
+
169
+ begin
170
+ require 'rubygems'
171
+ rescue LoadError; end
172
+
173
+ module Haml
174
+ module Filters
175
+ # Does not parse the filtered text.
176
+ # This is useful for large blocks of text without HTML tags,
177
+ # when you don't want lines starting with `.` or `-`
178
+ # to be parsed.
179
+ module Plain
180
+ include Base
181
+
182
+ # @see Base#render
183
+ def render(text); text; end
184
+ end
185
+
186
+ # Surrounds the filtered text with `<script>` and CDATA tags.
187
+ # Useful for including inline Javascript.
188
+ module Javascript
189
+ include Base
190
+
191
+ # @see Base#render_with_options
192
+ def render_with_options(text, options)
193
+ <<END
194
+ <script type=#{options[:attr_wrapper]}text/javascript#{options[:attr_wrapper]}>
195
+ //<![CDATA[
196
+ #{text.rstrip.gsub("\n", "\n ")}
197
+ //]]>
198
+ </script>
199
+ END
200
+ end
201
+ end
202
+
203
+ # Surrounds the filtered text with CDATA tags.
204
+ module Cdata
205
+ include Base
206
+
207
+ # @see Base#render
208
+ def render(text)
209
+ "<![CDATA[#{("\n" + text).rstrip.gsub("\n", "\n ")}\n]]>"
210
+ end
211
+ end
212
+
213
+ # Works the same as {Plain}, but HTML-escapes the text
214
+ # before placing it in the document.
215
+ module Escaped
216
+ include Base
217
+
218
+ # @see Base#render
219
+ def render(text)
220
+ Haml::Helpers.html_escape text
221
+ end
222
+ end
223
+
224
+ # Parses the filtered text with the normal Ruby interpreter.
225
+ # All output sent to `$stdout`, such as with `puts`,
226
+ # is output into the Haml document.
227
+ # Not available if the {file:HAML_REFERENCE.md#suppress_eval-option `:suppress_eval`} option is set to true.
228
+ # The Ruby code is evaluated in the same context as the Haml template.
229
+ module Ruby
230
+ include Base
231
+ lazy_require 'stringio'
232
+
233
+ # @see Base#compile
234
+ def compile(precompiler, text)
235
+ return if precompiler.options[:suppress_eval]
236
+ precompiler.instance_eval do
237
+ push_silent <<-FIRST.gsub("\n", ';') + text + <<-LAST.gsub("\n", ';')
238
+ _haml_old_stdout = $stdout
239
+ $stdout = StringIO.new(_hamlout.buffer, 'a')
240
+ FIRST
241
+ _haml_old_stdout, $stdout = $stdout, _haml_old_stdout
242
+ _haml_old_stdout.close
243
+ LAST
244
+ end
245
+ end
246
+ end
247
+
248
+ # Inserts the filtered text into the template with whitespace preserved.
249
+ # `preserve`d blocks of text aren't indented,
250
+ # and newlines are replaced with the HTML escape code for newlines,
251
+ # to preserve nice-looking output.
252
+ #
253
+ # @see Haml::Helpers#preserve
254
+ module Preserve
255
+ include Base
256
+
257
+ # @see Base#render
258
+ def render(text)
259
+ Haml::Helpers.preserve text
260
+ end
261
+ end
262
+
263
+ # Parses the filtered text with {Sass} to produce CSS output.
264
+ module Sass
265
+ include Base
266
+ lazy_require 'sass/plugin'
267
+
268
+ # @see Base#render
269
+ def render(text)
270
+ ::Sass::Engine.new(text, ::Sass::Plugin.engine_options).render
271
+ end
272
+ end
273
+
274
+ # Parses the filtered text with ERB, like an RHTML template.
275
+ # Not available if the {file:HAML_REFERENCE.md#suppress_eval-option `:suppress_eval`} option is set to true.
276
+ # Embedded Ruby code is evaluated in the same context as the Haml template.
277
+ module ERB
278
+ include Base
279
+ lazy_require 'erb'
280
+
281
+ # @see Base#compile
282
+ def compile(precompiler, text)
283
+ return if precompiler.options[:suppress_eval]
284
+ src = ::ERB.new(text).src.sub(/^#coding:.*?\n/, '').
285
+ sub(/^_erbout = '';/, "").gsub("\n", ';')
286
+ precompiler.send(:push_silent, src)
287
+ end
288
+ end
289
+
290
+ # Parses the filtered text with [Textile](http://www.textism.com/tools/textile).
291
+ # Only works if [RedCloth](http://redcloth.org) is installed.
292
+ module Textile
293
+ include Base
294
+ lazy_require 'redcloth'
295
+
296
+ # @see Base#render
297
+ def render(text)
298
+ ::RedCloth.new(text).to_html(:textile)
299
+ end
300
+ end
301
+ RedCloth = Textile
302
+ Filters.defined['redcloth'] = RedCloth
303
+
304
+ # Parses the filtered text with [Markdown](http://daringfireball.net/projects/markdown).
305
+ # Only works if [RDiscount](http://github.com/rtomayko/rdiscount),
306
+ # [RPeg-Markdown](http://github.com/rtomayko/rpeg-markdown),
307
+ # [Maruku](http://maruku.rubyforge.org),
308
+ # or [BlueCloth](www.deveiate.org/projects/BlueCloth) are installed.
309
+ module Markdown
310
+ include Base
311
+ lazy_require 'rdiscount', 'peg_markdown', 'maruku', 'bluecloth'
312
+
313
+ # @see Base#render
314
+ def render(text)
315
+ engine = case @required
316
+ when 'rdiscount'
317
+ ::RDiscount
318
+ when 'peg_markdown'
319
+ ::PEGMarkdown
320
+ when 'maruku'
321
+ ::Maruku
322
+ when 'bluecloth'
323
+ ::BlueCloth
324
+ end
325
+ engine.new(text).to_html
326
+ end
327
+ end
328
+
329
+ # Parses the filtered text with [Maruku](http://maruku.rubyforge.org),
330
+ # which has some non-standard extensions to Markdown.
331
+ module Maruku
332
+ include Base
333
+ lazy_require 'maruku'
334
+
335
+ # @see Base#render
336
+ def render(text)
337
+ ::Maruku.new(text).to_html
338
+ end
339
+ end
340
+ end
341
+ end
@@ -0,0 +1,560 @@
1
+ if defined?(ActionView)
2
+ require 'haml/helpers/action_view_mods'
3
+ require 'haml/helpers/action_view_extensions'
4
+ end
5
+
6
+ module Haml
7
+ # This module contains various helpful methods to make it easier to do various tasks.
8
+ # {Haml::Helpers} is automatically included in the context
9
+ # that a Haml template is parsed in, so all these methods are at your
10
+ # disposal from within the template.
11
+ module Helpers
12
+ # An object that raises an error when \{#to\_s} is called.
13
+ # It's used to raise an error when the return value of a helper is used
14
+ # when it shouldn't be.
15
+ class ErrorReturn
16
+ # @param message [String] The error message to raise when \{#to\_s} is called
17
+ def initialize(method)
18
+ @message = <<MESSAGE
19
+ #{method} outputs directly to the Haml template.
20
+ Disregard its return value and use the - operator,
21
+ or use capture_haml to get the value as a String.
22
+ MESSAGE
23
+ end
24
+
25
+ # Raises an error.
26
+ #
27
+ # @raise [Haml::Error] The error
28
+ def to_s
29
+ raise Haml::Error.new(@message)
30
+ rescue Haml::Error => e
31
+ e.backtrace.shift
32
+
33
+ # If the ErrorReturn is used directly in the template,
34
+ # we don't want Haml's stuff to get into the backtrace,
35
+ # so we get rid of the format_script line.
36
+ #
37
+ # We also have to subtract one from the Haml line number
38
+ # since the value is passed to format_script the line after
39
+ # it's actually used.
40
+ if e.backtrace.first =~ /^\(eval\):\d+:in `format_script/
41
+ e.backtrace.shift
42
+ e.backtrace.first.gsub!(/^\(haml\):(\d+)/) {|s| "(haml):#{$1.to_i - 1}"}
43
+ end
44
+ raise e
45
+ end
46
+
47
+ # @return [String] A human-readable string representation
48
+ def inspect
49
+ "Haml::Helpers::ErrorReturn(#{@message.inspect})"
50
+ end
51
+ end
52
+
53
+ self.extend self
54
+
55
+ @@action_view_defined = defined?(ActionView)
56
+ @@force_no_action_view = false
57
+
58
+ # @return [Boolean] Whether or not ActionView is loaded
59
+ def self.action_view?
60
+ @@action_view_defined
61
+ end
62
+
63
+ # Note: this does **not** need to be called when using Haml helpers
64
+ # normally in Rails.
65
+ #
66
+ # Initializes the current object as though it were in the same context
67
+ # as a normal ActionView instance using Haml.
68
+ # This is useful if you want to use the helpers in a context
69
+ # other than the normal setup with ActionView.
70
+ # For example:
71
+ #
72
+ # context = Object.new
73
+ # class << context
74
+ # include Haml::Helpers
75
+ # end
76
+ # context.init_haml_helpers
77
+ # context.haml_tag :p, "Stuff"
78
+ #
79
+ def init_haml_helpers
80
+ @haml_buffer = Haml::Buffer.new(@haml_buffer, Haml::Engine.new('').send(:options_for_buffer))
81
+ nil
82
+ end
83
+
84
+ # Runs a block of code in a non-Haml context
85
+ # (i.e. \{#is\_haml?} will return false).
86
+ #
87
+ # This is mainly useful for rendering sub-templates such as partials in a non-Haml language,
88
+ # particularly where helpers may behave differently when run from Haml.
89
+ #
90
+ # Note that this is automatically applied to Rails partials.
91
+ #
92
+ # @yield A block which won't register as Haml
93
+ def non_haml
94
+ was_active = @haml_buffer.active?
95
+ @haml_buffer.active = false
96
+ yield
97
+ ensure
98
+ @haml_buffer.active = was_active
99
+ end
100
+
101
+ # Uses \{#preserve} to convert any newlines inside whitespace-sensitive tags
102
+ # into the HTML entities for endlines.
103
+ #
104
+ # @param tags [Array<String>] Tags that should have newlines escaped
105
+ #
106
+ # @overload find_and_preserve(input, tags = haml_buffer.options[:preserve])
107
+ # Escapes newlines within a string.
108
+ #
109
+ # @param input [String] The string within which to escape newlines
110
+ # @overload find_and_preserve(tags = haml_buffer.options[:preserve])
111
+ # Escapes newlines within a block of Haml code.
112
+ #
113
+ # @yield The block within which to escape newlines
114
+ def find_and_preserve(input = nil, tags = haml_buffer.options[:preserve], &block)
115
+ return find_and_preserve(capture_haml(&block), input || tags) if block
116
+
117
+ input = input.to_s
118
+ input.gsub(/<(#{tags.map(&Regexp.method(:escape)).join('|')})([^>]*)>(.*?)(<\/\1>)/im) do
119
+ "<#{$1}#{$2}>#{preserve($3)}</#{$1}>"
120
+ end
121
+ end
122
+
123
+ # Takes any string, finds all the newlines, and converts them to
124
+ # HTML entities so they'll render correctly in
125
+ # whitespace-sensitive tags without screwing up the indentation.
126
+ #
127
+ # @overload perserve(input)
128
+ # Escapes newlines within a string.
129
+ #
130
+ # @param input [String] The string within which to escape all newlines
131
+ # @overload perserve
132
+ # Escapes newlines within a block of Haml code.
133
+ #
134
+ # @yield The block within which to escape newlines
135
+ def preserve(input = '', &block)
136
+ return preserve(capture_haml(&block)) if block
137
+
138
+ input.chomp("\n").gsub(/\n/, '&#x000A;').gsub(/\r/, '')
139
+ end
140
+ alias_method :flatten, :preserve
141
+
142
+ # Takes an `Enumerable` object and a block
143
+ # and iterates over the enum,
144
+ # yielding each element to a Haml block
145
+ # and putting the result into `<li>` elements.
146
+ # This creates a list of the results of the block.
147
+ # For example:
148
+ #
149
+ # = list_of([['hello'], ['yall']]) do |i|
150
+ # = i[0]
151
+ #
152
+ # Produces:
153
+ #
154
+ # <li>hello</li>
155
+ # <li>yall</li>
156
+ #
157
+ # And
158
+ #
159
+ # = list_of({:title => 'All the stuff', :description => 'A book about all the stuff.'}) do |key, val|
160
+ # %h3= key.humanize
161
+ # %p= val
162
+ #
163
+ # Produces:
164
+ #
165
+ # <li>
166
+ # <h3>Title</h3>
167
+ # <p>All the stuff</p>
168
+ # </li>
169
+ # <li>
170
+ # <h3>Description</h3>
171
+ # <p>A book about all the stuff.</p>
172
+ # </li>
173
+ #
174
+ # @param enum [Enumerable] The list of objects to iterate over
175
+ # @yield [item] A block which contains Haml code that goes within list items
176
+ # @yieldparam item An element of `enum`
177
+ def list_of(enum, &block)
178
+ to_return = enum.collect do |i|
179
+ result = capture_haml(i, &block)
180
+
181
+ if result.count("\n") > 1
182
+ result.gsub!("\n", "\n ")
183
+ result = "\n #{result.strip}\n"
184
+ else
185
+ result.strip!
186
+ end
187
+
188
+ "<li>#{result}</li>"
189
+ end
190
+ to_return.join("\n")
191
+ end
192
+
193
+ # Returns a hash containing default assignments for the `xmlns`, `lang`, and `xml:lang`
194
+ # attributes of the `html` HTML element.
195
+ # For example,
196
+ #
197
+ # %html{html_attrs}
198
+ #
199
+ # becomes
200
+ #
201
+ # <html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en-US' lang='en-US'>
202
+ #
203
+ # @param lang [String] The value of `xml:lang` and `lang`
204
+ # @return [Hash<#to_s, String>] The attribute hash
205
+ def html_attrs(lang = 'en-US')
206
+ {:xmlns => "http://www.w3.org/1999/xhtml", 'xml:lang' => lang, :lang => lang}
207
+ end
208
+
209
+ # Increments the number of tabs the buffer automatically adds
210
+ # to the lines of the template.
211
+ # For example:
212
+ #
213
+ # %h1 foo
214
+ # - tab_up
215
+ # %p bar
216
+ # - tab_down
217
+ # %strong baz
218
+ #
219
+ # Produces:
220
+ #
221
+ # <h1>foo</h1>
222
+ # <p>bar</p>
223
+ # <strong>baz</strong>
224
+ #
225
+ # @param i [Fixnum] The number of tabs by which to increase the indentation
226
+ # @see #tab_down
227
+ def tab_up(i = 1)
228
+ haml_buffer.tabulation += i
229
+ end
230
+
231
+ # Decrements the number of tabs the buffer automatically adds
232
+ # to the lines of the template.
233
+ #
234
+ # @param i [Fixnum] The number of tabs by which to decrease the indentation
235
+ # @see #tab_up
236
+ def tab_down(i = 1)
237
+ haml_buffer.tabulation -= i
238
+ end
239
+
240
+ # Surrounds a block of Haml code with strings,
241
+ # with no whitespace in between.
242
+ # For example:
243
+ #
244
+ # = surround '(', ')' do
245
+ # %a{:href => "food"} chicken
246
+ #
247
+ # Produces:
248
+ #
249
+ # (<a href='food'>chicken</a>)
250
+ #
251
+ # and
252
+ #
253
+ # = surround '*' do
254
+ # %strong angry
255
+ #
256
+ # Produces:
257
+ #
258
+ # *<strong>angry</strong>*
259
+ #
260
+ # @param front [String] The string to add before the Haml
261
+ # @param back [String] The string to add after the Haml
262
+ # @yield A block of Haml to surround
263
+ def surround(front, back = front, &block)
264
+ output = capture_haml(&block)
265
+
266
+ "#{front}#{output.chomp}#{back}\n"
267
+ end
268
+
269
+ # Prepends a string to the beginning of a Haml block,
270
+ # with no whitespace between.
271
+ # For example:
272
+ #
273
+ # = precede '*' do
274
+ # %span.small Not really
275
+ #
276
+ # Produces:
277
+ #
278
+ # *<span class='small'>Not really</span>
279
+ #
280
+ # @param str [String] The string to add before the Haml
281
+ # @yield A block of Haml to prepend to
282
+ def precede(str, &block)
283
+ "#{str}#{capture_haml(&block).chomp}\n"
284
+ end
285
+
286
+ # Appends a string to the end of a Haml block,
287
+ # with no whitespace between.
288
+ # For example:
289
+ #
290
+ # click
291
+ # = succeed '.' do
292
+ # %a{:href=>"thing"} here
293
+ #
294
+ # Produces:
295
+ #
296
+ # click
297
+ # <a href='thing'>here</a>.
298
+ #
299
+ # @param str [String] The string to add after the Haml
300
+ # @yield A block of Haml to append to
301
+ def succeed(str, &block)
302
+ "#{capture_haml(&block).chomp}#{str}\n"
303
+ end
304
+
305
+ # Captures the result of a block of Haml code,
306
+ # gets rid of the excess indentation,
307
+ # and returns it as a string.
308
+ # For example, after the following,
309
+ #
310
+ # .foo
311
+ # - foo = capture_haml(13) do |a|
312
+ # %p= a
313
+ #
314
+ # the local variable `foo` would be assigned to `"<p>13</p>\n"`.
315
+ #
316
+ # @param args [Array] Arguments to pass into the block
317
+ # @yield [args] A block of Haml code that will be converted to a string
318
+ # @yieldparam args [Array] `args`
319
+ def capture_haml(*args, &block)
320
+ buffer = eval('_hamlout', block.binding) rescue haml_buffer
321
+ with_haml_buffer(buffer) do
322
+ position = haml_buffer.buffer.length
323
+
324
+ haml_buffer.capture_position = position
325
+ block.call(*args)
326
+
327
+ captured = haml_buffer.buffer.slice!(position..-1).split(/^/)
328
+
329
+ min_tabs = nil
330
+ captured.each do |line|
331
+ tabs = line.index(/[^ ]/) || line.length
332
+ min_tabs ||= tabs
333
+ min_tabs = min_tabs > tabs ? tabs : min_tabs
334
+ end
335
+
336
+ captured.map do |line|
337
+ line[min_tabs..-1]
338
+ end.join
339
+ end
340
+ ensure
341
+ haml_buffer.capture_position = nil
342
+ end
343
+
344
+ # @deprecated This will be removed in version 2.4.
345
+ # @see \{#haml\_concat}
346
+ def puts(*args)
347
+ warn <<END
348
+ DEPRECATION WARNING:
349
+ The Haml #puts helper is deprecated and will be removed in version 2.4.
350
+ Use the #haml_concat helper instead.
351
+ END
352
+ haml_concat(*args)
353
+ end
354
+
355
+ # Outputs text directly to the Haml buffer, with the proper indentation.
356
+ #
357
+ # @param text [#to_s] The text to output
358
+ def haml_concat(text = "")
359
+ haml_buffer.buffer << haml_indent << text.to_s << "\n"
360
+ ErrorReturn.new("haml_concat")
361
+ end
362
+
363
+ # @return [String] The indentation string for the current line
364
+ def haml_indent
365
+ ' ' * haml_buffer.tabulation
366
+ end
367
+
368
+ # Creates an HTML tag with the given name and optionally text and attributes.
369
+ # Can take a block that will run between the opening and closing tags.
370
+ # If the block is a Haml block or outputs text using \{#haml\_concat},
371
+ # the text will be properly indented.
372
+ #
373
+ # `flags` is a list of symbol flags
374
+ # like those that can be put at the end of a Haml tag
375
+ # (`:/`, `:<`, and `:>`).
376
+ # Currently, only `:/` and `:<` are supported.
377
+ #
378
+ # `haml_tag` outputs directly to the buffer;
379
+ # its return value should not be used.
380
+ # If you need to get the results as a string,
381
+ # use \{#capture\_haml\}.
382
+ #
383
+ # For example,
384
+ #
385
+ # haml_tag :table do
386
+ # haml_tag :tr do
387
+ # haml_tag :td, {:class => 'cell'} do
388
+ # haml_tag :strong, "strong!"
389
+ # haml_concat "data"
390
+ # end
391
+ # haml_tag :td do
392
+ # haml_concat "more_data"
393
+ # end
394
+ # end
395
+ # end
396
+ #
397
+ # outputs
398
+ #
399
+ # <table>
400
+ # <tr>
401
+ # <td class='cell'>
402
+ # <strong>
403
+ # strong!
404
+ # </strong>
405
+ # data
406
+ # </td>
407
+ # <td>
408
+ # more_data
409
+ # </td>
410
+ # </tr>
411
+ # </table>
412
+ #
413
+ # @param name [#to_s] The name of the tag
414
+ # @param flags [Array<Symbol>] Haml end-of-tag flags
415
+ #
416
+ # @overload haml_tag(name, *flags, attributes = {})
417
+ # @yield The block of Haml code within the tag
418
+ # @overload haml_tag(name, text, *flags, attributes = {})
419
+ # @param text [#to_s] The text within the tag
420
+ def haml_tag(name, *rest, &block)
421
+ ret = ErrorReturn.new("haml_tag")
422
+
423
+ name = name.to_s
424
+ text = rest.shift.to_s unless [Symbol, Hash, NilClass].any? {|t| rest.first.is_a? t}
425
+ flags = []
426
+ flags << rest.shift while rest.first.is_a? Symbol
427
+ attributes = Haml::Precompiler.build_attributes(haml_buffer.html?,
428
+ haml_buffer.options[:attr_wrapper],
429
+ rest.shift || {})
430
+
431
+ if text.nil? && block.nil? && (haml_buffer.options[:autoclose].include?(name) || flags.include?(:/))
432
+ haml_concat "<#{name}#{attributes} />"
433
+ return ret
434
+ end
435
+
436
+ if flags.include?(:/)
437
+ raise Error.new("Self-closing tags can't have content.") if text
438
+ raise Error.new("Illegal nesting: nesting within a self-closing tag is illegal.") if block
439
+ end
440
+
441
+ tag = "<#{name}#{attributes}>"
442
+ if block.nil?
443
+ tag << text.to_s << "</#{name}>"
444
+ haml_concat tag
445
+ return ret
446
+ end
447
+
448
+ if text
449
+ raise Error.new("Illegal nesting: content can't be both given to haml_tag :#{name} and nested within it.")
450
+ end
451
+
452
+ if flags.include?(:<)
453
+ tag << capture_haml(&block).strip << "</#{name}>"
454
+ haml_concat tag
455
+ return ret
456
+ end
457
+
458
+ haml_concat tag
459
+ tab_up
460
+ block.call
461
+ tab_down
462
+ haml_concat "</#{name}>"
463
+
464
+ ret
465
+ end
466
+
467
+ # Characters that need to be escaped to HTML entities from user input
468
+ HTML_ESCAPE = { '&'=>'&amp;', '<'=>'&lt;', '>'=>'&gt;', '"'=>'&quot;', "'"=>'&#039;', }
469
+
470
+ # Returns a copy of `text` with ampersands, angle brackets and quotes
471
+ # escaped into HTML entities.
472
+ #
473
+ # @param text [String] The string to sanitize
474
+ # @return [String] The sanitized string
475
+ def html_escape(text)
476
+ text.to_s.gsub(/[\"><&]/) { |s| HTML_ESCAPE[s] }
477
+ end
478
+
479
+ # Escapes HTML entities in `text`, but without escaping an ampersand
480
+ # that is already part of an escaped entity.
481
+ #
482
+ # @param text [String] The string to sanitize
483
+ # @return [String] The sanitized string
484
+ def escape_once(text)
485
+ text.to_s.gsub(/[\"><]|&(?!([a-zA-Z]+|(#\d+));)/) { |s| HTML_ESCAPE[s] }
486
+ end
487
+
488
+ # Returns whether or not the current template is a Haml template.
489
+ #
490
+ # This function, unlike other {Haml::Helpers} functions,
491
+ # also works in other `ActionView` templates,
492
+ # where it will always return false.
493
+ #
494
+ # @return [Boolean] Whether or not the current template is a Haml template
495
+ def is_haml?
496
+ !@haml_buffer.nil? && @haml_buffer.active?
497
+ end
498
+
499
+ # Returns whether or not `block` is defined directly in a Haml template.
500
+ #
501
+ # @param block [Proc] A Ruby block
502
+ # @return [Boolean] Whether or not `block` is defined directly in a Haml template
503
+ def block_is_haml?(block)
504
+ eval('_hamlout', block.binding)
505
+ true
506
+ rescue
507
+ false
508
+ end
509
+
510
+ private
511
+
512
+ # Runs a block of code with the given buffer as the currently active buffer.
513
+ #
514
+ # @param buffer [Haml::Buffer] The Haml buffer to use temporarily
515
+ # @yield A block in which the given buffer should be used
516
+ def with_haml_buffer(buffer)
517
+ @haml_buffer, old_buffer = buffer, @haml_buffer
518
+ old_buffer.active, was_active = false, old_buffer.active? if old_buffer
519
+ @haml_buffer.active = true
520
+ yield
521
+ ensure
522
+ @haml_buffer.active = false
523
+ old_buffer.active = was_active if old_buffer
524
+ @haml_buffer = old_buffer
525
+ end
526
+
527
+ # The current {Haml::Buffer} object.
528
+ #
529
+ # @return [Haml::Buffer]
530
+ def haml_buffer
531
+ @haml_buffer
532
+ end
533
+
534
+ # Gives a proc the same local `_hamlout` and `_erbout` variables
535
+ # that the current template has.
536
+ #
537
+ # @param proc [#call] The proc to bind
538
+ # @return [Proc] A new proc with the new variables bound
539
+ def haml_bind_proc(&proc)
540
+ _hamlout = haml_buffer
541
+ _erbout = _hamlout.buffer
542
+ proc { |*args| proc.call(*args) }
543
+ end
544
+
545
+ include ActionViewExtensions if self.const_defined? "ActionViewExtensions"
546
+ end
547
+ end
548
+
549
+ class Object
550
+ # Haml overrides various `ActionView` helpers,
551
+ # which call an \{#is\_haml?} method
552
+ # to determine whether or not the current context object
553
+ # is a proper Haml context.
554
+ # Because `ActionView` helpers may be included in non-`ActionView::Base` classes,
555
+ # it's a good idea to define \{#is\_haml?} for all objects.
556
+ def is_haml?
557
+ false
558
+ end
559
+ end
560
+