hamlit 2.9.3

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 (107) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +16 -0
  3. data/.travis.yml +45 -0
  4. data/CHANGELOG.md +676 -0
  5. data/Gemfile +28 -0
  6. data/LICENSE.txt +44 -0
  7. data/README.md +150 -0
  8. data/REFERENCE.md +266 -0
  9. data/Rakefile +117 -0
  10. data/benchmark/boolean_attribute.haml +6 -0
  11. data/benchmark/class_attribute.haml +5 -0
  12. data/benchmark/common_attribute.haml +3 -0
  13. data/benchmark/data_attribute.haml +4 -0
  14. data/benchmark/dynamic_attributes/boolean_attribute.haml +4 -0
  15. data/benchmark/dynamic_attributes/class_attribute.haml +4 -0
  16. data/benchmark/dynamic_attributes/common_attribute.haml +2 -0
  17. data/benchmark/dynamic_attributes/data_attribute.haml +2 -0
  18. data/benchmark/dynamic_attributes/id_attribute.haml +2 -0
  19. data/benchmark/dynamic_boolean_attribute.haml +4 -0
  20. data/benchmark/etc/attribute_builder.haml +5 -0
  21. data/benchmark/etc/real_sample.haml +888 -0
  22. data/benchmark/etc/real_sample.rb +11 -0
  23. data/benchmark/etc/static_analyzer.haml +1 -0
  24. data/benchmark/etc/string_interpolation.haml +2 -0
  25. data/benchmark/etc/tags.haml +3 -0
  26. data/benchmark/etc/tags_loop.haml +2 -0
  27. data/benchmark/ext/build_data.rb +17 -0
  28. data/benchmark/ext/build_id.rb +13 -0
  29. data/benchmark/id_attribute.haml +3 -0
  30. data/benchmark/plain.haml +4 -0
  31. data/benchmark/script.haml +4 -0
  32. data/benchmark/slim/LICENSE +21 -0
  33. data/benchmark/slim/context.rb +11 -0
  34. data/benchmark/slim/run-benchmarks.rb +94 -0
  35. data/benchmark/slim/view.erb +23 -0
  36. data/benchmark/slim/view.haml +18 -0
  37. data/benchmark/slim/view.slim +17 -0
  38. data/benchmark/utils/benchmark_ips_extension.rb +43 -0
  39. data/bin/bench +77 -0
  40. data/bin/console +11 -0
  41. data/bin/ruby +3 -0
  42. data/bin/setup +7 -0
  43. data/bin/stackprof +27 -0
  44. data/bin/test +24 -0
  45. data/exe/hamlit +6 -0
  46. data/ext/hamlit/extconf.rb +10 -0
  47. data/ext/hamlit/hamlit.c +553 -0
  48. data/ext/hamlit/hescape.c +108 -0
  49. data/ext/hamlit/hescape.h +20 -0
  50. data/hamlit.gemspec +45 -0
  51. data/lib/hamlit.rb +11 -0
  52. data/lib/hamlit/attribute_builder.rb +173 -0
  53. data/lib/hamlit/attribute_compiler.rb +123 -0
  54. data/lib/hamlit/attribute_parser.rb +110 -0
  55. data/lib/hamlit/cli.rb +130 -0
  56. data/lib/hamlit/compiler.rb +97 -0
  57. data/lib/hamlit/compiler/children_compiler.rb +112 -0
  58. data/lib/hamlit/compiler/comment_compiler.rb +36 -0
  59. data/lib/hamlit/compiler/doctype_compiler.rb +46 -0
  60. data/lib/hamlit/compiler/script_compiler.rb +102 -0
  61. data/lib/hamlit/compiler/silent_script_compiler.rb +24 -0
  62. data/lib/hamlit/compiler/tag_compiler.rb +74 -0
  63. data/lib/hamlit/engine.rb +37 -0
  64. data/lib/hamlit/error.rb +15 -0
  65. data/lib/hamlit/escapable.rb +13 -0
  66. data/lib/hamlit/filters.rb +75 -0
  67. data/lib/hamlit/filters/base.rb +12 -0
  68. data/lib/hamlit/filters/cdata.rb +20 -0
  69. data/lib/hamlit/filters/coffee.rb +17 -0
  70. data/lib/hamlit/filters/css.rb +33 -0
  71. data/lib/hamlit/filters/erb.rb +10 -0
  72. data/lib/hamlit/filters/escaped.rb +22 -0
  73. data/lib/hamlit/filters/javascript.rb +33 -0
  74. data/lib/hamlit/filters/less.rb +20 -0
  75. data/lib/hamlit/filters/markdown.rb +10 -0
  76. data/lib/hamlit/filters/plain.rb +29 -0
  77. data/lib/hamlit/filters/preserve.rb +22 -0
  78. data/lib/hamlit/filters/ruby.rb +10 -0
  79. data/lib/hamlit/filters/sass.rb +15 -0
  80. data/lib/hamlit/filters/scss.rb +15 -0
  81. data/lib/hamlit/filters/text_base.rb +25 -0
  82. data/lib/hamlit/filters/tilt_base.rb +49 -0
  83. data/lib/hamlit/force_escapable.rb +29 -0
  84. data/lib/hamlit/helpers.rb +15 -0
  85. data/lib/hamlit/html.rb +14 -0
  86. data/lib/hamlit/identity.rb +13 -0
  87. data/lib/hamlit/object_ref.rb +30 -0
  88. data/lib/hamlit/parser.rb +49 -0
  89. data/lib/hamlit/parser/MIT-LICENSE +20 -0
  90. data/lib/hamlit/parser/README.md +30 -0
  91. data/lib/hamlit/parser/haml_buffer.rb +348 -0
  92. data/lib/hamlit/parser/haml_compiler.rb +553 -0
  93. data/lib/hamlit/parser/haml_error.rb +61 -0
  94. data/lib/hamlit/parser/haml_helpers.rb +727 -0
  95. data/lib/hamlit/parser/haml_options.rb +286 -0
  96. data/lib/hamlit/parser/haml_parser.rb +800 -0
  97. data/lib/hamlit/parser/haml_util.rb +288 -0
  98. data/lib/hamlit/parser/haml_xss_mods.rb +109 -0
  99. data/lib/hamlit/rails_helpers.rb +51 -0
  100. data/lib/hamlit/rails_template.rb +59 -0
  101. data/lib/hamlit/railtie.rb +10 -0
  102. data/lib/hamlit/ruby_expression.rb +32 -0
  103. data/lib/hamlit/string_splitter.rb +88 -0
  104. data/lib/hamlit/template.rb +28 -0
  105. data/lib/hamlit/utils.rb +18 -0
  106. data/lib/hamlit/version.rb +4 -0
  107. metadata +361 -0
