123456789_123456789_123456789_123456789_123456789_

Class: ActionView::Template::Handlers::ERB

Relationships & Source Files
Namespace Children
Classes:
Inherits: Object
Defined in: actionview/lib/action_view/template/handlers/erb.rb,
actionview/lib/action_view/template/handlers/erb/erubi.rb

Constant Summary

Class Attribute Summary

Class Method Summary

Instance Attribute Summary

Instance Method Summary

Class Attribute Details

.erb_implementation (rw)

[ GitHub ]

  
# File 'actionview/lib/action_view/template/handlers/erb.rb', line 17

class_attribute :erb_implementation, default: Erubi

.erb_implementation?Boolean (rw)

[ GitHub ]

  
# File 'actionview/lib/action_view/template/handlers/erb.rb', line 17

class_attribute :erb_implementation, default: Erubi

.erb_trim_mode (rw)

[ GitHub ]

  
# File 'actionview/lib/action_view/template/handlers/erb.rb', line 14

class_attribute :erb_trim_mode, default: "-"

.erb_trim_mode?Boolean (rw)

[ GitHub ]

  
# File 'actionview/lib/action_view/template/handlers/erb.rb', line 14

class_attribute :erb_trim_mode, default: "-"

.escape_ignore_list (rw)

[ GitHub ]

  
# File 'actionview/lib/action_view/template/handlers/erb.rb', line 20

class_attribute :escape_ignore_list, default: ["text/plain"]

.escape_ignore_list?Boolean (rw)

[ GitHub ]

  
# File 'actionview/lib/action_view/template/handlers/erb.rb', line 20

class_attribute :escape_ignore_list, default: ["text/plain"]

.strip_trailing_newlines (rw)

[ GitHub ]

  
# File 'actionview/lib/action_view/template/handlers/erb.rb', line 23

class_attribute :strip_trailing_newlines, default: false

.strip_trailing_newlines?Boolean (rw)

[ GitHub ]

  
# File 'actionview/lib/action_view/template/handlers/erb.rb', line 23

class_attribute :strip_trailing_newlines, default: false

Class Method Details

.call(template, source)

[ GitHub ]

  
# File 'actionview/lib/action_view/template/handlers/erb.rb', line 29

def self.call(template, source)
  new.call(template, source)
end

Instance Attribute Details

#erb_implementation (rw)

[ GitHub ]

  
# File 'actionview/lib/action_view/template/handlers/erb.rb', line 17

class_attribute :erb_implementation, default: Erubi

#erb_implementation?Boolean (rw)

[ GitHub ]

  
# File 'actionview/lib/action_view/template/handlers/erb.rb', line 17

class_attribute :erb_implementation, default: Erubi

#erb_trim_mode (rw)

[ GitHub ]

  
# File 'actionview/lib/action_view/template/handlers/erb.rb', line 14

class_attribute :erb_trim_mode, default: "-"

#erb_trim_mode?Boolean (rw)

[ GitHub ]

  
# File 'actionview/lib/action_view/template/handlers/erb.rb', line 14

class_attribute :erb_trim_mode, default: "-"

#escape_ignore_list (rw)

[ GitHub ]

  
# File 'actionview/lib/action_view/template/handlers/erb.rb', line 20

class_attribute :escape_ignore_list, default: ["text/plain"]

#escape_ignore_list?Boolean (rw)

[ GitHub ]

  
# File 'actionview/lib/action_view/template/handlers/erb.rb', line 20

class_attribute :escape_ignore_list, default: ["text/plain"]

#handles_encoding?Boolean (readonly)

[ GitHub ]

  
# File 'actionview/lib/action_view/template/handlers/erb.rb', line 37

def handles_encoding?
  true
end

#strip_trailing_newlines (rw)

[ GitHub ]

  
# File 'actionview/lib/action_view/template/handlers/erb.rb', line 23

class_attribute :strip_trailing_newlines, default: false

#strip_trailing_newlines?Boolean (rw)

[ GitHub ]

  
# File 'actionview/lib/action_view/template/handlers/erb.rb', line 23

class_attribute :strip_trailing_newlines, default: false

#supports_streaming?Boolean (readonly)

[ GitHub ]

  
# File 'actionview/lib/action_view/template/handlers/erb.rb', line 33

def supports_streaming?
  true
end

Instance Method Details

#call(template, source)

[ GitHub ]

  
# File 'actionview/lib/action_view/template/handlers/erb.rb', line 65

def call(template, source)
  # First, convert to BINARY, so in case the encoding is
  # wrong, we can still find an encoding tag
  # (<%# encoding %>) inside the String using a regular
  # expression
  template_source = source.b

  erb = template_source.gsub(ENCODING_TAG, "")
  encoding = $2

  erb.force_encoding valid_encoding(source.dup, encoding)

  # Always make sure we return a String in the default_internal
  erb.encode!

  # Strip trailing newlines from the template if enabled
  erb.chomp! if strip_trailing_newlines

  options = {
    escape: (self.class.escape_ignore_list.include? template.type),
    trim: (self.class.erb_trim_mode == "-")
  }

  if ActionView::Base.annotate_rendered_view_with_filenames && template.format == :html
    options[:preamble] = "@output_buffer.safe_append='<!-- BEGIN #{template.short_identifier} -->';"
    options[:postamble] = "@output_buffer.safe_append='<!-- END #{template.short_identifier} -->';@output_buffer"
  end

  self.class.erb_implementation.new(erb, options).src
