haml 4.0.0 → 5.0.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 (87) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +1 -1
  3. data/CHANGELOG.md +117 -5
  4. data/FAQ.md +7 -17
  5. data/MIT-LICENSE +1 -1
  6. data/README.md +85 -42
  7. data/REFERENCE.md +181 -86
  8. data/Rakefile +47 -51
  9. data/lib/haml/attribute_builder.rb +163 -0
  10. data/lib/haml/attribute_compiler.rb +215 -0
  11. data/lib/haml/attribute_parser.rb +144 -0
  12. data/lib/haml/buffer.rb +38 -128
  13. data/lib/haml/compiler.rb +88 -295
  14. data/lib/haml/engine.rb +25 -41
  15. data/lib/haml/error.rb +3 -0
  16. data/lib/haml/escapable.rb +49 -0
  17. data/lib/haml/exec.rb +33 -19
  18. data/lib/haml/filters.rb +20 -24
  19. data/lib/haml/generator.rb +41 -0
  20. data/lib/haml/helpers/action_view_extensions.rb +3 -2
  21. data/lib/haml/helpers/action_view_mods.rb +44 -66
  22. data/lib/haml/helpers/action_view_xss_mods.rb +1 -0
  23. data/lib/haml/helpers/safe_erubi_template.rb +27 -0
  24. data/lib/haml/helpers/safe_erubis_template.rb +16 -4
  25. data/lib/haml/helpers/xss_mods.rb +18 -12
  26. data/lib/haml/helpers.rb +122 -58
  27. data/lib/haml/options.rb +39 -46
  28. data/lib/haml/parser.rb +278 -217
  29. data/lib/haml/{template/plugin.rb → plugin.rb} +8 -15
  30. data/lib/haml/railtie.rb +21 -11
  31. data/lib/haml/sass_rails_filter.rb +17 -4
  32. data/lib/haml/template/options.rb +12 -2
  33. data/lib/haml/template.rb +12 -6
  34. data/lib/haml/temple_engine.rb +120 -0
  35. data/lib/haml/temple_line_counter.rb +29 -0
  36. data/lib/haml/util.rb +80 -199
  37. data/lib/haml/version.rb +2 -1
  38. data/lib/haml.rb +2 -1
  39. data/test/attribute_parser_test.rb +101 -0
  40. data/test/engine_test.rb +306 -176
  41. data/test/filters_test.rb +32 -19
  42. data/test/gemfiles/Gemfile.rails-4.0.x +11 -0
  43. data/test/gemfiles/Gemfile.rails-4.0.x.lock +87 -0
  44. data/test/gemfiles/Gemfile.rails-4.1.x +5 -0
  45. data/test/gemfiles/Gemfile.rails-4.2.x +5 -0
  46. data/test/gemfiles/Gemfile.rails-5.0.x +4 -0
  47. data/test/helper_test.rb +282 -96
  48. data/test/options_test.rb +22 -0
  49. data/test/parser_test.rb +71 -4
  50. data/test/results/bemit.xhtml +4 -0
  51. data/test/results/eval_suppressed.xhtml +4 -4
  52. data/test/results/helpers.xhtml +43 -41
  53. data/test/results/helpful.xhtml +6 -3
  54. data/test/results/just_stuff.xhtml +21 -20
  55. data/test/results/list.xhtml +9 -9
  56. data/test/results/nuke_inner_whitespace.xhtml +22 -22
  57. data/test/results/nuke_outer_whitespace.xhtml +84 -92
  58. data/test/results/original_engine.xhtml +17 -17
  59. data/test/results/partial_layout.xhtml +4 -3
  60. data/test/results/partial_layout_erb.xhtml +4 -3
  61. data/test/results/partials.xhtml +11 -10
  62. data/test/results/silent_script.xhtml +63 -63
  63. data/test/results/standard.xhtml +156 -159
  64. data/test/results/tag_parsing.xhtml +19 -19
  65. data/test/results/very_basic.xhtml +2 -2
  66. data/test/results/whitespace_handling.xhtml +56 -50
  67. data/test/template_test.rb +44 -53
  68. data/test/template_test_helper.rb +38 -0
  69. data/test/templates/_text_area_helper.html.haml +4 -0
  70. data/test/templates/bemit.haml +3 -0
  71. data/test/templates/just_stuff.haml +1 -0
  72. data/test/templates/partial_layout_erb.erb +1 -1
  73. data/test/templates/standard_ugly.haml +1 -0
  74. data/test/templates/with_bom.haml +1 -0
  75. data/test/temple_line_counter_test.rb +40 -0
  76. data/test/test_helper.rb +26 -12
  77. data/test/util_test.rb +6 -47
  78. metadata +88 -106
  79. data/lib/haml/helpers/rails_323_textarea_fix.rb +0 -24
  80. data/test/gemfiles/Gemfile.rails-3.0.x +0 -5
  81. data/test/gemfiles/Gemfile.rails-3.1.x +0 -6
  82. data/test/gemfiles/Gemfile.rails-3.2.x +0 -5
  83. data/test/gemfiles/Gemfile.rails-master +0 -4
  84. data/test/templates/_av_partial_1_ugly.haml +0 -9
  85. data/test/templates/_av_partial_2_ugly.haml +0 -5
  86. data/test/templates/action_view_ugly.haml +0 -47
  87. data/test/templates/standard_ugly.haml +0 -43
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+ require 'action_view'
3
+
4
+ module Haml
5
+ class ErubiTemplateHandler < ActionView::Template::Handlers::ERB::Erubi
6
+
7
+ def initialize(*args, &blk)
8
+ @newline_pending = 0
9
+ super
10
+ end
11
+ end
12
+
13
+ class SafeErubiTemplate < Tilt::ErubiTemplate
14
+ def prepare
15
+ @options.merge! engine_class: Haml::ErubiTemplateHandler
16
+ super
17
+ end
18
+
19
+ def precompiled_preamble(locals)
20
+ [super, "@output_buffer = ActionView::OutputBuffer.new;"].join("\n")
21
+ end
22
+
23
+ def precompiled_postamble(locals)
24
+ [super, '@output_buffer.to_s'].join("\n")
25
+ end
26
+ end
27
+ end
@@ -1,20 +1,32 @@
1
+ # frozen_string_literal: true
2
+ require 'action_view'
3
+
1
4
  module Haml
