Class: ORB::RailsTemplate

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

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.optionsObject



8
9
10
11
12
13
14
15
16
# File 'lib/orb/rails_template.rb', line 8

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

.set_options(opts) ⇒ Object



18
19
20
# File 'lib/orb/rails_template.rb', line 18

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

Instance Method Details

#call(template, source = nil) ⇒ Object



23
24
25
26
27
28
29
30
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
# File 'lib/orb/rails_template.rb', line 23

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



90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/orb/rails_template.rb', line 90

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



108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/orb/rails_template.rb', line 108

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)


121
122
123
# File 'lib/orb/rails_template.rb', line 121

def supports_streaming?
  RailsTemplate.options[:streaming]
end

#translate_location(spot, backtrace_location, source) ⇒ Object



71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/orb/rails_template.rb', line 71

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