rails_mini_profiler 0.4.0 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +48 -34
- data/app/adapters/rails_mini_profiler/database_adapter.rb +16 -0
- data/app/controllers/rails_mini_profiler/application_controller.rb +9 -15
- data/app/controllers/rails_mini_profiler/profiled_requests_controller.rb +44 -14
- data/app/helpers/rails_mini_profiler/application_helper.rb +1 -1
- data/app/helpers/rails_mini_profiler/profiled_requests_helper.rb +10 -0
- data/app/javascript/images/check.svg +3 -0
- data/app/javascript/images/chevron.svg +3 -0
- data/app/javascript/images/copy.svg +6 -0
- data/app/javascript/images/filter.svg +1 -0
- data/app/javascript/images/logo_variant.svg +2 -2
- data/app/javascript/images/search.svg +4 -5
- data/app/javascript/js/checklist_controller.js +48 -0
- data/app/javascript/js/clipboard_controller.js +53 -0
- data/app/javascript/js/enable_controller.js +23 -0
- data/app/javascript/js/filter_controller.js +48 -0
- data/app/javascript/js/search_controller.js +18 -0
- data/app/javascript/js/select_controller.js +52 -0
- data/app/javascript/packs/rails-mini-profiler.js +69 -53
- data/app/javascript/stylesheets/components/buttons.scss +59 -0
- data/app/javascript/stylesheets/components/dropdown.scss +103 -0
- data/app/javascript/stylesheets/components/input.scss +10 -0
- data/app/javascript/stylesheets/{navbar.scss → components/navbar.scss} +7 -13
- data/app/javascript/stylesheets/components/page_header.scss +7 -0
- data/app/javascript/stylesheets/components/pagination.scss +14 -13
- data/app/javascript/stylesheets/components/placeholder.scss +36 -0
- data/app/javascript/stylesheets/components/profiled_request_table.scss +55 -0
- data/app/javascript/stylesheets/components/trace.scss +93 -0
- data/app/javascript/stylesheets/flamegraph.scss +3 -2
- data/app/javascript/stylesheets/flashes.scss +3 -5
- data/app/javascript/stylesheets/profiled_requests.scss +12 -161
- data/app/javascript/stylesheets/rails-mini-profiler.scss +81 -66
- data/app/javascript/stylesheets/traces.scss +52 -56
- data/app/models/rails_mini_profiler/profiled_request.rb +1 -1
- data/app/models/rails_mini_profiler/trace.rb +0 -15
- data/app/presenters/rails_mini_profiler/controller_trace_presenter.rb +10 -2
- data/app/presenters/rails_mini_profiler/instantiation_trace_presenter.rb +15 -3
- data/app/presenters/rails_mini_profiler/profiled_request_presenter.rb +10 -17
- data/app/presenters/rails_mini_profiler/render_partial_trace_presenter.rb +6 -2
- data/app/presenters/rails_mini_profiler/render_template_trace_presenter.rb +6 -2
- data/app/presenters/rails_mini_profiler/sequel_trace_presenter.rb +16 -4
- data/app/presenters/rails_mini_profiler/trace_presenter.rb +10 -8
- data/app/search/rails_mini_profiler/base_search.rb +67 -0
- data/app/search/rails_mini_profiler/profiled_request_search.rb +33 -0
- data/app/search/rails_mini_profiler/trace_search.rb +27 -0
- data/app/views/rails_mini_profiler/badge.html.erb +2 -2
- data/app/views/rails_mini_profiler/profiled_requests/index.html.erb +8 -58
- data/app/views/rails_mini_profiler/profiled_requests/shared/header/_header.erb +19 -0
- data/app/views/rails_mini_profiler/profiled_requests/shared/table/_placeholder.erb +12 -0
- data/app/views/rails_mini_profiler/profiled_requests/shared/table/_table.erb +14 -0
- data/app/views/rails_mini_profiler/profiled_requests/shared/table/_table_head.erb +125 -0
- data/app/views/rails_mini_profiler/profiled_requests/shared/table/_table_row.erb +21 -0
- data/app/views/rails_mini_profiler/profiled_requests/{shared → show}/_trace.html.erb +24 -7
- data/app/views/rails_mini_profiler/profiled_requests/show/_trace_list.erb +12 -0
- data/app/views/rails_mini_profiler/profiled_requests/show/_trace_list_header.erb +87 -0
- data/app/views/rails_mini_profiler/profiled_requests/show/_trace_list_placeholder.erb +12 -0
- data/app/views/rails_mini_profiler/profiled_requests/show.html.erb +5 -18
- data/app/views/rails_mini_profiler/shared/_head.erb +1 -1
- data/lib/generators/rails_mini_profiler/templates/rails_mini_profiler.rb.erb +2 -0
- data/lib/rails_mini_profiler/badge.rb +21 -12
- data/lib/rails_mini_profiler/configuration/user_interface.rb +31 -1
- data/lib/rails_mini_profiler/configuration.rb +7 -0
- data/lib/rails_mini_profiler/engine.rb +12 -8
- data/lib/rails_mini_profiler/flamegraph_guard.rb +10 -6
- data/lib/rails_mini_profiler/guard.rb +19 -1
- data/lib/rails_mini_profiler/middleware.rb +12 -10
- data/lib/rails_mini_profiler/request_context.rb +22 -18
- data/lib/rails_mini_profiler/request_wrapper.rb +12 -55
- data/lib/rails_mini_profiler/response_wrapper.rb +21 -17
- data/lib/rails_mini_profiler/tracers/controller_tracer.rb +29 -0
- data/lib/rails_mini_profiler/tracers/instantiation_tracer.rb +17 -0
- data/lib/rails_mini_profiler/tracers/null_trace.rb +7 -0
- data/lib/rails_mini_profiler/tracers/registry.rb +76 -0
- data/lib/rails_mini_profiler/tracers/rmp_tracer.rb +17 -0
- data/lib/rails_mini_profiler/tracers/sequel_tracer.rb +51 -0
- data/lib/rails_mini_profiler/tracers/sequel_tracker.rb +40 -0
- data/lib/rails_mini_profiler/tracers/subscriptions.rb +28 -0
- data/lib/rails_mini_profiler/{models → tracers}/trace.rb +11 -3
- data/lib/rails_mini_profiler/tracers/trace_factory.rb +27 -0
- data/lib/rails_mini_profiler/tracers/tracer.rb +45 -0
- data/lib/rails_mini_profiler/tracers/view_tracer.rb +29 -0
- data/lib/rails_mini_profiler/tracers.rb +12 -83
- data/lib/rails_mini_profiler/user.rb +1 -1
- data/lib/rails_mini_profiler/version.rb +1 -1
- data/lib/rails_mini_profiler.rb +4 -8
- data/vendor/assets/images/check.svg +3 -0
- data/vendor/assets/images/chevron.svg +3 -0
- data/vendor/assets/images/copy.svg +6 -0
- data/vendor/assets/images/filter.svg +1 -0
- data/vendor/assets/images/logo_variant.svg +2 -2
- data/vendor/assets/images/search.svg +4 -5
- data/vendor/assets/javascripts/rails-mini-profiler.css +1 -1
- data/vendor/assets/javascripts/rails-mini-profiler.js +1 -1
- metadata +58 -13
- data/app/models/rails_mini_profiler/controller_trace.rb +0 -37
- data/app/models/rails_mini_profiler/instantiation_trace.rb +0 -37
- data/app/models/rails_mini_profiler/render_partial_trace.rb +0 -37
- data/app/models/rails_mini_profiler/render_template_trace.rb +0 -37
- data/app/models/rails_mini_profiler/rmp_trace.rb +0 -35
- data/app/models/rails_mini_profiler/sequel_trace.rb +0 -37
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsMiniProfiler
|
4
|
+
module Tracers
|
5
|
+
class ControllerTracer < Tracer
|
6
|
+
class << self
|
7
|
+
def subscribes_to
|
8
|
+
'process_action.action_controller'
|
9
|
+
end
|
10
|
+
|
11
|
+
def build_from(event)
|
12
|
+
new(event).trace
|
13
|
+
end
|
14
|
+
|
15
|
+
def presents
|
16
|
+
ControllerTracePresenter
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def trace
|
21
|
+
@event[:payload] = @event[:payload]
|
22
|
+
.slice(:view_runtime, :db_runtime)
|
23
|
+
.reject { |_k, v| v.blank? }
|
24
|
+
.transform_values { |value| value&.round(2) }
|
25
|
+
super
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsMiniProfiler
|
4
|
+
module Tracers
|
5
|
+
class InstantiationTracer < Tracer
|
6
|
+
class << self
|
7
|
+
def subscribes_to
|
8
|
+
'instantiation.active_record'
|
9
|
+
end
|
10
|
+
|
11
|
+
def presents
|
12
|
+
InstantiationTracePresenter
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsMiniProfiler
|
4
|
+
module Tracers
|
5
|
+
# This serves as a central store for tracers. Based on the configuration, indidual tracers are registered and are
|
6
|
+
# then available to subscribe to events or render traced events.
|
7
|
+
#
|
8
|
+
# @api private
|
9
|
+
class Registry
|
10
|
+
class << self
|
11
|
+
def setup!(config)
|
12
|
+
new(config)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(config)
|
17
|
+
@config = config
|
18
|
+
@config.tracers.each { |tracer| add(tracer) }
|
19
|
+
end
|
20
|
+
|
21
|
+
# Tracers that are available in the application, indexed by events they subscribe to.
|
22
|
+
#
|
23
|
+
# @return [Hash] a hash where keys are event names and values are the corresponding tracers
|
24
|
+
def tracers
|
25
|
+
@tracers ||=
|
26
|
+
tracer_map
|
27
|
+
.values
|
28
|
+
.each_with_object({}) do |tracer, obj|
|
29
|
+
subscriptions = wrap(tracer.subscribes_to)
|
30
|
+
subscriptions.each { |subscription| obj[subscription] = tracer }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Presenters that are available in the application, indexed by events they should present.
|
35
|
+
#
|
36
|
+
# @return [Hash] a hash where keys are event names and values are the corresponding presenters
|
37
|
+
def presenters
|
38
|
+
@presenters ||=
|
39
|
+
tracer_map
|
40
|
+
.values
|
41
|
+
.each_with_object({}) do |tracer, obj|
|
42
|
+
presenters = tracer.presents
|
43
|
+
if presenters.is_a?(Hash)
|
44
|
+
obj.merge!(presenters)
|
45
|
+
else
|
46
|
+
obj[tracer.subscribes_to] = presenters
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def tracer_map
|
54
|
+
@tracer_map ||= {}
|
55
|
+
end
|
56
|
+
|
57
|
+
def add(tracer)
|
58
|
+
tracer = tracer.to_sym
|
59
|
+
tracer = "#{tracer.to_s.camelize}Tracer"
|
60
|
+
constant = "RailsMiniProfiler::Tracers::#{tracer}".safe_constantize
|
61
|
+
|
62
|
+
tracer_map[tracer] = constant if constant
|
63
|
+
end
|
64
|
+
|
65
|
+
def wrap(object)
|
66
|
+
if object.nil?
|
67
|
+
[]
|
68
|
+
elsif object.respond_to?(:to_ary)
|
69
|
+
object.to_ary || [object]
|
70
|
+
else
|
71
|
+
[object]
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsMiniProfiler
|
4
|
+
module Tracers
|
5
|
+
class RmpTracer < Tracer
|
6
|
+
class << self
|
7
|
+
def subscribes_to
|
8
|
+
'rails_mini_profiler.total_time'
|
9
|
+
end
|
10
|
+
|
11
|
+
def presents
|
12
|
+
RmpTracePresenter
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsMiniProfiler
|
4
|
+
module Tracers
|
5
|
+
class SequelTracer < Tracer
|
6
|
+
class << self
|
7
|
+
def subscribes_to
|
8
|
+
'sql.active_record'
|
9
|
+
end
|
10
|
+
|
11
|
+
def build_from(event)
|
12
|
+
new(event).trace
|
13
|
+
end
|
14
|
+
|
15
|
+
def presents
|
16
|
+
SequelTracePresenter
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def trace
|
21
|
+
return NullTrace.new if ignore?
|
22
|
+
|
23
|
+
payload = @event[:payload].slice(:name, :sql, :binds, :type_casted_binds)
|
24
|
+
typecasted_binds = payload[:type_casted_binds]
|
25
|
+
# Sometimes, typecasted binds are a proc. Not sure why. In those instances, we extract the typecasted
|
26
|
+
# values from the proc by executing call.
|
27
|
+
typecasted_binds = typecasted_binds.call if typecasted_binds.respond_to?(:call)
|
28
|
+
payload[:binds] = transform_binds(payload[:binds], typecasted_binds)
|
29
|
+
payload.delete(:type_casted_binds)
|
30
|
+
payload.reject { |_k, v| v.blank? }
|
31
|
+
@event[:payload] = payload
|
32
|
+
super
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def transform_binds(binds, type_casted_binds)
|
38
|
+
binds.each_with_object([]).with_index do |(binding, object), i|
|
39
|
+
name = binding.name
|
40
|
+
value = type_casted_binds[i]
|
41
|
+
object << { name: name, value: value }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def ignore?
|
46
|
+
payload = @event[:payload]
|
47
|
+
!SqlTracker.new(name: payload[:name], query: payload[:sql]).track?
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsMiniProfiler
|
4
|
+
module Tracers
|
5
|
+
# Utility to clean up and simplify SQL Notification events.
|
6
|
+
#
|
7
|
+
# @api private
|
8
|
+
class SqlTracker
|
9
|
+
TRACKED_SQL_COMMANDS = %w[SELECT INSERT UPDATE DELETE].freeze
|
10
|
+
UNTRACKED_NAMES = %w[SCHEMA].freeze
|
11
|
+
UNTRACKED_TABLES = %w[
|
12
|
+
SCHEMA_MIGRATIONS
|
13
|
+
SQLITE_MASTER
|
14
|
+
ACTIVE_MONITORING_METRICS
|
15
|
+
SQLITE_TEMP_MASTER
|
16
|
+
SQLITE_VERSION
|
17
|
+
AR_INTERNAL_METADATA
|
18
|
+
].freeze
|
19
|
+
|
20
|
+
def initialize(query:, name:)
|
21
|
+
@query = query.to_s.upcase
|
22
|
+
@name = name.to_s.upcase
|
23
|
+
end
|
24
|
+
|
25
|
+
def track?
|
26
|
+
query.start_with?(*TRACKED_SQL_COMMANDS) &&
|
27
|
+
!name.start_with?(*UNTRACKED_NAMES) &&
|
28
|
+
!untracked_tables?
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
attr_reader :query, :name
|
34
|
+
|
35
|
+
def untracked_tables?
|
36
|
+
UNTRACKED_TABLES.any? { |table| query.include?(table) }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsMiniProfiler
|
4
|
+
module Tracers
|
5
|
+
# Subscribe to application events. This is used during engine startup.
|
6
|
+
# @api private
|
7
|
+
class Subscriptions
|
8
|
+
class << self
|
9
|
+
# Subscribe to each individual active support event using a callback.
|
10
|
+
def setup!(subscriptions, &callback)
|
11
|
+
subscriptions.each do |event|
|
12
|
+
subscribe(event, &callback)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def subscribe(*subscriptions, &callback)
|
19
|
+
subscriptions.each do |subscription|
|
20
|
+
ActiveSupport::Notifications.subscribe(subscription) do |event|
|
21
|
+
callback.call(event)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module RailsMiniProfiler
|
4
|
-
module
|
4
|
+
module Tracers
|
5
5
|
# A simplified representation of a trace.
|
6
6
|
#
|
7
7
|
# Is transformed into [RailsMiniProfiler::Trace] when recording has finished.
|
@@ -23,15 +23,23 @@ module RailsMiniProfiler
|
|
23
23
|
# @!attribute backtrace
|
24
24
|
# @return [String] the line where this trace was recorded
|
25
25
|
# @!attribute allocations
|
26
|
-
# @return [Integer] the number of
|
26
|
+
# @return [Integer] the number of allocations
|
27
27
|
# @!attribute created_at
|
28
28
|
# @return [DateTime] the creation date
|
29
29
|
# @!attribute updated_at
|
30
30
|
# @return [DateTime] the last updated date
|
31
31
|
#
|
32
32
|
# @api private
|
33
|
-
class Trace < BaseModel
|
33
|
+
class Trace < RailsMiniProfiler::Models::BaseModel
|
34
34
|
attr_accessor :id, :name, :start, :finish, :duration, :payload, :backtrace, :allocations, :created_at, :updated_at
|
35
|
+
|
36
|
+
def ignore?
|
37
|
+
false
|
38
|
+
end
|
39
|
+
|
40
|
+
def transform!
|
41
|
+
self
|
42
|
+
end
|
35
43
|
end
|
36
44
|
end
|
37
45
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsMiniProfiler
|
4
|
+
module Tracers
|
5
|
+
# Creates traces based on the tracers registered with the application
|
6
|
+
#
|
7
|
+
# For example, an event with name 'sequel' will result in the lookup of a tracers that responds to that particular
|
8
|
+
# event. The tracer itself will then build the trace from the event.
|
9
|
+
#
|
10
|
+
# @api private
|
11
|
+
class TraceFactory
|
12
|
+
def initialize(registry)
|
13
|
+
@tracers = registry.tracers
|
14
|
+
end
|
15
|
+
|
16
|
+
# Create a new trace from an event
|
17
|
+
#
|
18
|
+
# @param event [ActiveSupport::Notifications::Event] an event from the application
|
19
|
+
#
|
20
|
+
# @return [Trace] a processed trace
|
21
|
+
def create(event)
|
22
|
+
tracer = @tracers[event.name] || Tracer
|
23
|
+
tracer.build_from(event)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsMiniProfiler
|
4
|
+
module Tracers
|
5
|
+
class Tracer
|
6
|
+
class << self
|
7
|
+
def subscribes_to
|
8
|
+
[]
|
9
|
+
end
|
10
|
+
|
11
|
+
def presents
|
12
|
+
TracePresenter
|
13
|
+
end
|
14
|
+
|
15
|
+
def build_from(event)
|
16
|
+
new(event).trace
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize(event)
|
21
|
+
@event = event_data(event)
|
22
|
+
end
|
23
|
+
|
24
|
+
def trace
|
25
|
+
Trace.new(**@event)
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def event_data(event)
|
31
|
+
start = (event.time.to_f * 100_000).to_i
|
32
|
+
finish = (event.end.to_f * 100_000).to_i
|
33
|
+
{
|
34
|
+
name: event.name,
|
35
|
+
start: start,
|
36
|
+
finish: finish,
|
37
|
+
duration: finish - start,
|
38
|
+
allocations: event.allocations,
|
39
|
+
backtrace: Rails.backtrace_cleaner.clean(caller),
|
40
|
+
payload: event.payload
|
41
|
+
}
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsMiniProfiler
|
4
|
+
module Tracers
|
5
|
+
class ViewTracer < Tracer
|
6
|
+
class << self
|
7
|
+
def subscribes_to
|
8
|
+
%w[render_template.action_view render_partial.action_view]
|
9
|
+
end
|
10
|
+
|
11
|
+
def build_from(event)
|
12
|
+
new(event).trace
|
13
|
+
end
|
14
|
+
|
15
|
+
def presents
|
16
|
+
{
|
17
|
+
'render_template.action_view' => RenderTemplateTracePresenter,
|
18
|
+
'render_partial.action_view' => RenderPartialTracePresenter
|
19
|
+
}
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def trace
|
24
|
+
@event[:payload].slice!(:identifier, :count)
|
25
|
+
super
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -1,85 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
def setup!(&callback)
|
16
|
-
DEFAULT_SUBSCRIPTIONS.each do |event|
|
17
|
-
subscribe(event, &callback)
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
def build_trace(event)
|
22
|
-
start = (event.time.to_f * 100_000).to_i
|
23
|
-
finish = (event.end.to_f * 100_000).to_i
|
24
|
-
Models::Trace.new(
|
25
|
-
name: event.name,
|
26
|
-
start: start,
|
27
|
-
finish: finish,
|
28
|
-
duration: finish - start,
|
29
|
-
allocations: event.allocations,
|
30
|
-
backtrace: Rails.backtrace_cleaner.clean(caller),
|
31
|
-
payload: format_payload(event)
|
32
|
-
)
|
33
|
-
end
|
34
|
-
|
35
|
-
private
|
36
|
-
|
37
|
-
def subscribe(*subscriptions, &callback)
|
38
|
-
subscriptions.each do |subscription|
|
39
|
-
ActiveSupport::Notifications.subscribe(subscription) do |event|
|
40
|
-
callback.call(event)
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
def format_payload(event)
|
46
|
-
case event.name
|
47
|
-
when 'sql.active_record'
|
48
|
-
transform_sql_event(event)
|
49
|
-
when 'render_template.action_view', 'render_partial.action_view'
|
50
|
-
event.payload.slice(:identifier, :count)
|
51
|
-
when 'process_action.action_controller'
|
52
|
-
transform_controller_event(event)
|
53
|
-
else
|
54
|
-
event.payload
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
def transform_sql_event(event)
|
59
|
-
payload = event.payload.slice(:name, :sql, :binds, :type_casted_binds)
|
60
|
-
typecasted_binds = payload[:type_casted_binds]
|
61
|
-
# Sometimes, typecasted binds are a proc. Not sure why. In those instances, we extract the typecasted
|
62
|
-
# values from the proc by executing call.
|
63
|
-
typecasted_binds = typecasted_binds.call if typecasted_binds.respond_to?(:call)
|
64
|
-
payload[:binds] = transform_binds(payload[:binds], typecasted_binds)
|
65
|
-
payload.delete(:type_casted_binds)
|
66
|
-
payload.reject { |_k, v| v.blank? }
|
67
|
-
end
|
68
|
-
|
69
|
-
def transform_binds(binds, type_casted_binds)
|
70
|
-
binds.each_with_object([]).with_index do |(binding, object), i|
|
71
|
-
name = binding.name
|
72
|
-
value = type_casted_binds[i]
|
73
|
-
object << { name: name, value: value }
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
def transform_controller_event(event)
|
78
|
-
payload = event.payload
|
79
|
-
.slice(:view_runtime, :db_runtime)
|
80
|
-
.transform_values { |value| value&.round(2) }
|
81
|
-
payload.reject { |_k, v| v.blank? }
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|
85
|
-
end
|
3
|
+
require 'rails_mini_profiler/tracers/registry'
|
4
|
+
require 'rails_mini_profiler/tracers/subscriptions'
|
5
|
+
require 'rails_mini_profiler/tracers/trace'
|
6
|
+
require 'rails_mini_profiler/tracers/tracer'
|
7
|
+
require 'rails_mini_profiler/tracers/controller_tracer'
|
8
|
+
require 'rails_mini_profiler/tracers/instantiation_tracer'
|
9
|
+
require 'rails_mini_profiler/tracers/sequel_tracker'
|
10
|
+
require 'rails_mini_profiler/tracers/sequel_tracer'
|
11
|
+
require 'rails_mini_profiler/tracers/view_tracer'
|
12
|
+
require 'rails_mini_profiler/tracers/rmp_tracer'
|
13
|
+
require 'rails_mini_profiler/tracers/null_trace'
|
14
|
+
require 'rails_mini_profiler/tracers/trace_factory'
|
data/lib/rails_mini_profiler.rb
CHANGED
@@ -7,23 +7,19 @@ require 'pagy'
|
|
7
7
|
require 'rails_mini_profiler/version'
|
8
8
|
require 'rails_mini_profiler/engine'
|
9
9
|
|
10
|
-
require 'rails_mini_profiler/user'
|
11
|
-
require 'rails_mini_profiler/request_context'
|
12
|
-
|
13
10
|
require 'rails_mini_profiler/models/base_model'
|
14
|
-
require 'rails_mini_profiler/
|
11
|
+
require 'rails_mini_profiler/tracers'
|
12
|
+
require 'rails_mini_profiler/configuration'
|
15
13
|
|
14
|
+
require 'rails_mini_profiler/user'
|
15
|
+
require 'rails_mini_profiler/request_context'
|
16
16
|
require 'rails_mini_profiler/logger'
|
17
|
-
require 'rails_mini_profiler/configuration'
|
18
|
-
require 'rails_mini_profiler/configuration/storage'
|
19
|
-
require 'rails_mini_profiler/configuration/user_interface'
|
20
17
|
require 'rails_mini_profiler/request_wrapper'
|
21
18
|
require 'rails_mini_profiler/response_wrapper'
|
22
19
|
require 'rails_mini_profiler/guard'
|
23
20
|
require 'rails_mini_profiler/flamegraph_guard'
|
24
21
|
require 'rails_mini_profiler/redirect'
|
25
22
|
require 'rails_mini_profiler/badge'
|
26
|
-
require 'rails_mini_profiler/tracers'
|
27
23
|
require 'rails_mini_profiler/middleware'
|
28
24
|
|
29
25
|
# Main namespace for Rails Mini Profiler
|
@@ -0,0 +1,6 @@
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg"
|
2
|
+
viewBox="0 0 488.3 488.3" style="enable-background:new 0 0 488.3 488.3" xml:space="preserve">
|
3
|
+
<path fill="currentColor"
|
4
|
+
d="M314.25 85.4h-227c-21.3 0-38.6 17.3-38.6 38.6v325.7c0 21.3 17.3 38.6 38.6 38.6h227c21.3 0 38.6-17.3 38.6-38.6V124c-.1-21.3-17.4-38.6-38.6-38.6zm11.5 364.2c0 6.4-5.2 11.6-11.6 11.6h-227c-6.4 0-11.6-5.2-11.6-11.6V124c0-6.4 5.2-11.6 11.6-11.6h227c6.4 0 11.6 5.2 11.6 11.6v325.6z"/>
|
5
|
+
<path fill="currentColor"
|
6
|
+
d="M401.05 0h-227c-21.3 0-38.6 17.3-38.6 38.6 0 7.5 6 13.5 13.5 13.5s13.5-6 13.5-13.5c0-6.4 5.2-11.6 11.6-11.6h227c6.4 0 11.6 5.2 11.6 11.6v325.7c0 6.4-5.2 11.6-11.6 11.6-7.5 0-13.5 6-13.5 13.5s6 13.5 13.5 13.5c21.3 0 38.6-17.3 38.6-38.6V38.6c0-21.3-17.3-38.6-38.6-38.6z"/></svg>
|
@@ -0,0 +1 @@
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-filter"><polygon points="22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3"></polygon></svg>
|
@@ -1,5 +1,5 @@
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1070" height="1070" viewBox="0 0 1070 1070" version="1.1">
|
2
|
-
<style
|
1
|
+
<svg id="logo-variant" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1070" height="1070" viewBox="0 0 1070 1070" version="1.1">
|
2
|
+
<style>#logo-variant path{fill:none;stroke-width:64;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4}</style>
|
3
3
|
<defs>
|
4
4
|
<linearGradient id="lightShadow">
|
5
5
|
<stop offset="0" style="stop-color:#c22121;stop-opacity:1"/>
|
@@ -1,10 +1,9 @@
|
|
1
1
|
<?xml version="1.0" encoding="UTF-8"?>
|
2
2
|
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
3
|
-
<
|
4
|
-
|
5
|
-
<g id="Search" transform="translate(2.000000, 2.000000)" fill="#000000" fill-rule="nonzero">
|
3
|
+
<g id="Iconly/Bulk/Search" stroke="none" stroke-width="1" fill="currentColor" fill-rule="evenodd">
|
4
|
+
<g id="Search" transform="translate(2.000000, 2.000000)" fill="currentColor" fill-rule="nonzero">
|
6
5
|
<ellipse id="Ellipse_746" cx="8.59921927" cy="8.65324385" rx="8.59921927" ry="8.65324385"></ellipse>
|
7
|
-
<path d="M18.674623,19.9552573 C18.3405833,19.9444414 18.0229443,19.8069986 17.7853553,19.5704698 L15.7489321,17.1901566 C15.3123366,16.7908936 15.2766365,16.1123232 15.668898,15.6689038 L15.668898,15.6689038 C15.8525005,15.4831065 16.1021409,15.3786387 16.3625268,15.3786387 C16.6229128,15.3786387 16.8725531,15.4831065 17.0561557,15.6689038 L19.6172468,17.7181208 C19.9861582,18.0957076 20.0999999,18.656254 19.9078887,19.1492153 C19.7157774,19.6421767 19.2536179,19.9754211 18.7279791,20 L18.674623,19.9552573 Z" id="Path_34202" opacity="0.400000006"></path>
|
6
|
+
<path fill="currentColor" d="M18.674623,19.9552573 C18.3405833,19.9444414 18.0229443,19.8069986 17.7853553,19.5704698 L15.7489321,17.1901566 C15.3123366,16.7908936 15.2766365,16.1123232 15.668898,15.6689038 L15.668898,15.6689038 C15.8525005,15.4831065 16.1021409,15.3786387 16.3625268,15.3786387 C16.6229128,15.3786387 16.8725531,15.4831065 17.0561557,15.6689038 L19.6172468,17.7181208 C19.9861582,18.0957076 20.0999999,18.656254 19.9078887,19.1492153 C19.7157774,19.6421767 19.2536179,19.9754211 18.7279791,20 L18.674623,19.9552573 Z" id="Path_34202" opacity="0.400000006"></path>
|
8
7
|
</g>
|
9
8
|
</g>
|
10
|
-
</svg>
|
9
|
+
</svg>
|
@@ -1 +1 @@
|
|
1
|
-
@import url("https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600;700&display=swap");.flash{margin-top:1rem;padding:1rem;border-radius:5px}.flash-error{color:#fff;background:var(--red-500)}.flash-notice{color:#fff;background:var(--green-400)}#wrapper{height:100vh;width:100%}#speedscope-iframe{width:100%;height:100%;border:none}.header{margin:0;padding:1.5rem 0;display:flex;justify-content:center;box-shadow:0 0 20px 0 rgba(0,0,0,.2);background:var(--primary)}.header a{color:#fff;text-decoration:none}.nav{width:var(--main-width);justify-content:space-between}.home,.nav{display:flex}.home{align-items:center;text-decoration:none}.home-logo{width:64px;height:64px}.home-title{padding:0 0 0 1rem;margin:0}.header-links{margin:0;padding:0;list-style:none;display:flex;align-items:center;font-weight:700}main{width:100%;display:flex;justify-content:center}.main-section{width:var(--main-width)}.placeholder{display:flex;flex-direction:column;justify-content:center;align-items:center;width:100%;height:450px}.placeholder-image{height:30%;width:30%;-webkit-filter:grayscale(1) brightness(2.5)}.placeholder-text{text-align:center;color:var(--grey-400)}.profiled-requests-actions{display:flex;padding:1rem 0;justify-content:space-between;align-items:center}.search-field{padding:.5rem;border-radius:.25em;border:1px solid var(--grey-400);color:var(--grey-700)}.clear-action button{background:var(--red-500);color:#fff}.table{width:100%;border-collapse:collapse;border-radius:5px;border:1px hidden var(--border-color);box-shadow:0 0 0 1px var(--border-color)}.table thead tr{color:var(--grey-400)}.table thead th{font-weight:400}.table tr{border:1px solid var(--border-color);color:var(--grey-700);cursor:pointer}.table tr:nth-child(2n){background:var(--grey-50)}.table tbody tr:hover{background:var(--grey-100)}.table td,.table th{padding:.5rem 1rem}.request-path{max-width:300px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.request-buttons{z-index:1;text-align:right}.request-buttons a{text-decoration:none;padding:0 .25rem;color:var(--grey-700)}.request-buttons a:active,.request-buttons a:focus-visible,.request-buttons a:hover{color:var(--blue-500)}.request-buttons a.link-disabled{cursor:default;pointer-events:none;text-decoration:none;color:var(--grey-200)}.request-details-data{display:flex;margin:0;padding:0}.data-item{display:flex;flex-direction:column;align-items:flex-start;list-style:none;margin-right:3rem;padding:0}.data-item small{color:var(--grey-400)}.data-item span{margin:.25rem 0}[class*=request-method-get],[class*=request-status-2]{color:#fff;background:var(--green-400)!important}[class*=request-method-patch],[class*=request-method-put],[class*=request-status-4]{color:#fff;background:var(--yellow-400)!important}[class*=request-method-delete],[class*=request-status-5]{color:#fff;background:var(--red-500)!important}.request-details-actions{margin:0;padding:2em 0;display:flex;align-items:center;justify-content:space-between}.flamegraph-button button{background:var(--grey-200)}.trace-list{margin:0;padding:0}.trace{list-style:none;display:flex;padding:.25em 0;align-items:center;justify-content:flex-start}.trace:nth-child(odd){background:var(--grey-100)}.trace .trace-bar{position:relative;margin:0;height:16px;padding:0;cursor:pointer;background:linear-gradient(to top right,var(--grey-500),var(--grey-400))}.instantiation-trace .trace-bar{background:linear-gradient(to top right,var(--green-400),var(--green-300))}.sequel-trace .trace-bar{background:linear-gradient(to top right,var(--green-500),var(--green-400))}.controller-trace .trace-bar{background:linear-gradient(to top right,var(--yellow-500),var(--yellow-400))}.render-partial-trace .trace-bar,.render-template-trace .trace-bar{background:linear-gradient(to top right,var(--blue-500),var(--blue-400))}.trace-name{margin:0;box-sizing:border-box;padding:0 .5em;overflow:hidden;font-size:14px;text-align:right;text-overflow:ellipsis;color:var(--grey-400)}.trace-payload{margin:0}.sequel-trace-query{padding:1em;background:var(--grey-100)}.sequel-trace-binds,.sequel-trace-query{overflow:auto;max-height:100px;white-space:pre-wrap}.sequel-trace-binds{margin:0 0 1em;padding:.5em 1em;font-size:12px;background:var(--grey-50)}.trace-table{margin-top:1em;width:100%;border-collapse:collapse;border:1px hidden var(--border-color)}.pagy-nav{display:flex;align-items:center;justify-content:center;padding:1em 0}.pagy-nav>.page.active,.pagy-nav>.page.disabled,.pagy-nav>.page>a{padding:.5rem 1rem;color:var(--grey-900);background:#fff;border:1px solid var(--border-color);border-right:none;text-decoration:none;cursor:pointer}.pagy-nav>.page.active:hover,.pagy-nav>.page.disabled:hover,.pagy-nav>.page>a:hover{background:var(--grey-100)}.pagy-nav>.page.prev.disabled,.pagy-nav>.page.prev a{border-radius:5px 0 0 5px}.pagy-nav>.page.next.disabled,.pagy-nav>.page.next a{border-radius:0 5px 5px 0;border-right:1px solid var(--border-color)}.pagy-nav>.page.active{cursor:default;border-color:var(--red-500);color:#fff}.pagy-nav>.page.active,.pagy-nav>.page.active:hover{background:var(--red-500)}.pagy-nav>.page.disabled{cursor:default;color:var(--grey-500)}.pagy-nav>.page.disabled:hover{background:#fff}@font-face{font-family:Open Sans;font-weight:400;font-style:normal}@font-face{font-family:Open Sans;font-weight:600;font-style:normal}@font-face{font-family:Open Sans;font-weight:700;font-style:normal}html{--grey-50:#f9fafb;--grey-100:#f3f4f6;--grey-200:#e5e7eb;--grey-400:#9ca3af;--grey-500:#6b7280;--grey-700:#374151;--grey-900:#111827;--red-400:#f87171;--red-500:#ef4444;--red-600:#dc2626;--yellow-400:#fbbf24;--yellow-500:#f59e0b;--green-300:#6ee7b7;--green-400:#34d399;--green-500:#10b981;--blue-400:#60a5fa;--blue-500:#3b82f6;--main-width:1056px;--primary:var(--red-600);--border-color:var(--grey-200);--text-color:var(--grey-900);font-family:Open Sans,monospace}body,html{width:100%;height:100%;margin:0;padding:0}body{color:var(--text-color)}h1{padding:2rem 0 1rem}button{border:none;border-radius:.25rem;padding:.5em;text-align:center;text-decoration:none;display:inline-block;font-size:1rem;cursor:pointer}button:hover{box-shadow:0 .25rem .25rem 0 var(--grey-50)}.text-left{text-align:left}.text-right{text-align:right}.pill{margin:.2rem 0;padding:.1rem .4rem;border-radius:5px;font-size:.9rem;font-weight:600;letter-spacing:.1rem;background:var(--grey-200)}.popover{display:flex;flex-direction:column;width:600px;padding:1em;color:#000}.popover-header{display:flex;padding-bottom:1em;flex-direction:row;justify-content:space-between;align-items:center}.popover-description{margin:0;padding:0}.popover-close{background:transparent;padding:0;font-weight:700;font-size:20px;color:var(--grey-400)}.popover-body{padding:1em 0}.popover-footer{border-top:1px solid var(--grey-50)}.tippy-box[data-theme~=rmp]{background:#fff;box-shadow:8px 0 30px 4px rgba(0,0,0,.2),8px 4px 10px 0 rgba(0,0,0,.05);border-radius:3px;transition:all .2s cubic-bezier(.19,1,.22,1)}.tippy-box[data-theme~=rmp][data-placement^=top]>.tippy-arrow:before{border-top-color:#fff}.tippy-box[data-theme~=rmp][data-placement^=bottom]>.tippy-arrow:before{border-bottom-color:#fff}.tippy-box[data-theme~=rmp][data-placement^=left]>.tippy-arrow:before{border-left-color:#fff}.tippy-box[data-theme~=rmp][data-placement^=right]>.tippy-arrow:before{border-right-color:#fff}
|
1
|
+
@import url("https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600;700&display=swap");.flash{border-radius:5px;margin-top:1rem;padding:1rem}.flash-error{background:var(--red-500);color:#fff}.flash-notice{background:var(--green-400);color:#fff}#wrapper{height:100vh;width:100%}#speedscope-iframe{border:none;height:100%;width:100%}.table{border:1px hidden var(--border-color);border-collapse:collapse;border-radius:5px;box-shadow:0 0 0 1px var(--border-color);table-layout:fixed;width:100%}.table td,.table th{padding:.5rem 1rem}.table thead tr{color:var(--grey-500)}.table tr:nth-child(2n){background:var(--grey-50)}.table thead th{font-weight:400}.table-filter-icon{margin-left:.5rem;width:14px}.table tbody tr:not(.no-row){border:1px solid var(--border-color);color:var(--grey-700);cursor:pointer}.table tbody tr:not(.no-row):hover{background:var(--grey-100)}.request-checkbox{height:1rem;width:1rem;z-index:1}.request-path{max-width:280px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.search-field{border:1px solid var(--grey-400);border-radius:5px;box-sizing:border-box;padding:.5rem}.profiled-requests-header{align-items:center;display:flex;flex-direction:row;justify-content:space-between}.clear-action button{background:var(--red-500);color:#fff;font-weight:600}.clear-action button:hover{background:var(--red-600)}.profiled-request-details{align-items:center;display:flex;justify-content:space-between;padding-bottom:2rem}.data-item,.request-details-data{display:flex}.data-item{align-items:flex-start;flex-direction:column;list-style:none;margin-right:3rem;padding:0}.data-item small{color:var(--grey-400)}.data-item span{margin:.25rem 0}[class*=request-method-get],[class*=request-status-2]{background:var(--green-400)!important;color:#fff}[class*=request-method-patch],[class*=request-method-put],[class*=request-status-4]{background:var(--yellow-400)!important;color:#fff}[class*=request-method-delete],[class*=request-status-5]{background:var(--red-500)!important;color:#fff}.trace-list{border:1px solid var(--grey-200);border-radius:5px;padding:1rem}.trace-details-table{border:1px hidden var(--border-color);border-collapse:collapse;margin-top:1em;width:100%}.trace-details-table tr th{font-weight:var(--fw-semibold)}.trace-list-header{padding-bottom:1rem}.trace-list-filters,.trace-list-header{align-items:center;display:flex;justify-content:space-between}.trace-list-filters{color:var(--grey-500);width:60%}.button,button{border:none;border-radius:5px;cursor:pointer;display:inline-block;font-size:1rem;padding:.5em;text-align:center;text-decoration:none}.button:disabled,button:disabled{background:var(--grey-200);cursor:not-allowed}.btn-red{background:var(--red-500);color:#fff;font-weight:600;outline:none}.btn-red:hover{background:var(--red-600)}.btn-grey{background:var(--grey-200)}.btn-grey:hover{background:var(--grey-100)}.btn-white{background:#fff;border:1px solid var(--grey-200)}.btn-white:hover{background:var(--grey-100)}button:hover{box-shadow:0 .25rem .25rem 0 var(--grey-50)}button.none{background:none;border:none;outline:none;padding:0}button.none:hover{box-shadow:none}input[type=submit]{border:none;border-radius:5px;cursor:pointer;display:inline-block;font-size:1rem;padding:.5em;text-align:center;text-decoration:none}.header{background:var(--primary);box-shadow:0 0 20px 0 rgba(0,0,0,.2);display:flex;justify-content:center;margin:0;padding:1.5rem 0}.header a{color:#fff;text-decoration:none}.nav{justify-content:space-between;width:var(--main-width)}.home,.nav{display:flex}.home{align-items:center;text-decoration:none}.home-logo{height:64px;width:64px}.home-title{margin:0;padding:0 0 0 1rem}.header-links{font-weight:700;list-style:none;margin:0;padding:0}.header-links,.pagy-nav{align-items:center;display:flex}.pagy-nav{justify-content:center;padding:1em 0}.pagy-nav>.page.active,.pagy-nav>.page.disabled,.pagy-nav>.page>a{background:#fff;border:1px solid var(--border-color);border-right:none;color:var(--grey-900);cursor:pointer;padding:.5rem 1rem;text-decoration:none}.pagy-nav>.page.prev.disabled,.pagy-nav>.page.prev a{border-radius:5px 0 0 5px}.pagy-nav>.page.next.disabled,.pagy-nav>.page.next a{border-radius:0 5px 5px 0;border-right:1px solid var(--border-color)}.pagy-nav>.page.active{border-color:var(--red-500);color:#fff;cursor:default}.pagy-nav>.page.active,.pagy-nav>.page.active:hover{background:var(--red-500)}.pagy-nav>.page.disabled{color:var(--grey-500);cursor:default}.pagy-nav>.page.disabled:hover{background:#fff}.pagy-nav>.page.active:hover,.pagy-nav>.page.disabled:hover,.pagy-nav>.page>a:hover{background:var(--grey-100)}.page-header{padding:2.5rem 0}.page-header h1{font-size:28px}.dropdown-body{padding:.33rem .5rem}.dropdown-search-field{border:1px solid var(--grey-400);border-radius:5px;color:var(--grey-700);padding:.5rem}.dropdown-toggle{align-items:center;color:inherit;display:flex}.dropdown-toggle:hover{color:var(--grey-900)}.dropdown-container{background:#fff;border:1px solid var(--grey-200);border-radius:5px;box-shadow:0 8px 30px rgba(0,0,0,.12);box-sizing:border-box;color:var(--grey-400);cursor:default;margin-top:.3em;min-width:240px;overflow:hidden;position:absolute;z-index:1}.dropdown-container,.dropdown-search,.dropdown-search button{display:flex;flex-direction:column}.dropdown-entry{display:flex;padding:.33rem 1rem;text-decoration:none}.dropdown-entry:hover{background:var(--grey-100);color:var(--grey-900)}.dropdown-entry input{margin-right:.5em}.dropdown-header{align-items:center;background:var(--grey-100);border-bottom:1px solid var(--grey-200);display:flex;font-size:.833rem;justify-content:space-between;padding:.5rem 1rem;text-align:left}.dropdown-header button{background:none;border:none;color:var(--grey-500);font-size:.833rem;outline:none;padding:0;text-decoration:underline}.dropdown-header button:hover{box-shadow:none;color:var(--grey-900)}.dropdown-footer{background:var(--red-500);border:none;border-radius:0;color:#fff;font-weight:600;outline:none;padding:.5rem;width:100%}.dropdown-footer:hover{background:var(--red-600)}.placeholder{align-items:center;display:flex;flex-direction:column;justify-content:center;padding-bottom:2rem;width:100%}.placeholder-image{-webkit-filter:grayscale(1) brightness(2.5);height:30%;width:30%}.placeholder-text{color:var(--grey-400);padding:1rem 0;text-align:center}.placeholder-text h2{padding-bottom:1rem}.placeholder-link,.placeholder-link:visited{color:var(--grey-400)}.placeholder-link:hover{color:var(--grey-900)}.trace{align-items:center;display:flex;justify-content:flex-start;list-style:none;padding:.25em 0}.trace:nth-child(odd){background:var(--grey-100)}.trace .trace-bar{background:linear-gradient(to top right,var(--grey-500),var(--grey-400));cursor:pointer;height:16px;margin:0;padding:0;position:relative}.instantiation-trace .trace-bar{background:linear-gradient(to top right,var(--green-400),var(--green-300))}.sequel-trace .trace-bar{background:linear-gradient(to top right,var(--green-500),var(--green-400))}.controller-trace .trace-bar{background:linear-gradient(to top right,var(--yellow-500),var(--yellow-400))}.render-partial-trace .trace-bar,.render-template-trace .trace-bar{background:linear-gradient(to top right,var(--blue-500),var(--blue-400))}.trace-name{box-sizing:border-box;color:var(--grey-400);font-size:14px;margin:0;overflow:hidden;padding:0 .5em;text-align:right;text-overflow:ellipsis}.trace-payload{margin:0}.sequel-trace-query{background:var(--grey-100);padding:1em}.sequel-trace-binds,.sequel-trace-query{max-height:100px;overflow:auto;white-space:pre-wrap}.sequel-trace-binds{background:var(--grey-50);font-size:12px;margin:0 0 1em;padding:.5em 1em}.backtrace{align-items:center;background:var(--grey-100);display:flex;flex-direction:row;justify-content:space-between;padding:.5em}.backtrace button{background:none;color:var(--grey-500);height:20px;margin:0;overflow:auto;padding:0}.backtrace button svg{height:20px;width:20px}@font-face{font-family:Open Sans;font-style:normal;font-weight:400}@font-face{font-family:Open Sans;font-style:normal;font-weight:600}@font-face{font-family:Open Sans;font-style:normal;font-weight:700}html{--grey-50:#f9fafb;--grey-100:#f3f4f6;--grey-200:#e5e7eb;--grey-400:#9ca3af;--grey-500:#6b7280;--grey-700:#374151;--grey-900:#111827;--red-400:#f87171;--red-500:#ef4444;--red-600:#dc2626;--yellow-400:#fbbf24;--yellow-500:#fbbf24;--yellow-600:#d97706;--yellow-700:#b45309;--green-300:#6ee7b7;--green-400:#34d399;--green-500:#10b981;--blue-400:#60a5fa;--blue-500:#3b82f6;--main-width:1056px;--primary:var(--red-600);--border-color:var(--grey-200);--text-color:var(--grey-900);--fw-normal:400;--fw-semibold:600;--fw-bold:700;font-family:Open Sans,monospace;height:100%;width:100%}*,body,html{margin:0;padding:0}body{color:var(--text-color);height:100%}body,main{width:100%}main{display:flex;justify-content:center}.main-section{width:var(--main-width)}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.hidden{display:none}.flex-row{display:flex;flex-direction:row}.flex-column{display:flex;flex-direction:column}.pill{background:var(--grey-200);border-radius:5px;font-size:.9rem;font-weight:600;letter-spacing:.1rem;margin:.2rem 0;padding:.1rem .4rem}.popover{color:#000;display:flex;flex-direction:column;padding:1em;width:600px}.popover-header{align-items:center;display:flex;flex-direction:row;justify-content:space-between;padding-bottom:1em}.popover-description{margin:0;padding:0}.popover-close{background:transparent;color:var(--grey-400);font-size:20px;font-weight:700;padding:0}.popover-body{padding:1em 0}.popover-footer{border-top:1px solid var(--grey-50)}.tippy-box[data-theme~=rmp]{background:#fff;border-radius:5px;box-shadow:8px 0 30px 4px rgba(0,0,0,.2),8px 4px 10px 0 rgba(0,0,0,.05);transition:all .2s cubic-bezier(.19,1,.22,1)}.tippy-box[data-theme~=rmp][data-placement^=top]>.tippy-arrow:before{border-top-color:#fff}.tippy-box[data-theme~=rmp][data-placement^=bottom]>.tippy-arrow:before{border-bottom-color:#fff}.tippy-box[data-theme~=rmp][data-placement^=left]>.tippy-arrow:before{border-left-color:#fff}.tippy-box[data-theme~=rmp][data-placement^=right]>.tippy-arrow:before{border-right-color:#fff}
|