5
+
6
+ class ErubisTemplateHandler < ActionView::Template::Handlers::Erubis
7
+
8
+ def initialize(*args, &blk)
9
+ @newline_pending = 0
10
+ super
11
+ end
12
+ end
13
+
2
14
  class SafeErubisTemplate < Tilt::ErubisTemplate
3
15
 
4
16
  def initialize_engine
5
17
  end
6
18
 
7
19
  def prepare
8
- @options.merge! :engine_class => ActionView::Template::Handlers::Erubis
20
+ @options.merge! :engine_class => Haml::ErubisTemplateHandler
9
21
  super
10
22
  end
11
23
 
12
24
  def precompiled_preamble(locals)
13
- [super, "@output_buffer = output_buffer ||= nil || ActionView::OutputBuffer.new;"]
25
+ [super, "@output_buffer = ActionView::OutputBuffer.new;"].join("\n")
14
26
  end
15
27
 
16
28
  def precompiled_postamble(locals)
17
- [super, '@output_buffer.to_s']
29
+ [super, '@output_buffer.to_s'].join("\n")
18
30
  end
19
31
  end
20
- end
32
+ end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Haml
2
3
  module Helpers
3
4
  # This module overrides Haml helpers to work properly
@@ -7,8 +8,8 @@ module Haml
7
8
  module XssMods
8
9
  def self.included(base)
9
10
  %w[html_escape find_and_preserve preserve list_of surround
10
- precede succeed capture_haml haml_concat haml_indent
11
- haml_tag escape_once].each do |name|
11
+ precede succeed capture_haml haml_concat haml_internal_concat haml_indent
12
+ escape_once].each do |name|
12
13
  base.send(:alias_method, "#{name}_without_haml_xss", name)
