rails_pulse 0.1.0

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 (160) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +638 -0
  4. data/Rakefile +207 -0
  5. data/app/assets/images/rails_pulse/dashboard.png +0 -0
  6. data/app/assets/images/rails_pulse/menu.svg +1 -0
  7. data/app/assets/images/rails_pulse/rails-pulse-logo.png +0 -0
  8. data/app/assets/images/rails_pulse/request.png +0 -0
  9. data/app/assets/images/rails_pulse/routes.png +0 -0
  10. data/app/assets/stylesheets/rails_pulse/application.css +102 -0
  11. data/app/assets/stylesheets/rails_pulse/components/alert.css +24 -0
  12. data/app/assets/stylesheets/rails_pulse/components/badge.css +58 -0
  13. data/app/assets/stylesheets/rails_pulse/components/base.css +79 -0
  14. data/app/assets/stylesheets/rails_pulse/components/breadcrumb.css +31 -0
  15. data/app/assets/stylesheets/rails_pulse/components/button.css +99 -0
  16. data/app/assets/stylesheets/rails_pulse/components/card.css +19 -0
  17. data/app/assets/stylesheets/rails_pulse/components/chart.css +18 -0
  18. data/app/assets/stylesheets/rails_pulse/components/csp_safe_positioning.css +86 -0
  19. data/app/assets/stylesheets/rails_pulse/components/descriptive_list.css +9 -0
  20. data/app/assets/stylesheets/rails_pulse/components/dialog.css +56 -0
  21. data/app/assets/stylesheets/rails_pulse/components/flash.css +47 -0
  22. data/app/assets/stylesheets/rails_pulse/components/input.css +80 -0
  23. data/app/assets/stylesheets/rails_pulse/components/layouts.css +63 -0
  24. data/app/assets/stylesheets/rails_pulse/components/menu.css +43 -0
  25. data/app/assets/stylesheets/rails_pulse/components/popover.css +36 -0
  26. data/app/assets/stylesheets/rails_pulse/components/prose.css +144 -0
  27. data/app/assets/stylesheets/rails_pulse/components/row.css +24 -0
  28. data/app/assets/stylesheets/rails_pulse/components/sidebar_menu.css +79 -0
  29. data/app/assets/stylesheets/rails_pulse/components/skeleton.css +5 -0
  30. data/app/assets/stylesheets/rails_pulse/components/table.css +37 -0
  31. data/app/assets/stylesheets/rails_pulse/components/utilities.css +36 -0
  32. data/app/controllers/concerns/chart_table_concern.rb +82 -0
  33. data/app/controllers/concerns/response_range_concern.rb +24 -0
  34. data/app/controllers/concerns/time_range_concern.rb +67 -0
  35. data/app/controllers/concerns/zoom_range_concern.rb +40 -0
  36. data/app/controllers/rails_pulse/application_controller.rb +67 -0
  37. data/app/controllers/rails_pulse/assets_controller.rb +33 -0
  38. data/app/controllers/rails_pulse/caches_controller.rb +115 -0
  39. data/app/controllers/rails_pulse/csp_test_controller.rb +57 -0
  40. data/app/controllers/rails_pulse/dashboard_controller.rb +6 -0
  41. data/app/controllers/rails_pulse/operations_controller.rb +219 -0
  42. data/app/controllers/rails_pulse/queries_controller.rb +121 -0
  43. data/app/controllers/rails_pulse/requests_controller.rb +69 -0
  44. data/app/controllers/rails_pulse/routes_controller.rb +99 -0
  45. data/app/helpers/rails_pulse/application_helper.rb +111 -0
  46. data/app/helpers/rails_pulse/breadcrumbs_helper.rb +62 -0
  47. data/app/helpers/rails_pulse/cached_component_helper.rb +73 -0
  48. data/app/helpers/rails_pulse/chart_formatters.rb +43 -0
  49. data/app/helpers/rails_pulse/chart_helper.rb +140 -0
  50. data/app/helpers/rails_pulse/formatting_helper.rb +29 -0
  51. data/app/helpers/rails_pulse/status_helper.rb +279 -0
  52. data/app/helpers/rails_pulse/table_helper.rb +54 -0
  53. data/app/javascript/rails_pulse/application.js +119 -0
  54. data/app/javascript/rails_pulse/controllers/color_scheme_controller.js +20 -0
  55. data/app/javascript/rails_pulse/controllers/context_menu_controller.js +16 -0
  56. data/app/javascript/rails_pulse/controllers/dialog_controller.js +21 -0
  57. data/app/javascript/rails_pulse/controllers/expandable_row_controller.js +67 -0
  58. data/app/javascript/rails_pulse/controllers/form_controller.js +39 -0
  59. data/app/javascript/rails_pulse/controllers/icon_controller.js +170 -0
  60. data/app/javascript/rails_pulse/controllers/index_controller.js +230 -0
  61. data/app/javascript/rails_pulse/controllers/menu_controller.js +60 -0
  62. data/app/javascript/rails_pulse/controllers/pagination_controller.js +69 -0
  63. data/app/javascript/rails_pulse/controllers/popover_controller.js +91 -0
  64. data/app/javascript/rails_pulse/controllers/timezone_controller.js +106 -0
  65. data/app/javascript/rails_pulse/theme.js +416 -0
  66. data/app/jobs/rails_pulse/application_job.rb +4 -0
  67. data/app/jobs/rails_pulse/cleanup_job.rb +21 -0
  68. data/app/mailers/rails_pulse/application_mailer.rb +6 -0
  69. data/app/models/rails_pulse/application_record.rb +7 -0
  70. data/app/models/rails_pulse/component_cache_key.rb +33 -0
  71. data/app/models/rails_pulse/dashboard/charts/average_response_time.rb +27 -0
  72. data/app/models/rails_pulse/dashboard/charts/p95_response_time.rb +37 -0
  73. data/app/models/rails_pulse/dashboard/tables/slow_queries.rb +59 -0
  74. data/app/models/rails_pulse/dashboard/tables/slow_routes.rb +45 -0
  75. data/app/models/rails_pulse/operation.rb +87 -0
  76. data/app/models/rails_pulse/queries/cards/average_query_times.rb +52 -0
  77. data/app/models/rails_pulse/queries/cards/execution_rate.rb +57 -0
  78. data/app/models/rails_pulse/queries/cards/percentile_query_times.rb +71 -0
  79. data/app/models/rails_pulse/queries/charts/average_query_times.rb +112 -0
  80. data/app/models/rails_pulse/query.rb +58 -0
  81. data/app/models/rails_pulse/request.rb +64 -0
  82. data/app/models/rails_pulse/requests/charts/average_response_times.rb +99 -0
  83. data/app/models/rails_pulse/requests/charts/operations_chart.rb +35 -0
  84. data/app/models/rails_pulse/route.rb +77 -0
  85. data/app/models/rails_pulse/routes/cards/average_response_times.rb +54 -0
  86. data/app/models/rails_pulse/routes/cards/error_rate_per_route.rb +73 -0
  87. data/app/models/rails_pulse/routes/cards/percentile_response_times.rb +73 -0
  88. data/app/models/rails_pulse/routes/cards/request_count_totals.rb +59 -0
  89. data/app/models/rails_pulse/routes/charts/average_response_times.rb +115 -0
  90. data/app/models/rails_pulse/routes/tables/index.rb +63 -0
  91. data/app/services/rails_pulse/sql_query_normalizer.rb +124 -0
  92. data/app/views/layouts/rails_pulse/_menu_items.html.erb +19 -0
  93. data/app/views/layouts/rails_pulse/_sidebar_menu.html.erb +44 -0
  94. data/app/views/layouts/rails_pulse/application.html.erb +72 -0
  95. data/app/views/rails_pulse/caches/show.html.erb +9 -0
  96. data/app/views/rails_pulse/components/_breadcrumbs.html.erb +12 -0
  97. data/app/views/rails_pulse/components/_code_panel.html.erb +12 -0
  98. data/app/views/rails_pulse/components/_metric_card.html.erb +55 -0
  99. data/app/views/rails_pulse/components/_metric_row.html.erb +9 -0
  100. data/app/views/rails_pulse/components/_operation_details_popover.html.erb +241 -0
  101. data/app/views/rails_pulse/components/_panel.html.erb +56 -0
  102. data/app/views/rails_pulse/components/_sparkline_stats.html.erb +15 -0
  103. data/app/views/rails_pulse/components/_table.html.erb +50 -0
  104. data/app/views/rails_pulse/components/_table_head.html.erb +20 -0
  105. data/app/views/rails_pulse/components/_table_pagination.html.erb +45 -0
  106. data/app/views/rails_pulse/components/_time_period.html.erb +16 -0
  107. data/app/views/rails_pulse/csp_test/show.html.erb +207 -0
  108. data/app/views/rails_pulse/dashboard/charts/_bar_chart.html.erb +1 -0
  109. data/app/views/rails_pulse/dashboard/index.html.erb +64 -0
  110. data/app/views/rails_pulse/dashboard/tables/_routes_table.html.erb +32 -0
  111. data/app/views/rails_pulse/dashboard/tables/_standard_table.html.erb +1 -0
  112. data/app/views/rails_pulse/operations/_operation_analysis_application.html.erb +43 -0
  113. data/app/views/rails_pulse/operations/_operation_analysis_database.html.erb +12 -0
  114. data/app/views/rails_pulse/operations/_operation_analysis_generic.html.erb +15 -0
  115. data/app/views/rails_pulse/operations/_operation_analysis_other.html.erb +69 -0
  116. data/app/views/rails_pulse/operations/_operation_analysis_view.html.erb +39 -0
  117. data/app/views/rails_pulse/operations/show.html.erb +79 -0
  118. data/app/views/rails_pulse/queries/_show_table.html.erb +19 -0
  119. data/app/views/rails_pulse/queries/_table.html.erb +31 -0
  120. data/app/views/rails_pulse/queries/index.html.erb +64 -0
  121. data/app/views/rails_pulse/queries/show.html.erb +86 -0
  122. data/app/views/rails_pulse/requests/_operations.html.erb +85 -0
  123. data/app/views/rails_pulse/requests/_table.html.erb +31 -0
  124. data/app/views/rails_pulse/requests/index.html.erb +64 -0
  125. data/app/views/rails_pulse/requests/show.html.erb +44 -0
  126. data/app/views/rails_pulse/routes/_table.html.erb +29 -0
  127. data/app/views/rails_pulse/routes/index.html.erb +65 -0
  128. data/app/views/rails_pulse/routes/show.html.erb +67 -0
  129. data/app/views/rails_pulse/skeletons/_chart.html.erb +3 -0
  130. data/app/views/rails_pulse/skeletons/_metric_card.html.erb +20 -0
  131. data/app/views/rails_pulse/skeletons/_panel.html.erb +19 -0
  132. data/app/views/rails_pulse/skeletons/_table.html.erb +8 -0
  133. data/config/importmap.rb +12 -0
  134. data/config/initializers/rails_charts_csp_patch.rb +83 -0
  135. data/config/initializers/rails_pulse.rb +198 -0
  136. data/config/routes.rb +16 -0
  137. data/db/migrate/20250227235904_create_routes.rb +12 -0
  138. data/db/migrate/20250227235915_create_requests.rb +19 -0
  139. data/db/migrate/20250228000000_create_queries.rb +14 -0
  140. data/db/migrate/20250228000056_create_operations.rb +24 -0
  141. data/lib/generators/rails_pulse/install_generator.rb +17 -0
  142. data/lib/generators/rails_pulse/templates/rails_pulse.rb +198 -0
  143. data/lib/rails_pulse/cleanup_service.rb +212 -0
  144. data/lib/rails_pulse/configuration.rb +176 -0
  145. data/lib/rails_pulse/engine.rb +88 -0
  146. data/lib/rails_pulse/middleware/asset_server.rb +84 -0
  147. data/lib/rails_pulse/middleware/request_collector.rb +120 -0
  148. data/lib/rails_pulse/migration.rb +29 -0
  149. data/lib/rails_pulse/subscribers/operation_subscriber.rb +280 -0
  150. data/lib/rails_pulse/version.rb +3 -0
  151. data/lib/rails_pulse.rb +38 -0
  152. data/lib/tasks/rails_pulse_tasks.rake +138 -0
  153. data/public/rails-pulse-assets/csp-test.js +110 -0
  154. data/public/rails-pulse-assets/rails-pulse-icons.js +89 -0
  155. data/public/rails-pulse-assets/rails-pulse-icons.js.map +13 -0
  156. data/public/rails-pulse-assets/rails-pulse.css +1 -0
  157. data/public/rails-pulse-assets/rails-pulse.css.map +1 -0
  158. data/public/rails-pulse-assets/rails-pulse.js +183 -0
  159. data/public/rails-pulse-assets/rails-pulse.js.map +7 -0
  160. metadata +339 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 418546b7a8b0324941402583074cd0a569d51ef4b8e4b0d02261957d16c6d2ab
