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



27
28
29
30
31
32
33
34
35
# File 'lib/orb/rails_template.rb', line 27

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

.set_options(opts) ⇒ Object



37
38
39
# File 'lib/orb/rails_template.rb', line 37

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

Instance Method Details

#call(template, source = nil) ⇒ Object



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
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/orb/rails_template.rb', line 42

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



109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/orb/rails_template.rb', line 109

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



127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/orb/rails_template.rb', line 127

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)


140
141
142
# File 'lib/orb/rails_template.rb', line 140

def supports_streaming?
  RailsTemplate.options[:streaming]
end

#translate_location(spot, backtrace_location, source) ⇒ Object



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

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