module BetterHtml::BetterErb::RuntimeChecks

Public Class Methods

new(erb, config: BetterHtml.config, **options) click to toggle source
Calls superclass method
# File lib/better_html/better_erb/runtime_checks.rb, line 6
def initialize(erb, config: BetterHtml.config, **options)
  @parser = HtmlTokenizer::Parser.new
  @config = config
  super(erb, **options)
end

Public Instance Methods

validate!() click to toggle source
# File lib/better_html/better_erb/runtime_checks.rb, line 12
def validate!
  check_parser_errors

  unless @parser.context == :none
    raise BetterHtml::HtmlError, 'Detected an open tag at the end of this document.'
  end
end

Private Instance Methods

add_expr_auto_escaped(src, code, auto_escape) click to toggle source
# File lib/better_html/better_erb/runtime_checks.rb, line 30
def add_expr_auto_escaped(src, code, auto_escape)
  flush_newline_if_pending(src)

  src << "#{wrap_method}(@output_buffer, (#{parser_context.inspect}), '#{escape_text(code)}'.freeze, #{auto_escape})"
  method_name = "safe_#{@parser.context}_append"
  if code =~ self.class::BLOCK_EXPR
    block_check(src, "<%=#{code}%>")
    src << ".#{method_name}= " << code
  else
    src << ".#{method_name}=(" << code << ");"
  end
  @parser.append_placeholder("<%=#{code}%>")
end
block_check(src, code) click to toggle source
# File lib/better_html/better_erb/runtime_checks.rb, line 78
def block_check(src, code)
  unless @parser.context == :none || @parser.context == :rawtext
    s = "Ruby statement not allowed.\n"
    s << "In '#{@parser.context}' on line #{@parser.line_number} column #{@parser.column_number}:\n"
    prefix = extract_line(@parser.line_number)
    code = code.lines.first
    s << "#{prefix}#{code}\n"
    s << "#{' ' * prefix.size}#{'^' * code.size}"
    raise BetterHtml::DontInterpolateHere, s
  end
end
build_location(line, column, length) click to toggle source
# File lib/better_html/better_erb/runtime_checks.rb, line 151
def build_location(line, column, length)
  s = "On line #{line} column #{column}:\n"
  s << "#{extract_line(line)}\n"
  s << "#{' ' * column}#{'^' * length}"
end
check_attribute_name(type, start, stop, line, column) click to toggle source
# File lib/better_html/better_erb/runtime_checks.rb, line 124
def check_attribute_name(type, start, stop, line, column)
  text = @parser.document[start...stop]
  return if @config.partial_attribute_name_pattern === text

  s = "Invalid attribute name #{text.inspect} does not match "\
    "regular expression #{@config.partial_attribute_name_pattern.inspect}\n"
  s << build_location(line, column, text.size)
  raise BetterHtml::HtmlError, s
end
check_parser_errors() click to toggle source
# File lib/better_html/better_erb/runtime_checks.rb, line 90
def check_parser_errors
  errors = @parser.errors
  return if errors.empty?

  s = "#{errors.size} error(s) found in HTML document.\n"
  errors.each do |error|
    s = "#{error.message}\n"
    s << "On line #{error.line} column #{error.column}:\n"
    line = extract_line(error.line)
    s << "#{line}\n"
    s << "#{' ' * (error.column)}#{'^' * (line.size - error.column)}"
  end

  raise BetterHtml::HtmlError, s
end
check_quoted_value(type, start, stop, line, column) click to toggle source
# File lib/better_html/better_erb/runtime_checks.rb, line 134
def check_quoted_value(type, start, stop, line, column)
  return if @config.allow_single_quoted_attributes
  text = @parser.document[start...stop]
  return if text == '"'

  s = "Single-quoted attributes are not allowed\n"
  s << build_location(line, column, text.size)
  raise BetterHtml::HtmlError, s
end
check_tag_name(type, start, stop, line, column) click to toggle source
# File lib/better_html/better_erb/runtime_checks.rb, line 113
def check_tag_name(type, start, stop, line, column)
  text = @parser.document[start...stop]
  return if text.upcase == "!DOCTYPE"
  return if @config.partial_tag_name_pattern === text

  s = "Invalid tag name #{text.inspect} does not match "\
    "regular expression #{@config.partial_tag_name_pattern.inspect}\n"
  s << build_location(line, column, text.size)
  raise BetterHtml::HtmlError, s
end
check_token(type, *args) click to toggle source
# File lib/better_html/better_erb/runtime_checks.rb, line 106
def check_token(type, *args)
  check_tag_name(type, *args) if type == :tag_name
  check_attribute_name(type, *args) if type == :attribute_name
  check_quoted_value(type, *args) if type == :attribute_quoted_value_start
  check_unquoted_value(type, *args) if type == :attribute_unquoted_value
end
check_unquoted_value(type, start, stop, line, column) click to toggle source
# File lib/better_html/better_erb/runtime_checks.rb, line 144
def check_unquoted_value(type, start, stop, line, column)
  return if @config.allow_unquoted_attributes
  s = "Unquoted attribute values are not allowed\n"
  s << build_location(line, column, stop-start)
  raise BetterHtml::HtmlError, s
end
class_name() click to toggle source
# File lib/better_html/better_erb/runtime_checks.rb, line 22
def class_name
  "BetterHtml::BetterErb::ValidatedOutputBuffer"
end
extract_line(line) click to toggle source
# File lib/better_html/better_erb/runtime_checks.rb, line 157
def extract_line(line)
  line = @parser.document.lines[line-1]
  line.nil? ? "" : line.gsub(/\n$/, '')
end
parser_context() click to toggle source
# File lib/better_html/better_erb/runtime_checks.rb, line 44
def parser_context
  if [:quoted_value, :unquoted_value, :space_after_attribute].include?(@parser.context)
    {
      tag_name: @parser.tag_name,
      attribute_name: @parser.attribute_name,
      attribute_value: @parser.attribute_value,
      attribute_quoted: @parser.attribute_quoted?,
      quote_character: @parser.quote_character,
    }
  elsif [:attribute_name, :after_attribute_name, :after_equal].include?(@parser.context)
    {
      tag_name: @parser.tag_name,
      attribute_name: @parser.attribute_name,
    }
  elsif [:tag, :tag_name, :tag_end].include?(@parser.context)
    {
      tag_name: @parser.tag_name,
    }
  elsif @parser.context == :rawtext
    {
      tag_name: @parser.tag_name,
      rawtext_text: @parser.rawtext_text,
    }
  elsif @parser.context == :comment
    {
      comment_text: @parser.comment_text,
    }
  elsif [:none, :solidus_or_tag_name].include?(@parser.context)
    {}
  else
    raise RuntimeError, "Tried to interpolate into unknown location #{@parser.context}."
  end
end
wrap_method() click to toggle source
# File lib/better_html/better_erb/runtime_checks.rb, line 26
def wrap_method
  "#{class_name}.wrap"
end