13
14
  base.send(:alias_method, name, "#{name}_with_haml_xss")
14
15
  end
@@ -61,24 +62,29 @@ module Haml
61
62
  Haml::Util.html_safe(capture_haml_without_haml_xss(*args, &block))
62
63
  end
63
64
 
64
- # Input is escaped
65
+ # Input will be escaped unless this is in a `with_raw_haml_concat`
66
+ # block. See #Haml::Helpers::ActionViewExtensions#with_raw_haml_concat.
65
67
  def haml_concat_with_haml_xss(text = "")
66
- raw = instance_variable_defined?('@_haml_concat_raw') ? @_haml_concat_raw : false
67
- haml_concat_without_haml_xss(raw ? text : haml_xss_html_escape(text))
68
+ raw = instance_variable_defined?(:@_haml_concat_raw) ? @_haml_concat_raw : false
69
+ if raw
70
+ haml_internal_concat_raw text
71
+ else
72
+ haml_internal_concat text
73
+ end
74
+ ErrorReturn.new("haml_concat")
68
75
  end
69
76
 
77
+ # Input is escaped
78
+ def haml_internal_concat_with_haml_xss(text="", newline=true, indent=true)
79
+ haml_internal_concat_without_haml_xss(haml_xss_html_escape(text), newline, indent)
80
+ end
81
+ private :haml_internal_concat_with_haml_xss
82
+
70
83
  # Output is always HTML safe
71
84
  def haml_indent_with_haml_xss
72
85
  Haml::Util.html_safe(haml_indent_without_haml_xss)
73
86
  end
74
87
 
75
- # Input is escaped, haml_concat'ed output is always HTML safe
76
- def haml_tag_with_haml_xss(name, *rest, &block)
77
- name = haml_xss_html_escape(name.to_s)
78
- rest.unshift(haml_xss_html_escape(rest.shift.to_s)) unless [Symbol, Hash, NilClass].any? {|t| rest.first.is_a? t}
79
- with_raw_haml_concat {haml_tag_without_haml_xss(name, *rest, &block)}
80
- end
81
-
82
88
  # Output is always HTML safe
83
89
  def escape_once_with_haml_xss(*args)
84
90
  Haml::Util.html_safe(escape_once_without_haml_xss(*args))
data/lib/haml/helpers.rb CHANGED
@@ -1,3 +1,6 @@
1
+ # frozen_string_literal: false
2
+ require 'erb'
3
+
1
4
  module Haml
2
5
  # This module contains various helpful methods to make it easier to do various tasks.
3
6
  # {Haml::Helpers} is automatically included in the context
@@ -106,7 +109,11 @@ MESSAGE
106
109
  # @yield The block within which to escape newlines
107
110
  def find_and_preserve(input = nil, tags = haml_buffer.options[:preserve], &block)
108
111
  return find_and_preserve(capture_haml(&block), input || tags) if block