@@ -0,0 +1,61 @@
1
+ module Hamlit
2
+ # An exception raised by Haml code.
3
+ class HamlError < StandardError
4
+
5
+ MESSAGES = {
6
+ :bad_script_indent => '"%s" is indented at wrong level: expected %d, but was at %d.',
7
+ :cant_run_filter => 'Can\'t run "%s" filter; you must require its dependencies first',
8
+ :cant_use_tabs_and_spaces => "Indentation can't use both tabs and spaces.",
9
+ :deeper_indenting => "The line was indented %d levels deeper than the previous line.",
10
+ :filter_not_defined => 'Filter "%s" is not defined.',
11
+ :gem_install_filter_deps => '"%s" filter\'s %s dependency missing: try installing it or adding it to your Gemfile',
12
+ :illegal_element => "Illegal element: classes and ids must have values.",
13
+ :illegal_nesting_content => "Illegal nesting: nesting within a tag that already has content is illegal.",
14
+ :illegal_nesting_header => "Illegal nesting: nesting within a header command is illegal.",
15
+ :illegal_nesting_line => "Illegal nesting: content can't be both given on the same line as %%%s and nested within it.",
16
+ :illegal_nesting_plain => "Illegal nesting: nesting within plain text is illegal.",
17
+ :illegal_nesting_self_closing => "Illegal nesting: nesting within a self-closing tag is illegal.",
18
+ :inconsistent_indentation => "Inconsistent indentation: %s used for indentation, but the rest of the document was indented using %s.",
19
+ :indenting_at_start => "Indenting at the beginning of the document is illegal.",
20
+ :install_haml_contrib => 'To use the "%s" filter, please install the haml-contrib gem.',
21
+ :invalid_attribute_list => 'Invalid attribute list: %s.',
22
+ :invalid_filter_name => 'Invalid filter name ":%s".',
23
+ :invalid_tag => 'Invalid tag: "%s".',
24
+ :missing_if => 'Got "%s" with no preceding "if"',
25
+ :no_ruby_code => "There's no Ruby code for %s to evaluate.",
26
+ :self_closing_content => "Self-closing tags can't have content.",
27
+ :unbalanced_brackets => 'Unbalanced brackets.',
28
+ :no_end => <<-END
29
+ You don't need to use "- end" in Haml. Un-indent to close a block:
30
+ - if foo?
31
+ %strong Foo!
32
+ - else
33
+ Not foo.
34
+ %p This line is un-indented, so it isn't part of the "if" block
35
+ END
36
+ }
37
+
38
+ def self.message(key, *args)
39
+ string = MESSAGES[key] or raise "[HAML BUG] No error messages for #{key}"
40
+ (args.empty? ? string : string % args).rstrip
41
+ end
42
+
43
+ # The line of the template on which the error occurred.
44
+ #
45
+ # @return [Fixnum]
46
+ attr_reader :line
47
+
48
+ # @param message [String] The error message
49
+ # @param line [Fixnum] See \{#line}
50
+ def initialize(message = nil, line = nil)
51
+ super(message)
52
+ @line = line
53
+ end
54
+ end
55
+
56
+ # SyntaxError is the type of exception raised when Haml encounters an
57
+ # ill-formatted document.
58
+ # It's not particularly interesting,
59
+ # except in that it's a subclass of {Haml::Error}.
60
+ class HamlSyntaxError < HamlError; end
61
+ end
@@ -0,0 +1,727 @@
1
+ require 'hamlit/parser/haml_error'
2
+ require 'hamlit/parser/haml_options'
3
+ require 'hamlit/parser/haml_compiler'
4
+ require 'hamlit/parser/haml_parser'
5
+
6
+ module Hamlit
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 HamlHelpers
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
+ def initialize(method)
17
+ @message = <<MESSAGE
18
+ #{method} outputs directly to the Haml template.
19
+ Disregard its return value and use the - operator,
20
+ or use capture_haml to get the value as a String.
21
+ MESSAGE
22
+ end
23
+
24
+ # Raises an error.
25
+ #
26
+ # @raise [Haml::Error] The error
27
+ def to_s
28
+ raise ::Hamlit::HamlError.new(@message)
29
+ rescue ::Hamlit::HamlError => e
30
+ e.backtrace.shift
31
+
32
+ # If the ErrorReturn is used directly in the template,
33
+ # we don't want Haml's stuff to get into the backtrace,
34
+ # so we get rid of the format_script line.
35
+ #
36
+ # We also have to subtract one from the Haml line number
37
+ # since the value is passed to format_script the line after
38
+ # it's actually used.
39
+ if e.backtrace.first =~ /^\(eval\):\d+:in `format_script/
40
+ e.backtrace.shift
41
+ e.backtrace.first.gsub!(/^\(haml\):(\d+)/) {|s| "(haml):#{$1.to_i - 1}"}
42
+ end
43
+ raise e
44
+ end
45
+
46
+ # @return [String] A human-readable string representation
47
+ def inspect
48
+ "::Hamlit::HamlHelpers::ErrorReturn(#{@message.inspect})"
49
+ end
50
+ end
51
+
52
+ self.extend self
53
+
54
+ @@action_view_defined = false
55
+
56
+ # @return [Boolean] Whether or not ActionView is loaded
57
+ def self.action_view?
58
+ @@action_view_defined
59
+ end
60
+
61
+ # Note: this does **not** need to be called when using Haml helpers
62
+ # normally in Rails.
63
+ #
64
+ # Initializes the current object as though it were in the same context
65
+ # as a normal ActionView instance using Haml.
66
+ # This is useful if you want to use the helpers in a context
67
+ # other than the normal setup with ActionView.
68
+ # For example:
69
+ #
70
+ # context = Object.new
71
+ # class << context
72
+ # include Haml::Helpers
73
+ # end
74
+ # context.init_haml_helpers
75
+ # context.haml_tag :p, "Stuff"
76
+ #
77
+ def init_haml_helpers
78
+ @haml_buffer = ::Hamlit::HamlBuffer.new(haml_buffer, ::Hamlit::HamlOptions.new.for_buffer)
79
+ nil
80
+ end
81
+
82
+ # Runs a block of code in a non-Haml context
83
+ # (i.e. \{#is\_haml?} will return false).
84
+ #
85
+ # This is mainly useful for rendering sub-templates such as partials in a non-Haml language,
86
+ # particularly where helpers may behave differently when run from Haml.
87
+ #
88
+ # Note that this is automatically applied to Rails partials.
89
+ #
90
+ # @yield A block which won't register as Haml
91
+ def non_haml
92
+ was_active = @haml_buffer.active?
93
+ @haml_buffer.active = false
94
+ yield
95
+ ensure
96
+ @haml_buffer.active = was_active
97
+ end
98
+
99
+ # Uses \{#preserve} to convert any newlines inside whitespace-sensitive tags
100
+ # into the HTML entities for endlines.
101
+ #
102
+ # @param tags [Array<String>] Tags that should have newlines escaped
103
+ #
104
+ # @overload find_and_preserve(input, tags = haml_buffer.options[:preserve])
105
+ # Escapes newlines within a string.
106
+ #
107
+ # @param input [String] The string within which to escape newlines
108
+ # @overload find_and_preserve(tags = haml_buffer.options[:preserve])
109
+ # Escapes newlines within a block of Haml code.
110
+ #
111
+ # @yield The block within which to escape newlines
112
+ def find_and_preserve(input = nil, tags = haml_buffer.options[:preserve], &block)
113
+ return find_and_preserve(capture_haml(&block), input || tags) if block
114
+ tags = tags.each_with_object('') do |t, s|
115
+ s << '|' unless s.empty?
116
+ s << Regexp.escape(t)
117
+ end
118
+ re = /<(#{tags})([^>]*)>(.*?)(<\/\1>)/im
119
+ input.to_s.gsub(re) do |s|
120
+ s =~ re # Can't rely on $1, etc. existing since Rails' SafeBuffer#gsub is incompatible
121
+ "<#{$1}#{$2}>#{preserve($3)}</#{$1}>"
122
+ end
123
+ end
124
+
125
+ # Takes any string, finds all the newlines, and converts them to
126
+ # HTML entities so they'll render correctly in
127
+ # whitespace-sensitive tags without screwing up the indentation.
128
+ #
129
+ # @overload preserve(input)
130
+ # Escapes newlines within a string.
131
+ #
132
+ # @param input [String] The string within which to escape all newlines
133
+ # @overload preserve
134
+ # Escapes newlines within a block of Haml code.
135
+ #
136
+ # @yield The block within which to escape newlines
137
+ def preserve(input = nil, &block)
138
+ return preserve(capture_haml(&block)) if block
139
+ s = input.to_s.chomp("\n")
140
+ s.gsub!(/\n/, '&#x000A;')
141
+ s.delete!("\r")
142
+ s
143
+ end
144
+ alias_method :flatten, :preserve
145
+
146
+ # Takes an `Enumerable` object and a block
147
+ # and iterates over the enum,
148
+ # yielding each element to a Haml block
149
+ # and putting the result into `<li>` elements.
150
+ # This creates a list of the results of the block.
151
+ # For example:
152
+ #
153
+ # = list_of([['hello'], ['yall']]) do |i|
154
+ # = i[0]
155
+ #
156
+ # Produces:
157
+ #
158
+ # <li>hello</li>
159
+ # <li>yall</li>
160
+ #
161
+ # And:
162
+ #
163
+ # = list_of({:title => 'All the stuff', :description => 'A book about all the stuff.'}) do |key, val|
164
+ # %h3= key.humanize
165
+ # %p= val
166
+ #
167
+ # Produces:
168
+ #
169
+ # <li>
170
+ # <h3>Title</h3>
171
+ # <p>All the stuff</p>
172
+ # </li>
173
+ # <li>
174
+ # <h3>Description</h3>
175
+ # <p>A book about all the stuff.</p>
176
+ # </li>
177
+ #
178
+ # While:
179
+ #
180
+ # = list_of(["Home", "About", "Contact", "FAQ"], {class: "nav", role: "nav"}) do |item|
181
+ # %a{ href="#" }= item
182
+ #
183
+ # Produces:
184
+ #
185
+ # <li class='nav' role='nav'>
186
+ # <a href='#'>Home</a>
187
+ # </li>
188
+ # <li class='nav' role='nav'>
189
+ # <a href='#'>About</a>
190
+ # </li>
191
+ # <li class='nav' role='nav'>
192
+ # <a href='#'>Contact</a>
193
+ # </li>
194
+ # <li class='nav' role='nav'>
195
+ # <a href='#'>FAQ</a>
196
+ # </li>
197
+ #
198
+ # `[[class", "nav"], [role", "nav"]]` could have been used instead of `{class: "nav", role: "nav"}` (or any enumerable collection where each pair of items responds to #to_s)
199
+ #
200
+ # @param enum [Enumerable] The list of objects to iterate over
201
+ # @param [Enumerable<#to_s,#to_s>] opts Each key/value pair will become an attribute pair for each list item element.
202
+ # @yield [item] A block which contains Haml code that goes within list items
203
+ # @yieldparam item An element of `enum`
204
+ def list_of(enum, opts={}, &block)
205
+ opts_attributes = opts.each_with_object('') {|(k, v), s| s << " #{k}='#{v}'"}
206
+ enum.each_with_object('') do |i, ret|
207
+ result = capture_haml(i, &block)
208
+
209
+ if result.count("\n") > 1
210
+ result.gsub!("\n", "\n ")
211
+ result = "\n #{result.strip!}\n"
212
+ else
213
+ result.strip!
214
+ end
215
+
216
+ ret << "\n" unless ret.empty?
217
+ ret << %Q!<li#{opts_attributes}>#{result}</li>!
218
+ end
219
+ end
220
+
221
+ # Returns a hash containing default assignments for the `xmlns`, `lang`, and `xml:lang`
222
+ # attributes of the `html` HTML element.
223
+ # For example,
224
+ #
225
+ # %html{html_attrs}
226
+ #
227
+ # becomes
228
+ #
229
+ # <html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en-US' lang='en-US'>
230
+ #
231
+ # @param lang [String] The value of `xml:lang` and `lang`
232
+ # @return [{#to_s => String}] The attribute hash
233
+ def html_attrs(lang = 'en-US')
234
+ if haml_buffer.options[:format] == :xhtml
235
+ {:xmlns => "http://www.w3.org/1999/xhtml", 'xml:lang' => lang, :lang => lang}
236
+ else
237
+ {:lang => lang}
238
+ end
239
+ end
240
+
241
+ # Increments the number of tabs the buffer automatically adds
242
+ # to the lines of the template.
243
+ # For example:
244
+ #
245
+ # %h1 foo
246
+ # - tab_up
247
+ # %p bar
248
+ # - tab_down
249
+ # %strong baz
250
+ #
251
+ # Produces:
252
+ #
253
+ # <h1>foo</h1>
254
+ # <p>bar</p>
255
+ # <strong>baz</strong>
256
+ #
257
+ # @param i [Fixnum] The number of tabs by which to increase the indentation
258
+ # @see #tab_down
259
+ def tab_up(i = 1)
260
+ haml_buffer.tabulation += i
261
+ end
262
+
263
+ # Decrements the number of tabs the buffer automatically adds
264
+ # to the lines of the template.
265
+ #
266
+ # @param i [Fixnum] The number of tabs by which to decrease the indentation
267
+ # @see #tab_up
268
+ def tab_down(i = 1)
269
+ haml_buffer.tabulation -= i
270
+ end
271
+
272
+ # Sets the number of tabs the buffer automatically adds
273
+ # to the lines of the template,
274
+ # but only for the duration of the block.
275
+ # For example:
276
+ #
277
+ # %h1 foo
278
+ # - with_tabs(2) do
279
+ # %p bar
280
+ # %strong baz
281
+ #
282
+ # Produces:
283
+ #
284
+ # <h1>foo</h1>
285
+ # <p>bar</p>
286
+ # <strong>baz</strong>
287
+ #
288
+ #
289
+ # @param i [Fixnum] The number of tabs to use
290
+ # @yield A block in which the indentation will be `i` spaces
291
+ def with_tabs(i)
292
+ old_tabs = haml_buffer.tabulation
293
+ haml_buffer.tabulation = i
294
+ yield
295
+ ensure
296
+ haml_buffer.tabulation = old_tabs
297
+ end
298
+
299
+ # Surrounds a block of Haml code with strings,
300
+ # with no whitespace in between.
301
+ # For example:
302
+ #
303
+ # = surround '(', ')' do
304
+ # %a{:href => "food"} chicken
305
+ #
306
+ # Produces:
307
+ #
308
+ # (<a href='food'>chicken</a>)
309
+ #
310
+ # and
311
+ #
312
+ # = surround '*' do
313
+ # %strong angry
314
+ #
315
+ # Produces:
316
+ #
317
+ # *<strong>angry</strong>*
318
+ #
319
+ # @param front [String] The string to add before the Haml
320
+ # @param back [String] The string to add after the Haml
321
+ # @yield A block of Haml to surround
322
+ def surround(front, back = front, &block)
323
+ output = capture_haml(&block)
324
+
325
+ "#{front}#{output.chomp}#{back}\n"
326
+ end
327
+
328
+ # Prepends a string to the beginning of a Haml block,
329
+ # with no whitespace between.
330
+ # For example:
331
+ #
332
+ # = precede '*' do
333
+ # %span.small Not really
334
+ #
335
+ # Produces:
336
+ #
337
+ # *<span class='small'>Not really</span>
338
+ #
339
+ # @param str [String] The string to add before the Haml
340
+ # @yield A block of Haml to prepend to
341
+ def precede(str, &block)
342
+ "#{str}#{capture_haml(&block).chomp}\n"
343
+ end
344
+
345
+ # Appends a string to the end of a Haml block,
346
+ # with no whitespace between.
347
+ # For example:
348
+ #
349
+ # click
350
+ # = succeed '.' do
351
+ # %a{:href=>"thing"} here
352
+ #
353
+ # Produces:
354
+ #
355
+ # click
356
+ # <a href='thing'>here</a>.
357
+ #
358
+ # @param str [String] The string to add after the Haml
359
+ # @yield A block of Haml to append to
360
+ def succeed(str, &block)
361
+ "#{capture_haml(&block).chomp}#{str}\n"
362
+ end
363
+
364
+ # Captures the result of a block of Haml code,
365
+ # gets rid of the excess indentation,
366
+ # and returns it as a string.
367
+ # For example, after the following,
368
+ #
369
+ # .foo
370
+ # - foo = capture_haml(13) do |a|
371
+ # %p= a
372
+ #
373
+ # the local variable `foo` would be assigned to `"<p>13</p>\n"`.
374
+ #
375
+ # @param args [Array] Arguments to pass into the block
376
+ # @yield [args] A block of Haml code that will be converted to a string
377
+ # @yieldparam args [Array] `args`
378
+ def capture_haml(*args, &block)
379
+ buffer = eval('if defined? _hamlout then _hamlout else nil end', block.binding) || haml_buffer
380
+ with_haml_buffer(buffer) do
381
+ position = haml_buffer.buffer.length
382
+
383
+ haml_buffer.capture_position = position
384
+ value = block.call(*args)
385
+
386
+ captured = haml_buffer.buffer.slice!(position..-1)
387
+
388
+ if captured == '' and value != haml_buffer.buffer
389
+ captured = (value.is_a?(String) ? value : nil)
390
+ end
391
+
392
+ return nil if captured.nil?
393
+ return (haml_buffer.options[:ugly] ? captured : prettify(captured))
394
+ end
395
+ ensure
396
+ haml_buffer.capture_position = nil
397
+ end
398
+
399
+ # Outputs text directly to the Haml buffer, with the proper indentation.
400
+ #
401
+ # @param text [#to_s] The text to output
402
+ def haml_concat(text = "")
403
+ haml_internal_concat text
404
+ ErrorReturn.new("haml_concat")
405
+ end
406
+
407
+ # Internal method to write directly to the buffer with control of
408
+ # whether the first line should be indented, and if there should be a
409
+ # final newline.
410
+ #
411
+ # Lines added will have the proper indentation. This can be controlled
412
+ # for the first line.
413
+ #
414
+ # Used by #haml_concat and #haml_tag.
415
+ #
416
+ # @param text [#to_s] The text to output
417
+ # @param newline [Boolean] Whether to add a newline after the text
418
+ # @param indent [Boolean] Whether to add indentation to the first line
419
+ def haml_internal_concat(text = "", newline = true, indent = true)
420
+ if haml_buffer.options[:ugly] || haml_buffer.tabulation == 0
421
+ haml_buffer.buffer << "#{text}#{"\n" if newline}"
422
+ else
423
+ haml_buffer.buffer << %[#{haml_indent if indent}#{text.to_s.gsub("\n", "\n#{haml_indent}")}#{"\n" if newline}]
424
+ end
425
+ end
426
+ private :haml_internal_concat
427
+
428
+ # Allows writing raw content. `haml_internal_concat_raw` isn't
429
+ # effected by XSS mods. Used by #haml_tag to write the actual tags.
430
+ alias :haml_internal_concat_raw :haml_internal_concat
431
+
432
+ # @return [String] The indentation string for the current line
433
+ def haml_indent
434
+ ' ' * haml_buffer.tabulation
435
+ end
436
+
437
+ # Creates an HTML tag with the given name and optionally text and attributes.
438
+ # Can take a block that will run between the opening and closing tags.
439
+ # If the block is a Haml block or outputs text using \{#haml\_concat},
440
+ # the text will be properly indented.
441
+ #
442
+ # `name` can be a string using the standard Haml class/id shorthand
443
+ # (e.g. "span#foo.bar", "#foo").
444
+ # Just like standard Haml tags, these class and id values
445
+ # will be merged with manually-specified attributes.
446
+ #
447
+ # `flags` is a list of symbol flags
448
+ # like those that can be put at the end of a Haml tag
449
+ # (`:/`, `:<`, and `:>`).
450
+ # Currently, only `:/` and `:<` are supported.
451
+ #
452
+ # `haml_tag` outputs directly to the buffer;
453
+ # its return value should not be used.
454
+ # If you need to get the results as a string,
455
+ # use \{#capture\_haml\}.
456
+ #
457
+ # For example,
458
+ #
459
+ # haml_tag :table do
460
+ # haml_tag :tr do
461
+ # haml_tag 'td.cell' do
462
+ # haml_tag :strong, "strong!"
463
+ # haml_concat "data"
464
+ # end
465
+ # haml_tag :td do
466
+ # haml_concat "more_data"
467
+ # end
468
+ # end
469
+ # end
470
+ #
471
+ # outputs
472
+ #
473
+ # <table>
474
+ # <tr>
475
+ # <td class='cell'>
476
+ # <strong>
477
+ # strong!
478
+ # </strong>
479
+ # data
480
+ # </td>
481
+ # <td>
482
+ # more_data
483
+ # </td>
484
+ # </tr>
485
+ # </table>
486
+ #
487
+ # @param name [#to_s] The name of the tag
488
+ #
489
+ # @overload haml_tag(name, *rest, attributes = {})
490
+ # @yield The block of Haml code within the tag
491
+ # @overload haml_tag(name, text, *flags, attributes = {})
492
+ # @param text [#to_s] The text within the tag
493
+ # @param flags [Array<Symbol>] Haml end-of-tag flags
494
+ def haml_tag(name, *rest, &block)
495
+ ret = ErrorReturn.new("haml_tag")
496
+
497
+ text = rest.shift.to_s unless [Symbol, Hash, NilClass].any? {|t| rest.first.is_a? t}
498
+ flags = []
499
+ flags << rest.shift while rest.first.is_a? Symbol
500
+ attrs = (rest.shift || {})
501
+ attrs.keys.each {|key| attrs[key.to_s] = attrs.delete(key)} unless attrs.empty?
502
+ name, attrs = merge_name_and_attributes(name.to_s, attrs)
503
+
504
+ attributes = ::Hamlit::HamlCompiler.build_attributes(haml_buffer.html?,
505
+ haml_buffer.options[:attr_wrapper],
506
+ haml_buffer.options[:escape_attrs],
507
+ haml_buffer.options[:hyphenate_data_attrs],
508
+ attrs)
509
+
510
+ if text.nil? && block.nil? && (haml_buffer.options[:autoclose].include?(name) || flags.include?(:/))
511
+ haml_internal_concat_raw "<#{name}#{attributes}#{' /' if haml_buffer.options[:format] == :xhtml}>"
512
+ return ret
513
+ end
514
+
515
+ if flags.include?(:/)
516
+ raise ::Hamlit::HamlError.new(::Hamlit::HamlError.message(:self_closing_content)) if text
517
+ raise ::Hamlit::HamlError.new(::Hamlit::HamlError.message(:illegal_nesting_self_closing)) if block
518
+ end
519
+
520
+ tag = "<#{name}#{attributes}>"
521
+ end_tag = "</#{name}>"
522
+ if block.nil?
523
+ text = text.to_s
524
+ if text.include?("\n")
525
+ haml_internal_concat_raw tag
526
+ tab_up
527
+ haml_internal_concat text
528
+ tab_down
529
+ haml_internal_concat_raw end_tag
530
+ else
531
+ haml_internal_concat_raw tag, false
532
+ haml_internal_concat text, false, false
533
+ haml_internal_concat_raw end_tag, true, false
534
+ end
535
+ return ret
536
+ end
537
+
538
+ if text
539
+ raise ::Hamlit::HamlError.new(::Hamlit::HamlError.message(:illegal_nesting_line, name))
540
+ end
541
+
542
+ if flags.include?(:<)
543
+ haml_internal_concat_raw tag, false
544
+ haml_internal_concat "#{capture_haml(&block).strip}", false, false
545
+ haml_internal_concat_raw end_tag, true, false
546
+ return ret
547
+ end
548
+
549
+ haml_internal_concat_raw tag
550
+ tab_up
551
+ block.call
552
+ tab_down
553
+ haml_internal_concat_raw end_tag
554
+
555
+ ret
556
+ end
557
+
558
+ # Conditionally wrap a block in an element. If `condition` is `true` then
559
+ # this method renders the tag described by the arguments in `tag` (using
560
+ # \{#haml_tag}) with the given block inside, otherwise it just renders the block.
561
+ #
562
+ # For example,
563
+ #
564
+ # - haml_tag_if important, '.important' do
565
+ # %p
566
+ # A (possibly) important paragraph.
567
+ #
568
+ # will produce
569
+ #
570
+ # <div class='important'>
571
+ # <p>
572
+ # A (possibly) important paragraph.
573
+ # </p>
574
+ # </div>
575
+ #
576
+ # if `important` is truthy, and just
577
+ #
578
+ # <p>
579
+ # A (possibly) important paragraph.
580
+ # </p>
581
+ #
582
+ # otherwise.
583
+ #
584
+ # Like \{#haml_tag}, `haml_tag_if` outputs directly to the buffer and its
585
+ # return value should not be used. Use \{#capture_haml} if you need to use
586
+ # its results as a string.
587
+ #
588
+ # @param condition The condition to test to determine whether to render
589
+ # the enclosing tag
590
+ # @param tag Definition of the enclosing tag. See \{#haml_tag} for details
591
+ # (specifically the form that takes a block)
592
+ def haml_tag_if(condition, *tag)
593
+ if condition
594
+ haml_tag(*tag){ yield }
595
+ else
596
+ yield
597
+ end
598
+ ErrorReturn.new("haml_tag_if")
599
+ end
600
+
601
+ # Characters that need to be escaped to HTML entities from user input
602
+ HTML_ESCAPE = { '&' => '&amp;', '<' => '&lt;', '>' => '&gt;', '"' => '&quot;', "'" => '&#039;' }
603
+
604
+ HTML_ESCAPE_REGEX = /[\"><&]/
605
+
606
+ # Returns a copy of `text` with ampersands, angle brackets and quotes
607
+ # escaped into HTML entities.
608
+ #
609
+ # Note that if ActionView is loaded and XSS protection is enabled
610
+ # (as is the default for Rails 3.0+, and optional for version 2.3.5+),
611
+ # this won't escape text declared as "safe".
612
+ #
613
+ # @param text [String] The string to sanitize
614
+ # @return [String] The sanitized string
615
+ def html_escape(text)
616
+ text = text.to_s
617
+ text.gsub(HTML_ESCAPE_REGEX, HTML_ESCAPE)
618
+ end
619
+
620
+ HTML_ESCAPE_ONCE_REGEX = /[\"><]|&(?!(?:[a-zA-Z]+|#(?:\d+|[xX][0-9a-fA-F]+));)/
621
+
622
+ # Escapes HTML entities in `text`, but without escaping an ampersand
623
+ # that is already part of an escaped entity.
624
+ #
625
+ # @param text [String] The string to sanitize
626
+ # @return [String] The sanitized string
627
+ def escape_once(text)
628
+ text = text.to_s
629
+ text.gsub(HTML_ESCAPE_ONCE_REGEX, HTML_ESCAPE)
630
+ end
631
+
632
+ # Returns whether or not the current template is a Haml template.
633
+ #
634
+ # This function, unlike other {Haml::Helpers} functions,
635
+ # also works in other `ActionView` templates,
636
+ # where it will always return false.
637
+ #
638
+ # @return [Boolean] Whether or not the current template is a Haml template
639
+ def is_haml?
640
+ !@haml_buffer.nil? && @haml_buffer.active?
641
+ end
642
+
643
+ # Returns whether or not `block` is defined directly in a Haml template.
644
+ #
645
+ # @param block [Proc] A Ruby block
646
+ # @return [Boolean] Whether or not `block` is defined directly in a Haml template
647
+ def block_is_haml?(block)
648
+ eval('!!defined?(_hamlout)', block.binding)
649
+ end
650
+
651
+ private
652
+
653
+ # Parses the tag name used for \{#haml\_tag}
654
+ # and merges it with the Ruby attributes hash.
655
+ def merge_name_and_attributes(name, attributes_hash = {})
656
+ # skip merging if no ids or classes found in name
657
+ return name, attributes_hash unless name =~ /^(.+?)?([\.#].*)$/
658
+
659
+ return $1 || "div", ::Hamlit::HamlBuffer.merge_attrs(
660
+ ::Hamlit::HamlParser.parse_class_and_id($2), attributes_hash)
661
+ end
662
+
663
+ # Runs a block of code with the given buffer as the currently active buffer.
664
+ #
665
+ # @param buffer [Haml::Buffer] The Haml buffer to use temporarily
666
+ # @yield A block in which the given buffer should be used
667
+ def with_haml_buffer(buffer)
668
+ @haml_buffer, old_buffer = buffer, @haml_buffer
669
+ old_buffer.active, old_was_active = false, old_buffer.active? if old_buffer
670
+ @haml_buffer.active, was_active = true, @haml_buffer.active?
671
+ yield
672
+ ensure
673
+ @haml_buffer.active = was_active
674
+ old_buffer.active = old_was_active if old_buffer
675
+ @haml_buffer = old_buffer
676
+ end
677
+
678
+ # The current {Haml::Buffer} object.
679
+ #
680
+ # @return [Haml::Buffer]
681
+ def haml_buffer
682
+ @haml_buffer if defined? @haml_buffer
683
+ end
684
+
685
+ # Gives a proc the same local `_hamlout` and `_erbout` variables
686
+ # that the current template has.
687
+ #
688
+ # @param proc [#call] The proc to bind
689
+ # @return [Proc] A new proc with the new variables bound
690
+ def haml_bind_proc(&proc)
691
+ _hamlout = haml_buffer
692
+ #double assignment is to avoid warnings
693
+ _erbout = _erbout = _hamlout.buffer
694
+ proc { |*args| proc.call(*args) }
695
+ end
696
+
697
+ def prettify(text)
698
+ text = text.split(/^/)
699
+ text.delete('')
700
+
701
+ min_tabs = nil
702
+ text.each do |line|
703
+ tabs = line.index(/[^ ]/) || line.length
704
+ min_tabs ||= tabs
705
+ min_tabs = min_tabs > tabs ? tabs : min_tabs
706
+ end
707
+
708
+ text.each_with_object('') do |line, str|
709
+ str << line.slice(min_tabs, line.length)
710
+ end
711
+ end
712
+ end
713
+ end
714
+
715
+ # @private
716
+ class Object
717
+ # Haml overrides various `ActionView` helpers,
718
+ # which call an \{#is\_haml?} method
719
+ # to determine whether or not the current context object
720
+ # is a proper Haml context.
721
+ # Because `ActionView` helpers may be included in non-`ActionView::Base` classes,
722
+ # it's a good idea to define \{#is\_haml?} for all objects.
723
+ def is_haml?
724
+ false
725
+ end
726
+ alias :is_haml? :is_haml?
727
+ end