end

#find_lineno_offset(compiled, source_lines, highlight, error_lineno) (private)

Return the offset between the error lineno and the source lineno. Searches in reverse from the backtrace lineno so we have a better chance of finding the correct line

The compiled template is likely to be longer than the source. Use the difference between the compiled and source sizes to determine the earliest line that could contain the highlight.

[ GitHub ]

  
# File 'actionview/lib/action_view/template/handlers/erb.rb', line 119

def find_lineno_offset(compiled, source_lines, highlight, error_lineno)
  first_index = error_lineno - 1 - compiled.size + source_lines.size
  first_index = 0 if first_index < 0

  last_index = error_lineno - 1
  last_index = source_lines.size - 1 if last_index >= source_lines.size

  last_index.downto(first_index) do |line_index|
    next unless source_lines[line_index].include?(highlight)
    return error_lineno - 1 - line_index
  end

  raise LocationParsingError, "Couldn't find code snippet"
end

#find_offset(compiled, source_tokens, error_column) (private)

Find which token in the source template spans the byte range that contains the error_column, then return the offset compared to the original source template.

Iterate consecutive pairs of CODE or TEXT tokens, requiring a match of the first token before matching either token.

For example, if we want to find tokens A, B, C, we do the following:

  1. Find a match for A: test error_column or advance scanner.

  2. Find a match for B or A:

a. If B: start over with next token set (B, C).
b. If A: test error_column or advance scanner.
c. Otherwise: Advance 1 byte

Prioritize matching the next token over the current token once a match for the current token has been found. This is to prevent the current token from looping past the next token if they both match (i.e. if the current token is a single space character).

[ GitHub ]

  
# File 'actionview/lib/action_view/template/handlers/erb.rb', line 152

def find_offset(compiled, source_tokens, error_column)
  compiled = StringScanner.new(compiled)
  offset_source_tokens(source_tokens).each_cons(2) do |(name, str, offset), (_, next_str, _)|
    matched_str = false

    until compiled.eos?
      if matched_str && next_str && compiled.match?(next_str)
        break
      elsif compiled.match?(str)
        matched_str = true

        if name == :CODE && compiled.pos <= error_column && compiled.pos + str.bytesize >= error_column
          return compiled.pos - offset
        end

        compiled.pos += str.bytesize
      else
        compiled.pos += 1
      end
    end
  end

  raise LocationParsingError, "Couldn't find code snippet"
end

#offset_source_tokens(source_tokens) (private)

[ GitHub ]

  
# File 'actionview/lib/action_view/template/handlers/erb.rb', line 177

def offset_source_tokens(source_tokens)
  source_offset = 0
  with_offset = source_tokens.filter_map do |name, str|
    result = [:CODE, str, source_offset] if name == :CODE || name == :PLAIN
    result = [:TEXT, str, source_offset] if name == :TEXT
    source_offset += str.bytesize
    result
  end
  with_offset << [:EOS, nil, source_offset]
end

#translate_location(spot, _backtrace_location, source)

Translate an error location returned by ErrorHighlight to the correct source location inside the template.

[ GitHub ]

  
# File 'actionview/lib/action_view/template/handlers/erb.rb', line 43

def translate_location(spot, _backtrace_location, source)
  compiled = spot[:script_lines]
  highlight = compiled[spot[:first_lineno] - 1]&.byteslice((spot[:first_column] - 1)...spot[:last_column])
  return nil if highlight.blank?

  source_lines = source.lines
  lineno_delta = find_lineno_offset(compiled, source_lines, highlight, spot[:first_lineno])

  tokens = ::ERB::Util.tokenize(source_lines[spot[:first_lineno] - lineno_delta - 1])
  column_delta = find_offset(spot[:snippet], tokens, spot[:first_column])

  spot[:first_lineno] -= lineno_delta
  spot[:last_lineno] -= lineno_delta
  spot[:first_column] -= column_delta
  spot[:last_column] -= column_delta
  spot[:script_lines] = source_lines

  spot
rescue NotImplementedError, LocationParsingError
  nil
end

#valid_encoding(string, encoding) (private)

Raises:

[ GitHub ]

  
# File 'actionview/lib/action_view/template/handlers/erb.rb', line 97

def valid_encoding(string, encoding)
  # If a magic encoding comment was found, tag the
  # String with this encoding. This is for a case
  # where the original String was assumed to be,
  # for instance, UTF-8, but a magic comment
  # proved otherwise
  string.force_encoding(encoding) if encoding

  # If the String is valid, return the encoding we found
  return string.encoding if string.valid_encoding?

  # Otherwise, raise an exception
  raise WrongEncodingError.new(string, string.encoding)
end