rails_pulse 0.1.1 → 0.1.2

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.
Files changed (81) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +72 -176
  3. data/Rakefile +77 -2
  4. data/app/assets/stylesheets/rails_pulse/application.css +0 -12
  5. data/app/controllers/concerns/chart_table_concern.rb +21 -4
  6. data/app/controllers/concerns/response_range_concern.rb +6 -3
  7. data/app/controllers/concerns/time_range_concern.rb +5 -10
  8. data/app/controllers/concerns/zoom_range_concern.rb +1 -1
  9. data/app/controllers/rails_pulse/application_controller.rb +8 -4
  10. data/app/controllers/rails_pulse/dashboard_controller.rb +12 -0
  11. data/app/controllers/rails_pulse/queries_controller.rb +65 -50
  12. data/app/controllers/rails_pulse/requests_controller.rb +24 -12
  13. data/app/controllers/rails_pulse/routes_controller.rb +59 -24
  14. data/app/helpers/rails_pulse/application_helper.rb +0 -1
  15. data/app/helpers/rails_pulse/chart_formatters.rb +3 -3
  16. data/app/helpers/rails_pulse/chart_helper.rb +6 -2
  17. data/app/helpers/rails_pulse/status_helper.rb +10 -4
  18. data/app/javascript/rails_pulse/controllers/index_controller.js +117 -33
  19. data/app/javascript/rails_pulse/controllers/pagination_controller.js +17 -27
  20. data/app/jobs/rails_pulse/backfill_summaries_job.rb +41 -0
  21. data/app/jobs/rails_pulse/summary_job.rb +53 -0
  22. data/app/models/rails_pulse/dashboard/charts/average_response_time.rb +28 -7
  23. data/app/models/rails_pulse/dashboard/charts/p95_response_time.rb +18 -22
  24. data/app/models/rails_pulse/dashboard/tables/slow_queries.rb +18 -7
  25. data/app/models/rails_pulse/dashboard/tables/slow_routes.rb +34 -41
  26. data/app/models/rails_pulse/operation.rb +1 -1
  27. data/app/models/rails_pulse/queries/cards/average_query_times.rb +47 -23
  28. data/app/models/rails_pulse/queries/cards/execution_rate.rb +33 -26
  29. data/app/models/rails_pulse/queries/cards/percentile_query_times.rb +34 -45
  30. data/app/models/rails_pulse/queries/charts/average_query_times.rb +23 -97
  31. data/app/models/rails_pulse/queries/tables/index.rb +74 -0
  32. data/app/models/rails_pulse/query.rb +1 -0
  33. data/app/models/rails_pulse/requests/charts/average_response_times.rb +23 -84
  34. data/app/models/rails_pulse/route.rb +1 -6
  35. data/app/models/rails_pulse/routes/cards/average_response_times.rb +45 -23
  36. data/app/models/rails_pulse/routes/cards/error_rate_per_route.rb +38 -45
  37. data/app/models/rails_pulse/routes/cards/percentile_response_times.rb +34 -47
  38. data/app/models/rails_pulse/routes/cards/request_count_totals.rb +30 -25
  39. data/app/models/rails_pulse/routes/charts/average_response_times.rb +23 -100
  40. data/app/models/rails_pulse/routes/tables/index.rb +57 -40
  41. data/app/models/rails_pulse/summary.rb +143 -0
  42. data/app/services/rails_pulse/summary_service.rb +199 -0
  43. data/app/views/layouts/rails_pulse/application.html.erb +4 -4
  44. data/app/views/rails_pulse/components/_empty_state.html.erb +11 -0
  45. data/app/views/rails_pulse/components/_metric_card.html.erb +10 -24
  46. data/app/views/rails_pulse/dashboard/index.html.erb +54 -36
  47. data/app/views/rails_pulse/queries/_show_table.html.erb +1 -1
  48. data/app/views/rails_pulse/queries/_table.html.erb +10 -12
  49. data/app/views/rails_pulse/queries/index.html.erb +41 -34
  50. data/app/views/rails_pulse/queries/show.html.erb +38 -31
  51. data/app/views/rails_pulse/requests/_operations.html.erb +32 -26
  52. data/app/views/rails_pulse/requests/_table.html.erb +1 -3
  53. data/app/views/rails_pulse/requests/index.html.erb +42 -34
  54. data/app/views/rails_pulse/routes/_table.html.erb +13 -13
  55. data/app/views/rails_pulse/routes/index.html.erb +43 -35
  56. data/app/views/rails_pulse/routes/show.html.erb +42 -35
  57. data/config/initializers/rails_pulse.rb +0 -12
  58. data/db/migrate/20241222000001_create_rails_pulse_summaries.rb +54 -0
  59. data/db/rails_pulse_schema.rb +121 -0
  60. data/lib/generators/rails_pulse/install_generator.rb +41 -4
  61. data/lib/generators/rails_pulse/templates/db/rails_pulse_schema.rb +60 -0
  62. data/lib/generators/rails_pulse/templates/rails_pulse.rb +0 -12
  63. data/lib/rails_pulse/configuration.rb +0 -11
  64. data/lib/rails_pulse/engine.rb +0 -1
  65. data/lib/rails_pulse/version.rb +1 -1
  66. data/lib/tasks/rails_pulse.rake +58 -0
  67. data/public/rails-pulse-assets/rails-pulse.css +1 -1
  68. data/public/rails-pulse-assets/rails-pulse.css.map +1 -1
  69. data/public/rails-pulse-assets/rails-pulse.js +1 -1
  70. data/public/rails-pulse-assets/rails-pulse.js.map +3 -3
  71. data/public/rails-pulse-assets/search.svg +43 -0
  72. metadata +27 -11
  73. data/app/controllers/rails_pulse/caches_controller.rb +0 -115
  74. data/app/helpers/rails_pulse/cached_component_helper.rb +0 -73
  75. data/app/models/rails_pulse/component_cache_key.rb +0 -33
  76. data/app/views/rails_pulse/caches/show.html.erb +0 -9
  77. data/db/migrate/20250227235904_create_routes.rb +0 -12
  78. data/db/migrate/20250227235915_create_requests.rb +0 -19
  79. data/db/migrate/20250228000000_create_queries.rb +0 -14
  80. data/db/migrate/20250228000056_create_operations.rb +0 -24
  81. data/lib/rails_pulse/migration.rb +0 -29
