rails_pulse 0.4.2 → 0.5.0.pre.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 (203) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +58 -1
  3. data/README.md +15 -4
  4. data/app/assets/stylesheets/rails_pulse/components/chart.css +11 -0
  5. data/app/assets/stylesheets/rails_pulse/components/descriptive_list.css +19 -1
  6. data/app/assets/stylesheets/rails_pulse/components/row.css +3 -17
  7. data/app/controllers/concerns/chart_table_concern.rb +28 -69
  8. data/app/controllers/concerns/deployment_markers_concern.rb +3 -1
  9. data/app/controllers/concerns/metric_card_concern.rb +6 -7
  10. data/app/controllers/rails_pulse/api/v1/base_controller.rb +133 -0
  11. data/app/controllers/rails_pulse/api/v1/capabilities_controller.rb +27 -0
  12. data/app/controllers/rails_pulse/api/v1/coverage_controller.rb +158 -0
  13. data/app/controllers/rails_pulse/api/v1/deployments_controller.rb +40 -0
  14. data/app/controllers/rails_pulse/api/v1/exceptions_controller.rb +77 -0
  15. data/app/controllers/rails_pulse/api/v1/insights_controller.rb +62 -0
  16. data/app/controllers/rails_pulse/api/v1/job_runs_controller.rb +34 -0
  17. data/app/controllers/rails_pulse/api/v1/jobs_controller.rb +172 -0
  18. data/app/controllers/rails_pulse/api/v1/queries_controller.rb +123 -0
  19. data/app/controllers/rails_pulse/api/v1/requests_controller.rb +74 -0
  20. data/app/controllers/rails_pulse/api/v1/routes_controller.rb +103 -0
  21. data/app/controllers/rails_pulse/application_controller.rb +1 -1
  22. data/app/controllers/rails_pulse/dashboard_controller.rb +25 -21
  23. data/app/controllers/rails_pulse/deployments_controller.rb +23 -13
  24. data/app/controllers/rails_pulse/exceptions_controller.rb +10 -12
  25. data/app/controllers/rails_pulse/jobs_controller.rb +1 -2
  26. data/app/controllers/rails_pulse/queries_controller.rb +6 -5
  27. data/app/controllers/rails_pulse/requests_controller.rb +15 -14
  28. data/app/controllers/rails_pulse/routes_controller.rb +4 -8
  29. data/app/helpers/rails_pulse/application_helper.rb +15 -0
  30. data/app/helpers/rails_pulse/chart_helper.rb +9 -2
  31. data/app/helpers/rails_pulse/formatting_helper.rb +11 -12
  32. data/app/javascript/rails_pulse/controllers/chart_controller.js +80 -43
  33. data/app/jobs/rails_pulse/backfill_summaries_job.rb +4 -2
  34. data/app/jobs/rails_pulse/cloud_health_job.rb +11 -0
  35. data/app/jobs/rails_pulse/cloud_sync_job.rb +11 -0
  36. data/app/jobs/rails_pulse/summary_job.rb +13 -0
  37. data/app/models/concerns/rails_pulse/has_metadata.rb +15 -0
  38. data/app/models/concerns/rails_pulse/taggable.rb +5 -12
  39. data/app/models/rails_pulse/cards/base.rb +20 -11
  40. data/app/models/rails_pulse/charts/base.rb +5 -18
  41. data/app/models/rails_pulse/charts/percentile_chart_base.rb +3 -5
  42. data/app/models/rails_pulse/cleanup_run.rb +32 -0
  43. data/app/models/rails_pulse/cloud/buffered_batch.rb +55 -0
  44. data/app/models/rails_pulse/cloud/installation.rb +55 -0
  45. data/app/models/rails_pulse/dashboard/charts/response_time_percentiles.rb +4 -5
  46. data/app/models/rails_pulse/dashboard/charts/throughput_and_errors.rb +4 -5
  47. data/app/models/rails_pulse/dashboard/concerns/attention_classification.rb +104 -0
  48. data/app/models/rails_pulse/dashboard/concerns/time_range_helper.rb +25 -2
  49. data/app/models/rails_pulse/dashboard/health_summary.rb +38 -13
  50. data/app/models/rails_pulse/dashboard/needs_attention.rb +31 -98
  51. data/app/models/rails_pulse/dashboard/storage_pressure.rb +39 -63
  52. data/app/models/rails_pulse/dashboard/storage_status.rb +130 -39
  53. data/app/models/rails_pulse/deployment.rb +2 -7
  54. data/app/models/rails_pulse/event.rb +41 -0
  55. data/app/models/rails_pulse/exception_occurrence.rb +1 -1
  56. data/app/models/rails_pulse/exceptions/cards/exception_rate.rb +3 -6
  57. data/app/models/rails_pulse/exceptions/cards/open_groups.rb +1 -4
  58. data/app/models/rails_pulse/exceptions/cards/total_occurrences.rb +3 -6
  59. data/app/models/rails_pulse/exceptions/charts/occurrence_volume.rb +4 -7
  60. data/app/models/rails_pulse/job_run.rb +1 -1
  61. data/app/models/rails_pulse/jobs/cards/failure_rate.rb +8 -14
  62. data/app/models/rails_pulse/jobs/cards/p95_duration.rb +8 -14
  63. data/app/models/rails_pulse/jobs/cards/total_runs.rb +5 -11
  64. data/app/models/rails_pulse/jobs/charts/duration.rb +1 -2
  65. data/app/models/rails_pulse/jobs/charts/execution_volume.rb +1 -1
  66. data/app/models/rails_pulse/jobs/charts/failure_rate.rb +1 -2
  67. data/app/models/rails_pulse/like_pattern.rb +35 -0
  68. data/app/models/rails_pulse/queries/cards/average_query_times.rb +6 -12
  69. data/app/models/rails_pulse/queries/cards/database_load.rb +0 -9
  70. data/app/models/rails_pulse/queries/cards/execution_rate.rb +5 -11
  71. data/app/models/rails_pulse/queries/cards/percentile_query_times.rb +6 -12
  72. data/app/models/rails_pulse/queries/charts/database_load.rb +5 -8
  73. data/app/models/rails_pulse/queries/charts/execution_volume.rb +1 -1
  74. data/app/models/rails_pulse/request.rb +1 -1
  75. data/app/models/rails_pulse/routes/cards/error_rates.rb +5 -15
  76. data/app/models/rails_pulse/routes/cards/percentile_response_times.rb +5 -17
  77. data/app/models/rails_pulse/routes/cards/request_count_totals.rb +5 -17
  78. data/app/models/rails_pulse/routes/charts/error_rate.rb +1 -1
  79. data/app/models/rails_pulse/routes/charts/request_volume.rb +1 -1
  80. data/app/models/rails_pulse/summary.rb +5 -0
  81. data/app/models/rails_pulse/tables/base.rb +5 -6
  82. data/app/models/rails_pulse/time_range.rb +309 -0
  83. data/app/models/rails_pulse/time_window.rb +15 -0
  84. data/app/models/rails_pulse/writer_heartbeat.rb +90 -0
  85. data/app/serializers/rails_pulse/api/v1/deployment_serializer.rb +23 -0
  86. data/app/serializers/rails_pulse/api/v1/exception_group_serializer.rb +23 -0
  87. data/app/serializers/rails_pulse/api/v1/exception_occurrence_serializer.rb +22 -0
  88. data/app/serializers/rails_pulse/api/v1/job_run_serializer.rb +28 -0
  89. data/app/serializers/rails_pulse/api/v1/job_serializer.rb +26 -0
  90. data/app/serializers/rails_pulse/api/v1/query_serializer.rb +25 -0
  91. data/app/serializers/rails_pulse/api/v1/request_serializer.rb +22 -0
  92. data/app/serializers/rails_pulse/api/v1/route_serializer.rb +19 -0
  93. data/app/services/rails_pulse/cloud/batch.rb +83 -0
  94. data/app/services/rails_pulse/cloud/client.rb +68 -0
  95. data/app/services/rails_pulse/cloud/combined_metrics.rb +49 -0
  96. data/app/services/rails_pulse/cloud/health_item.rb +77 -0
  97. data/app/services/rails_pulse/cloud/path_prefix.rb +50 -0
  98. data/app/services/rails_pulse/cloud/preview.rb +83 -0
  99. data/app/services/rails_pulse/cloud/query_shape.rb +195 -0
  100. data/app/services/rails_pulse/cloud/record_items.rb +60 -0
  101. data/app/services/rails_pulse/cloud/route_pattern.rb +111 -0
  102. data/app/services/rails_pulse/cloud/summary_items.rb +188 -0
  103. data/app/services/rails_pulse/cloud/sync.rb +275 -0
  104. data/app/services/rails_pulse/config_recommendations.rb +132 -0
  105. data/app/services/rails_pulse/deployment_comparison.rb +177 -0
  106. data/app/services/rails_pulse/exception_capture_service.rb +1 -1
  107. data/app/services/rails_pulse/operations/change_point.rb +1 -1
  108. data/app/services/rails_pulse/operations/comparison.rb +1 -1
  109. data/app/services/rails_pulse/period_insights.rb +131 -0
  110. data/app/services/rails_pulse/sql_query_normalizer.rb +222 -122
  111. data/app/services/rails_pulse/summary_service/from_child_periods.rb +161 -0
  112. data/app/services/rails_pulse/summary_service/from_raw_rows.rb +115 -0
  113. data/app/services/rails_pulse/summary_service/metrics.rb +38 -0
  114. data/app/services/rails_pulse/summary_service.rb +121 -133
  115. data/app/services/rails_pulse/tag_filter_service.rb +5 -5
  116. data/app/views/layouts/rails_pulse/_time_range_selector.html.erb +4 -1
  117. data/app/views/rails_pulse/components/_operation_details_popover.html.erb +1 -1
  118. data/app/views/rails_pulse/components/_table_head.html.erb +2 -2
  119. data/app/views/rails_pulse/dashboard/_chart_panel.html.erb +6 -1
  120. data/app/views/rails_pulse/dashboard/_deployments_panel.html.erb +2 -2
  121. data/app/views/rails_pulse/dashboard/_health_badge.html.erb +29 -20
  122. data/app/views/rails_pulse/dashboard/_storage_panel.html.erb +13 -3
  123. data/app/views/rails_pulse/dashboard/index.html.erb +16 -2
  124. data/app/views/rails_pulse/jobs/_chart_tabs.html.erb +1 -0
  125. data/app/views/rails_pulse/jobs/index.html.erb +1 -1
  126. data/app/views/rails_pulse/queries/_chart_tabs.html.erb +1 -0
  127. data/app/views/rails_pulse/queries/index.html.erb +1 -1
  128. data/app/views/rails_pulse/queries/show.html.erb +1 -1
  129. data/app/views/rails_pulse/requests/index.html.erb +1 -1
  130. data/app/views/rails_pulse/routes/_chart_tabs.html.erb +1 -0
  131. data/app/views/rails_pulse/routes/index.html.erb +1 -1
  132. data/app/views/rails_pulse/routes/show.html.erb +1 -1
  133. data/app/views/rails_pulse/shared/_aggregation_zone_badge.html.erb +4 -0
  134. data/app/views/rails_pulse/storage/show.html.erb +39 -2
  135. data/config/routes.rb +23 -0
  136. data/db/rails_pulse_cloud_schema.rb +58 -0
  137. data/db/rails_pulse_migrate/20260506000001_create_rails_pulse_exceptions.rb +1 -1
  138. data/db/rails_pulse_migrate/20260921000001_create_rails_pulse_events.rb +21 -0
  139. data/db/rails_pulse_schema.rb +20 -2
  140. data/exe/rails-pulse +7 -0
  141. data/lib/generators/rails_pulse/base_methods.rb +19 -0
  142. data/lib/generators/rails_pulse/install_cloud_generator.rb +84 -0
  143. data/lib/generators/rails_pulse/templates/db/rails_pulse_cloud_schema.rb +58 -0
  144. data/lib/generators/rails_pulse/templates/db/rails_pulse_schema.rb +20 -2
  145. data/lib/generators/rails_pulse/templates/migrations/install_rails_pulse_cloud_tables.rb +30 -0
  146. data/lib/generators/rails_pulse/templates/rails_pulse.rb +71 -19
  147. data/lib/generators/rails_pulse/upgrade_generator.rb +9 -16
  148. data/lib/rails_pulse/cleanup_service.rb +37 -20
  149. data/lib/rails_pulse/cli/agent_files/agents.md +89 -0
  150. data/lib/rails_pulse/cli/agent_files/claude_skill.md +182 -0
  151. data/lib/rails_pulse/cli/base_command.rb +37 -0
  152. data/lib/rails_pulse/cli/client.rb +64 -0
  153. data/lib/rails_pulse/cli/config.rb +87 -0
  154. data/lib/rails_pulse/cli/configure.rb +75 -0
  155. data/lib/rails_pulse/cli/coverage.rb +91 -0
  156. data/lib/rails_pulse/cli/deployments.rb +54 -0
  157. data/lib/rails_pulse/cli/exceptions.rb +116 -0
  158. data/lib/rails_pulse/cli/formatter.rb +31 -0
  159. data/lib/rails_pulse/cli/insights.rb +70 -0
  160. data/lib/rails_pulse/cli/install.rb +145 -0
  161. data/lib/rails_pulse/cli/job_runs.rb +53 -0
  162. data/lib/rails_pulse/cli/jobs.rb +71 -0
  163. data/lib/rails_pulse/cli/main.rb +76 -0
  164. data/lib/rails_pulse/cli/mcp.rb +47 -0
  165. data/lib/rails_pulse/cli/queries.rb +67 -0
  166. data/lib/rails_pulse/cli/requests.rb +57 -0
  167. data/lib/rails_pulse/cli/routes.rb +73 -0
  168. data/lib/rails_pulse/configuration.rb +100 -2
  169. data/lib/rails_pulse/current.rb +11 -0
  170. data/lib/rails_pulse/engine.rb +12 -3
  171. data/lib/rails_pulse/installers/config_updater.rb +9 -2
  172. data/lib/rails_pulse/job_run_collector.rb +15 -15
  173. data/lib/rails_pulse/mcp/server.rb +78 -0
  174. data/lib/rails_pulse/mcp/tools/coverage.rb +96 -0
  175. data/lib/rails_pulse/mcp/tools/deployments.rb +117 -0
  176. data/lib/rails_pulse/mcp/tools/endpoint.rb +167 -0
  177. data/lib/rails_pulse/mcp/tools/errors.rb +104 -0
  178. data/lib/rails_pulse/mcp/tools/exception_detail.rb +118 -0
  179. data/lib/rails_pulse/mcp/tools/exceptions.rb +128 -0
  180. data/lib/rails_pulse/mcp/tools/helpers.rb +162 -0
  181. data/lib/rails_pulse/mcp/tools/insights.rb +99 -0
  182. data/lib/rails_pulse/mcp/tools/jobs.rb +155 -0
  183. data/lib/rails_pulse/mcp/tools/queries.rb +142 -0
  184. data/lib/rails_pulse/mcp/tools/routes.rb +108 -0
  185. data/lib/rails_pulse/mcp/tools/slow_requests.rb +118 -0
  186. data/lib/rails_pulse/middleware/request_collector.rb +9 -9
  187. data/lib/rails_pulse/scoped_inflector.rb +35 -0
  188. data/lib/rails_pulse/statistics.rb +27 -0
  189. data/lib/rails_pulse/subscribers/exception_subscriber.rb +2 -2
  190. data/lib/rails_pulse/subscribers/operation_subscriber.rb +15 -29
  191. data/lib/rails_pulse/tasks/status_reporter.rb +201 -0
  192. data/lib/rails_pulse/tracker.rb +76 -14
  193. data/lib/rails_pulse/version.rb +1 -1
  194. data/lib/rails_pulse.rb +0 -4
  195. data/lib/tasks/rails_pulse.rake +6 -0
  196. data/lib/tasks/rails_pulse_tasks.rake +7 -0
  197. data/public/rails-pulse-assets/rails-pulse.css +1 -1
  198. data/public/rails-pulse-assets/rails-pulse.js +13 -13
  199. metadata +123 -24
  200. data/app/controllers/concerns/page_timings.rb +0 -10
  201. data/app/controllers/concerns/response_range_concern.rb +0 -45
  202. data/app/controllers/concerns/time_range_concern.rb +0 -152
  203. data/app/controllers/concerns/zoom_range_concern.rb +0 -86
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails_pulse
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.2
4
+ version: 0.5.0.pre.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Scott Harvey
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-10-01 00:00:00.000000000 Z
11
+ date: 2026-10-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -16,7 +16,7 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 7.1.0
19
+ version: 7.2.0
20
20
  - - "<"
