# 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