module ERB::Linter::Converter
Constants
- ATTR_NAME
- DOUBLE_QUOTE_ATTR
- ERB_TAG
- HTML_ATTR
- HTML_TAG
- SINGLE_QUOTE_ATTR
- UNQUOTED_ATTR
- UNQUOTED_VALUE
Public Instance Methods
erb2html(source)
click to toggle source
(credit goes to the Deface gem for the original implementation)
# File lib/erb/linter/converter.rb, line 23 def erb2html(source) source = +source # encode all erb tags so that the HTML looks valid erb_tags = {} source.gsub!(ERB_TAG) do |tag| uid = ["ERB_", SecureRandom.uuid].join.delete('-') erb_tags[uid] = tag uid end erb_tags_matcher = Regexp.union(erb_tags.keys) # transform/escape all the erb-attributes first source.gsub!(HTML_TAG).each do |match| line = Regexp.last_match.to_a[1..-1] tag = [line[0], line[2]+line[3]] # scan each attribute into an array attr_scanner = StringScanner.new(line[1]) attributes = [] attributes << (attr_scanner.scan(HTML_ATTR) or raise "Can't scan: #{attr_scanner.string}") until attr_scanner.eos? # attributes.compact! i = -1 attributes.map! do |attribute| if attribute.match?(erb_tags_matcher) space, attribute = attribute.scan(/\A(\s+)(.*)\z/m).flatten attribute.gsub!(erb_tags_matcher, erb_tags) attribute = case attribute when /\A#{ERB_TAG}\z/ %{data-erb-#{i += 1}="#{CGI.escapeHTML(attribute)}"} when /\A#{ATTR_NAME}=/ name, value = attribute.split("=", 2) quote = '"' if value.match(/\A['"]/) quote = value[0] value = value[1...-1] end %{data-erb-#{name}=#{quote}#{CGI.escapeHTML(value)}#{quote}} else raise "Don't know how to process attribute: #{attribute.inspect}" end "#{space}#{attribute}" else attribute end end [tag[0], *attributes, tag[1]].join end # restore the encoded erb tags source.gsub!(erb_tags_matcher, erb_tags) # replaces all <% %> not inside opening html tags replacements = [ { %r{<%\s*end\s*-?%>} => "</erb>" }, { %r{(^\s*)<%(\s*(?:else|elsif\b.*)\s*)-?%>} => "\\1</erb>\n\\1<erb silent erb-code-start\\2erb-code-end>" }, { "<%=" => "<erb loud erb-code-start" }, { "<%" => "<erb silent erb-code-start" }, { "-%>" => "erb-code-end>" }, { "%>" => "erb-code-end>" }, ] replacements.each{ |h| h.each { |replace, with| source.gsub! replace, with } } source.scan(/(erb-code-start)((?:(?!erb-code-end)[\s\S])*)(erb-code-end)/).each do |match| source.sub!("#{match[0]}#{match[1]}#{match[2]}") do |_m| code = match[1] # is nil when the parsing is broken, meaning it's an open expression if Ripper.sexp(code).nil? "erb-code=\"#{CGI.escapeHTML(code.gsub("\n", " "))}\"" else "erb-code=\"#{CGI.escapeHTML(code.gsub("\n", " "))}\"></erb" end end end source end