21
21
  - !ruby/object:Gem::Version
22
22
  version: 9.0.0
@@ -26,24 +26,10 @@ dependencies:
26
26
  requirements:
27
27
  - - ">="
28
28
  - !ruby/object:Gem::Version
29
- version: 7.1.0
29
+ version: 7.2.0
30
30
  - - "<"
31
31
  - !ruby/object:Gem::Version
32
32
  version: 9.0.0
33
- - !ruby/object:Gem::Dependency
34
- name: request_store
35
- requirement: !ruby/object:Gem::Requirement
36
- requirements:
37
- - - "~>"
38
- - !ruby/object:Gem::Version
39
- version: '1.5'
40
- type: :runtime
41
- prerelease: false
42
- version_requirements: !ruby/object:Gem::Requirement
43
- requirements:
44
- - - "~>"
45
- - !ruby/object:Gem::Version
46
- version: '1.5'
47
33
  - !ruby/object:Gem::Dependency
48
34
  name: ransack
49
35
  requirement: !ruby/object:Gem::Requirement
@@ -64,6 +50,20 @@ dependencies:
64
50
  - - "<"
65
51
  - !ruby/object:Gem::Version
66
52
  version: '6'
53
+ - !ruby/object:Gem::Dependency
54
+ name: thor
55
+ requirement: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - "~>"
58
+ - !ruby/object:Gem::Version
59
+ version: '1.0'
60
+ type: :runtime
61
+ prerelease: false
62
+ version_requirements: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - "~>"
65
+ - !ruby/object:Gem::Version
66
+ version: '1.0'
67
67
  - !ruby/object:Gem::Dependency
