rails_pulse 0.1.1 โ†’ 0.1.3

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 (123) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +79 -177
  3. data/Rakefile +77 -2
  4. data/app/assets/images/rails_pulse/dashboard.png +0 -0
  5. data/app/assets/images/rails_pulse/request.png +0 -0
  6. data/app/assets/stylesheets/rails_pulse/application.css +28 -17
  7. data/app/assets/stylesheets/rails_pulse/components/badge.css +13 -0
  8. data/app/assets/stylesheets/rails_pulse/components/base.css +12 -2
  9. data/app/assets/stylesheets/rails_pulse/components/collapsible.css +30 -0
  10. data/app/assets/stylesheets/rails_pulse/components/popover.css +0 -1
  11. data/app/assets/stylesheets/rails_pulse/components/row.css +55 -3
  12. data/app/assets/stylesheets/rails_pulse/components/sidebar_menu.css +23 -0
  13. data/app/controllers/concerns/chart_table_concern.rb +21 -4
  14. data/app/controllers/concerns/response_range_concern.rb +6 -3
  15. data/app/controllers/concerns/time_range_concern.rb +5 -10
  16. data/app/controllers/concerns/zoom_range_concern.rb +32 -1
  17. data/app/controllers/rails_pulse/application_controller.rb +13 -5
  18. data/app/controllers/rails_pulse/dashboard_controller.rb +12 -0
  19. data/app/controllers/rails_pulse/queries_controller.rb +111 -51
  20. data/app/controllers/rails_pulse/requests_controller.rb +37 -12
  21. data/app/controllers/rails_pulse/routes_controller.rb +98 -24
  22. data/app/helpers/rails_pulse/application_helper.rb +0 -1
  23. data/app/helpers/rails_pulse/chart_formatters.rb +3 -3
  24. data/app/helpers/rails_pulse/chart_helper.rb +21 -9
  25. data/app/helpers/rails_pulse/status_helper.rb +10 -4
  26. data/app/javascript/rails_pulse/application.js +34 -3
  27. data/app/javascript/rails_pulse/controllers/collapsible_controller.js +32 -0
  28. data/app/javascript/rails_pulse/controllers/color_scheme_controller.js +2 -1
  29. data/app/javascript/rails_pulse/controllers/expandable_rows_controller.js +58 -0
  30. data/app/javascript/rails_pulse/controllers/index_controller.js +353 -39
  31. data/app/javascript/rails_pulse/controllers/pagination_controller.js +17 -27
  32. data/app/javascript/rails_pulse/controllers/popover_controller.js +28 -4
  33. data/app/javascript/rails_pulse/controllers/table_sort_controller.js +14 -0
  34. data/app/jobs/rails_pulse/backfill_summaries_job.rb +41 -0
  35. data/app/jobs/rails_pulse/summary_job.rb +53 -0
  36. data/app/models/rails_pulse/dashboard/charts/average_response_time.rb +28 -7
  37. data/app/models/rails_pulse/dashboard/charts/p95_response_time.rb +18 -22
  38. data/app/models/rails_pulse/dashboard/tables/slow_queries.rb +18 -7
  39. data/app/models/rails_pulse/dashboard/tables/slow_routes.rb +34 -41
  40. data/app/models/rails_pulse/operation.rb +1 -1
  41. data/app/models/rails_pulse/queries/cards/average_query_times.rb +49 -25
  42. data/app/models/rails_pulse/queries/cards/execution_rate.rb +40 -28
  43. data/app/models/rails_pulse/queries/cards/percentile_query_times.rb +37 -43
  44. data/app/models/rails_pulse/queries/charts/average_query_times.rb +23 -97
  45. data/app/models/rails_pulse/queries/tables/index.rb +74 -0
  46. data/app/models/rails_pulse/query.rb +47 -0
  47. data/app/models/rails_pulse/requests/charts/average_response_times.rb +23 -84
  48. data/app/models/rails_pulse/route.rb +1 -6
  49. data/app/models/rails_pulse/routes/cards/average_response_times.rb +45 -25
  50. data/app/models/rails_pulse/routes/cards/error_rate_per_route.rb +43 -45
  51. data/app/models/rails_pulse/routes/cards/percentile_response_times.rb +36 -44
  52. data/app/models/rails_pulse/routes/cards/request_count_totals.rb +37 -27
  53. data/app/models/rails_pulse/routes/charts/average_response_times.rb +23 -100
  54. data/app/models/rails_pulse/routes/tables/index.rb +57 -40
  55. data/app/models/rails_pulse/summary.rb +143 -0
  56. data/app/services/rails_pulse/analysis/backtrace_analyzer.rb +256 -0
  57. data/app/services/rails_pulse/analysis/base_analyzer.rb +67 -0
  58. data/app/services/rails_pulse/analysis/explain_plan_analyzer.rb +206 -0
  59. data/app/services/rails_pulse/analysis/index_recommendation_engine.rb +326 -0
  60. data/app/services/rails_pulse/analysis/n_plus_one_detector.rb +241 -0
  61. data/app/services/rails_pulse/analysis/query_characteristics_analyzer.rb +146 -0
  62. data/app/services/rails_pulse/analysis/suggestion_generator.rb +217 -0
  63. data/app/services/rails_pulse/query_analysis_service.rb +125 -0
  64. data/app/services/rails_pulse/summary_service.rb +199 -0
  65. data/app/views/layouts/rails_pulse/_sidebar_menu.html.erb +0 -1
  66. data/app/views/layouts/rails_pulse/application.html.erb +4 -6
  67. data/app/views/rails_pulse/components/_breadcrumbs.html.erb +1 -1
  68. data/app/views/rails_pulse/components/_code_panel.html.erb +17 -3
  69. data/app/views/rails_pulse/components/_empty_state.html.erb +11 -0
  70. data/app/views/rails_pulse/components/_metric_card.html.erb +37 -28
  71. data/app/views/rails_pulse/components/_panel.html.erb +1 -1
  72. data/app/views/rails_pulse/components/_sparkline_stats.html.erb +5 -7
  73. data/app/views/rails_pulse/components/_table_head.html.erb +6 -1
  74. data/app/views/rails_pulse/dashboard/index.html.erb +55 -37
  75. data/app/views/rails_pulse/operations/show.html.erb +17 -15
  76. data/app/views/rails_pulse/queries/_analysis_error.html.erb +15 -0
  77. data/app/views/rails_pulse/queries/_analysis_prompt.html.erb +27 -0
  78. data/app/views/rails_pulse/queries/_analysis_results.html.erb +87 -0
  79. data/app/views/rails_pulse/queries/_analysis_section.html.erb +39 -0
  80. data/app/views/rails_pulse/queries/_show_table.html.erb +2 -2
  81. data/app/views/rails_pulse/queries/_table.html.erb +11 -13
  82. data/app/views/rails_pulse/queries/index.html.erb +32 -28
  83. data/app/views/rails_pulse/queries/show.html.erb +45 -34
  84. data/app/views/rails_pulse/requests/_operations.html.erb +38 -45
  85. data/app/views/rails_pulse/requests/_table.html.erb +3 -3
  86. data/app/views/rails_pulse/requests/index.html.erb +33 -28
  87. data/app/views/rails_pulse/routes/_table.html.erb +14 -14
  88. data/app/views/rails_pulse/routes/index.html.erb +34 -29
  89. data/app/views/rails_pulse/routes/show.html.erb +43 -36
  90. data/config/initializers/rails_pulse.rb +0 -12
  91. data/config/routes.rb +5 -1
  92. data/db/migrate/20241222000001_create_rails_pulse_summaries.rb +54 -0
  93. data/db/migrate/20250916031656_add_analysis_to_rails_pulse_queries.rb +13 -0
  94. data/db/rails_pulse_schema.rb +130 -0
  95. data/lib/generators/rails_pulse/convert_to_migrations_generator.rb +65 -0
  96. data/lib/generators/rails_pulse/install_generator.rb +94 -4
  97. data/lib/generators/rails_pulse/templates/db/rails_pulse_schema.rb +60 -0
  98. data/lib/generators/rails_pulse/templates/migrations/install_rails_pulse_tables.rb +22 -0
  99. data/lib/generators/rails_pulse/templates/migrations/upgrade_rails_pulse_tables.rb +19 -0
  100. data/lib/generators/rails_pulse/templates/rails_pulse.rb +0 -12
  101. data/lib/generators/rails_pulse/upgrade_generator.rb +225 -0
  102. data/lib/rails_pulse/configuration.rb +0 -11
  103. data/lib/rails_pulse/engine.rb +0 -1
  104. data/lib/rails_pulse/version.rb +1 -1
  105. data/lib/tasks/rails_pulse.rake +77 -0
  106. data/public/rails-pulse-assets/rails-pulse.css +1 -1
  107. data/public/rails-pulse-assets/rails-pulse.css.map +1 -1
  108. data/public/rails-pulse-assets/rails-pulse.js +53 -53
  109. data/public/rails-pulse-assets/rails-pulse.js.map +4 -4
  110. data/public/rails-pulse-assets/search.svg +43 -0
  111. metadata +48 -14
  112. data/app/assets/images/rails_pulse/rails-pulse-logo.png +0 -0
  113. data/app/assets/images/rails_pulse/routes.png +0 -0
  114. data/app/controllers/rails_pulse/caches_controller.rb +0 -115
  115. data/app/helpers/rails_pulse/cached_component_helper.rb +0 -73
  116. data/app/javascript/rails_pulse/controllers/expandable_row_controller.js +0 -67
  117. data/app/models/rails_pulse/component_cache_key.rb +0 -33
  118. data/app/views/rails_pulse/caches/show.html.erb +0 -9
  119. data/db/migrate/20250227235904_create_routes.rb +0 -12
  120. data/db/migrate/20250227235915_create_requests.rb +0 -19
  121. data/db/migrate/20250228000000_create_queries.rb +0 -14
  122. data/db/migrate/20250228000056_create_operations.rb +0 -24
  123. data/lib/rails_pulse/migration.rb +0 -29
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9a7e7efdfd91dc728f40f7f661b81f02633ae414963dcf6de1ceff81636033b5
4
- data.tar.gz: 46a8441fd04bb5b771cc2107f0c7fb254e0400dcef824e7bd240980a605c7ba7
3
+ metadata.gz: 11728d425611e7ab3a9edb171f05638a783d1c8d3ded8dff112bb615e8a18d11
4
+ data.tar.gz: 3e16c2d59f4f4bd8d2df49625d9f6005edc98dec701212a6052f4abfd1cac6f3
5
5
  SHA512:
6
- metadata.gz: e8c5ce878786c71ef5d351f12881d6b3d93e940e090cec1843f65052aee58b084c396cbe61a00942704d534a5824274b24d7af4d033fb440a87a502137822867
7
- data.tar.gz: 30ad722ade45a6e57220373e04a8f99c01d417969921aaf3c7bba463b3a29c345bca787587bbaf6f517d21b7cf37e39fee23dbc4edbcc3680b1762a16a7f77fb
6
+ metadata.gz: 443645ed917ef9beea2d642ff8c4a7d2e57cc87559a09e74dfd75d20d18fb0ff11040eac6e825f3ff0af35637ea23757c88b24e78f35e32f64a7660168aa940a
7
+ data.tar.gz: d11227a7a11c24bff167104e712f9d075a394130a196938ed71b360e6cbc82a8f930672187b92d0009ed5c14fee882ff23d3d88a3df25b1ddeff3a8839452b5b
data/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
  **Real-time performance monitoring and debugging for Rails applications**
7
7
 
8
8
  ![Gem Version](https://img.shields.io/gem/v/rails_pulse)
9
- ![Rails Version](https://img.shields.io/badge/Rails-8.0+-blue)
9
+ ![Rails Version](https://img.shields.io/badge/Rails-7.2+-blue)
10
10
  ![License](https://img.shields.io/badge/License-MIT-green)
11
11
  ![Ruby Version](https://img.shields.io/badge/Ruby-3.3+-red)
12
12
  </div>
@@ -25,16 +25,14 @@
25
25
  - [Authentication](#authentication)
26
26
  - [Authentication Setup](#authentication-setup)
27
27
  - [Authentication Examples](#authentication-examples)
28
- - [Security Considerations](#security-considerations)
29
28
  - [Data Management](#data-management)
30
29
  - [Cleanup Strategies](#cleanup-strategies)
31
30
  - [Cleanup Configuration](#cleanup-configuration)
32
31
  - [Manual Cleanup Operations](#manual-cleanup-operations)
33
- - [How Cleanup Works](#how-cleanup-works)
34
- - [Multiple Database Support](#multiple-database-support)
32
+ - [Separate Database Support](#separate-database-support)
35
33
  - [Configuration](#configuration)
36
34
  - [Database Configuration](#database-configuration)
37
- - [Migration](#migration)
35
+ - [Schema Loading](#schema-loading)
38
36
  - [Testing](#testing)
39
37
  - [Technology Stack](#technology-stack)
40
38
  - [Advantages Over Other Solutions](#advantages-over-other-solutions)
@@ -44,33 +42,17 @@
44
42
 
45
43
  ## Introduction
46
44
 
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
45
+ Rails Pulse is a comprehensive performance monitoring and debugging gem that provides 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.
58
46
 
59
47
  ## Features
60
48
 
61
- ### ๐ŸŽฏ **Performance Monitoring**
49
+ ### Performance Monitoring
62
50
  - Interactive dashboard with response time charts and request analytics
63
51
  - SQL query performance tracking with slow query identification
64
52
  - Route-specific metrics with configurable performance thresholds
65
53
  - Week-over-week trend analysis with visual indicators
66
54
 
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**
55
+ ### Developer Experience
74
56
  - Zero configuration setup with sensible defaults
75
57
  - Beautiful responsive interface with dark/light mode
76
58
  - Smart caching with minimal performance overhead
@@ -78,7 +60,13 @@ Rails Pulse is a comprehensive performance monitoring and debugging gem that pro
78
60
 
79
61
  ## Screenshots
80
62
 
81
- <img src="app/assets/images/rails_pulse/dashboard.png" alt="Rails Pulse" />
63
+ <table>
64
+ <tr>
65
+ <td><img src="app/assets/images/rails_pulse/dashboard.png" alt="Rails Pulse Dashboard" width="400" /></td>
66
+ <td><img src="app/assets/images/rails_pulse/request.png" alt="Rails Pulse Requests" width="400" /></td>
67
+ </tr>
68
+ </table>
69
+
82
70
 
83
71
  ## Getting Started
84
72
 
@@ -102,22 +90,30 @@ Generate the installation files:
102
90
  rails generate rails_pulse:install
103
91
  ```
104
92
 
105
- Run the migrations:
93
+ Load the database schema:
106
94
 
107
95
  ```bash
108
- rails db:migrate
96
+ rails db:prepare
109
97
  ```
110
98
 
111
99
  Add the Rails Pulse route to your application:
112
100
 
113
101
  ```ruby
114
- # config/routes.rb
115
102
  Rails.application.routes.draw do
116
103
  mount RailsPulse::Engine => "/rails_pulse"
117
- # ... your other routes
118
104
  end
119
105
  ```
120
106
 
107
+ Schedule background jobs:
108
+
109
+ ```ruby
110
+ # Schedule to run 5 minutes past every hour. cron: 5 * * * *
111
+ RailsPulse::SummaryJob.perform_later
112
+
113
+ # Schedule to run daily. cron: 0 1 * * *
114
+ RailsPulse::CleanupJob.perform_later
115
+ ```
116
+
121
117
  ### Quick Setup
122
118
 
123
119
  Rails Pulse automatically starts collecting performance data once installed. Access your monitoring dashboard at:
@@ -135,14 +131,14 @@ RailsPulse.configure do |config|
135
131
  # Enable or disable Rails Pulse
136
132
  config.enabled = true
137
133
 
138
- # Set performance thresholds for routes (in milliseconds)
134
+ # Set performance thresholds for route response times (in milliseconds)
139
135
  config.route_thresholds = {
140
136
  slow: 500,
141
137
  very_slow: 1500,
142
138
  critical: 3000
143
139
  }
144
140
 
145
- # Set performance thresholds for requests (in milliseconds)
141
+ # Set performance thresholds for request response times (in milliseconds)
146
142
  config.request_thresholds = {
147
143
  slow: 700,
148
144
  very_slow: 2000,
@@ -157,8 +153,8 @@ RailsPulse.configure do |config|
157
153
  }
158
154
 
159
155
  # Asset tracking configuration
160
- config.track_assets = false # Ignore asset requests by default
161
- config.custom_asset_patterns = [] # Additional asset patterns to ignore
156
+ config.track_assets = false # Ignore asset requests by default
157
+ config.custom_asset_patterns = [] # Additional asset patterns to ignore
162
158
 
163
159
  # Rails Pulse mount path (optional)
164
160
  # Specify if Rails Pulse is mounted at a custom path to prevent self-tracking
@@ -171,7 +167,7 @@ RailsPulse.configure do |config|
171
167
 
172
168
  # Data cleanup
173
169
  config.archiving_enabled = true # Enable automatic cleanup
174
- config.full_retention_period = 2.weeks # Delete records older than this
170
+ config.full_retention_period = 2.weeks # Delete records older than this
175
171
  config.max_table_records = { # Maximum records per table
176
172
  rails_pulse_requests: 10000,
177
173
  rails_pulse_operations: 50000,
@@ -179,10 +175,6 @@ RailsPulse.configure do |config|
179
175
  rails_pulse_queries: 500
180
176
  }
181
177
 
182
- # Metric caching for performance
183
- config.component_cache_enabled = true
184
- config.component_cache_duration = 1.day
185
-
186
178
  # Multiple database support (optional)
187
179
  # Uncomment to store Rails Pulse data in a separate database
188
180
  # config.connects_to = {
@@ -219,7 +211,7 @@ end
219
211
 
220
212
  Rails Pulse works with any authentication system. Here are common patterns:
221
213
 
222
- #### **Devise with Admin Role**
214
+ #### Devise with Admin Role
223
215
 
224
216
  ```ruby
225
217
  config.authentication_method = proc {
@@ -229,36 +221,7 @@ config.authentication_method = proc {
229
221
  }
230
222
  ```
231
223
 
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**
224
+ #### Custom Authorization Logic
262
225
 
263
226
  ```ruby
264
227
  config.authentication_method = proc {
@@ -269,16 +232,6 @@ config.authentication_method = proc {
269
232
  }
270
233
  ```
271
234
 
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
235
  ## Data Management
283
236
 
284
237
  Rails Pulse provides data cleanup to prevent your monitoring database from growing indefinitely while preserving essential performance insights.
@@ -329,28 +282,22 @@ rails rails_pulse:cleanup_stats
329
282
 
330
283
  **Schedule automated cleanup:**
331
284
  ```ruby
332
- # Using whenever gem or similar scheduler
333
285
  RailsPulse::CleanupJob.perform_later
334
286
  ```
335
287
 
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
288
+ ## Separate Database Support
342
289
 
343
- This two-phase approach ensures you keep the most valuable recent performance data while maintaining manageable database sizes.
290
+ Rails Pulse supports storing performance monitoring data in a **separate database**. By default, Rails Pulse stores data in your main application database alongside your existing tables.
344
291
 
345
- ## Multiple Database Support
346
-
347
- Rails Pulse supports storing performance monitoring data in a separate database. This is particularly useful for:
292
+ Use a separate database when you want:
348
293
 
349
294
  - **Isolating monitoring data** from your main application database
350
295
  - **Using different database engines** optimized for time-series data
351
296
  - **Scaling monitoring independently** from your application
352
297
  - **Simplified backup strategies** with separate retention policies
353
298
 
299
+ **For shared database setup (default)**, no database configuration is needed - simply run `rails db:prepare` after installation.
300
+
354
301
  ### Configuration
355
302
 
356
303
  To use a separate database, configure the `connects_to` option in your Rails Pulse initializer:
@@ -380,6 +327,7 @@ production:
380
327
  rails_pulse:
381
328
  adapter: sqlite3
382
329
  database: storage/rails_pulse_production.sqlite3
330
+ migrations_paths: db/rails_pulse_migrate
383
331
  pool: 5
384
332
  timeout: 5000
385
333
 
@@ -392,6 +340,7 @@ production:
392
340
  username: rails_pulse_user
393
341
  password: <%= Rails.application.credentials.dig(:rails_pulse, :database_password) %>
394
342
  host: localhost
343
+ migrations_paths: db/rails_pulse_migrate
395
344
  pool: 5
396
345
 
397
346
  # For MySQL
@@ -403,35 +352,31 @@ production:
403
352
  username: rails_pulse_user
404
353
  password: <%= Rails.application.credentials.dig(:rails_pulse, :database_password) %>
405
354
  host: localhost
355
+ migrations_paths: db/rails_pulse_migrate
406
356
  pool: 5
407
357
  ```
408
358
 
409
- ### Migration
359
+ ### Schema Loading
410
360
 
411
- When using a separate database, run migrations targeting the Rails Pulse database:
361
+ After installation, load the Rails Pulse database schema:
412
362
 
413
363
  ```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
364
+ rails db:prepare
419
365
  ```
420
366
 
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.
367
+ This command works for both:
368
+ - Shared database setup (default): Loads tables into your main application database
369
+ - Separate database setup: Automatically loads tables into your configured Rails Pulse database
422
370
 
423
371
  ## Testing
424
372
 
425
- Rails Pulse includes a comprehensive test suite designed for speed and reliability across multiple databases (SQLite, MySQL, PostgreSQL) and Rails versions.
373
+ Rails Pulse includes a comprehensive test suite designed for speed and reliability across multiple databases and Rails versions.
426
374
 
427
375
  ### Running the Complete Test Suite
428
376
 
429
377
  ```bash
430
- # Run all tests (unit, functional, integration)
378
+ # Run all tests (unit, functional, integration, instrumentation)
431
379
  rails test:all
432
-
433
- # Run tests with speed optimizations
434
- rails test:fast
435
380
  ```
436
381
 
437
382
  ### Running Individual Test Types
@@ -452,83 +397,46 @@ rails test:integration
452
397
  ```bash
453
398
  # Run a specific test file
454
399
  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
400
  ```
465
401
 
466
- ### Multi-Rails Version Testing
402
+ ### Multi-Database and Rails Version Testing
467
403
 
468
- Test against multiple Rails versions using Appraisal:
404
+ Test against multiple databases and Rails versions using the matrix task:
469
405
 
470
406
  ```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
407
+ # Test all database and Rails version combinations locally
408
+ rails test:matrix
479
409
  ```
480
410
 
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
- ```
411
+ This command tests all combinations locally:
412
+ - **Databases**: SQLite3, PostgreSQL, MySQL2 (local testing only)
413
+ - **Rails versions**: 7.2, 8.0
502
414
 
503
- #### Development Environment Setup
415
+ **Note**: CI only tests SQLite3 + PostgreSQL for reliability. MySQL is available for local testing but excluded from CI due to flakiness.
504
416
 
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.
417
+ ### Development Environment Setup
510
418
 
511
- 2. **Copy the environment template:**
419
+ 1. **Copy the environment template:**
512
420
  ```bash
513
421
  cp .env.example .env
514
422
  ```
515
423
 
516
- 3. **Configure your database credentials in `.env`:**
424
+ 2. **Configure your database credentials in `.env` (for local multi-database testing):**
517
425
  ```bash
518
- # PostgreSQL Configuration
426
+ # PostgreSQL Configuration (used in CI + local)
519
427
  POSTGRES_USERNAME=your_username
520
428
  POSTGRES_PASSWORD=your_password
521
429
  POSTGRES_HOST=localhost
522
430
  POSTGRES_PORT=5432
523
431
 
524
- # MySQL Configuration
432
+ # MySQL Configuration (local testing only)
525
433
  MYSQL_USERNAME=root
526
434
  MYSQL_PASSWORD=your_password
527
435
  MYSQL_HOST=localhost
528
436
  MYSQL_PORT=3306
529
437
  ```
530
438
 
531
- 4. **Create test databases:**
439
+ 3. **Create test databases:**
532
440
  ```bash
533
441
  # PostgreSQL
534
442
  createdb rails_pulse_test
@@ -537,32 +445,32 @@ rails test:matrix_full # Test all databases (SQLite + PostgreSQL + MySQL)
537
445
  mysql -u root -p -e "CREATE DATABASE rails_pulse_test;"
538
446
  ```
539
447
 
540
- #### Manual Commands
541
- If you prefer the explicit approach:
448
+ ### Manual Database Testing
449
+
450
+ Test individual databases locally:
542
451
 
543
452
  ```bash
544
- # Test with SQLite (default, uses in-memory database)
453
+ # Test with SQLite (default)
545
454
  rails test:all
546
455
 
547
- # Test with PostgreSQL (requires local PostgreSQL setup)
548
- DATABASE_ADAPTER=postgresql FORCE_DB_CONFIG=true rails test:all
456
+ # Test with PostgreSQL
457
+ DB=postgresql FORCE_DB_CONFIG=true rails test:all
549
458
 
550
- # Test with MySQL (requires MySQL setup and mysql2 gem compilation)
551
- DATABASE_ADAPTER=mysql FORCE_DB_CONFIG=true rails test:all
459
+ # Test with MySQL (local only)
460
+ DB=mysql2 FORCE_DB_CONFIG=true rails test:all
552
461
  ```
553
462
 
554
- **Note**: Database switching is disabled by default for stability. The Rake tasks automatically handle the `FORCE_DB_CONFIG=true` requirement.
463
+ ### CI Testing
555
464
 
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
465
+ GitHub Actions CI automatically tests:
466
+ - **Databases**: SQLite3, PostgreSQL only (MySQL excluded for reliability)
467
+ - **Rails versions**: 7.2, 8.0
468
+ - **Environment**: Uses memory SQLite and PostgreSQL service
560
469
 
561
- ### Quick Testing Before Push
562
- ```bash
563
- # Recommended: Test the same databases as CI
564
- rails test:matrix
565
- ```
470
+ **Local vs CI differences**:
471
+ - **Local**: Can test all 3 databases (SQLite3, PostgreSQL, MySQL2)
472
+ - **CI**: Only SQLite3 + PostgreSQL for fast, reliable builds
473
+ - **Database switching**: Requires `FORCE_DB_CONFIG=true` locally
566
474
 
567
475
  ## Technology Stack
568
476
 
@@ -602,12 +510,6 @@ Rails Pulse is built using modern, battle-tested technologies that ensure reliab
602
510
  - **Customizable**: Full control over metrics, thresholds, and interface
603
511
  - **Asset Independence**: Works with any Rails build system (Sprockets, esbuild, Webpack, Vite)
604
512
 
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
513
  ### **vs. Custom Monitoring Solutions**
612
514
  - **Batteries Included**: Complete monitoring solution out of the box
613
515
  - **Proven Architecture**: Built on Rails best practices
data/Rakefile CHANGED
@@ -1,5 +1,8 @@
1
1
  require "bundler/setup"
2
2
 
3
+ # Load environment variables from .env file
4
+ require "dotenv/load" if File.exist?(".env")
5
+
3
6
  APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
4
7
  load "rails/tasks/engine.rake"
5
8
  load "rails/tasks/statistics.rake"
@@ -8,9 +11,9 @@ require "bundler/gem_tasks"
8
11
 
9
12
  # Test tasks
10
13
  namespace :test do
11
- desc "Run unit tests (models, helpers, services)"
14
+ desc "Run unit tests (models, helpers, services, instrumentation)"
12
15
  task :unit do
13
- sh "rails test test/models test/helpers test/services test/support"
16
+ sh "rails test test/models test/helpers test/services test/support test/instrumentation"
14
17
  end
15
18
 
16
19
  desc "Run functional tests (controllers)"
@@ -27,6 +30,78 @@ namespace :test do
27
30
  task :all do
28
31
  sh "rails test"
29
32
  end
33
+
34
+ desc "Run tests across all database and Rails version combinations (local only - CI uses sqlite3 + postgresql)"
35
+ task :matrix do
36
+ databases = [ "sqlite3", "postgresql", "mysql2" ]
37
+ rails_versions = [ "rails-7-2", "rails-8-0" ]
38
+
39
+ failed_combinations = []
40
+
41
+ databases.each do |database|
42
+ rails_versions.each do |rails_version|
43
+ puts "\n" + "=" * 80
44
+ puts "๐Ÿงช Local Testing: #{database.upcase} + #{rails_version.upcase}"
45
+ puts "(CI only tests SQLite3 + PostgreSQL for reliability)"
46
+ puts "=" * 80
47
+
48
+ begin
49
+ gemfile = "gemfiles/#{rails_version.gsub('-', '_')}.gemfile"
50
+
51
+ # Set environment variables
52
+ env_vars = {
53
+ "DB" => database,
54
+ "BUNDLE_GEMFILE" => gemfile,
55
+ "FORCE_DB_CONFIG" => "true"
56
+ }
57
+
58
+ # Add database-specific environment variables
59
+ case database
60
+ when "postgresql"
61
+ env_vars.merge!({
62
+ "POSTGRES_USERNAME" => ENV.fetch("POSTGRES_USERNAME", "postgres"),
63
+ "POSTGRES_PASSWORD" => ENV.fetch("POSTGRES_PASSWORD", ""),
64
+ "POSTGRES_HOST" => ENV.fetch("POSTGRES_HOST", "localhost"),
65
+ "POSTGRES_PORT" => ENV.fetch("POSTGRES_PORT", "5432")
66
+ })
67
+ when "mysql2"
68
+ env_vars.merge!({
69
+ "MYSQL_USERNAME" => ENV.fetch("MYSQL_USERNAME", "root"),
70
+ "MYSQL_PASSWORD" => ENV.fetch("MYSQL_PASSWORD", "password"),
71
+ "MYSQL_HOST" => ENV.fetch("MYSQL_HOST", "localhost"),
72
+ "MYSQL_PORT" => ENV.fetch("MYSQL_PORT", "3306")
73
+ })
74
+ end
75
+
76
+ # Build environment string
77
+ env_string = env_vars.map { |k, v| "#{k}=#{v}" }.join(" ")
78
+
79
+ # Run the test command
80
+ sh "#{env_string} bundle exec rails test:all"
81
+
82
+ puts "โœ… PASSED: #{database} + #{rails_version}"
83
+
84
+ rescue => e
85
+ puts "โŒ FAILED: #{database} + #{rails_version}"
86
+ puts "Error: #{e.message}"
87
+ failed_combinations << "#{database} + #{rails_version}"
88
+ end
89
+ end
90
+ end
91
+
92
+ puts "\n" + "=" * 80
93
+ puts "๐Ÿ Local Test Matrix Results"
94
+ puts "(CI automatically tests SQLite3 + PostgreSQL only)"
95
+ puts "=" * 80
96
+
97
+ if failed_combinations.empty?
98
+ puts "โœ… All combinations passed!"
99
+ else
100
+ puts "โŒ Failed combinations:"
101
+ failed_combinations.each { |combo| puts " - #{combo}" }
102
+ exit 1
103
+ end
104
+ end
30
105
  end
31
106
 
32
107
  # Override default test task
@@ -4,25 +4,48 @@
4
4
 
5
5
  a {
6
6
  text-decoration: underline;
7
- color: #0048b5;
7
+ color: var(--color-link);
8
8
  }
9
9
 
10
10
  #header {
11
- background-color: #ffc91f;
11
+ background-color: var(--header-bg);
12
12
  }
13
13
 
14
14
  #header a {
15
- color: black
15
+ color: var(--header-link);
16
+ text-decoration: none;
16
17
  }
17
18
 
18
19
  #header a:hover {
19
- background-color: #ffe284;
20
+ background-color: transparent;
21
+ text-decoration: underline;
20
22
  }
21
23
 
22
24
  a:hover {
23
25
  cursor: pointer;
24
26
  }
25
27
 
28
+ /* Dark mode */
29
+
30
+ /* Dark scheme tweaks via component variables */
31
+ html[data-color-scheme="dark"] .card {
32
+ /* Scope card surfaces slightly darker than page */
33
+ --color-bg: rgb(47, 47, 47);
34
+ --color-border: rgb(64, 64, 64);
35
+ }
36
+
37
+ /* Header colors are handled by --header-* tokens in base.css */
38
+
39
+ html[data-color-scheme="dark"] .badge--positive-inverse,
40
+ html[data-color-scheme="dark"] .badge--negative-inverse {
41
+ --badge-background: rgb(47, 47, 47);
42
+ }
43
+
44
+ html[data-color-scheme="dark"] .input {
45
+ --input-background: #535252;
46
+ --input-border-color: #7e7d7d;
47
+ }
48
+
26
49
  .hidden {
27
50
  display: none;
28
51
  }
@@ -66,7 +89,7 @@ a:hover {
66
89
  height: 16px;
67
90
  padding: 2px;
68
91
  position: absolute;
69
- top: 11px;
92
+ top: 20px;
70
93
  }
71
94
 
72
95
  /* REQUEST OPERATIONS BAR */
@@ -80,18 +103,6 @@ a:hover {
80
103
  position:absolute;
81
104
  top:0
82
105
  }
83
- .bar.db {
84
- background-color:#92c282
85
- }
86
- .bar.app {
87
- background-color:#00adc4
88
- }
89
- .bar.gc {
90
- background-color:#323333
91
- }
92
- .bar.view {
93
- background-color:#b48da3
94
- }
95
106
  .bar:first-child {
96
107
  border-bottom-left-radius:1px;
97
108
  border-top-left-radius:1px