@@ -6,8 +6,7 @@ module TimeRangeConcern
6
6
  const_set(:TIME_RANGE_OPTIONS, [
7
7
  [ "Last 24 hours", :last_day ],
8
8
  [ "Last Week", :last_week ],
9
- [ "Last Month", :last_month ],
10
- [ "All Time", :all_time ]
9
+ [ "Last Month", :last_month ]
11
10
  ].freeze)
12
11
  end
13
12
 
@@ -18,23 +17,19 @@ module TimeRangeConcern
18
17
 
19
18
  ransack_params = params[:q] || {}
20
19
 
21
- if ransack_params[:requests_occurred_at_gteq].present?
22
- # Custom time range from routes index chart zoom which filters requests through an association
23
- start_time = parse_time_param(ransack_params[:requests_occurred_at_gteq])
24
- end_time = parse_time_param(ransack_params[:requests_occurred_at_lt])
25
- elsif ransack_params[:occurred_at_gteq].present?
20
+ if ransack_params[:occurred_at_gteq].present?
26
21
  # Custom time range from chart zoom where there is no association
27
22
  start_time = parse_time_param(ransack_params[:occurred_at_gteq])
28
23
  end_time = parse_time_param(ransack_params[:occurred_at_lt])
29
- elsif ransack_params[:occurred_at_range]
24
+ elsif ransack_params[:period_start_range]
30
25
  # Predefined time range from dropdown
31
- selected_time_range = ransack_params[:occurred_at_range]
26
+ selected_time_range = ransack_params[:period_start_range]
32
27
  start_time =
33
28
  case selected_time_range.to_sym
34
29
  when :last_day then 1.day.ago
35
30
  when :last_week then 1.week.ago
36
31
  when :last_month then 1.month.ago
37
- when :all_time then 100.years.ago
32
+ else 1.day.ago # Default fallback
38
33
  end
39
34
  end
40
35
 
@@ -35,6 +35,6 @@ module ZoomRangeConcern
35
35
  end_time = end_time_obj&.end_of_day || end_time_obj
36
36
  end
37
37
 
38
- [ start_time, end_time ]
38
+ [ start_time.to_i, end_time.to_i ]
39
39
  end
40
40
  end
@@ -2,8 +2,9 @@ module RailsPulse
2
2
  class ApplicationController < ActionController::Base
