# frozen_string_literal: true require "uri" module Dial class Panel class << self def html env, profile_out_filename, ruby_vm_stat, gc_stat, gc_stat_heap, server_timing <<~HTML
#{formatted_rails_route_info env} | #{formatted_request_timing env} | #{formatted_profile_output env, profile_out_filename} #{formatted_rails_version} #{formatted_rack_version} #{formatted_ruby_version}

Server timing
#{formatted_server_timing server_timing}
RubyVM stat
#{formatted_ruby_vm_stat ruby_vm_stat}
GC stat
#{formatted_gc_stat gc_stat}
GC stat heap
#{formatted_gc_stat_heap gc_stat_heap}
HTML end private def style <<~CSS #dial { z-index: 9999; position: fixed; bottom: 0; right: 0; background-color: white; border-top-left-radius: 1rem; box-shadow: -0.2rem -0.2rem 0.4rem rgba(0, 0, 0, 0.5); display: flex; flex-direction: column; padding: 0.5rem; font-size: 0.85rem; color: black; #dial-preview { display: flex; flex-direction: column; cursor: pointer; } #dial-details { display: none; } .section { display: flex; flex-direction: column; margin: 0.25rem 0 0 0; } span { text-align: left; } hr { width: -moz-available; margin: 0.65rem 0 0 0; } details { margin: 0.5rem 0 0 0; text-align: left; } summary { margin: 0.25rem 0 0 0; cursor: pointer; } } CSS end def script <<~JS const dialPreview = document.getElementById("dial-preview"); const dialDetails = document.getElementById("dial-details"); dialPreview.addEventListener("click", () => { const collapsed = ["", "none"].includes(dialDetails.style.display); dialDetails.style.display = collapsed ? "block" : "none"; }); JS end def formatted_rails_route_info env rails_route_info = begin ::Rails.application.routes.recognize_path env[::Rack::PATH_INFO], method: env[::Rack::REQUEST_METHOD] rescue ::ActionController::RoutingError {} end.then do |info| "Controller: #{info[:controller] || "NA"} | Action: #{info[:action] || "NA"}" end end def formatted_request_timing env "Request timing: #{env[REQUEST_TIMING_HEADER]}ms" end def formatted_profile_output env, profile_out_filename uuid = profile_out_filename.delete_suffix ".json" host = env[::Rack::HTTP_HOST] base_url = ::Rails.application.routes.url_helpers.dial_url host: host profile_out_url = URI.encode_www_form_component base_url + "dial/profile?uuid=#{uuid}" "View profile" end def formatted_rails_version "Rails version: #{::Rails::VERSION::STRING}" end def formatted_rack_version "Rack version: #{::Rack.release}" end def formatted_ruby_version "Ruby version: #{::RUBY_DESCRIPTION}" end def formatted_server_timing server_timing if server_timing.any? server_timing # TODO: Nested sorting .sort_by { |_, timing| -timing } .map { |event, timing| "#{event}: #{timing}" }.join else "NA" end end def formatted_ruby_vm_stat ruby_vm_stat ruby_vm_stat.map { |key, value| "#{key}: #{value}" }.join end def formatted_gc_stat gc_stat gc_stat.map { |key, value| "#{key}: #{value}" }.join end def formatted_gc_stat_heap gc_stat_heap gc_stat_heap.map do |slot, stats| <<~HTML
Heap slot #{slot}
#{gc_stat_heap[slot].map { |key, value| "#{key}: #{value}" }.join}
HTML end.join end end end end