68
68
  name: sqlite3
69
69
  requirement: !ruby/object:Gem::Requirement
@@ -182,6 +182,20 @@ dependencies:
182
182
  - - ">="
183
183
  - !ruby/object:Gem::Version
184
184
  version: '0'
185
+ - !ruby/object:Gem::Dependency
186
+ name: mcp
187
+ requirement: !ruby/object:Gem::Requirement
188
+ requirements:
189
+ - - "~>"
190
+ - !ruby/object:Gem::Version
191
+ version: '1.0'
192
+ type: :development
193
+ prerelease: false
194
+ version_requirements: !ruby/object:Gem::Requirement
195
+ requirements:
196
+ - - "~>"
197
+ - !ruby/object:Gem::Version
198
+ version: '1.0'
185
199
  description: Self-hosted performance monitoring engine for Rails apps. Tracks slow
186
200
  requests, N+1 queries, and SQL performance. All data stays in your own database
187
201
  — no third-party cloud required.
@@ -189,6 +203,7 @@ email:
189
203
  - hey@railspulse.com
190
204
  executables:
191
205
  - rails_pulse_server
206
+ - rails-pulse
192
207
  extensions: []
193
208
  extra_rdoc_files: []
194
209
  files:
@@ -231,14 +246,21 @@ files:
231
246
  - app/controllers/concerns/chart_table_concern.rb
