Top Level Namespace

Defined Under Namespace

Modules: Syntropy

Constant Summary collapse

TAG_DEBUG_PROC =
->(level, fn, line, col) {
  {
    'data-syntropy-level' => level,
    'data-syntropy-fn'    => fn,
    'data-syntropy-loc'   => "vscode://file/#{fn}:#{line}:#{col}"
  }
}
ErrorPage =
->(error:, status:, backtrace:) {
  html {
    head {
      title "Syntropy error: #{error.message}"
      meta charset: 'utf-8'
      meta name: 'viewport', content: 'width=device-width, initial-scale=1.0'
      link rel: 'stylesheet', type: 'text/css', href: '/.syntropy/default_error_handler/style.css'
    }
    body {
      div {
        big status
        h2 error.message
        if backtrace
          p "Backtrace:"
          ul {
            backtrace.each {
              li {
                a(it[:entry], href: it[:url])
              }
            }
          }
        end
      }
      auto_refresh_watch!
    }
  }
}

Instance Method Summary collapse

Instance Method Details

#call(req) ⇒ Object

Handles incoming requests to the ‘watch.sse` route. Adds a queue to the list of watchers, and waits for the queue to be signalled. In the absence of file change, a timeout occurs after one minute, and the request is terminated.



24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/syntropy/applets/builtin/auto_refresh/watch.sse.rb', line 24

def call(req)
  queue = UM::Queue.new
  watchers[queue] = true

  req.send_headers('Content-Type' => 'text/event-stream')
  req.send_chunk("data: \n\n")
  @machine.timeout(60, Timeout::Error) do
    @machine.shift(queue)
    req.send_chunk("data: refresh\n\n")
  end
  req.send_chunk("retry: 0\n\n", done: true) rescue nil
rescue Timeout::Error
  req.send_chunk("retry: 0\n\n", done: true) rescue nil
rescue SystemCallError
  # ignore
rescue => e
  @logger&.error(
    message: 'Unexpected error encountered while serving auto refresh watcher',
    error: e
  )
  req.finish rescue nil
ensure
  watchers.delete(queue)
end

#error_response_html(req, error) ⇒ Object



42
43
44
45
46
47
# File 'lib/syntropy/applets/builtin/default_error_handler.rb', line 42

def error_response_html(req, error)
  status = Syntropy::Error.http_status(error)
  backtrace = transform_backtrace(error.backtrace)
  html = Papercraft.html(ErrorPage, error:, status:, backtrace:)
  req.html_response(html, ':status' => status)
end

#error_response_raw(req, error) ⇒ Object



49
50
51
52
53
54
55
56
57
# File 'lib/syntropy/applets/builtin/default_error_handler.rb', line 49

def error_response_raw(req, error)
  status = Syntropy::Error.http_status(error)
  response = {
    class: error.class.to_s,
    message: error.message,
    backtrace: error.backtrace
  }
  req.json_pretty_response(response, ':status' => status)
end

#signal!Object

Signals a file change by pushing to all watcher queues.



17
18
19
# File 'lib/syntropy/applets/builtin/auto_refresh/watch.sse.rb', line 17

def signal!
  watchers.each_key { @machine.push(it, true) }
end

#transform_backtrace(backtrace) ⇒ Object



31
32
33
34
35
36
37
38
39
40
# File 'lib/syntropy/applets/builtin/default_error_handler.rb', line 31

def transform_backtrace(backtrace)
  backtrace.map do
    if (m = it.match(/^(.+:\d+):/))
      location = m[1]
      { entry: it, url: "vscode://file/#{location}" }
    else
      { entry: it, url: nil }
    end
  end
end

#watchersObject

Returns a hash holding references to queues for ongoing ‘watch.sse` requests.



12
13
14
# File 'lib/syntropy/applets/builtin/auto_refresh/watch.sse.rb', line 12

def watchers
  @watchers ||= {}
end