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
data/lib/haml/buffer.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Haml
2
3
  # This class is used only internally. It holds the buffer of HTML that
3
4
  # is eventually output as the resulting document.
@@ -90,15 +91,14 @@ module Haml
90
91
  def initialize(upper = nil, options = {})
91
92
  @active = true
92
93
  @upper = upper
93
- @options = options
94
+ @options = Options.buffer_defaults
95
+ @options = @options.merge(options) unless options.empty?
94
96
  @buffer = new_encoded_string
95
97
  @tabulation = 0
96
98
 
97
99
  # The number of tabs that Engine thinks we should have
98
100
  # @real_tabs + @tabulation is the number of tabs actually output
99
101
  @real_tabs = 0
100
-
101
- @preserve_pattern = /<[\s]*#{@options[:preserve].join("|")}/i
102
102
  end
103
103
 
104
104
  # Appends text to the buffer, properly tabulated.
@@ -117,8 +117,8 @@ module Haml
117
117
  text.sub!(tabs, '') if dont_tab_up
118
118
  end
119
119
 
120
- @buffer << text
121
120
  @real_tabs += tab_change
121
+ @buffer << text
122
122
  end
123
123
 
124
124
  # Modifies the indentation of the document.
@@ -129,82 +129,13 @@ module Haml
129
129
  @real_tabs += tab_change
130
130
  end
131
131
 
132
- Haml::Util.def_static_method(self, :format_script, [:result],
133
- :preserve_script, :in_tag, :preserve_tag, :escape_html,
134
- :nuke_inner_whitespace, :interpolated, :ugly, <<RUBY)
135
- <% # Escape HTML here so that the safety of the string is preserved in Rails
136
- result_name = escape_html ? "html_escape(result.to_s)" : "result.to_s" %>
137
- <% unless ugly %>
138
- # If we're interpolated,
139
- # then the custom tabulation is handled in #push_text.
140
- # The easiest way to avoid it here is to reset @tabulation.
141
- <% if interpolated %>
142
- old_tabulation = @tabulation
143
- @tabulation = 0
144
- <% end %>
145
-
146
- <% if !(in_tag && preserve_tag && !nuke_inner_whitespace) %>
147
- tabulation = @real_tabs
148
- <% end %>
149
- result = <%= result_name %>.<% if nuke_inner_whitespace %>strip<% else %>rstrip<% end %>
150
- <% else %>
151
- result = <%= result_name %><% if nuke_inner_whitespace %>.strip<% end %>
152
- <% end %>
153
-
154
- <% if preserve_tag %>
155
- result = Haml::Helpers.preserve(result)
156
- <% elsif preserve_script %>
157
- result = Haml::Helpers.find_and_preserve(result, options[:preserve])
158
- <% end %>
159
-
160
- <% if ugly %>
161
- return result
162
- <% else %>
163
-
164
- return result if self.instance_variable_get(:@preserve_pattern).match(result)
165
-
166
- <% if !(in_tag && preserve_tag && !nuke_inner_whitespace) %>
167
- has_newline = result.include?("\\n")
168
- <% end %>
169
-
170
- <% if in_tag && !nuke_inner_whitespace %>
171
- <% unless preserve_tag %> if !has_newline <% end %>
172
- @real_tabs -= 1
173
- <% if interpolated %> @tabulation = old_tabulation <% end %>
174
- return result
175
- <% unless preserve_tag %> end <% end %>
176
- <% end %>
177
-
178
- <% if !(in_tag && preserve_tag && !nuke_inner_whitespace) %>
179
- # Precompiled tabulation may be wrong
180
- <% if !interpolated && !in_tag %>
181
- result = tabs + result if @tabulation > 0
182
- <% end %>
183
-
184
- if has_newline
185
- result = result.gsub "\\n", "\\n" + tabs(tabulation)
186
-
187
- # Add tabulation if it wasn't precompiled
188
- <% if in_tag && !nuke_inner_whitespace %> result = tabs(tabulation) + result <% end %>
189
- end
190
-
191
- <% if in_tag && !nuke_inner_whitespace %>
192
- result = "\\n\#{result}\\n\#{tabs(tabulation-1)}"
193
- @real_tabs -= 1
194
- <% end %>
195
- <% if interpolated %> @tabulation = old_tabulation <% end %>
196
- result
197
- <% end %>
198
- <% end %>
199
- RUBY
200
-
201
132
  def attributes(class_id, obj_ref, *attributes_hashes)
202
133
  attributes = class_id
203
134
  attributes_hashes.each do |old|
204
- self.class.merge_attrs(attributes, Hash[old.map {|k, v| [k.to_s, v]}])
135
+ AttributeBuilder.merge_attributes!(attributes, Hash[old.map {|k, v| [k.to_s, v]}])
205
136
  end