232
247
  - app/controllers/concerns/deployment_markers_concern.rb
233
248
  - app/controllers/concerns/metric_card_concern.rb
234
- - app/controllers/concerns/page_timings.rb
235
249
  - app/controllers/concerns/pagination_concern.rb
236
250
  - app/controllers/concerns/ransack_params_concern.rb
237
- - app/controllers/concerns/response_range_concern.rb
238
251
  - app/controllers/concerns/session_filters_concern.rb
239
252
  - app/controllers/concerns/tag_filter_concern.rb
240
- - app/controllers/concerns/time_range_concern.rb
241
- - app/controllers/concerns/zoom_range_concern.rb
253
+ - app/controllers/rails_pulse/api/v1/base_controller.rb
254
+ - app/controllers/rails_pulse/api/v1/capabilities_controller.rb
255
+ - app/controllers/rails_pulse/api/v1/coverage_controller.rb
256
+ - app/controllers/rails_pulse/api/v1/deployments_controller.rb
257
+ - app/controllers/rails_pulse/api/v1/exceptions_controller.rb
258
+ - app/controllers/rails_pulse/api/v1/insights_controller.rb
259
+ - app/controllers/rails_pulse/api/v1/job_runs_controller.rb
260
+ - app/controllers/rails_pulse/api/v1/jobs_controller.rb
261
+ - app/controllers/rails_pulse/api/v1/queries_controller.rb
262
+ - app/controllers/rails_pulse/api/v1/requests_controller.rb
263
+ - app/controllers/rails_pulse/api/v1/routes_controller.rb
242
264
  - app/controllers/rails_pulse/application_controller.rb
243
265
  - app/controllers/rails_pulse/assets_controller.rb
244
266
  - app/controllers/rails_pulse/csp_test_controller.rb
@@ -292,7 +314,10 @@ files:
292
314
  - app/jobs/rails_pulse/application_job.rb
293
315
  - app/jobs/rails_pulse/backfill_summaries_job.rb
294
316
  - app/jobs/rails_pulse/cleanup_job.rb
317
+ - app/jobs/rails_pulse/cloud_health_job.rb
318
+ - app/jobs/rails_pulse/cloud_sync_job.rb
295
319
  - app/jobs/rails_pulse/summary_job.rb
320
+ - app/models/concerns/rails_pulse/has_metadata.rb
296
321
  - app/models/concerns/rails_pulse/has_performance_status.rb
297
322
  - app/models/concerns/rails_pulse/taggable.rb
298
323
  - app/models/rails_pulse/application_record.rb
@@ -301,8 +326,12 @@ files:
301
326
  - app/models/rails_pulse/charts/base.rb
302
327
  - app/models/rails_pulse/charts/operations_chart.rb
303
328
  - app/models/rails_pulse/charts/percentile_chart_base.rb
329
+ - app/models/rails_pulse/cleanup_run.rb
330
+ - app/models/rails_pulse/cloud/buffered_batch.rb
331
+ - app/models/rails_pulse/cloud/installation.rb
304
332
  - app/models/rails_pulse/dashboard/charts/response_time_percentiles.rb
305
333
  - app/models/rails_pulse/dashboard/charts/throughput_and_errors.rb
334
+ - app/models/rails_pulse/dashboard/concerns/attention_classification.rb
306
335
  - app/models/rails_pulse/dashboard/concerns/threshold_constants.rb
307
336
  - app/models/rails_pulse/dashboard/concerns/time_range_helper.rb
308
337
  - app/models/rails_pulse/dashboard/health_summary.rb
@@ -310,6 +339,7 @@ files:
310
339
  - app/models/rails_pulse/dashboard/storage_pressure.rb
311
340
  - app/models/rails_pulse/dashboard/storage_status.rb
312
341
  - app/models/rails_pulse/deployment.rb
342
+ - app/models/rails_pulse/event.rb
313
343
  - app/models/rails_pulse/exception_group.rb
314
344
  - app/models/rails_pulse/exception_occurrence.rb
315
345
  - app/models/rails_pulse/exceptions/cards/exception_rate.rb
@@ -326,6 +356,7 @@ files:
326
356
  - app/models/rails_pulse/jobs/charts/execution_volume.rb
327
357
  - app/models/rails_pulse/jobs/charts/failure_rate.rb
328
358
  - app/models/rails_pulse/jobs/tables/index.rb
