class Graphiti::Debugger

Attributes

chunks[RW]
debug_models[RW]
enabled[RW]
preserve[RW]
pry[RW]

Public Class Methods

debug() { || ... } click to toggle source
# File lib/graphiti/debugger.rb, line 129
def debug
  if enabled
    begin
      self.chunks = []
      yield
    ensure
      flush
      self.chunks = [] unless preserve
    end
  else
    yield
  end
end
flush() click to toggle source
# File lib/graphiti/debugger.rb, line 151
def flush
  Graphiti.broadcast(:flush_debug, {}) do |payload|
    payload[:chunks] = chunks
    graph_statements.each do |chunk|
      flush_chunk(chunk)
    end
  end
end
on_data(name, start, stop, id, payload) click to toggle source
# File lib/graphiti/debugger.rb, line 12
def on_data(name, start, stop, id, payload)
  return [] unless enabled

  took = ((stop - start) * 1000.0).round(2)
  params = scrub_params(payload[:params])

  if payload[:exception]
    on_data_exception(payload, params)
  elsif payload[:sideload]
    if payload[:results]
      on_sideload_data(payload, params, took)
    end
  else
    on_primary_data(payload, params, took)
  end
end
on_render(name, start, stop, id, payload) click to toggle source
# File lib/graphiti/debugger.rb, line 94
def on_render(name, start, stop, id, payload)
  return [] unless enabled

  add_chunk do |logs|
    took = ((stop - start) * 1000.0).round(2)
    logs << [""]
    logs << ["=== Graphiti Debug", :green, true]
    if payload[:proxy]&.cached? && Graphiti.config.cache_rendering?
      logs << ["Rendering (cached):", :green, true]

      Graphiti::Util::CacheDebug.new(payload[:proxy]).analyze do |cache_debug|
        logs << ["Cache key for #{cache_debug.name}", :blue, true]
        logs << if cache_debug.volatile?
          [" \\_ volatile | Request count: #{cache_debug.request_count} | Hit count: #{cache_debug.hit_count}", :red, true]
        else
          [" \\_   stable | Request count: #{cache_debug.request_count} | Hit count: #{cache_debug.hit_count}", :blue, true]
        end

        if cache_debug.changed_key?
          logs << [" [x] cache key changed #{cache_debug.last_version[:etag]} -> #{cache_debug.current_version[:etag]}", :red]
          logs << ["      removed: #{cache_debug.removed_segments}", :red]
          logs << ["        added: #{cache_debug.added_segments}", :red]
        elsif cache_debug.new_key?
          logs << [" [+] cache key added #{cache_debug.current_version[:etag]}", :red, true]
        else
          logs << [" [✓] #{cache_debug.current_version[:etag]}", :green, true]
        end
      end
    else
      logs << ["Rendering:", :green, true]
    end
    logs << ["Took: #{took}ms", :magenta, true]
  end
end
to_a() click to toggle source
# File lib/graphiti/debugger.rb, line 143
def to_a
  debugs = []
  graph_statements.each do |chunk|
    debugs << chunk_to_hash(chunk)
  end
  debugs
end

Private Class Methods

add_chunk(resource = nil, parent = nil) { |logs, json| ... } click to toggle source
# File lib/graphiti/debugger.rb, line 169
def add_chunk(resource = nil, parent = nil)
  logs, json = [], {}
  yield(logs, json)
  chunks << {
    resource: resource,
    parent: parent,
    logs: logs,
    json: json,
    children: []
  }
end
chunk_to_hash(chunk) click to toggle source
# File lib/graphiti/debugger.rb, line 192
def chunk_to_hash(chunk)
  hash = {}
  hash.merge!(chunk[:json])
  sideloads = []
  chunk[:children].each do |child_chunk|
    sideloads << chunk_to_hash(child_chunk)
  end
  hash[:sideloads] = sideloads
  hash