4
+ data.tar.gz: 7d027dafdf22c964a503a4a8e66d6d7c0e7af8f3d299c3acd2c65b2a1bd63705
5
+ SHA512:
6
+ metadata.gz: 331a413a83ab2c81f43ee796676eb127dd0541f14912bba66982142ac3cfe5d2276e9c0aecd67aeb11e2219ffe66ad3f1f8c0791e7b68e5f1bba5f517072cdd6
7
+ data.tar.gz: 6d1885f4af1c42bef1caac9044aa72b514de7669536e4bf7f58b23e63ebbbddd32040106b72424d61bc7e7068241d0de1ce68816d6fe9b368e4279b4f62e6adf
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright Rails Pulse
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,638 @@
1
+ <div align="center">
2
+ <img src="app/assets/images/rails_pulse/rails-pulse-logo.png" alt="Rails Pulse" width="200" />
3
+
4
+ # Rails Pulse
5
+
6
+ **Real-time performance monitoring and debugging for Rails applications**
7
+
8
+ ![Gem Version](https://img.shields.io/gem/v/rails_pulse)
9
+ ![Rails Version](https://img.shields.io/badge/Rails-8.0+-blue)
10
+ ![License](https://img.shields.io/badge/License-MIT-green)
11
+ ![Ruby Version](https://img.shields.io/badge/Ruby-3.3+-red)
12
+ </div>
13
+
14
+ ---
15
+
16
+ ## Table of Contents
17
+
18
+ - [Introduction](#introduction)
19
+ - [Features](#features)
20
+ - [Screenshots](#screenshots)
21
+ - [Getting Started](#getting-started)
22
+ - [Installation](#installation)
23
+ - [Quick Setup](#quick-setup)
24
+ - [Basic Configuration](#basic-configuration)
25
+ - [Authentication](#authentication)
26
+ - [Authentication Setup](#authentication-setup)
27
+ - [Authentication Examples](#authentication-examples)
28
+ - [Security Considerations](#security-considerations)
29
+ - [Data Management](#data-management)
30
+ - [Cleanup Strategies](#cleanup-strategies)
31
+ - [Cleanup Configuration](#cleanup-configuration)
32
+ - [Manual Cleanup Operations](#manual-cleanup-operations)
33
+ - [How Cleanup Works](#how-cleanup-works)
34
+ - [Multiple Database Support](#multiple-database-support)
35
+ - [Configuration](#configuration)
36
+ - [Database Configuration](#database-configuration)
37
+ - [Migration](#migration)
38
+ - [Testing](#testing)
39
+ - [Technology Stack](#technology-stack)
40
+ - [Advantages Over Other Solutions](#advantages-over-other-solutions)
41
+ - [License](#license)
42
+
43
+ ---
44
+
45
+ ## Introduction
46
+
47
+ Rails Pulse is a comprehensive performance monitoring and debugging gem that provides real-time insights into your Rails application's health. Built as a Rails Engine, it seamlessly integrates with your existing application to capture, analyze, and visualize performance metrics without impacting your production workload.
48
+
49
+ **Why Rails Pulse?**
50
+
51
+ - **Visual**: Beautiful, responsive dashboards with actionable insights
52
+ - **Comprehensive**: Monitors requests, database queries, and application operations
53
+ - **Real-time**: Live performance metrics
54
+ - **Zero Configuration**: Works out of the box with sensible defaults
55
+ - **Lightweight**: Minimal performance overhead in production
56
+ - **Asset Independent**: Pre-compiled assets work with any Rails build system
57
+ - **CSP Compliant**: Secure by default with Content Security Policy support
58
+
59
+ ## Features
60
+
61
+ ### 🎯 **Performance Monitoring**
62
+ - Interactive dashboard with response time charts and request analytics
63
+ - SQL query performance tracking with slow query identification
64
+ - Route-specific metrics with configurable performance thresholds
65
+ - Week-over-week trend analysis with visual indicators
66
+
67
+ ### 🔒 **Production Ready**
68
+ - Content Security Policy (CSP) compliant with pre-compiled assets
69
+ - Flexible authentication system with multiple authentication methods
70
+ - Automatic data cleanup with configurable retention policies
71
+ - Zero build dependencies - works with any Rails setup
72
+
73
+ ### ⚡ **Developer Experience**
74
+ - Zero configuration setup with sensible defaults
75
+ - Beautiful responsive interface with dark/light mode
76
+ - Smart caching with minimal performance overhead
77
+ - Multiple database support (SQLite, PostgreSQL, MySQL)
78
+
79
+ ## Screenshots
80
+
81
+ <img src="app/assets/images/rails_pulse/dashboard.png" alt="Rails Pulse" />
82
+
83
+ ## Getting Started
84
+
85
+ ### Installation
86
+
87
+ Add Rails Pulse to your application's Gemfile:
88
+
89
+ ```ruby
90
+ gem 'rails_pulse'
91
+ ```
92
+
93
+ Install the gem:
94
+
95
+ ```bash
96
+ bundle install
97
+ ```
98
+
99
+ Generate the installation files:
100
+
101
+ ```bash
102
+ rails generate rails_pulse:install
103
+ ```
104
+
105
+ Run the migrations:
106
+
107
+ ```bash
108
+ rails db:migrate
109
+ ```
110
+
111
+ Add the Rails Pulse route to your application:
112
+
113
+ ```ruby
114
+ # config/routes.rb
115
+ Rails.application.routes.draw do
116
+ mount RailsPulse::Engine => "/rails_pulse"
117
+ # ... your other routes
118
+ end
119
+ ```
120
+
121
+ ### Quick Setup
122
+
123
+ Rails Pulse automatically starts collecting performance data once installed. Access your monitoring dashboard at:
124
+
125
+ ```
126
+ http://localhost:3000/rails_pulse
127
+ ```
128
+
129
+ ### Basic Configuration
130
+
131
+ Customize Rails Pulse in `config/initializers/rails_pulse.rb`:
132
+
133
+ ```ruby
134
+ RailsPulse.configure do |config|
135
+ # Enable or disable Rails Pulse
136
+ config.enabled = true
137
+
138
+ # Set performance thresholds for routes (in milliseconds)
139
+ config.route_thresholds = {
140
+ slow: 500,
141
+ very_slow: 1500,
142
+ critical: 3000
143
+ }
144
+
145
+ # Set performance thresholds for requests (in milliseconds)
146
+ config.request_thresholds = {
147
+ slow: 700,
148
+ very_slow: 2000,
149
+ critical: 4000
150
+ }
151
+
152
+ # Set performance thresholds for database queries (in milliseconds)
153
+ config.query_thresholds = {
154
+ slow: 100,
155
+ very_slow: 500,
156
+ critical: 1000
157
+ }
158
+
159
+ # Asset tracking configuration
160
+ config.track_assets = false # Ignore asset requests by default
161
+ config.custom_asset_patterns = [] # Additional asset patterns to ignore
162
+
163
+ # Rails Pulse mount path (optional)
164
+ # Specify if Rails Pulse is mounted at a custom path to prevent self-tracking
165
+ config.mount_path = nil # e.g., "/admin/monitoring"
166
+
167
+ # Route filtering - ignore specific routes from performance tracking
168
+ config.ignored_routes = [] # Array of strings or regex patterns
169
+ config.ignored_requests = [] # Array of request patterns to ignore
170
+ config.ignored_queries = [] # Array of query patterns to ignore
171
+
172
+ # Data cleanup
173
+ config.archiving_enabled = true # Enable automatic cleanup
174
+ config.full_retention_period = 2.weeks # Delete records older than this
175
+ config.max_table_records = { # Maximum records per table
176
+ rails_pulse_requests: 10000,
177
+ rails_pulse_operations: 50000,
178
+ rails_pulse_routes: 1000,
179
+ rails_pulse_queries: 500
180
+ }
181
+
182
+ # Metric caching for performance
183
+ config.component_cache_enabled = true
184
+ config.component_cache_duration = 1.day
185
+
186
+ # Multiple database support (optional)
187
+ # Uncomment to store Rails Pulse data in a separate database
188
+ # config.connects_to = {
189
+ # database: { writing: :rails_pulse, reading: :rails_pulse }
190
+ # }
191
+ end
192
+ ```
193
+
194
+ ## Authentication
195
+
196
+ Rails Pulse supports flexible authentication to secure access to your monitoring dashboard.
197
+
198
+ ### Authentication Setup
199
+
200
+ Enable authentication by configuring the following options in your Rails Pulse initializer:
201
+
202
+ ```ruby
203
+ # config/initializers/rails_pulse.rb
204
+ RailsPulse.configure do |config|
205
+ # Enable authentication
206
+ config.authentication_enabled = true
207
+
208
+ # Where to redirect unauthorized users (optional, defaults to "/")
209
+ config.authentication_redirect_path = "/login"
210
+
211
+ # Define your authentication logic
212
+ config.authentication_method = proc {
213
+ # Your authentication logic here
214
+ }
215
+ end
216
+ ```
217
+
218
+ ### Authentication Examples
219
+
220
+ Rails Pulse works with any authentication system. Here are common patterns:
221
+
222
+ #### **Devise with Admin Role**
223
+
224
+ ```ruby
225
+ config.authentication_method = proc {
226
+ unless user_signed_in? && current_user.admin?
227
+ redirect_to main_app.root_path, alert: "Access denied"
228
+ end
229
+ }
230
+ ```
231
+
232
+ #### **Custom Session-based Authentication**
233
+
234
+ ```ruby
235
+ config.authentication_method = proc {
236
+ unless session[:user_id] && User.find_by(id: session[:user_id])&.admin?
237
+ redirect_to main_app.login_path, alert: "Please log in as an admin"
238
+ end
239
+ }
240
+ ```
241
+
242
+ #### **HTTP Basic Authentication**
243
+
244
+ ```ruby
245
+ config.authentication_method = proc {
246
+ authenticate_or_request_with_http_basic do |username, password|
247
+ username == ENV['RAILS_PULSE_USERNAME'] &&
248
+ password == ENV['RAILS_PULSE_PASSWORD']
249
+ end
250
+ }
251
+ ```
252
+
253
+ #### **Warden Authentication**
254
+
255
+ ```ruby
256
+ config.authentication_method = proc {
257
+ warden.authenticate!(scope: :admin)
258
+ }
259
+ ```
260
+
261
+ #### **Custom Authorization Logic**
262
+
263
+ ```ruby
264
+ config.authentication_method = proc {
265
+ current_user = User.find_by(id: session[:user_id])
266
+ unless current_user&.can_access_monitoring?
267
+ render plain: "Forbidden", status: :forbidden
268
+ end
269
+ }
270
+ ```
271
+
272
+ ### Security Considerations
273
+
274
+ - **Production Security**: Always enable authentication in production environments
275
+ - **Admin-only Access**: Limit access to administrators or authorized personnel
276
+ - **Environment Variables**: Use environment variables for credentials, never hardcode
277
+ - **HTTPS Required**: Always use HTTPS in production when authentication is enabled
278
+ - **Regular Access Review**: Periodically review who has access to monitoring data
279
+
280
+ **Important**: The authentication method runs in the context of the Rails Pulse ApplicationController, giving you access to all standard Rails controller methods like `redirect_to`, `render`, `session`, and any methods from your host application's authentication system.
281
+
282
+ ## Data Management
283
+
284
+ Rails Pulse provides data cleanup to prevent your monitoring database from growing indefinitely while preserving essential performance insights.
285
+
286
+ ### Cleanup Strategies
287
+
288
+ **Time-based Cleanup**
289
+ - Automatically delete performance records older than a specified period
290
+ - Configurable retention period (default: 2 days)
291
+ - Keeps recent data for debugging while removing historical noise
292
+
293
+ **Count-based Cleanup**
294
+ - Enforce maximum record limits per table
295
+ - Prevents any single table from consuming excessive storage
296
+ - Configurable limits for each Rails Pulse table
297
+
298
+ ### Cleanup Configuration
299
+
300
+ ```ruby
301
+ RailsPulse.configure do |config|
302
+ # Enable or disable automatic cleanup
303
+ config.archiving_enabled = true
304
+
305
+ # Time-based retention
306
+ config.full_retention_period = 2.weeks
307
+
308
+ # Count-based retention - maximum records per table
309
+ config.max_table_records = {
310
+ rails_pulse_requests: 10000, # HTTP requests
311
+ rails_pulse_operations: 50000, # Operations within requests
312
+ rails_pulse_routes: 1000, # Unique routes
313
+ rails_pulse_queries: 500 # Normalized SQL queries
314
+ }
315
+ end
316
+ ```
317
+
318
+ ### Manual Cleanup Operations
319
+
320
+ **Run cleanup manually:**
321
+ ```bash
322
+ rails rails_pulse:cleanup
323
+ ```
324
+
325
+ **Check current database status:**
326
+ ```bash
327
+ rails rails_pulse:cleanup_stats
328
+ ```
329
+
330
+ **Schedule automated cleanup:**
331
+ ```ruby
332
+ # Using whenever gem or similar scheduler
333
+ RailsPulse::CleanupJob.perform_later
334
+ ```
335
+
336
+ ### How Cleanup Works
337
+
338
+ 1. **Time-based Phase**: Delete all records older than `full_retention_period`
339
+ 2. **Count-based Phase**: If tables still exceed limits, delete oldest remaining records
340
+ 3. **Safe Deletion**: Respects foreign key constraints (operations → requests → queries/routes)
341
+ 4. **Comprehensive Logging**: Detailed cleanup statistics and operation logs
342
+
343
+ This two-phase approach ensures you keep the most valuable recent performance data while maintaining manageable database sizes.
344
+
345
+ ## Multiple Database Support
346
+
347
+ Rails Pulse supports storing performance monitoring data in a separate database. This is particularly useful for:
348
+
349
+ - **Isolating monitoring data** from your main application database
350
+ - **Using different database engines** optimized for time-series data
351
+ - **Scaling monitoring independently** from your application
352
+ - **Simplified backup strategies** with separate retention policies
353
+
354
+ ### Configuration
355
+
356
+ To use a separate database, configure the `connects_to` option in your Rails Pulse initializer:
357
+
358
+ ```ruby
359
+ RailsPulse.configure do |config|
360
+ # Single separate database
361
+ config.connects_to = {
362
+ database: { writing: :rails_pulse, reading: :rails_pulse }
363
+ }
364
+
365
+ # Or primary/replica configuration
366
+ config.connects_to = {
367
+ database: { writing: :rails_pulse_primary, reading: :rails_pulse_replica }
368
+ }
369
+ end
370
+ ```
371
+
372
+ ### Database Configuration
373
+
374
+ Add the corresponding database configurations to your `config/database.yml`:
375
+
376
+ ```yaml
377
+ # For SQLite
378
+ production:
379
+ # ... your main database ...
380
+ rails_pulse:
381
+ adapter: sqlite3
382
+ database: storage/rails_pulse_production.sqlite3
383
+ pool: 5
384
+ timeout: 5000
385
+
386
+ # For PostgreSQL
387
+ production:
388
+ # ... your main database ...
389
+ rails_pulse:
390
+ adapter: postgresql
391
+ database: myapp_rails_pulse_production
392
+ username: rails_pulse_user
393
+ password: <%= Rails.application.credentials.dig(:rails_pulse, :database_password) %>
394
+ host: localhost
395
+ pool: 5
396
+
397
+ # For MySQL
398
+ production:
399
+ # ... your main database ...
400
+ rails_pulse:
401
+ adapter: mysql2
402
+ database: myapp_rails_pulse_production
403
+ username: rails_pulse_user
404
+ password: <%= Rails.application.credentials.dig(:rails_pulse, :database_password) %>
405
+ host: localhost
406
+ pool: 5
407
+ ```
408
+
409
+ ### Migration
410
+
411
+ When using a separate database, run migrations targeting the Rails Pulse database:
412
+
413
+ ```bash
414
+ # Run Rails Pulse migrations on the configured database
415
+ rails db:migrate
416
+
417
+ # If you need to run migrations on a specific database
418
+ RAILS_ENV=production rails db:migrate
419
+ ```
420
+
421
+ **Note:** Rails Pulse maintains full backward compatibility. If no `connects_to` configuration is provided, all data will be stored in your main application database as before.
422
+
423
+ ## Testing
424
+
425
+ Rails Pulse includes a comprehensive test suite designed for speed and reliability across multiple databases (SQLite, MySQL, PostgreSQL) and Rails versions.
426
+
427
+ ### Running the Complete Test Suite
428
+
429
+ ```bash
430
+ # Run all tests (unit, functional, integration)
431
+ rails test:all
432
+
433
+ # Run tests with speed optimizations
434
+ rails test:fast
435
+ ```
436
+
437
+ ### Running Individual Test Types
438
+
439
+ ```bash
440
+ # Unit tests (models, helpers, utilities)
441
+ rails test:unit
442
+
443
+ # Functional tests (controllers, views)
444
+ rails test:functional
445
+
446
+ # Integration tests (end-to-end workflows)
447
+ rails test:integration
448
+ ```
449
+
450
+ ### Running Individual Test Files
451
+
452
+ ```bash
453
+ # Run a specific test file
454
+ rails test test/models/rails_pulse/request_test.rb
455
+
456
+ # Run controller tests
457
+ rails test test/controllers/rails_pulse/dashboard_controller_test.rb
458
+
459
+ # Run helper tests
460
+ rails test test/helpers/rails_pulse/application_helper_test.rb
461
+
462
+ # Run factory verification tests
463
+ rails test test/factories_test.rb
464
+ ```
465
+
466
+ ### Multi-Rails Version Testing
467
+
468
+ Test against multiple Rails versions using Appraisal:
469
+
470
+ ```bash
471
+ # Install dependencies for all Rails versions
472
+ bundle exec appraisal install
473
+
474
+ # Run tests against all Rails versions
475
+ bundle exec appraisal rails test:all
476
+
477
+ # Run tests against specific Rails version
478
+ bundle exec appraisal rails-7-1 rails test:unit
479
+ ```
480
+
481
+ ### Test Performance Features
482
+
483
+ - **In-memory SQLite**: Unit and functional tests use fast in-memory databases
484
+ - **Transaction rollback**: Tests use database transactions for fast cleanup
485
+ - **Stubbed dependencies**: External calls and expensive operations are stubbed
486
+ - **Parallel execution**: Tests run in parallel when supported
487
+
488
+ ### Database Testing
489
+
490
+ Rails Pulse supports testing with multiple database adapters using simplified Rake tasks:
491
+
492
+ ```bash
493
+ # Quick Commands (Recommended)
494
+ rails test:sqlite # Test with SQLite (default)
495
+ rails test:postgresql # Test with PostgreSQL
496
+ rails test:mysql # Test with MySQL
497
+
498
+ # Test Matrix (before pushing)
499
+ rails test:matrix # Test SQLite + PostgreSQL
500
+ rails test:matrix_full # Test all databases (SQLite + PostgreSQL + MySQL)
501
+ ```
502
+
503
+ #### Development Environment Setup
504
+
505
+ 1. **Set up git hooks (optional but recommended):**
506
+ ```bash
507
+ ./scripts/setup-git-hooks
508
+ ```
509
+ This installs a pre-commit hook that runs RuboCop before each commit.
510
+
511
+ 2. **Copy the environment template:**
512
+ ```bash
513
+ cp .env.example .env
514
+ ```
515
+
516
+ 3. **Configure your database credentials in `.env`:**
517
+ ```bash
518
+ # PostgreSQL Configuration
519
+ POSTGRES_USERNAME=your_username
520
+ POSTGRES_PASSWORD=your_password
521
+ POSTGRES_HOST=localhost
522
+ POSTGRES_PORT=5432
523
+
524
+ # MySQL Configuration
525
+ MYSQL_USERNAME=root
526
+ MYSQL_PASSWORD=your_password
527
+ MYSQL_HOST=localhost
528
+ MYSQL_PORT=3306
529
+ ```
530
+
531
+ 4. **Create test databases:**
532
+ ```bash
533
+ # PostgreSQL
534
+ createdb rails_pulse_test
535
+
536
+ # MySQL
537
+ mysql -u root -p -e "CREATE DATABASE rails_pulse_test;"
538
+ ```
539
+
540
+ #### Manual Commands
541
+ If you prefer the explicit approach:
542
+
543
+ ```bash
544
+ # Test with SQLite (default, uses in-memory database)
545
+ rails test:all
546
+
547
+ # Test with PostgreSQL (requires local PostgreSQL setup)
548
+ DATABASE_ADAPTER=postgresql FORCE_DB_CONFIG=true rails test:all
549
+
550
+ # Test with MySQL (requires MySQL setup and mysql2 gem compilation)
551
+ DATABASE_ADAPTER=mysql FORCE_DB_CONFIG=true rails test:all
552
+ ```
553
+
554
+ **Note**: Database switching is disabled by default for stability. The Rake tasks automatically handle the `FORCE_DB_CONFIG=true` requirement.
555
+
556
+ **MySQL Testing**: MySQL testing requires:
557
+ - MySQL server running locally with a `rails_pulse_test` database
558
+ - Successful compilation of the `mysql2` gem (may require system dependencies like `zstd`)
559
+ - CI environments come pre-configured, but local setup may require additional dependencies
560
+
561
+ ### Quick Testing Before Push
562
+ ```bash
563
+ # Recommended: Test the same databases as CI
564
+ rails test:matrix
565
+ ```
566
+
567
+ ## Technology Stack
568
+
569
+ Rails Pulse is built using modern, battle-tested technologies that ensure reliability, performance, and maintainability:
570
+
571
+ ### **Frontend Technologies**
572
+ - **[CSS Zero](https://github.com/lazaronixon/css-zero)** - Modern utility-first CSS framework bundled for asset independence
573
+ - **[Stimulus](https://stimulus.hotwired.dev/)** - Progressive JavaScript framework for enhanced interactivity
574
+ - **[Turbo](https://turbo.hotwired.dev/)** - Fast navigation and real-time updates without full page reloads
575
+ - **[Turbo Frames](https://turbo.hotwired.dev/handbook/frames)** - Lazy loading and partial page updates for optimal performance
576
+
577
+ ### **Data Visualization**
578
+ - **[Rails Charts](https://github.com/railsjazz/rails_charts)** - Rails wrapper around Apache ECharts
579
+ - **[Lucide Icons](https://lucide.dev/)** - Beautiful, consistent iconography with pre-compiled SVG bundle
580
+
581
+ ### **Asset Management**
582
+ - **Pre-compiled Assets** - All CSS, JavaScript, and icons bundled into the gem
583
+ - **CSP-Safe Implementation** - Secure DOM methods and nonce-based asset loading
584
+ - **Build System** - Node.js-based build process for asset compilation
585
+ - **Zero External Dependencies** - Self-contained assets work with any Rails build system
586
+
587
+ ### **Performance & Optimization**
588
+ - **[Request Store](https://github.com/steveklabnik/request_store)** - Thread-safe request-scoped storage for performance data
589
+ - **[Rails Caching](https://guides.rubyonrails.org/caching_with_rails.html)** - Fragment caching with smart invalidation strategies
590
+ - **[ActiveRecord Instrumentation](https://guides.rubyonrails.org/active_support_instrumentation.html)** - Built-in Rails performance monitoring hooks
591
+
592
+ ### **Development & Testing**
593
+ - **[Rails Generators](https://guides.rubyonrails.org/generators.html)** - Automated installation and configuration
594
+ - **[Omakase Ruby Styling](https://github.com/rails/rubocop-rails-omakase)** - Consistent code formatting and style
595
+
596
+ ## Advantages Over Other Solutions
597
+
598
+ ### **vs. Application Performance Monitoring (APM) Services**
599
+ - **No External Dependencies**: Everything runs in your Rails application with pre-compiled assets
600
+ - **Zero Monthly Costs**: No subscription fees or usage-based pricing
601
+ - **Data Privacy**: All performance data stays in your database(s)
602
+ - **Customizable**: Full control over metrics, thresholds, and interface
603
+ - **Asset Independence**: Works with any Rails build system (Sprockets, esbuild, Webpack, Vite)
604
+
605
+ ### **vs. Built-in Rails Logging**
606
+ - **Visual Interface**: Beautiful dashboards instead of log parsing
607
+ - **Structured Data**: Queryable metrics instead of text logs
608
+ - **Historical Analysis**: Persistent storage with trend analysis
609
+ - **Real-time Monitoring**: Live updates and health scoring
610
+
611
+ ### **vs. Custom Monitoring Solutions**
612
+ - **Batteries Included**: Complete monitoring solution out of the box
613
+ - **Proven Architecture**: Built on Rails best practices
614
+ - **Community Driven**: Open source with active development
615
+ - **Professional Design**: Production-ready interface
616
+
617
+ ### **Key Differentiators**
618
+ - **Rails-Native**: Designed specifically for Rails applications
619
+ - **Developer Experience**: Optimized for debugging and development
620
+ - **Positive Focus**: Celebrates good performance alongside problem identification
621
+ - **Contextual Insights**: Deep Rails framework integration for meaningful metrics
622
+ - **Security First**: CSP-compliant by default with secure asset handling
623
+ - **Zero Build Dependencies**: Pre-compiled assets work with any Rails setup
624
+ - **Flexible Data Storage**: Support for multiple database backends (SQLite, PostgreSQL, MySQL)
625
+
626
+ ## License
627
+
628
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
629
+
630
+ ---
631
+
632
+ <div align="center">
633
+ <strong>Built with ❤️ for the Rails community</strong>
634
+
635
+ [Documentation](https://github.com/scottharvey/rails_pulse/wiki) •
636
+ [Issues](https://github.com/scottharvey/rails_pulse/issues) •
637
+ [Contributing](CONTRIBUTING.md)
638
+ </div>