359
+ - app/models/rails_pulse/like_pattern.rb
329
360
  - app/models/rails_pulse/operation.rb
330
361
  - app/models/rails_pulse/queries/cards/average_query_times.rb
331
362
  - app/models/rails_pulse/queries/cards/database_load.rb
@@ -347,8 +378,18 @@ files:
347
378
  - app/models/rails_pulse/routes/tables/index.rb
348
379
  - app/models/rails_pulse/summary.rb
349
380
  - app/models/rails_pulse/tables/base.rb
381
+ - app/models/rails_pulse/time_range.rb
350
382
  - app/models/rails_pulse/time_range_preference.rb
351
383
  - app/models/rails_pulse/time_window.rb
384
+ - app/models/rails_pulse/writer_heartbeat.rb
385
+ - app/serializers/rails_pulse/api/v1/deployment_serializer.rb
386
+ - app/serializers/rails_pulse/api/v1/exception_group_serializer.rb
387
+ - app/serializers/rails_pulse/api/v1/exception_occurrence_serializer.rb
388
+ - app/serializers/rails_pulse/api/v1/job_run_serializer.rb
389
+ - app/serializers/rails_pulse/api/v1/job_serializer.rb
390
+ - app/serializers/rails_pulse/api/v1/query_serializer.rb
391
+ - app/serializers/rails_pulse/api/v1/request_serializer.rb
392
+ - app/serializers/rails_pulse/api/v1/route_serializer.rb
352
393
  - app/services/rails_pulse/analysis/backtrace_analyzer.rb
353
394
  - app/services/rails_pulse/analysis/base_analyzer.rb
354
395
  - app/services/rails_pulse/analysis/explain_plan_analyzer.rb
@@ -356,6 +397,19 @@ files:
356
397
  - app/services/rails_pulse/analysis/n_plus_one_detector.rb
357
398
  - app/services/rails_pulse/analysis/query_characteristics_analyzer.rb
358
399
  - app/services/rails_pulse/analysis/suggestion_generator.rb
400
+ - app/services/rails_pulse/cloud/batch.rb
401
+ - app/services/rails_pulse/cloud/client.rb
402
+ - app/services/rails_pulse/cloud/combined_metrics.rb
403
+ - app/services/rails_pulse/cloud/health_item.rb
404
+ - app/services/rails_pulse/cloud/path_prefix.rb
405
+ - app/services/rails_pulse/cloud/preview.rb
406
+ - app/services/rails_pulse/cloud/query_shape.rb
407
+ - app/services/rails_pulse/cloud/record_items.rb
408
+ - app/services/rails_pulse/cloud/route_pattern.rb
409
+ - app/services/rails_pulse/cloud/summary_items.rb
410
+ - app/services/rails_pulse/cloud/sync.rb
411
+ - app/services/rails_pulse/config_recommendations.rb
412
+ - app/services/rails_pulse/deployment_comparison.rb
359
413
  - app/services/rails_pulse/exception_capture_service.rb
360
414
  - app/services/rails_pulse/exception_message_sanitizer.rb
361
415
  - app/services/rails_pulse/operation_suggestions.rb
@@ -365,6 +419,7 @@ files:
365
419
  - app/services/rails_pulse/operations/metric.rb
366
420
  - app/services/rails_pulse/operations/series.rb
367
421
  - app/services/rails_pulse/operations/subject.rb
422
+ - app/services/rails_pulse/period_insights.rb
368
423
  - app/services/rails_pulse/query_analysis_service.rb
369
424
  - app/services/rails_pulse/route_controller_action_backfiller.rb
370
425
  - app/services/rails_pulse/route_merger.rb
@@ -373,6 +428,9 @@ files:
373
428
  - app/services/rails_pulse/route_recognizer.rb
374
429
  - app/services/rails_pulse/sql_query_normalizer.rb
375
430
  - app/services/rails_pulse/summary_service.rb
431
+ - app/services/rails_pulse/summary_service/from_child_periods.rb
432
+ - app/services/rails_pulse/summary_service/from_raw_rows.rb
433
+ - app/services/rails_pulse/summary_service/metrics.rb
376
434
  - app/services/rails_pulse/tag_filter_service.rb
377
435
  - app/views/layouts/rails_pulse/_global_filters.html.erb
378
436
  - app/views/layouts/rails_pulse/_menu_items.html.erb
@@ -455,6 +513,7 @@ files:
455
513
  - app/views/rails_pulse/routes/_table.html.erb
456
514
  - app/views/rails_pulse/routes/index.html.erb
457
515
  - app/views/rails_pulse/routes/show.html.erb
516
+ - app/views/rails_pulse/shared/_aggregation_zone_badge.html.erb
458
517
  - app/views/rails_pulse/shared/_deployment_markers_toggle.html.erb
459
518
  - app/views/rails_pulse/shared/schema_outdated.html.erb
460
519
  - app/views/rails_pulse/skeletons/_chart.html.erb
@@ -465,6 +524,7 @@ files:
465
524
  - app/views/rails_pulse/tags/_tag_manager.html.erb
466
525
  - config/importmap.rb
467
526
  - config/routes.rb
527
+ - db/rails_pulse_cloud_schema.rb
468
528
  - db/rails_pulse_migrate/20260417000001_add_diagnostic_fields.rb
469
529
  - db/rails_pulse_migrate/20260419000001_add_p95_p99_duration_to_rails_pulse_jobs.rb
470
530
  - db/rails_pulse_migrate/20260420000001_expand_normalized_query_column.rb
@@ -477,13 +537,18 @@ files:
477
537
  - db/rails_pulse_migrate/20260610000002_change_rails_pulse_routes_to_multi_verb_model.rb
478
538
  - db/rails_pulse_migrate/20260610000003_add_null_action_unique_index_to_routes.rb
479
539
  - db/rails_pulse_migrate/20260823000001_add_location_to_exception_groups.rb
540
+ - db/rails_pulse_migrate/20260921000001_create_rails_pulse_events.rb
480
541
  - db/rails_pulse_schema.rb
542
+ - exe/rails-pulse
481
543
  - exe/rails_pulse_server
482
544
  - lib/generators/rails_pulse/base_methods.rb