3
3
  before_action :authenticate_rails_pulse_user!
4
4
 
5
- def set_pagination_limit
6
- session[:pagination_limit] = params[:limit].to_i if params[:limit].present?
5
+ def set_pagination_limit(limit = nil)
6
+ limit = limit || params[:limit]
7
+ session[:pagination_limit] = limit.to_i if limit.present?
7
8
  render json: { status: "ok" }
8
9
  end
9
10
 
@@ -54,8 +55,11 @@ module RailsPulse
54
55
  end
55
56
 
56
57
  def session_pagination_limit
57
- # Keep default small for optimal performance
58
- session[:pagination_limit] || 10
58
+ # Use URL param if present, otherwise session, otherwise default
59
+ limit = params[:limit].presence || session[:pagination_limit] || 10
60
+ # Update session if URL param was used
61
+ session[:pagination_limit] = limit.to_i if params[:limit].present?
62
+ limit.to_i
59
63
  end
60
64
 
61
65
  def store_pagination_limit(limit)
@@ -1,6 +1,18 @@
1
1
  module RailsPulse
2
2
  class DashboardController < ApplicationController
3
3
  def index
4
+ @average_query_times_metric_card = RailsPulse::Routes::Cards::AverageResponseTimes.new(route: nil).to_metric_card
5
+ @percentile_response_times_metric_card = RailsPulse::Routes::Cards::PercentileResponseTimes.new(route: nil).to_metric_card
6
+ @request_count_totals_metric_card = RailsPulse::Routes::Cards::RequestCountTotals.new(route: nil).to_metric_card
7
+ @error_rate_per_route_metric_card = RailsPulse::Routes::Cards::ErrorRatePerRoute.new(route: nil).to_metric_card
8
+
9
+ # Generate chart data for inline rendering
10
+ @average_response_time_chart_data = RailsPulse::Dashboard::Charts::AverageResponseTime.new.to_chart_data
11
+ @p95_response_time_chart_data = RailsPulse::Dashboard::Charts::P95ResponseTime.new.to_chart_data
12
+
13
+ # Generate table data for inline rendering
14
+ @slow_routes_table_data = RailsPulse::Dashboard::Tables::SlowRoutes.new.to_table_data
15
+ @slow_queries_table_data = RailsPulse::Dashboard::Tables::SlowQueries.new.to_table_data
4
16
  end
5
17
  end
6
18
  end
@@ -5,21 +5,23 @@ module RailsPulse
5
5
  before_action :set_query, only: :show
6
6
 
7
7
  def index
8
+ setup_metric_cards
8
9
  setup_chart_and_table_data
9
10
  end
10
11
 
11
12
  def show
13
+ setup_metric_cards
12
14
  setup_chart_and_table_data
13
15
  end
14
16
 
15
17
  private
16
18
 
17
19
  def chart_model
18
- show_action? ? Operation : Query
20
+ Summary
19
21
  end
20
22
 
21
23
  def table_model
22
- show_action? ? Operation : Query
24
+ show_action? ? Operation : Summary
23
25
  end
24
26
 
25
27
  def chart_class
@@ -31,73 +33,67 @@ module RailsPulse
31
33
  end
32
34
 
33
35
  def build_chart_ransack_params(ransack_params)
34
- base_params = ransack_params.except(:s)
36
+ base_params = ransack_params.except(:s).merge(
37
+ period_start_gteq: Time.at(@start_time),
38
+ period_start_lt: Time.at(@end_time)
39
+ )
40
+
41
+ # Only add duration filter if we have a meaningful threshold
42
+ base_params[:avg_duration_gteq] = @start_duration if @start_duration && @start_duration > 0
35
43
 
36
44
  if show_action?
37
- base_params.merge(
38
- query_id_eq: @query.id,
39
- occurred_at_gteq: @start_time,
40
- occurred_at_lt: @end_time,
41
- duration_gteq: @start_duration
42
- )
45
+ base_params.merge(summarizable_id_eq: @query.id)
43
46
  else
44
- base_params.merge(
45
- operations_occurred_at_gteq: @start_time,
46
- operations_occurred_at_lt: @end_time,
47
- operations_duration_gteq: @start_duration
48
- )
47
+ base_params
49
48
  end
50
49
  end
51
50
 
52
51
  def build_table_ransack_params(ransack_params)
53
52
  if show_action?
