This is a class method so it can be accessed from Buffer.
# File lib/haml/compiler.rb, line 348 348: def self.build_attributes(is_html, attr_wrapper, escape_attrs, attributes = {}) 349: quote_escape = attr_wrapper == '"' ? """ : "'" 350: other_quote_char = attr_wrapper == '"' ? "'" : '"' 351: 352: if attributes['data'].is_a?(Hash) 353: attributes = attributes.dup 354: attributes = 355: Haml::Util.map_keys(attributes.delete('data')) {|name| "data-#{name}"}.merge(attributes) 356: end 357: 358: result = attributes.collect do |attr, value| 359: next if value.nil? 360: 361: value = filter_and_join(value, ' ') if attr == 'class' 362: value = filter_and_join(value, '_') if attr == 'id' 363: 364: if value == true 365: next " #{attr}" if is_html 366: next " #{attr}=#{attr_wrapper}#{attr}#{attr_wrapper}" 367: elsif value == false 368: next 369: end 370: 371: escaped = 372: if escape_attrs == :once 373: Haml::Helpers.escape_once(value.to_s) 374: elsif escape_attrs 375: Haml::Helpers.html_escape(value.to_s) 376: else 377: value.to_s 378: end 379: value = Haml::Helpers.preserve(escaped) 380: if escape_attrs 381: # We want to decide whether or not to escape quotes 382: value = value.gsub('"', '"') 383: this_attr_wrapper = attr_wrapper 384: if value.include? attr_wrapper 385: if value.include? other_quote_char 386: value = value.gsub(attr_wrapper, quote_escape) 387: else 388: this_attr_wrapper = other_quote_char 389: end 390: end 391: else 392: this_attr_wrapper = attr_wrapper 393: end 394: " #{attr}=#{this_attr_wrapper}#{value}#{this_attr_wrapper}" 395: end 396: result.compact.sort.join 397: end
# File lib/haml/compiler.rb, line 399 399: def self.filter_and_join(value, separator) 400: return "" if value == "" 401: value = [value] unless value.is_a?(Array) 402: value = value.flatten.collect {|item| item ? item.to_s : nil}.compact.join(separator) 403: return !value.empty? && value 404: end
# File lib/haml/template/plugin.rb, line 59 59: def compile_silent_script_with_haml_block_deprecation(&block) 60: unless block && !@node.value[:keyword] && 61: @node.value[:text] =~ ActionView::Template::Handlers::Erubis::BLOCK_EXPR 62: return compile_silent_script_without_haml_block_deprecation(&block) 63: end 64: 65: @node.value[:text] = "_hamlout.append_if_string= #{@node.value[:text]}" 66: compile_silent_script_without_haml_block_deprecation(&block) 67: end
# File lib/haml/compiler.rb, line 444 444: def compile(node) 445: parent, @node = @node, node 446: block = proc {node.children.each {|c| compile c}} 447: send("compile_#{node.type}", &(block unless node.children.empty?)) 448: ensure 449: @node = parent 450: end
# File lib/haml/compiler.rb, line 185 185: def compile_comment 186: open = "<!--#{@node.value[:conditional]}" 187: 188: # Render it statically if possible 189: unless block_given? 190: push_text("#{open} #{@node.value[:text]} #{@node.value[:conditional] ? "<![endif]-->" : "-->"}") 191: return 192: end 193: 194: push_text(open, 1) 195: @output_tabs += 1 196: yield if block_given? 197: @output_tabs -= 1 198: push_text(@node.value[:conditional] ? "<![endif]-->" : "-->", 1) 199: end
# File lib/haml/compiler.rb, line 201 201: def compile_doctype 202: doctype = text_for_doctype 203: push_text doctype if doctype 204: end
# File lib/haml/compiler.rb, line 206 206: def compile_filter 207: unless filter = Filters.defined[@node.value[:name]] 208: raise Error.new("Filter \"#{@node.value[:name]}\" is not defined.", @node.line - 1) 209: end 210: filter.internal_compile(self, @node.value[:text]) 211: end
# File lib/haml/compiler.rb, line 89 89: def compile_haml_comment; end
# File lib/haml/compiler.rb, line 52 52: def compile_plain 53: push_text @node.value[:text] 54: end
# File lib/haml/compiler.rb, line 44 44: def compile_root 45: @dont_indent_next_line = @dont_tab_up_next_text = false 46: @output_line = 1 47: @indentation = nil 48: yield 49: flush_merged_text 50: end
# File lib/haml/compiler.rb, line 56 56: def compile_script(&block) 57: push_script(@node.value[:text], 58: :preserve_script => @node.value[:preserve], 59: :escape_html => @node.value[:escape_html], &block) 60: end
# File lib/haml/compiler.rb, line 62 62: def compile_silent_script 63: return if @options[:suppress_eval] 64: push_silent(@node.value[:text]) 65: keyword = @node.value[:keyword] 66: ruby_block = block_given? && !keyword 67: 68: if block_given? 69: # Store these values because for conditional statements, 70: # we want to restore them for each branch 71: @node.value[:dont_indent_next_line] = @dont_indent_next_line 72: @node.value[:dont_tab_up_next_text] = @dont_tab_up_next_text 73: yield 74: push_silent("end", :can_suppress) unless @node.value[:dont_push_end] 75: elsif keyword == "end" 76: if @node.parent.children.last.equal?(@node) 77: # Since this "end" is ending the block, 78: # we don't need to generate an additional one 79: @node.parent.value[:dont_push_end] = true 80: end 81: # Don't restore dont_* for end because it isn't a conditional branch. 82: elsif Parser::MID_BLOCK_KEYWORDS.include?(keyword) 83: # Restore dont_* for this conditional branch 84: @dont_indent_next_line = @node.parent.value[:dont_indent_next_line] 85: @dont_tab_up_next_text = @node.parent.value[:dont_tab_up_next_text] 86: end 87: end
# File lib/haml/compiler.rb, line 91 91: def compile_tag 92: t = @node.value 93: 94: # Get rid of whitespace outside of the tag if we need to 95: rstrip_buffer! if t[:nuke_outer_whitespace] 96: 97: dont_indent_next_line = 98: (t[:nuke_outer_whitespace] && !block_given?) || 99: (t[:nuke_inner_whitespace] && block_given?) 100: 101: if @options[:suppress_eval] 102: object_ref = "nil" 103: parse = false 104: value = t[:parse] ? nil : t[:value] 105: attributes_hashes = {} 106: preserve_script = false 107: else 108: object_ref = t[:object_ref] 109: parse = t[:parse] 110: value = t[:value] 111: attributes_hashes = t[:attributes_hashes] 112: preserve_script = t[:preserve_script] 113: end 114: 115: # Check if we can render the tag directly to text and not process it in the buffer 116: if object_ref == "nil" && attributes_hashes.empty? && !preserve_script 117: tag_closed = !block_given? && !t[:self_closing] && !parse 118: 119: open_tag = prerender_tag(t[:name], t[:self_closing], t[:attributes]) 120: if tag_closed 121: open_tag << "#{value}</#{t[:name]}>" 122: open_tag << "\n" unless t[:nuke_outer_whitespace] 123: elsif !(parse || t[:nuke_inner_whitespace] || 124: (t[:self_closing] && t[:nuke_outer_whitespace])) 125: open_tag << "\n" 126: end 127: 128: push_merged_text(open_tag, 129: tag_closed || t[:self_closing] || t[:nuke_inner_whitespace] ? 0 : 1, 130: !t[:nuke_outer_whitespace]) 131: 132: @dont_indent_next_line = dont_indent_next_line 133: return if tag_closed 134: else 135: if attributes_hashes.empty? 136: attributes_hashes = '' 137: elsif attributes_hashes.size == 1 138: attributes_hashes = ", #{attributes_hashes.first}" 139: else 140: attributes_hashes = ", (#{attributes_hashes.join(").merge(")})" 141: end 142: 143: push_merged_text "<#{t[:name]}", 0, !t[:nuke_outer_whitespace] 144: push_generated_script( 145: "_hamlout.attributes(#{inspect_obj(t[:attributes])}, #{object_ref}#{attributes_hashes})") 146: concat_merged_text( 147: if t[:self_closing] && xhtml? 148: " />" + (t[:nuke_outer_whitespace] ? "" : "\n") 149: else 150: ">" + ((if t[:self_closing] && html? 151: t[:nuke_outer_whitespace] 152: else 153: !block_given? || t[:preserve_tag] || t[:nuke_inner_whitespace] 154: end) ? "" : "\n") 155: end) 156: 157: if value && !parse 158: concat_merged_text("#{value}</#{t[:name]}>#{t[:nuke_outer_whitespace] ? "" : "\n"}") 159: elsif !t[:nuke_inner_whitespace] && !t[:self_closing] 160: @to_merge << [:text, '', 1] 161: end 162: 163: @dont_indent_next_line = dont_indent_next_line 164: end 165: 166: return if t[:self_closing] 167: 168: if value.nil? 169: @output_tabs += 1 unless t[:nuke_inner_whitespace] 170: yield if block_given? 171: @output_tabs -= 1 unless t[:nuke_inner_whitespace] 172: rstrip_buffer! if t[:nuke_inner_whitespace] 173: push_merged_text("</#{t[:name]}>" + (t[:nuke_outer_whitespace] ? "" : "\n"), 174: t[:nuke_inner_whitespace] ? 0 : 1, !t[:nuke_inner_whitespace]) 175: @dont_indent_next_line = t[:nuke_outer_whitespace] 176: return 177: end 178: 179: if parse 180: push_script(value, t.merge(:in_tag => true)) 181: concat_merged_text("</#{t[:name]}>" + (t[:nuke_outer_whitespace] ? "" : "\n")) 182: end 183: end
Concatenate `text` to `@buffer` without tabulation.
# File lib/haml/compiler.rb, line 267 267: def concat_merged_text(text) 268: @to_merge << [:text, text, 0] 269: end
# File lib/haml/compiler.rb, line 275 275: def flush_merged_text 276: return if @to_merge.empty? 277: 278: str = "" 279: mtabs = 0 280: @to_merge.each do |type, val, tabs| 281: case type 282: when :text 283: str << inspect_obj(val)[1...1] 284: mtabs += tabs 285: when :script 286: if mtabs != 0 && !@options[:ugly] 287: val = "_hamlout.adjust_tabs(#{mtabs}); " + val 288: end 289: str << "\#{#{val}}" 290: mtabs = 0 291: else 292: raise SyntaxError.new("[HAML BUG] Undefined entry in Haml::Compiler@to_merge.") 293: end 294: end 295: 296: unless str.empty? 297: @precompiled << 298: if @options[:ugly] 299: "_hamlout.buffer << \"#{str}\";" 300: else 301: "_hamlout.push_text(\"#{str}\", #{mtabs}, #{@dont_tab_up_next_text.inspect});" 302: end 303: end 304: @to_merge = [] 305: @dont_tab_up_next_text = false 306: end
# File lib/haml/compiler.rb, line 33 33: def locals_code(names) 34: names = names.keys if Hash == names 35: 36: names.map do |name| 37: # Can't use || because someone might explicitly pass in false with a symbol 38: sym_local = "_haml_locals[#{inspect_obj(name.to_sym)}]" 39: str_local = "_haml_locals[#{inspect_obj(name.to_s)}]" 40: "#{name} = #{sym_local}.nil? ? #{str_local} : #{sym_local}" 41: end.join(';') + ';' 42: end
Returns the string used as the return value of the precompiled method. This method exists so it can be monkeypatched to return modified values.
# File lib/haml/compiler.rb, line 29 29: def precompiled_method_return_value 30: "_erbout" 31: end
Returns the precompiled string with the preamble and postamble
# File lib/haml/compiler.rb, line 10 10: def precompiled_with_ambles(local_names) 11: preamble = beginextend Haml::Helpers_hamlout = @haml_buffer = Haml::Buffer.new(@haml_buffer, #{options_for_buffer.inspect})_erbout = _hamlout.buffer__in_erb_template = true.gsub("\n", ";") 12: postamble = #{precompiled_method_return_value}ensure@haml_buffer = @haml_buffer.upper if @haml_bufferend.gsub("\n", ";") 13: preamble + locals_code(local_names) + precompiled + postamble 14: end
# File lib/haml/compiler.rb, line 406 406: def prerender_tag(name, self_close, attributes) 407: attributes_string = Compiler.build_attributes( 408: html?, @options[:attr_wrapper], @options[:escape_attrs], attributes) 409: "<#{name}#{attributes_string}#{self_close && xhtml? ? ' /' : ''}>" 410: end
# File lib/haml/compiler.rb, line 342 342: def push_generated_script(text) 343: @to_merge << [:script, resolve_newlines + text] 344: @output_line += text.count("\n") 345: end
Adds `text` to `@buffer` with appropriate tabulation without parsing it.
# File lib/haml/compiler.rb, line 260 260: def push_merged_text(text, tab_change = 0, indent = true) 261: text = !indent || @dont_indent_next_line || @options[:ugly] ? text : "#{' ' * @output_tabs}#{text}" 262: @to_merge << [:text, text, tab_change] 263: @dont_indent_next_line = false 264: end
Causes `text` to be evaluated in the context of the scope object and the result to be added to `@buffer`.
If `opts[:preserve_script]` is true, Haml::Helpers#find_and_flatten is run on the result before it is added to `@buffer`
# File lib/haml/compiler.rb, line 313 313: def push_script(text, opts = {}) 314: return if options[:suppress_eval] 315: 316: args = ]preserve_script in_tag preserve_tag escape_html nuke_inner_whitespace] 317: args.map! {|name| opts[name.to_sym]} 318: args << !block_given? << @options[:ugly] 319: 320: no_format = @options[:ugly] && 321: !(opts[:preserve_script] || opts[:preserve_tag] || opts[:escape_html]) 322: output_expr = "(#{text}\n)" 323: static_method = "_hamlout.#{static_method_name(:format_script, *args)}" 324: 325: # Prerender tabulation unless we're in a tag 326: push_merged_text '' unless opts[:in_tag] 327: 328: unless block_given? 329: push_generated_script(no_format ? "#{text}\n" : "#{static_method}(#{output_expr});") 330: concat_merged_text("\n") unless opts[:in_tag] || opts[:nuke_inner_whitespace] 331: return 332: end 333: 334: flush_merged_text 335: push_silent "haml_temp = #{text}" 336: yield 337: push_silent('end', :can_suppress) unless @node.value[:dont_push_end] 338: @precompiled << "_hamlout.buffer << #{no_format ? "haml_temp.to_s;" : "#{static_method}(haml_temp);"}" 339: concat_merged_text("\n") unless opts[:in_tag] || opts[:nuke_inner_whitespace] || @options[:ugly] 340: end
Evaluates `text` in the context of the scope object, but does not output the result.
# File lib/haml/compiler.rb, line 251 251: def push_silent(text, can_suppress = false) 252: flush_merged_text 253: return if can_suppress && options[:suppress_eval] 254: @precompiled << "#{resolve_newlines}#{text}\n" 255: @output_line += text.count("\n") + 1 256: end
# File lib/haml/compiler.rb, line 271 271: def push_text(text, tab_change = 0) 272: push_merged_text("#{text}\n", tab_change) 273: end
# File lib/haml/compiler.rb, line 412 412: def resolve_newlines 413: diff = @node.line - @output_line 414: return "" if diff <= 0 415: @output_line = @node.line 416: "\n" * [diff, 0].max 417: end
Get rid of and whitespace at the end of the buffer or the merged text
# File lib/haml/compiler.rb, line 421 421: def rstrip_buffer!(index = 1) 422: last = @to_merge[index] 423: if last.nil? 424: push_silent("_hamlout.rstrip!", false) 425: @dont_tab_up_next_text = true 426: return 427: end 428: 429: case last.first 430: when :text 431: last[1].rstrip! 432: if last[1].empty? 433: @to_merge.slice! index 434: rstrip_buffer! index 435: end 436: when :script 437: last[1].gsub!(/\(haml_temp, (.*?)\);$/, '(haml_temp.rstrip, \1);') 438: rstrip_buffer! index - 1 439: else 440: raise SyntaxError.new("[HAML BUG] Undefined entry in Haml::Compiler@to_merge.") 441: end 442: end
# File lib/haml/compiler.rb, line 213 213: def text_for_doctype 214: if @node.value[:type] == "xml" 215: return nil if html? 216: wrapper = @options[:attr_wrapper] 217: return "<?xml version=#{wrapper}1.0#{wrapper} encoding=#{wrapper}#{@node.value[:encoding] || "utf-8"}#{wrapper} ?>" 218: end 219: 220: if html5? 221: '<!DOCTYPE html>' 222: else 223: if xhtml? 224: if @node.value[:version] == "1.1" 225: '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">' 226: elsif @node.value[:version] == "5" 227: '<!DOCTYPE html>' 228: else 229: case @node.value[:type] 230: when "strict"; '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">' 231: when "frameset"; '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">' 232: when "mobile"; '<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.2//EN" "http://www.openmobilealliance.org/tech/DTD/xhtml-mobile12.dtd">' 233: when "rdfa"; '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML+RDFa 1.0//EN" "http://www.w3.org/MarkUp/DTD/xhtml-rdfa-1.dtd">' 234: when "basic"; '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML Basic 1.1//EN" "http://www.w3.org/TR/xhtml-basic/xhtml-basic11.dtd">' 235: else '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">' 236: end 237: end 238: 239: elsif html4? 240: case @node.value[:type] 241: when "strict"; '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">' 242: when "frameset"; '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">' 243: else '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">' 244: end 245: end 246: end 247: end
Disabled; run with --debug to generate this.
Generated with the Darkfish Rdoc Generator 1.1.6.