483
545
  - lib/generators/rails_pulse/convert_to_migrations_generator.rb
546
+ - lib/generators/rails_pulse/install_cloud_generator.rb
484
547
  - lib/generators/rails_pulse/install_generator.rb
485
548
  - lib/generators/rails_pulse/schema_parser.rb
549
+ - lib/generators/rails_pulse/templates/db/rails_pulse_cloud_schema.rb
486
550
  - lib/generators/rails_pulse/templates/db/rails_pulse_schema.rb
551
+ - lib/generators/rails_pulse/templates/migrations/install_rails_pulse_cloud_tables.rb
487
552
  - lib/generators/rails_pulse/templates/migrations/install_rails_pulse_tables.rb
488
553
  - lib/generators/rails_pulse/templates/migrations/upgrade_rails_pulse_tables.rb
489
554
  - lib/generators/rails_pulse/templates/rails_pulse.rb
@@ -494,12 +559,45 @@ files:
494
559
  - lib/rails_pulse/adapters/job_wrapper.rb
495
560
  - lib/rails_pulse/adapters/sidekiq_middleware.rb
496
561
  - lib/rails_pulse/cleanup_service.rb
562
+ - lib/rails_pulse/cli/agent_files/agents.md
563
+ - lib/rails_pulse/cli/agent_files/claude_skill.md
564
+ - lib/rails_pulse/cli/base_command.rb
565
+ - lib/rails_pulse/cli/client.rb
566
+ - lib/rails_pulse/cli/config.rb
567
+ - lib/rails_pulse/cli/configure.rb
568
+ - lib/rails_pulse/cli/coverage.rb
569
+ - lib/rails_pulse/cli/deployments.rb
570
+ - lib/rails_pulse/cli/exceptions.rb
571
+ - lib/rails_pulse/cli/formatter.rb
572
+ - lib/rails_pulse/cli/insights.rb
573
+ - lib/rails_pulse/cli/install.rb
574
+ - lib/rails_pulse/cli/job_runs.rb
575
+ - lib/rails_pulse/cli/jobs.rb
576
+ - lib/rails_pulse/cli/main.rb
577
+ - lib/rails_pulse/cli/mcp.rb
578
+ - lib/rails_pulse/cli/queries.rb
579
+ - lib/rails_pulse/cli/requests.rb
580
+ - lib/rails_pulse/cli/routes.rb
497
581
  - lib/rails_pulse/configuration.rb
582
+ - lib/rails_pulse/current.rb
498
583
  - lib/rails_pulse/engine.rb
499
584
  - lib/rails_pulse/installers/config_installer.rb
500
585
  - lib/rails_pulse/installers/config_updater.rb
501
586
  - lib/rails_pulse/installers/migration_installer.rb
502
587
  - lib/rails_pulse/job_run_collector.rb
588
+ - lib/rails_pulse/mcp/server.rb
589
+ - lib/rails_pulse/mcp/tools/coverage.rb
590
+ - lib/rails_pulse/mcp/tools/deployments.rb
591
+ - lib/rails_pulse/mcp/tools/endpoint.rb
592
+ - lib/rails_pulse/mcp/tools/errors.rb
593
+ - lib/rails_pulse/mcp/tools/exception_detail.rb
594
+ - lib/rails_pulse/mcp/tools/exceptions.rb
595
+ - lib/rails_pulse/mcp/tools/helpers.rb
596
+ - lib/rails_pulse/mcp/tools/insights.rb
597
+ - lib/rails_pulse/mcp/tools/jobs.rb
598
+ - lib/rails_pulse/mcp/tools/queries.rb
599
+ - lib/rails_pulse/mcp/tools/routes.rb
600
+ - lib/rails_pulse/mcp/tools/slow_requests.rb
503
601
  - lib/rails_pulse/middleware/asset_server.rb
504
602
  - lib/rails_pulse/middleware/request_collector.rb
505
603
  - lib/rails_pulse/packaged_assets.rb
@@ -507,6 +605,7 @@ files:
507
605
  - lib/rails_pulse/rack_compat.rb
508
606
  - lib/rails_pulse/route_indexes.rb
509
607
  - lib/rails_pulse/schema_check.rb
608
+ - lib/rails_pulse/scoped_inflector.rb
510
609
  - lib/rails_pulse/standalone.rb
511
610
  - lib/rails_pulse/statistics.rb
512
611
  - lib/rails_pulse/stats/cleanup_stats_reporter.rb
@@ -551,7 +650,7 @@ metadata:
551
650
  bug_tracker_uri: https://github.com/railspulse/rails_pulse/issues
552
651
  rubygems_mfa_required: 'true'
553
652
  post_install_message: |
554
- Rails Pulse 0.4.2 installed.
653
+ Rails Pulse 0.5.0.pre.2 installed.
555
654
 
556
655
  UPGRADING from any 0.3.x? This release changes route identity and DROPS
557
656
  rails_pulse_routes.method. Back up your database first, then run:
@@ -574,7 +673,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
574
673
  requirements:
575
674
  - - ">="
576
675
  - !ruby/object:Gem::Version
577
- version: 3.1.0
676
+ version: 3.2.0
578
677
  required_rubygems_version: !ruby/object:Gem::Requirement
579
678
  requirements:
580
679
  - - ">="