109
- re = /<(#{tags.map(&Regexp.method(:escape)).join('|')})([^>]*)>(.*?)(<\/\1>)/im
112
+ tags = tags.each_with_object('') do |t, s|
113
+ s << '|' unless s.empty?
114
+ s << Regexp.escape(t)
115
+ end
116
+ re = /<(#{tags})([^>]*)>(.*?)(<\/\1>)/im
110
117
  input.to_s.gsub(re) do |s|
111
118
  s =~ re # Can't rely on $1, etc. existing since Rails' SafeBuffer#gsub is incompatible
112
119
  "<#{$1}#{$2}>#{preserve($3)}</#{$1}>"
@@ -117,17 +124,20 @@ MESSAGE
117
124
  # HTML entities so they'll render correctly in
118
125
  # whitespace-sensitive tags without screwing up the indentation.
119
126
  #
120
- # @overload perserve(input)
127
+ # @overload preserve(input)
121
128
  # Escapes newlines within a string.
122
129
  #
123
130
  # @param input [String] The string within which to escape all newlines
124
- # @overload perserve
131
+ # @overload preserve
125
132
  # Escapes newlines within a block of Haml code.
126
133
  #
127
134
  # @yield The block within which to escape newlines
128
135
  def preserve(input = nil, &block)
129
136
  return preserve(capture_haml(&block)) if block
130
- input.to_s.chomp("\n").gsub(/\n/, '&#x000A;').gsub(/\r/, '')
137
+ s = input.to_s.chomp("\n")
138
+ s.gsub!(/\n/, '&#x000A;')
139
+ s.delete!("\r")
140
+ s
131
141
  end
132
142
  alias_method :flatten, :preserve
133
143
 
@@ -190,20 +200,20 @@ MESSAGE
190
200
  # @yield [item] A block which contains Haml code that goes within list items
191
201
  # @yieldparam item An element of `enum`
192
202
  def list_of(enum, opts={}, &block)
193
- opts_attributes = opts.empty? ? "" : " ".<<(opts.map{|k,v| "#{k}='#{v}'" }.join(" "))
194
- to_return = enum.collect do |i|
203
+ opts_attributes = opts.each_with_object('') {|(k, v), s| s << " #{k}='#{v}'"}
204
+ enum.each_with_object('') do |i, ret|
195
205
  result = capture_haml(i, &block)
196
206
 
197
207
  if result.count("\n") > 1
198
- result = result.gsub("\n", "\n ")
199
- result = "\n #{result.strip}\n"
208
+ result.gsub!("\n", "\n ")
209
+ result = "\n #{result.strip!}\n"
200
210
  else
201
- result = result.strip
211
+ result.strip!
202
212
  end
203
213
 
204
- %Q!<li#{opts_attributes}>#{result}</li>!
214
+ ret << "\n" unless ret.empty?
215
+ ret << %Q!<li#{opts_attributes}>#{result}</li>!
205
216
  end
206
- to_return.join("\n")
207
217
  end
208
218
 
209
219
  # Returns a hash containing default assignments for the `xmlns`, `lang`, and `xml:lang`
@@ -219,7 +229,11 @@ MESSAGE
219
229
  # @param lang [String] The value of `xml:lang` and `lang`
220
230
  # @return [{#to_s => String}] The attribute hash
221
231
  def html_attrs(lang = 'en-US')
222
- {:xmlns => "http://www.w3.org/1999/xhtml", 'xml:lang' => lang, :lang => lang}
232
+ if haml_buffer.options[:format] == :xhtml
233
+ {:xmlns => "http://www.w3.org/1999/xhtml", 'xml:lang' => lang, :lang => lang}
234
+ else
235
+ {:lang => lang}
236
+ end
223
237
  end
224
238
 
225
239
  # Increments the number of tabs the buffer automatically adds
@@ -365,24 +379,15 @@ MESSAGE
365
379
  position = haml_buffer.buffer.length
366
380
 
367
381
  haml_buffer.capture_position = position
368
- block.call(*args)
382
+ value = block.call(*args)
369
383
 
370
384
  captured = haml_buffer.buffer.slice!(position..-1)
371
- return captured if haml_buffer.options[:ugly]
372
- # Note that the "reject" is needed for rbx 1.2.4, which includes empty
373
- # strings in the returned array when splitting by /^/.
374
- captured = captured.split(/^/).reject {|x| x == ""}
375
-
376
- min_tabs = nil
377
- captured.each do |line|
378
- tabs = line.index(/[^ ]/) || line.length
379
- min_tabs ||= tabs
380
- min_tabs = min_tabs > tabs ? tabs : min_tabs
385
+
386
+ if captured == '' and value != haml_buffer.buffer
387
+ captured = (value.is_a?(String) ? value : nil)
381
388
  end
382
389
 
383
- captured.map do |line|
384
- line[min_tabs..-1]
385
- end.join
390
+ captured
386
391
  end
387
392
  ensure
388
393
  haml_buffer.capture_position = nil
@@ -392,14 +397,34 @@ MESSAGE
392
397
  #
393
398
  # @param text [#to_s] The text to output
394
399
  def haml_concat(text = "")
395
- unless haml_buffer.options[:ugly] || haml_indent == 0
396
- haml_buffer.buffer << haml_indent <<
397
- text.to_s.gsub("\n", "\n" + haml_indent) << "\n"
400
+ haml_internal_concat text
401
+ ErrorReturn.new("haml_concat")
402
+ end
403
+
404
+ # Internal method to write directly to the buffer with control of
405
+ # whether the first line should be indented, and if there should be a
406
+ # final newline.
407
+ #
408
+ # Lines added will have the proper indentation. This can be controlled
409
+ # for the first line.
410
+ #
411
+ # Used by #haml_concat and #haml_tag.
412
+ #
413
+ # @param text [#to_s] The text to output
414
+ # @param newline [Boolean] Whether to add a newline after the text
415
+ # @param indent [Boolean] Whether to add indentation to the first line
416
+ def haml_internal_concat(text = "", newline = true, indent = true)
417
+ if haml_buffer.tabulation == 0
418
+ haml_buffer.buffer << "#{text}#{"\n" if newline}"
398
419
  else
399
- haml_buffer.buffer << text.to_s << "\n"
420
+ haml_buffer.buffer << %[#{haml_indent if indent}#{text.to_s.gsub("\n", "\n#{haml_indent}")}#{"\n" if newline}]
400
421
  end
401
- ErrorReturn.new("haml_concat")
402
422
  end
423
+ private :haml_internal_concat
424
+
425
+ # Allows writing raw content. `haml_internal_concat_raw` isn't
426
+ # effected by XSS mods. Used by #haml_tag to write the actual tags.
427
+ alias :haml_internal_concat_raw :haml_internal_concat
403
428
 
404
429
  # @return [String] The indentation string for the current line
405
430
  def haml_indent
@@ -473,14 +498,14 @@ MESSAGE
473
498
  attrs.keys.each {|key| attrs[key.to_s] = attrs.delete(key)} unless attrs.empty?
474
499
  name, attrs = merge_name_and_attributes(name.to_s, attrs)
475
500
 
476
- attributes = Haml::Compiler.build_attributes(haml_buffer.html?,
501
+ attributes = Haml::AttributeBuilder.build_attributes(haml_buffer.html?,
477
502
  haml_buffer.options[:attr_wrapper],
478
503
  haml_buffer.options[:escape_attrs],
479
504
  haml_buffer.options[:hyphenate_data_attrs],
480
505
  attrs)
481
506
 
482
507
  if text.nil? && block.nil? && (haml_buffer.options[:autoclose].include?(name) || flags.include?(:/))
483
- haml_concat "<#{name}#{attributes} />"
508
+ haml_internal_concat_raw "<#{name}#{attributes}#{' /' if haml_buffer.options[:format] == :xhtml}>"
484
509
  return ret
485
510
  end
486
511
 
@@ -490,17 +515,19 @@ MESSAGE
490
515
  end
491
516
 
492
517
  tag = "<#{name}#{attributes}>"
518
+ end_tag = "</#{name}>"
493
519
  if block.nil?
494
520
  text = text.to_s
495
521
  if text.include?("\n")
496
- haml_concat tag
522
+ haml_internal_concat_raw tag
497
523
  tab_up
498
- haml_concat text
524
+ haml_internal_concat text
499
525
  tab_down
500
- haml_concat "</#{name}>"
526
+ haml_internal_concat_raw end_tag
501
527
  else
502
- tag << text << "</#{name}>"
503
- haml_concat tag
528
+ haml_internal_concat_raw tag, false
529
+ haml_internal_concat text, false, false
530
+ haml_internal_concat_raw end_tag, true, false
504
531
  end
505
532
  return ret
506
533
  end
@@ -510,22 +537,68 @@ MESSAGE
510
537
  end
511
538
 
512
539
  if flags.include?(:<)
513
- tag << capture_haml(&block).strip << "</#{name}>"
514
- haml_concat tag
540
+ haml_internal_concat_raw tag, false
541
+ haml_internal_concat "#{capture_haml(&block).strip}", false, false
542
+ haml_internal_concat_raw end_tag, true, false
515
543
  return ret
516
544
  end
517
545
 
518
- haml_concat tag
546
+ haml_internal_concat_raw tag
519
547
  tab_up
520
548
  block.call
521
549
  tab_down
522
- haml_concat "</#{name}>"
550
+ haml_internal_concat_raw end_tag
523
551
 
524
552
  ret
525
553
  end
526
554
 
555
+ # Conditionally wrap a block in an element. If `condition` is `true` then
556
+ # this method renders the tag described by the arguments in `tag` (using
557
+ # \{#haml_tag}) with the given block inside, otherwise it just renders the block.
558
+ #
559
+ # For example,
560
+ #
561
+ # - haml_tag_if important, '.important' do
562
+ # %p
563
+ # A (possibly) important paragraph.
564
+ #
565
+ # will produce
566
+ #
567
+ # <div class='important'>
568
+ # <p>
569
+ # A (possibly) important paragraph.
570
+ # </p>
571
+ # </div>
572
+ #
573
+ # if `important` is truthy, and just
574
+ #
575
+ # <p>
576
+ # A (possibly) important paragraph.
577
+ # </p>
578
+ #
579
+ # otherwise.
580
+ #
581
+ # Like \{#haml_tag}, `haml_tag_if` outputs directly to the buffer and its
582
+ # return value should not be used. Use \{#capture_haml} if you need to use
583
+ # its results as a string.
584
+ #
585
+ # @param condition The condition to test to determine whether to render
586
+ # the enclosing tag
587
+ # @param tag Definition of the enclosing tag. See \{#haml_tag} for details
588
+ # (specifically the form that takes a block)
589
+ def haml_tag_if(condition, *tag)
590
+ if condition
591
+ haml_tag(*tag){ yield }
592
+ else
593
+ yield
594
+ end
595
+ ErrorReturn.new("haml_tag_if")
596
+ end
597
+
527
598
  # Characters that need to be escaped to HTML entities from user input
528
- HTML_ESCAPE = { '&'=>'&amp;', '<'=>'&lt;', '>'=>'&gt;', '"'=>'&quot;', "'"=>'&#039;', }
599
+ HTML_ESCAPE = { '&' => '&amp;', '<' => '&lt;', '>' => '&gt;', '"' => '&quot;', "'" => '&#39;' }
600
+
601
+ HTML_ESCAPE_REGEX = /['"><&]/
529
602
 
530
603
  # Returns a copy of `text` with ampersands, angle brackets and quotes
531
604
  # escaped into HTML entities.
@@ -537,28 +610,19 @@ MESSAGE
537
610
  # @param text [String] The string to sanitize
538
611
  # @return [String] The sanitized string
539
612
  def html_escape(text)
540
- pattern = '[\"><&]'
541
- regex = if RUBY_VERSION >= '1.9'
542
- Regexp.new(pattern.force_encoding(text.encoding), Regexp::FIXEDENCODING)
543
- else
544
- Regexp.new(pattern)
545
- end
546
- text.to_s.gsub(regex) {|s| HTML_ESCAPE[s]}
613
+ ERB::Util.html_escape(text)
547
614
  end
548
615
 
616
+ HTML_ESCAPE_ONCE_REGEX = /['"><]|&(?!(?:[a-zA-Z]+|#(?:\d+|[xX][0-9a-fA-F]+));)/
617
+
549
618
  # Escapes HTML entities in `text`, but without escaping an ampersand
550
619
  # that is already part of an escaped entity.
551
620
  #
552
621
  # @param text [String] The string to sanitize
553
622
  # @return [String] The sanitized string
554
623
  def escape_once(text)
555
- pattern = '[\"><]|&(?!(?:[a-zA-Z]+|(#\d+));)'
556
- regex = if RUBY_VERSION >= '1.9'
557
- Regexp.new(pattern.force_encoding(text.encoding), Regexp::FIXEDENCODING)
558
- else
559
- Regexp.new(pattern)
560
- end
561
- text.to_s.gsub(regex) {|s| HTML_ESCAPE[s]}
624
+ text = text.to_s
625
+ text.gsub(HTML_ESCAPE_ONCE_REGEX, HTML_ESCAPE)
562
626
  end
563
627
 
564
628
  # Returns whether or not the current template is a Haml template.
@@ -588,7 +652,7 @@ MESSAGE
588
652
  # skip merging if no ids or classes found in name
589
653
  return name, attributes_hash unless name =~ /^(.+?)?([\.#].*)$/
590
654
 
591
- return $1 || "div", Buffer.merge_attrs(
655
+ return $1 || "div", AttributeBuilder.merge_attributes!(
592
656
  Haml::Parser.parse_class_and_id($2), attributes_hash)
593
657
  end
594
658
 
data/lib/haml/options.rb CHANGED
@@ -1,38 +1,19 @@
1
+ # frozen_string_literal: true
1
2
  module Haml
2
3
  # This class encapsulates all of the configuration options that Haml
3
4
  # understands. Please see the {file:REFERENCE.md#options Haml Reference} to
4
5
  # learn how to set the options.
5
6
  class Options
6
7
 
7
- @defaults = {
8
- :attr_wrapper => "'",
9
- :autoclose => %w(meta img link br hr input area param col base),
10
- :encoding => "UTF-8",
11
- :escape_attrs => true,
12
- :escape_html => false,
13
- :filename => '(haml)',
14
- :format => :html5,
15
- :hyphenate_data_attrs => true,
16
- :line => 1,
17
- :mime_type => 'text/html',
18
- :preserve => %w(textarea pre code),
19
- :remove_whitespace => false,
20
- :suppress_eval => false,
21
- :ugly => false,
22
- :cdata => false,
23
- :parser_class => ::Haml::Parser,
24
- :compiler_class => ::Haml::Compiler
25
- }
26
-
27
8
  @valid_formats = [:html4, :html5, :xhtml]
28
9
 
29
- @buffer_option_keys = [:autoclose, :preserve, :attr_wrapper, :ugly, :format,
10
+ @buffer_option_keys = [:autoclose, :preserve, :attr_wrapper, :format,
30
11
  :encoding, :escape_html, :escape_attrs, :hyphenate_data_attrs, :cdata]
31
12
 
32
13
  # The default option values.
33
14
  # @return Hash
34
15
  def self.defaults
35
- @defaults
16
+ @defaults ||= Haml::TempleEngine.options.to_hash.merge(encoding: 'UTF-8')
36
17
  end
37
18
 
38
19
  # An array of valid values for the `:format` option.
@@ -48,6 +29,22 @@ module Haml
48
29
  @buffer_option_keys
49
30
  end
50
31
 
32
+ # Returns a subset of defaults: those that {Haml::Buffer} cares about.
33
+ # @return [{Symbol => Object}] The options hash
34
+ def self.buffer_defaults
35
+ @buffer_defaults ||= buffer_option_keys.inject({}) do |hash, key|
36
+ hash.merge(key => defaults[key])
37
+ end
38
+ end
39
+
40
+ def self.wrap(options)
41
+ if options.is_a?(Options)
42
+ options
43
+ else
44
+ Options.new(options)
45
+ end
46
+ end
47
+
51
48
  # The character that should wrap element attributes. This defaults to `'`
52
49
  # (an apostrophe). Characters of this type within the attributes will be
53
50
  # escaped (e.g. by replacing them with `&apos;`) if the character is an
@@ -61,7 +58,6 @@ module Haml
61
58
  attr_accessor :autoclose
62
59
 
63
60
  # The encoding to use for the HTML output.
64
- # Only available on Ruby 1.9 or higher.
65
61
  # This can be a string or an `Encoding` Object. Note that Haml **does not**
66
62
  # automatically re-encode Ruby values; any strings coming from outside the
67
63
  # application should be converted before being passed into the Haml
@@ -144,13 +140,6 @@ module Haml
144
140
  # Defaults to `false`.
145
141
  attr_accessor :suppress_eval
146
142
 
147
- # If set to `true`, Haml makes no attempt to properly indent or format the
148
- # HTML output. This significantly improves rendering performance but makes
149
- # viewing the source unpleasant.
150
- #
151
- # Defaults to `true` in Rails production mode, and `false` everywhere else.
152
- attr_accessor :ugly
153
-
154
143
  # Whether to include CDATA sections around javascript and css blocks when
155
144
  # using the `:javascript` or `:css` filters.
156
145
  #
@@ -167,9 +156,17 @@ module Haml
167
156
  # The compiler class to use. Defaults to Haml::Compiler.
168
157
  attr_accessor :compiler_class
169
158
 
159
+ # Enable template tracing. If true, it will add a 'data-trace' attribute to
160
+ # each tag generated by Haml. The value of the attribute will be the
161
+ # source template name and the line number from which the tag was generated,
162
+ # separated by a colon. On Rails applications, the path given will be a
163
+ # relative path as from the views directory. On non-Rails applications,
164
+ # the path will be the full path.
165
+ attr_accessor :trace
166
+
170
167
  def initialize(values = {}, &block)
171
168
  defaults.each {|k, v| instance_variable_set :"@#{k}", v}
172
- values.reject {|k, v| !defaults.has_key?(k) || v.nil?}.each {|k, v| send("#{k}=", v)}
169
+ values.each {|k, v| send("#{k}=", v) if defaults.has_key?(k) && !v.nil?}
173
170
  yield if block_given?
174
171
  end
175
172
 
@@ -186,8 +183,7 @@ module Haml
186
183
  send "#{key}=", value
187
184
  end
188
185
 
189
- [:escape_attrs, :hyphenate_data_attrs, :remove_whitespace, :suppress_eval,
190
- :ugly].each do |method|
186
+ [:escape_attrs, :hyphenate_data_attrs, :remove_whitespace, :suppress_eval].each do |method|
191
187
  class_eval(<<-END)
192
188
  def #{method}?
193
189
  !! @#{method}
@@ -239,21 +235,16 @@ module Haml
239
235
  end
240
236
 
241
237
  def remove_whitespace=(value)
242
- @ugly = true if value
243
238
  @remove_whitespace = value
244
239
  end
245
240
 
246
- if RUBY_VERSION < "1.9"
247
- attr_writer :encoding
248
- else
249
- def encoding=(value)
250
- return unless value
251
- @encoding = value.is_a?(Encoding) ? value.name : value.to_s
252
- @encoding = "UTF-8" if @encoding.upcase == "US-ASCII"
253
- end
241
+ def encoding=(value)
242
+ return unless value
243
+ @encoding = value.is_a?(Encoding) ? value.name : value.to_s
244
+ @encoding = "UTF-8" if @encoding.upcase == "US-ASCII"
254
245
  end
255
246
 
256
- # Returns a subset of options: those that {Haml::Buffer} cares about.
247
+ # Returns a non-default subset of options: those that {Haml::Buffer} cares about.
257
248
  # All of the values here are such that when `#inspect` is called on the hash,
258
249
  # it can be `Kernel#eval`ed to get the same result back.
259
250
  #
@@ -262,7 +253,10 @@ module Haml
262
253
  # @return [{Symbol => Object}] The options hash
263
254
  def for_buffer
264
255
  self.class.buffer_option_keys.inject({}) do |hash, key|
265
- hash[key] = send(key)
256
+ value = public_send(key)
257
+ if self.class.buffer_defaults[key] != value
258
+ hash[key] = value
259
+ end
266
260
  hash
267
261
  end
268
262
  end
@@ -272,6 +266,5 @@ module Haml
272
266
  def defaults
273
267
  self.class.defaults
274
268
  end
275
-
276
269
  end
277
- end
270
+ end