Class: ORB::RailsTemplate

Inherits:
Object
  • Object
show all
Defined in:
lib/orb/rails_template.rb

Defined Under Namespace

Classes: CaptureBuffer

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.optionsObject



15
16
17
18
19
20
21
22
23
24
# File 'lib/orb/rails_template.rb', line 15

def options
  @options ||= {
    generator: ::Temple::Generators::RailsOutputBuffer,
    capture_generator: CaptureBuffer,
    use_html_safe: true,
    streaming: true,
    buffer_class: 'ActionView::OutputBuffer',
    disable_capture: true,
  }
end

.set_options(opts) ⇒ Object



26
27
28
# File 'lib/orb/rails_template.rb', line 26

def set_options(opts)
  options.update(opts)
end

Instance Method Details

#call(template, source = nil) ⇒ Object



31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/orb/rails_template.rb', line 31

def call(template, source = nil)
  source ||= template.source
  options = RailsTemplate.options

  # Make the filename available in parser etc.
  options = options.merge(file: template.identifier) if template.respond_to?(:identifier)

  # Set type
  options = options.merge(format: :xhtml) if template.respond_to?(:type) && template.type == 'text/xml'

  # Annotations
  if ActionView::Base.try(:annotate_rendered_view_with_filenames) && template.format == :html
    options = options.merge(
      preamble: "<!-- BEGIN #{template.short_identifier} -->\n",
      postamble: "<!-- END #{template.short_identifier} -->\n",
    )
  end

  # Pipe through the ORB Temple engine
  code = ORB::Temple::Engine.new(options).call(source)

  # Validate generated code with Prism to catch syntax errors BEFORE Rails does.
  # This is a workaround for a Rails 8.1.1 bug where SyntaxErrorProxy fails
  # is_a?(Exception) checks in ActiveSupport::ErrorReporter#report.
  # See: rails-syntax-error-bug.md for details.
  #
  # Only run in development mode to avoid performance impact in production.
  # In production, syntax errors will still be caught but with less friendly display.
  if defined?(Prism) && defined?(Rails) && Rails.env.local?
    result = Prism.parse(code)
    if result.failure?
      first_error = result.errors.first
      error_line = first_error.location.start_line
      message = first_error.message

      # Return code that raises the error when executed.
      # This way Rails' normal error handling will kick in, providing proper
      # extracted source display and backtrace. We add newlines to position
      # the error at the correct line number in the template.
      escaped_message = message.gsub('\\', '\\\\\\\\').gsub("'", "\\\\'")
      return "#{'\\n' * (error_line - 1)}raise ORB::CompilerError.new('#{escaped_message}', #{error_line})"
    end
  end

  code
end

#find_offset(snippet, src_tokens, snippet_error_column) ⇒ Object



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/orb/rails_template.rb', line 98

def find_offset(snippet, src_tokens, snippet_error_column)
  offset = 0
  passed_tokens = []

  # Pass over tokens until we are just over the snippet error column
  # then the column of the last token is the offset (without the token static offset for tags {% %})
  while (tok = src_tokens.shift)
    offset = snippet.index(tok.value, offset)
    raise "text not found" unless offset
    raise "we went too far" if offset > snippet_error_column

    passed_tokens << tok
  end
rescue StandardError
  offset_token = passed_tokens.last
  offset_from_token(offset_token)
end

#offset_from_token(token) ⇒ Object



116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/orb/rails_template.rb', line 116

def offset_from_token(token)
  case token.type
  when :tag_open, :tag_close
    token.column + 1
  when :public_comment, :private_comment
    token.column + 4
  when :block_open, :block_close
    token.column + 2 + token.value.length
  when :printing_expression, :control_expression
    token.column + 2
  end
end

#supports_streaming?Boolean

Returns:

  • (Boolean)


129
130
131
# File 'lib/orb/rails_template.rb', line 129

def supports_streaming?
  RailsTemplate.options[:streaming]
end

#translate_location(spot, backtrace_location, source) ⇒ Object



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/orb/rails_template.rb', line 79

def translate_location(spot, backtrace_location, source)
  offending_line_source = source.lines[backtrace_location.lineno - 1]
  tokens = ORB::Utils::ORB.tokenize(offending_line_source)
  new_column = find_offset(spot[:snippet], tokens, spot[:first_column])

  lineno_delta = spot[:first_lineno] - backtrace_location.lineno
  spot[:first_lineno] -= lineno_delta
  spot[:last_lineno] -= lineno_delta

  column_delta = spot[:first_column] - new_column
  spot[:first_column] -= column_delta
  spot[:last_column] -= column_delta
  spot[:script_lines] = source.lines

  spot
rescue StandardError => _e
  spot
end