@@ -1,10 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- PageTimings = Struct.new(
4
- :start_time, :end_time,
5
- :table_start_time, :table_end_time,
6
- :zoom_start, :zoom_end,
7
- :time_diff_hours, :start_duration,
8
- :selected_time_range, :selected_response_range,
9
- keyword_init: true
10
- )
@@ -1,45 +0,0 @@
1
- # ResponseRangeConcern
2
- #
3
- # Handles performance threshold filtering (slow, very_slow, critical).
4
- # Converts symbolic threshold names to numeric values based on resource type.
5
- # Supports both page-specific duration filters and global performance threshold filters.
6
- module ResponseRangeConcern
7
- extend ActiveSupport::Concern
8
- include RansackParamsConcern
9
-
10
- def setup_duration_range(type = :route)
11
- thresholds = RailsPulse.configuration.public_send("#{type}_thresholds")
12
-
13
- # Check all duration-related parameters
14
- # avg_duration for Summary, duration for Request/Operation, duration_gteq for direct Ransack filtering
15
- duration_param = ransack_params[:avg_duration] || ransack_params[:duration] || ransack_params[:duration_gteq]
16
-
17
- # Priority 1: Page-specific duration filter
18
- if duration_param.present?
19
- selected_range = duration_param
20
- start_duration =
21
- case duration_param.to_sym
22
- when :slow then thresholds[:slow]
23
- when :very_slow then thresholds[:very_slow]
24
- when :critical then thresholds[:critical]
25
- else 0
26
- end
27
- # Priority 2: Global performance threshold filter
28
- elsif (global_threshold = session_global_filters["performance_threshold"]).present?
29
- selected_range = global_threshold.to_sym
30
- start_duration =
31
- case global_threshold.to_sym
32
- when :slow then thresholds[:slow]
33
- when :very_slow then thresholds[:very_slow]
34
- when :critical then thresholds[:critical]
35
- else 0
36
- end
37
- # Priority 3: No filter (show all)
38
- else
39
- start_duration = 0
40
- selected_range = :all
41
- end
42
-
43
- [ start_duration, selected_range ]
44
- end
45
- end
@@ -1,152 +0,0 @@
1
- # TimeRangeConcern
2
- #
3
- # Handles time range selection and filtering across all controllers.
4
- # Supports multiple time range sources with priority order:
5
- # 1. Page-specific preset/custom range (from dropdown/picker)
6
- # 2. Chart zoom parameters
7
- # 3. Time range selector (session)
8
- # 4. Global filters (session)
9
- # 5. Default time range
10
- #
11
- # Normalizes times to beginning/end of hour or day based on range duration.
12
- module TimeRangeConcern
13
- extend ActiveSupport::Concern
14
- include RansackParamsConcern
15
-
16
- included do
17
- # Define the constant in the including class - ordered by most common usage
18
- const_set(:TIME_RANGE_OPTIONS, [
19
- [ "Last 24 hours", :last_24_hours ],
20
- [ "Last 7 days", :last_7_days ],
21
- [ "Last 14 days", :last_14_days ],
22
- [ "Last 30 days", :last_30_days ],
23
- [ "Custom range", :custom ]
24
- ].freeze)
25
- end
26
-
27
- def default_time_range_key
28
- :last_24_hours
29
- end
30
-
31
- def setup_time_range
32
- default_key = default_time_range_key
33
-
34
- start_time = case default_key
35
- when :last_24_hours then 1.day.ago
36
- when :last_7_days then 1.week.ago
37
- when :last_14_days then 2.weeks.ago
38
- when :last_30_days then 1.month.ago
39
- else 1.day.ago
40
- end
41
- end_time = Time.zone.now
42
- selected_time_range = default_key
43
-
44
- # Normalized up front so a symbol-keyed hash from a Marshal-backed session
45
- # store reads the same as the string-keyed one the cookie store produces;
46
- # anything unrecognized becomes nil and falls through to later priorities.
47
- session_preference = RailsPulse::TimeRangePreference.normalize(session[:time_range_preference])
48
-
49
- # Priority 1: Page-specific preset from dropdown (check this first!)
50
- if ransack_params[:period_start_range].present? && ransack_params[:period_start_range].to_sym != :custom
51
- # Predefined time range from dropdown
52
- selected_time_range = ransack_params[:period_start_range]
53
-
54
- start_time =
55
- case selected_time_range.to_sym
56
- when :last_24_hours then 1.day.ago
57
- when :last_7_days then 1.week.ago
58
- when :last_14_days then 2.weeks.ago
59
- when :last_30_days then 1.month.ago
60
- else 1.day.ago # Default fallback
61
- end
62
- # Priority 2: Page-specific custom datetime range from picker (only if period_start_range is :custom)
63
- elsif ransack_params[:period_start_range].present? && ransack_params[:period_start_range].to_sym == :custom && ransack_params[:custom_date_range].present? && ransack_params[:custom_date_range].include?(" to ")
64
- # Custom datetime range from custom range picker
65
- dates = ransack_params[:custom_date_range].split(" to ")
66
- custom_start = parse_time_param(dates[0].strip)
67
- custom_end = parse_time_param(dates[1].strip)
68
- if custom_start && custom_end
69
- start_time = custom_start
70
- end_time = custom_end
71
- selected_time_range = :custom
72
- end
73
- # Priority 3: Page-specific filters (chart zoom)
74
- elsif ransack_params[:occurred_at_gteq].present? && ransack_params[:occurred_at_lt].present?
75
- # Custom time range from chart zoom
76
- zoom_start = parse_time_param(ransack_params[:occurred_at_gteq])
77
- zoom_end = parse_time_param(ransack_params[:occurred_at_lt])
78
- if zoom_start && zoom_end
79
- start_time = zoom_start
80
- end_time = zoom_end
81
- selected_time_range = :custom
82
- end
83
- # Priority 4: Time range selector (from session)
84
- elsif RailsPulse::TimeRangePreference.custom?(session_preference)
85
- # Custom range from time range selector
86
- start_time = parse_time_param(session_preference["start_time"]) || start_time
87
- end_time = parse_time_param(session_preference["end_time"]) || end_time
88
- selected_time_range = :custom
89
- elsif session_preference.is_a?(String) && session_preference.present?
90
- # Preset from time range selector
91
- selected_time_range = session_preference.to_sym
92
- start_time =
93
- case selected_time_range
94
- when :last_24_hours then 1.day.ago
95
- when :last_7_days then 1.week.ago
96
- when :last_14_days then 2.weeks.ago
97
- when :last_30_days then 1.month.ago
98
- else start_time
99
- end
100
- # Priority 5: Global filters (from session)
101
- elsif session_global_filters["start_time"].present? || session_global_filters["end_time"].present?
102
- start_time = parse_time_param(session_global_filters["start_time"]) || start_time
103
- end_time = parse_time_param(session_global_filters["end_time"]) || end_time
104
- selected_time_range = :custom
105
- end
106
- # Priority 6: Default time range (already set above)
107
-
108
- time_diff = (end_time.to_i - start_time.to_i) / 3600.0
109
-
110
- # in_time_zone before rounding: a parsed custom-range string carries the
111
- # server OS's local offset, not Time.zone, and beginning_of_day/_hour
112
- # round in whatever offset the receiver has. Summary data is always
113
- # bucketed by Time.zone, so without this the boundary can miss every
114
- # summary row and render charts empty. No-op for already-Time.zone values.
115
- start_time = start_time.in_time_zone
116
- end_time = end_time.in_time_zone
117
-
118
- if time_diff <= 25
119
- start_time = start_time.beginning_of_hour
120
- end_time = end_time.end_of_hour
121
- else
122
- start_time = start_time.beginning_of_day
123
- end_time = end_time.end_of_day
124
- end
125
-
126
- # Convert selected_time_range to string for backward compatibility with tests
127
- [ start_time.to_i, end_time.to_i, selected_time_range.to_s, time_diff ]
128
- end
129
-
130
- private
131
-
132
- # Returns nil for anything that does not parse, so a hand-edited or stale
133
- # value falls back to the default range instead of raising.
134
- def parse_time_param(param)
135
- case param
136
- when Time, DateTime
137
- param.in_time_zone
138
- when String
139
- return nil if param.blank?
140
-
141
- # Parse as server local time (not UTC, not Time.zone)
142
- # This ensures flatpickr datetime strings are interpreted in server's timezone
143
- Time.parse(param).localtime
144
- when Numeric
145
- Time.zone.at(param)
146
- else
147
- nil
148
- end
149
- rescue ArgumentError, TypeError, RangeError
150
- nil
151
- end
152
- end
@@ -1,86 +0,0 @@
1
- # ZoomRangeConcern
2
- #
3
- # Handles chart zoom and column selection for filtering table data.
4
- # When users zoom into a chart or click a chart column, this concern calculates
5
- # the appropriate time boundaries for filtering the table while keeping the chart unchanged.
6
- # Normalizes zoom times to hour/day boundaries based on the overall time range.
7
- module ZoomRangeConcern
8
- extend ActiveSupport::Concern
9
-
10
- def setup_zoom_range(main_start_time, main_end_time)
11
- # Extract column selection parameter (but don't delete it yet - we need it for the view)
12
- selected_column_time = params[:selected_column_time]
13
-
14
- # Extract zoom parameters from params (this removes them from params)
15
- zoom_start = params.delete(:zoom_start_time)
16
- zoom_end = params.delete(:zoom_end_time)
17
-
18
- # Handle column selection with highest precedence for table filtering
19
- if selected_column_time
20
- column_start, column_end = normalize_column_time(selected_column_time.to_i, main_start_time, main_end_time)
21
- table_start_time = column_start
22
- table_end_time = column_end
23
- # Don't set zoom times for column selection - let chart show full range
24
- return [ zoom_start, zoom_end, table_start_time, table_end_time ]
25
- end
26
-
27
- # Normalize zoom times to beginning/end of day or hour like we do for main time range
28
- if zoom_start && zoom_end
29
- zoom_start, zoom_end = normalize_zoom_times(zoom_start.to_i, zoom_end.to_i)
30
- end
31
-
32
- # Calculate table times - use zoom if present, otherwise fallback to main times
33
- table_start_time = zoom_start || main_start_time
34
- table_end_time = zoom_end || main_end_time
35
-
36
- [ zoom_start, zoom_end, table_start_time, table_end_time ]
37
- end
38
-
39
- private
40
-
41
- def normalize_column_time(column_time, main_start_time, main_end_time)
42
- # Convert from JavaScript milliseconds to Unix seconds
43
- # Chart data uses milliseconds (timestamp * 1000), so divide by 1000 to get seconds
44
- column_time_seconds = column_time / 1000
45
-
46
- # Determine period type based on main time range (same logic as ChartTableConcern)
47
- time_diff_hours = (main_end_time - main_start_time) / 3600.0
48
-
49
- if time_diff_hours <= 25
50
- # Hourly period - normalize to beginning/end of hour
51
- column_time_obj = Time.zone&.at(column_time_seconds) || Time.at(column_time_seconds)
52
- start_time = column_time_obj&.beginning_of_hour || column_time_obj
53
- end_time = column_time_obj&.end_of_hour || column_time_obj
54
- else
55
- # Daily period - normalize to beginning/end of day
56
- column_time_obj = Time.zone&.at(column_time_seconds) || Time.at(column_time_seconds)
57
- start_time = column_time_obj&.beginning_of_day || column_time_obj
58
- end_time = column_time_obj&.end_of_day || column_time_obj
59
- end
60
-
61
- [ start_time.to_i, end_time.to_i ]
62
- end
63
-
64
- def normalize_zoom_times(start_time, end_time)
65
- # Convert from JavaScript milliseconds to Unix seconds
66
- # Chart data uses milliseconds (timestamp * 1000), so divide by 1000 to get seconds
67
- start_time_seconds = start_time / 1000
68
- end_time_seconds = end_time / 1000
69
-
70
- time_diff = (end_time_seconds - start_time_seconds) / 3600.0
71
-
72
- if time_diff <= 25
73
- start_time_obj = Time.zone&.at(start_time_seconds) || Time.at(start_time_seconds)
74
- end_time_obj = Time.zone&.at(end_time_seconds) || Time.at(end_time_seconds)
75
- start_time = start_time_obj&.beginning_of_hour || start_time_obj
76
- end_time = end_time_obj&.end_of_hour || end_time_obj
77
- else
78
- start_time_obj = Time.zone&.at(start_time_seconds) || Time.at(start_time_seconds)
79
- end_time_obj = Time.zone&.at(end_time_seconds) || Time.at(end_time_seconds)
80
- start_time = start_time_obj&.beginning_of_day || start_time_obj
81
- end_time = end_time_obj&.end_of_day || end_time_obj
82
- end
83
-
84
- [ start_time.to_i, end_time.to_i ]
85
- end
86
- end