206
- self.class.merge_attrs(attributes, parse_object_ref(obj_ref)) if obj_ref
207
- Compiler.build_attributes(
137
+ AttributeBuilder.merge_attributes!(attributes, parse_object_ref(obj_ref)) if obj_ref
138
+ AttributeBuilder.build_attributes(
208
139
  html?, @options[:attr_wrapper], @options[:escape_attrs], @options[:hyphenate_data_attrs], attributes)
209
140
  end
210
141
 
@@ -219,58 +150,35 @@ RUBY
219
150
  buffer << buffer.slice!(capture_position..-1).rstrip
220
151
  end
221
152
 
222
- # Merges two attribute hashes.
223
- # This is the same as `to.merge!(from)`,
224
- # except that it merges id, class, and data attributes.
225
- #
226
- # ids are concatenated with `"_"`,
227
- # and classes are concatenated with `" "`.
228
- # data hashes are simply merged.
153
+ # Works like #{find_and_preserve}, but allows the first newline after a
154
+ # preserved opening tag to remain unencoded, and then outdents the content.
155
+ # This change was motivated primarily by the change in Rails 3.2.3 to emit
156
+ # a newline after textarea helpers.
229
157
  #
230
- # Destructively modifies both `to` and `from`.
231
- #
232
- # @param to [{String => String}] The attribute hash to merge into
233
- # @param from [{String => #to_s}] The attribute hash to merge from
234
- # @return [{String => String}] `to`, after being merged
235
- def self.merge_attrs(to, from)
236
- from['id'] = Compiler.filter_and_join(from['id'], '_') if from['id']
237
- if to['id'] && from['id']
238
- to['id'] << '_' << from.delete('id').to_s
239
- elsif to['id'] || from['id']
240
- from['id'] ||= to['id']
241
- end
242
-
243
- from['class'] = Compiler.filter_and_join(from['class'], ' ') if from['class']
244
- if to['class'] && from['class']
245
- # Make sure we don't duplicate class names
246
- from['class'] = (from['class'].to_s.split(' ') | to['class'].split(' ')).sort.join(' ')
247
- elsif to['class'] || from['class']
248
- from['class'] ||= to['class']
158
+ # @param input [String] The text to process
159
+ # @since Haml 4.0.1
160
+ # @private
161
+ def fix_textareas!(input)
162
+ return input unless toplevel? && input.include?('<textarea'.freeze)
163
+
164
+ pattern = /<(textarea)([^>]*)>(\n|&#x000A;)(.*?)<\/textarea>/im
165
+ input.gsub!(pattern) do |s|
166
+ match = pattern.match(s)
167
+ content = match[4]
168
+ if match[3] == '&#x000A;'
169
+ content.sub!(/\A /, '&#x0020;')
170
+ else
171
+ content.sub!(/\A[ ]*/, '')
172
+ end
173
+ "<#{match[1]}#{match[2]}>\n#{content}</#{match[1]}>"
249
174
  end
250
-
251
- from_data = from.delete('data') || {}
252
- to_data = to.delete('data') || {}
253
-
254
- # forces to_data & from_data into a hash
255
- from_data = { nil => from_data } unless from_data.is_a?(Hash)
256
- to_data = { nil => to_data } unless to_data.is_a?(Hash)
257
-
258
- merged_data = to_data.merge(from_data)
259
-
260
- to['data'] = merged_data unless merged_data.empty?
261
- to.merge!(from)
175
+ input
262
176
  end
263
177
 
264
178
  private
265
179
 
266
- if RUBY_VERSION < "1.9"
267
- def new_encoded_string
268
- ""
269
- end
270
- else
271
- def new_encoded_string
272
- "".encode(Encoding.find(options[:encoding]))
273
- end
180
+ def new_encoded_string
181
+ "".encode(options[:encoding])
274
182
  end
275
183
 
276
184
  @@tab_cache = {}
@@ -308,18 +216,20 @@ RUBY
308
216
  id = "#{ prefix }_#{ id }"
309
217
  end
310
218
 
311
- {'id' => id, 'class' => class_name}
219
+ { 'id'.freeze => id, 'class'.freeze => class_name }
312
220
  end
313
221
 
314
222
  # Changes a word from camel case to underscores.
315
223
  # Based on the method of the same name in Rails' Inflector,
316
224
  # but copied here so it'll run properly without Rails.
317
225
  def underscore(camel_cased_word)
318
- camel_cased_word.to_s.gsub(/::/, '_').
319
- gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
320
- gsub(/([a-z\d])([A-Z])/,'\1_\2').
321
- tr("-", "_").
322
- downcase
226
+ word = camel_cased_word.to_s.dup
227
+ word.gsub!(/::/, '_')
228
+ word.gsub!(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
229
+ word.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
230
+ word.tr!('-', '_')
231
+ word.downcase!
232
+ word
323
233
  end
324
234
  end
325
235
  end