54
- ransack_params.merge(
55
- query_id_eq: @query.id,
56
- occurred_at_gteq: @table_start_time,
57
- occurred_at_lt: @table_end_time,
58
- duration_gteq: @start_duration
53
+ # For Operation model on show page
54
+ params = ransack_params.merge(
55
+ occurred_at_gteq: Time.at(@table_start_time),
56
+ occurred_at_lt: Time.at(@table_end_time),
57
+ query_id_eq: @query.id
59
58
  )
59
+ params[:duration_gteq] = @start_duration if @start_duration && @start_duration > 0
60
+ params
60
61
  else
61
- ransack_params.merge(
62
- operations_occurred_at_gteq: @table_start_time,
63
- operations_occurred_at_lt: @table_end_time,
64
- operations_duration_gteq: @start_duration
62
+ # For Summary model on index page
63
+ params = ransack_params.merge(
64
+ period_start_gteq: Time.at(@table_start_time),
65
+ period_start_lt: Time.at(@table_end_time)
65
66
  )
67
+ params[:avg_duration_gteq] = @start_duration if @start_duration && @start_duration > 0
68
+ params
66
69
  end
67
70
  end
68
71
 
69
72
  def default_table_sort
70
- "occurred_at desc"
73
+ show_action? ? "occurred_at desc" : "period_start desc"
71
74
  end
72
75
 
73
76
  def build_table_results
74
77
  if show_action?
75
- @ransack_query.result.select("id", "occurred_at", "duration")
78
+ @ransack_query.result
76
79
  else
77
- # Optimized query: Use INNER JOIN since we only want queries with operations in time range
78
- # This dramatically reduces the dataset before aggregation
79
- @ransack_query.result(distinct: false)
80
- .joins("INNER JOIN rails_pulse_operations ON rails_pulse_operations.query_id = rails_pulse_queries.id")
81
- .where("rails_pulse_operations.occurred_at >= ? AND rails_pulse_operations.occurred_at < ?",
82
- @table_start_time, @table_end_time)
83
- .group("rails_pulse_queries.id, rails_pulse_queries.normalized_sql, rails_pulse_queries.created_at, rails_pulse_queries.updated_at")
84
- .select(
85
- "rails_pulse_queries.*",
86
- optimized_aggregations_sql
87
- )
80
+ Queries::Tables::Index.new(
81
+ ransack_query: @ransack_query,
82
+ period_type: period_type,
83
+ start_time: @start_time,
84
+ params: params
85
+ ).to_table
88
86
  end
89
87
  end
90
88
 
91
89
  private
92
90
 
93
- def optimized_aggregations_sql
94
- # Efficient aggregations that work with our composite indexes
95
- [
96
- "COALESCE(AVG(rails_pulse_operations.duration), 0) AS average_query_time_ms",
97
- "COUNT(rails_pulse_operations.id) AS execution_count",
98
- "COALESCE(SUM(rails_pulse_operations.duration), 0) AS total_time_consumed",
99
- "MAX(rails_pulse_operations.occurred_at) AS occurred_at"
100
- ].join(", ")
91
+ def setup_metric_cards
92
+ return if turbo_frame_request?
93
+
94
+ @average_query_times_metric_card = RailsPulse::Queries::Cards::AverageQueryTimes.new(query: @query).to_metric_card
95
+ @percentile_query_times_metric_card = RailsPulse::Queries::Cards::PercentileQueryTimes.new(query: @query).to_metric_card
96
+ @execution_rate_metric_card = RailsPulse::Queries::Cards::ExecutionRate.new(query: @query).to_metric_card
101
97
  end
102
98
 
103
99
  def show_action?
@@ -108,14 +104,33 @@ module RailsPulse
108
104
  show_action? ? :set_pagination_limit : :store_pagination_limit
109
105
  end
110
106
 
111
- def set_query
112
- @query = Query.find(params[:id])
107
+ def setup_table_data(ransack_params)
108
+ table_ransack_params = build_table_ransack_params(ransack_params)
109
+ @ransack_query = table_model.ransack(table_ransack_params)
110
+
111
+ # Only apply default sort if not using Queries::Tables::Index (which handles its own sorting)
112
+ if show_action?
113
+ @ransack_query.sorts = default_table_sort if @ransack_query.sorts.empty?
114
+ end
115
+
116
+ table_results = build_table_results
117
+ handle_pagination
118
+
119
+ @pagy, @table_data = pagy(table_results, limit: session_pagination_limit)
120
+ end
121
+
122
+ def handle_pagination
123
+ method = pagination_method
124
+ send(method, params[:limit]) if params[:limit].present?
113
125
  end
114
126
 
115
- def setup_metic_cards
116
- @average_query_times_card = Queries::Cards::AverageQueryTimes.new(query: @query).to_metric_card
117
- @percentile_response_times_card = Queries::Cards::PercentileQueryTimes.new(query: @query).to_metric_card
118
- @execution_rate_card = Queries::Cards::ExecutionRate.new(query: @query).to_metric_card
127
+ def setup_time_and_response_ranges
128
+ @start_time, @end_time, @selected_time_range, @time_diff_hours = setup_time_range
129
+ @start_duration, @selected_response_range = setup_duration_range(:query)
130
+ end
131
+
132
+ def set_query
133
+ @query = Query.find(params[:id])
119
134
  end
120
135
  end
121
136
  end
@@ -5,6 +5,13 @@ module RailsPulse
5
5
  before_action :set_request, only: :show
6
6
 
7
7
  def index
8
+ unless turbo_frame_request?
9
+ @average_response_times_metric_card = RailsPulse::Routes::Cards::AverageResponseTimes.new(route: nil).to_metric_card
10
+ @percentile_response_times_metric_card = RailsPulse::Routes::Cards::PercentileResponseTimes.new(route: nil).to_metric_card
11
+ @request_count_totals_metric_card = RailsPulse::Routes::Cards::RequestCountTotals.new(route: nil).to_metric_card
12
+ @error_rate_per_route_metric_card = RailsPulse::Routes::Cards::ErrorRatePerRoute.new(route: nil).to_metric_card
13
+ end
14
+
8
15
  setup_chart_and_table_data
9
16
  end
10
17
 
@@ -15,7 +22,7 @@ module RailsPulse
15
22
  private
16
23
 
17
24
  def chart_model
18
- Request
25
+ Summary
19
26
  end
20
27
 
21
28
  def table_model
@@ -27,23 +34,27 @@ module RailsPulse
27
34
  end
28
35
 
29
36
  def chart_options
30
- { route: true }
37
+ {}
31
38
  end
32
39
 
33
40
  def build_chart_ransack_params(ransack_params)
34
- ransack_params.except(:s).merge(
35
- occurred_at_gteq: @start_time,
36
- occurred_at_lt: @end_time,
37
- duration_gteq: @start_duration
41
+ base_params = ransack_params.except(:s).merge(
42
+ period_start_gteq: Time.at(@start_time),
43
+ period_start_lt: Time.at(@end_time)
38
44
  )
45
+
46
+ # Only add duration filter if we have a meaningful threshold
47
+ base_params[:avg_duration_gteq] = @start_duration if @start_duration && @start_duration > 0
48
+ base_params
39
49
  end
40
50
 
41
51
  def build_table_ransack_params(ransack_params)
42
- ransack_params.merge(
43
- occurred_at_gteq: @table_start_time,
44
- occurred_at_lt: @table_end_time,
45
- duration_gteq: @start_duration
52
+ params = ransack_params.merge(
53
+ occurred_at_gteq: Time.at(@table_start_time),
54
+ occurred_at_lt: Time.at(@table_end_time)
46
55
  )
56
+ params[:duration_gteq] = @start_duration if @start_duration && @start_duration > 0
57
+ params
47
58
  end
48
59
 
49
60
  def default_table_sort
@@ -52,13 +63,14 @@ module RailsPulse
52
63
 
53
64
  def build_table_results
54
65
  @ransack_query.result
55
- .includes(:route)
66
+ .joins(:route)
56
67
  .select(
57
68
  "rails_pulse_requests.id",
58
69
  "rails_pulse_requests.occurred_at",
59
70
  "rails_pulse_requests.duration",
60
71
  "rails_pulse_requests.status",
61
- "rails_pulse_requests.route_id"
72
+ "rails_pulse_requests.route_id",
73
+ "rails_pulse_routes.path"
62
74
  )
63
75
  end
64
76
 
@@ -5,21 +5,32 @@ module RailsPulse
5
5
  before_action :set_route, only: :show
6
6
 
7
7
  def index
8
+ setup_metric_cards
8
9
  setup_chart_and_table_data
9
10
  end
10
11
 
11
12
  def show
13
+ setup_metric_cards
12
14
  setup_chart_and_table_data
13
15
  end
14
16
 
15
17
  private
16
18
 
19
+ def setup_metric_cards
20
+ return if turbo_frame_request?
21
+
22
+ @average_query_times_metric_card = RailsPulse::Routes::Cards::AverageResponseTimes.new(route: @route).to_metric_card
23
+ @percentile_response_times_metric_card = RailsPulse::Routes::Cards::PercentileResponseTimes.new(route: @route).to_metric_card
24
+ @request_count_totals_metric_card = RailsPulse::Routes::Cards::RequestCountTotals.new(route: @route).to_metric_card
25
+ @error_rate_per_route_metric_card = RailsPulse::Routes::Cards::ErrorRatePerRoute.new(route: @route).to_metric_card
26
+ end
27
+
17
28
  def chart_model
18
- show_action? ? Request : Route
29
+ Summary
19
30
  end
20
31
 
21
32
  def table_model
22
- show_action? ? Request : Route
33
+ show_action? ? Request : Summary
23
34
  end
24
35
 
25
36
  def chart_class
@@ -31,49 +42,53 @@ module RailsPulse
31
42
  end
32
43
 
33
44
  def build_chart_ransack_params(ransack_params)
34
- base_params = ransack_params.except(:s).merge(duration_field => @start_duration)
45
+ base_params = ransack_params.except(:s).merge(
46
+ period_start_gteq: Time.at(@start_time),
47
+ period_start_lt: Time.at(@end_time)
48
+ )
49
+
50
+ # Only add duration filter if we have a meaningful threshold
51
+ base_params[:avg_duration_gteq] = @start_duration if @start_duration && @start_duration > 0
35
52
 
36
53
  if show_action?
37
- base_params.merge(
38
- route_id_eq: @route.id,
39
- occurred_at_gteq: @start_time,
40
- occurred_at_lt: @end_time
41
- )
54
+ base_params.merge(summarizable_id_eq: @route.id)
42
55
  else
43
- base_params.merge(
44
- requests_occurred_at_gteq: @start_time,
45
- requests_occurred_at_lt: @end_time
46
- )
56
+ base_params
47
57
  end
48
58
  end
49
59
 
50
60
  def build_table_ransack_params(ransack_params)
51
- base_params = ransack_params.merge(duration_field => @start_duration)
52
-
53
61
  if show_action?
54
- base_params.merge(
55
- route_id_eq: @route.id,
56
- occurred_at_gteq: @table_start_time,
57
- occurred_at_lt: @table_end_time
62
+ # For Request model on show page
63
+ params = ransack_params.merge(
64
+ occurred_at_gteq: Time.at(@table_start_time),
65
+ occurred_at_lt: Time.at(@table_end_time),
66
+ route_id_eq: @route.id
58
67
  )
68
+ params[:duration_gteq] = @start_duration if @start_duration && @start_duration > 0
69
+ params
59
70
  else
60
- base_params.merge(
61
- requests_occurred_at_gteq: @table_start_time,
62
- requests_occurred_at_lt: @table_end_time
71
+ # For Summary model on index page
72
+ params = ransack_params.merge(
73
+ period_start_gteq: Time.at(@table_start_time),
74
+ period_start_lt: Time.at(@table_end_time)
63
75
  )
76
+ params[:avg_duration_gteq] = @start_duration if @start_duration && @start_duration > 0
77
+ params
64
78
  end
65
79
  end
66
80
 
67
81
  def default_table_sort
68
- show_action? ? "occurred_at desc" : "average_response_time_ms desc"
82
+ show_action? ? "occurred_at desc" : "avg_duration desc"
69
83
  end
70
84
 
71
85
  def build_table_results
72
86
  if show_action?
73
- @ransack_query.result.select("id", "route_id", "occurred_at", "duration", "status")
87
+ @ransack_query.result
74
88
  else
75
89
  Routes::Tables::Index.new(
76
90
  ransack_query: @ransack_query,
91
+ period_type: period_type,
77
92
  start_time: @start_time,
78
93
  params: params
79
94
  ).to_table
@@ -81,13 +96,33 @@ module RailsPulse
81
96
  end
82
97
 
83
98
  def duration_field
84
- show_action? ? :duration_gteq : :requests_duration_gteq
99
+ :avg_duration
85
100
  end
86
101
 
87
102
  def show_action?
88
103
  action_name == "show"
89
104
  end
90
105
 
106
+ def setup_table_data(ransack_params)
107
+ table_ransack_params = build_table_ransack_params(ransack_params)
108
+ @ransack_query = table_model.ransack(table_ransack_params)
109
+
110
+ # Only apply default sort if not using Routes::Tables::Index (which handles its own sorting)
111
+ if show_action?
112
+ @ransack_query.sorts = default_table_sort if @ransack_query.sorts.empty?
113
+ end
114
+
115
+ table_results = build_table_results
116
+ handle_pagination
117
+
118
+ @pagy, @table_data = pagy(table_results, limit: session_pagination_limit)
119
+ end
120
+
121
+ def handle_pagination
122
+ method = pagination_method
123
+ send(method, params[:limit]) if params[:limit].present?
124
+ end
125
+
91
126
  def pagination_method
92
127
  show_action? ? :set_pagination_limit : :store_pagination_limit
93
128
  end
@@ -3,7 +3,6 @@ module RailsPulse
3
3
  include Pagy::Frontend
4
4
 
5
5
  include BreadcrumbsHelper
6
- include CachedComponentHelper
7
6
  include ChartHelper
8
7
  include FormattingHelper
9
8
  include StatusHelper
@@ -1,6 +1,6 @@
1
1
  module RailsPulse
2
2
  module ChartFormatters
3
- def self.occurred_at_as_time_or_date(time_diff_hours)
3
+ def self.period_as_time_or_date(time_diff_hours)
4
4
  if time_diff_hours <= 25
5
5
  <<~JS
6
6
  function(value) {
@@ -25,7 +25,7 @@ module RailsPulse
25
25
  const data = params[0];
26
26
  const date = new Date(data.axisValue * 1000);
27
27
  const dateString = date.getHours().toString().padStart(2, '0') + ':00';
28
- return `${dateString} <br /> ${data.marker} ${parseInt(data.data.value)} ms`;
28
+ return `${dateString} <br /> ${data.marker} ${parseInt(data.data)} ms`;
29
29
  }
30
30
  JS
31
31
  else
@@ -34,7 +34,7 @@ module RailsPulse
34
34
  const data = params[0];
35
35
  const date = new Date(data.axisValue * 1000);
36
36
  const dateString = date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
37
- return `${dateString} <br /> ${data.marker} ${parseInt(data.data.value)} ms`;
37
+ return `${dateString} <br /> ${data.marker} ${parseInt(data.data)} ms`;
38
38
  }
39
39
  JS
40
40
  end
@@ -117,9 +117,13 @@ module RailsPulse
117
117
  # Chart data is a hash like: { 1234567890 => { value: 123.45 } }
118
118
  chart_timestamps = chart_data.keys
119
119
 
120
+ # Convert zoom parameters to integers (timestamps)
121
+ zoom_start_int = zoom_start.respond_to?(:to_i) ? zoom_start.to_i : zoom_start
122
+ zoom_end_int = zoom_end.respond_to?(:to_i) ? zoom_end.to_i : zoom_end
123
+
120
124
  if chart_timestamps.any?
121
- closest_start = chart_timestamps.min_by { |ts| (ts - zoom_start).abs }
122
- closest_end = chart_timestamps.min_by { |ts| (ts - zoom_end).abs }
125
+ closest_start = chart_timestamps.min_by { |ts| (ts - zoom_start_int).abs }
126
+ closest_end = chart_timestamps.min_by { |ts| (ts - zoom_end_int).abs }
123
127
 
124
128
  # Find the array indices of these timestamps
125
129
  start_index = chart_timestamps.index(closest_start)
@@ -256,13 +256,19 @@ module RailsPulse
256
256
 
257
257
  def event_color(operation_type)
258
258
  case operation_type
259
- when "sql" then "#92c282;"
260
- when "template", "partial", "layout", "collection" then "#b77cbf"
261
- when "controller" then "#00adc4"
262
- else "gray"
259
+ when "sql"
260
+ "#d27d6b"
261
+ when "template", "partial", "layout", "collection"
262
+ "#6c7ab9"
263
+ when "controller"
264
+ "#5ba6b0"
265
+ else
266
+ "#a6a6a6"
263
267
  end
264
268
  end
265
269
 
270
+
271
+
266
272
  def duration_options(type = :route)
267
273
  thresholds = RailsPulse.configuration.public_send("#{type}_thresholds")
268
274