end
flush_chunk(chunk, depth = 0) click to toggle source
# File lib/graphiti/debugger.rb, line 203
def flush_chunk(chunk, depth = 0)
  chunk[:logs].each do |args|
    indent = "   " * depth
    args[0] = "#{indent}#{args[0]}"
    Graphiti.log(*args)
  end

  chunk[:children].each do |child_chunk|
    flush_chunk(child_chunk, depth + 1)
  end
end
graph_statements() click to toggle source
# File lib/graphiti/debugger.rb, line 181
def graph_statements
  @chunks.each do |chunk|
    if (parent = chunk[:parent])
      relevant = chunks.find { |c| c[:resource] == parent }
      relevant[:children].unshift(chunk) if relevant
    end
  end
  @chunks.reject! { |c| !!c[:parent] }
  @chunks
end
on_data_exception(payload, params) click to toggle source
# File lib/graphiti/debugger.rb, line 29
        def on_data_exception(payload, params)
  unless payload[:exception_object].instance_variable_get(:@__graphiti_debug)
    add_chunk do |logs, json|
      logs << ["\n=== Graphiti Debug ERROR", :red, true]
      if (sideload = payload[:sideload])
        logs << ["#{sideload.parent_resource.class}: Sideload \"#{sideload.name}\"", :red, true]
        json[:parent_resource] = sideload.parent_resource.class.name
        json[:sideload] = sideload.name
      end
      if params
        query = "#{payload[:resource].class.name}.#{payload[:action]}(#{JSON.pretty_generate(params)}).data"
        logs << [query, :cyan, true]
        logs << ["The error occurred when running the above query. Copy/paste it into a rake task or Rails console session to reproduce. Keep in mind you may have to set context.", :yellow, true]
      else
        query = "This sideload is done manually via .scope - no debug information available."
        logs << [query, :cyan, true]
      end
      json[:query] = query

      logs << "\n\n"
      payload[:exception_object]&.instance_variable_set(:@__graphiti_debug, json)
    end
  end
end
on_primary_data(payload, params, took) click to toggle source
# File lib/graphiti/debugger.rb, line 77
        def on_primary_data(payload, params, took)
  results = results(payload[:results])
  add_chunk(payload[:resource], payload[:parent]) do |logs, json|
    logs << [""]
    logs << ["=== Graphiti Debug", :green, true]
    title = "Top Level Data Retrieval (+ sideloads):"
    logs << [title, :green, true]
    json[:title] = title
    query = "#{payload[:resource].class.name}.#{payload[:action]}(#{params.inspect})"
    logs << [query, :cyan, true]
    json[:query] = query
    logs << ["Returned Models: #{results}"] if debug_models
    logs << ["Took: #{took}ms", :magenta, true]
    json[:took] = took
  end
end
on_sideload_data(payload, params, took) click to toggle source
# File lib/graphiti/debugger.rb, line 58
        def on_sideload_data(payload, params, took)
  sideload = payload[:sideload]
  results = results(payload[:results])
  add_chunk(payload[:resource], payload[:parent]) do |logs, json|
    logs << [" \\_ #{sideload.name}", :yellow, true]
    json[:name] = sideload.name
    query = if sideload.class.scope_proc
      "#{payload[:resource].class.name}: Manual sideload via .scope"
    else
      "#{payload[:resource].class.name}.#{payload[:action]}(#{params.inspect})"
    end
    logs << ["    #{query}", :cyan, true]
    json[:query] = query
    logs << ["    Returned Models: #{results}"] if debug_models
    logs << ["    Took: #{took}ms", :magenta, true]
    json[:took] = took
  end
end
results(raw_results) click to toggle source
# File lib/graphiti/debugger.rb, line 54
        def results(raw_results)
  raw_results.map { |r| "[#{r.class.name}, #{r.id.inspect}]" }.join(", ")
end
scrub_params(params) click to toggle source
# File lib/graphiti/debugger.rb, line 162
def scrub_params(params)
  params ||= {}
  params = params.to_unsafe_h if params.respond_to?(:to_unsafe_h)
  params.reject! { |k, v| [:controller, :action, :format, :debug].include?(k.to_sym) }
  params.deep_symbolize_keys
end