rails_pulse 0.1.2 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +66 -20
- data/Rakefile +169 -86
- data/app/assets/images/rails_pulse/dashboard.png +0 -0
- data/app/assets/images/rails_pulse/request.png +0 -0
- data/app/assets/stylesheets/rails_pulse/application.css +28 -5
- data/app/assets/stylesheets/rails_pulse/components/badge.css +13 -0
- data/app/assets/stylesheets/rails_pulse/components/base.css +12 -2
- data/app/assets/stylesheets/rails_pulse/components/collapsible.css +30 -0
- data/app/assets/stylesheets/rails_pulse/components/popover.css +0 -1
- data/app/assets/stylesheets/rails_pulse/components/row.css +55 -3
- data/app/assets/stylesheets/rails_pulse/components/sidebar_menu.css +23 -0
- data/app/controllers/concerns/zoom_range_concern.rb +31 -0
- data/app/controllers/rails_pulse/application_controller.rb +5 -1
- data/app/controllers/rails_pulse/queries_controller.rb +49 -10
- data/app/controllers/rails_pulse/requests_controller.rb +46 -20
- data/app/controllers/rails_pulse/routes_controller.rb +40 -1
- data/app/helpers/rails_pulse/breadcrumbs_helper.rb +1 -1
- data/app/helpers/rails_pulse/chart_helper.rb +16 -8
- data/app/helpers/rails_pulse/formatting_helper.rb +21 -2
- data/app/javascript/rails_pulse/application.js +34 -3
- data/app/javascript/rails_pulse/controllers/collapsible_controller.js +32 -0
- data/app/javascript/rails_pulse/controllers/color_scheme_controller.js +2 -1
- data/app/javascript/rails_pulse/controllers/expandable_rows_controller.js +58 -0
- data/app/javascript/rails_pulse/controllers/index_controller.js +249 -11
- data/app/javascript/rails_pulse/controllers/popover_controller.js +28 -4
- data/app/javascript/rails_pulse/controllers/table_sort_controller.js +14 -0
- data/app/models/rails_pulse/dashboard/tables/slow_queries.rb +1 -1
- data/app/models/rails_pulse/dashboard/tables/slow_routes.rb +1 -1
- data/app/models/rails_pulse/queries/cards/average_query_times.rb +20 -20
- data/app/models/rails_pulse/queries/cards/execution_rate.rb +58 -14
- data/app/models/rails_pulse/queries/cards/percentile_query_times.rb +14 -9
- data/app/models/rails_pulse/queries/charts/average_query_times.rb +3 -7
- data/app/models/rails_pulse/query.rb +46 -0
- data/app/models/rails_pulse/request.rb +1 -1
- data/app/models/rails_pulse/requests/charts/average_response_times.rb +2 -2
- data/app/models/rails_pulse/requests/tables/index.rb +77 -0
- data/app/models/rails_pulse/routes/cards/average_response_times.rb +18 -20
- data/app/models/rails_pulse/routes/cards/error_rate_per_route.rb +14 -9
- data/app/models/rails_pulse/routes/cards/percentile_response_times.rb +14 -9
- data/app/models/rails_pulse/routes/cards/request_count_totals.rb +29 -13
- data/app/models/rails_pulse/routes/tables/index.rb +4 -2
- data/app/models/rails_pulse/summary.rb +7 -7
- data/app/services/rails_pulse/analysis/backtrace_analyzer.rb +256 -0
- data/app/services/rails_pulse/analysis/base_analyzer.rb +67 -0
- data/app/services/rails_pulse/analysis/explain_plan_analyzer.rb +206 -0
- data/app/services/rails_pulse/analysis/index_recommendation_engine.rb +326 -0
- data/app/services/rails_pulse/analysis/n_plus_one_detector.rb +241 -0
- data/app/services/rails_pulse/analysis/query_characteristics_analyzer.rb +154 -0
- data/app/services/rails_pulse/analysis/suggestion_generator.rb +217 -0
- data/app/services/rails_pulse/query_analysis_service.rb +125 -0
- data/app/views/layouts/rails_pulse/_sidebar_menu.html.erb +0 -1
- data/app/views/layouts/rails_pulse/application.html.erb +0 -2
- data/app/views/rails_pulse/components/_breadcrumbs.html.erb +1 -1
- data/app/views/rails_pulse/components/_code_panel.html.erb +17 -3
- data/app/views/rails_pulse/components/_empty_state.html.erb +1 -1
- data/app/views/rails_pulse/components/_metric_card.html.erb +28 -5
- data/app/views/rails_pulse/components/_operation_details_popover.html.erb +1 -1
- data/app/views/rails_pulse/components/_panel.html.erb +1 -1
- data/app/views/rails_pulse/components/_sparkline_stats.html.erb +5 -7
- data/app/views/rails_pulse/components/_table_head.html.erb +6 -1
- data/app/views/rails_pulse/dashboard/index.html.erb +2 -2
- data/app/views/rails_pulse/operations/show.html.erb +17 -15
- data/app/views/rails_pulse/queries/_analysis_error.html.erb +15 -0
- data/app/views/rails_pulse/queries/_analysis_prompt.html.erb +27 -0
- data/app/views/rails_pulse/queries/_analysis_results.html.erb +117 -0
- data/app/views/rails_pulse/queries/_analysis_section.html.erb +39 -0
- data/app/views/rails_pulse/queries/_show_table.html.erb +34 -6
- data/app/views/rails_pulse/queries/_table.html.erb +4 -8
- data/app/views/rails_pulse/queries/index.html.erb +48 -51
- data/app/views/rails_pulse/queries/show.html.erb +56 -52
- data/app/views/rails_pulse/requests/_operations.html.erb +30 -43
- data/app/views/rails_pulse/requests/_table.html.erb +31 -18
- data/app/views/rails_pulse/requests/index.html.erb +55 -50
- data/app/views/rails_pulse/requests/show.html.erb +0 -2
- data/app/views/rails_pulse/routes/_requests_table.html.erb +39 -0
- data/app/views/rails_pulse/routes/_table.html.erb +4 -10
- data/app/views/rails_pulse/routes/index.html.erb +49 -52
- data/app/views/rails_pulse/routes/show.html.erb +6 -8
- data/config/initializers/rails_charts_csp_patch.rb +32 -40
- data/config/routes.rb +5 -1
- data/db/migrate/20250930105043_install_rails_pulse_tables.rb +23 -0
- data/db/rails_pulse_schema.rb +10 -1
- data/lib/generators/rails_pulse/convert_to_migrations_generator.rb +81 -0
- data/lib/generators/rails_pulse/install_generator.rb +75 -18
- data/lib/generators/rails_pulse/templates/db/rails_pulse_schema.rb +72 -2
- data/lib/generators/rails_pulse/templates/migrations/install_rails_pulse_tables.rb +23 -0
- data/lib/generators/rails_pulse/templates/migrations/upgrade_rails_pulse_tables.rb +19 -0
- data/lib/generators/rails_pulse/upgrade_generator.rb +226 -0
- data/lib/rails_pulse/engine.rb +21 -0
- data/lib/rails_pulse/version.rb +1 -1
- data/lib/tasks/rails_pulse.rake +27 -8
- data/public/rails-pulse-assets/rails-pulse.css +1 -1
- data/public/rails-pulse-assets/rails-pulse.css.map +1 -1
- data/public/rails-pulse-assets/rails-pulse.js +53 -53
- data/public/rails-pulse-assets/rails-pulse.js.map +4 -4
- metadata +25 -6
- data/app/assets/images/rails_pulse/rails-pulse-logo.png +0 -0
- data/app/assets/images/rails_pulse/routes.png +0 -0
- data/app/javascript/rails_pulse/controllers/expandable_row_controller.js +0 -67
- data/db/migrate/20241222000001_create_rails_pulse_summaries.rb +0 -54
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4d5eb9055acbfc951ef6874c484b10f0c4c9a02f4886148fd917ec1f83cdc386
|
4
|
+
data.tar.gz: fe1db6e87b0788868ab99a4c5ee2c818fb2720b8aaa88a59d86023a8a4bc0f92
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 17141eaefe36c4ee6e857fe36ae3ee7c9e500cb6b3dc07ba79b2ba20248cb1a01920d5f0441c09279af0ab04efa020ffe144aeef1f4fb00036094378c1e2fc65
|
7
|
+
data.tar.gz: 8bf37d00fc3b161eb84587eef5562cebe493adb6a0729897f00aed9d38a69b1847ba82781473a8d2b3deb4ad1acea764b014460a758bc12ef686028096974355
|
data/README.md
CHANGED
@@ -60,7 +60,13 @@ Rails Pulse is a comprehensive performance monitoring and debugging gem that pro
|
|
60
60
|
|
61
61
|
## Screenshots
|
62
62
|
|
63
|
-
<
|
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
|
+
|
64
70
|
|
65
71
|
## Getting Started
|
66
72
|
|
@@ -81,15 +87,22 @@ bundle install
|
|
81
87
|
Generate the installation files:
|
82
88
|
|
83
89
|
```bash
|
90
|
+
# Install with single database setup (default - recommended)
|
84
91
|
rails generate rails_pulse:install
|
85
|
-
```
|
86
92
|
|
87
|
-
|
93
|
+
# Or install with separate database setup
|
94
|
+
rails generate rails_pulse:install --database=separate
|
95
|
+
```
|
88
96
|
|
97
|
+
**For single database setup (default):**
|
89
98
|
```bash
|
90
|
-
rails db:
|
99
|
+
rails db:migrate # Creates Rails Pulse tables in your main database
|
91
100
|
```
|
92
101
|
|
102
|
+
**For separate database setup:**
|
103
|
+
1. Configure `config/database.yml` with your Rails Pulse database settings
|
104
|
+
2. Run: `rails db:prepare` to create and load the schema
|
105
|
+
|
93
106
|
Add the Rails Pulse route to your application:
|
94
107
|
|
95
108
|
```ruby
|
@@ -101,10 +114,10 @@ end
|
|
101
114
|
Schedule background jobs:
|
102
115
|
|
103
116
|
```ruby
|
104
|
-
# Schedule to run 5 minutes past every hour
|
117
|
+
# Schedule to run 5 minutes past every hour. cron: 5 * * * *
|
105
118
|
RailsPulse::SummaryJob.perform_later
|
106
119
|
|
107
|
-
# Schedule to run daily
|
120
|
+
# Schedule to run daily. cron: 0 1 * * *
|
108
121
|
RailsPulse::CleanupJob.perform_later
|
109
122
|
```
|
110
123
|
|
@@ -116,6 +129,10 @@ Rails Pulse automatically starts collecting performance data once installed. Acc
|
|
116
129
|
http://localhost:3000/rails_pulse
|
117
130
|
```
|
118
131
|
|
132
|
+
**Database Setup:**
|
133
|
+
- **Single Database (default)**: Rails Pulse tables are created in your main database - no additional configuration needed
|
134
|
+
- **Separate Database**: See the [Separate Database Support](#separate-database-support) section for setup instructions
|
135
|
+
|
119
136
|
### Basic Configuration
|
120
137
|
|
121
138
|
Customize Rails Pulse in `config/initializers/rails_pulse.rb`:
|
@@ -281,20 +298,36 @@ RailsPulse::CleanupJob.perform_later
|
|
281
298
|
|
282
299
|
## Separate Database Support
|
283
300
|
|
284
|
-
Rails Pulse
|
301
|
+
Rails Pulse offers two database setup options to fit your application's needs:
|
302
|
+
|
303
|
+
### Option 1: Single Database (Default - Recommended)
|
304
|
+
|
305
|
+
Stores Rails Pulse data in your main application database alongside your existing tables. This is the simplest setup and works great for most applications.
|
306
|
+
|
307
|
+
**Advantages:**
|
308
|
+
- Zero additional configuration required
|
309
|
+
- Simpler backup and deployment strategies
|
310
|
+
- Works with any database (SQLite, PostgreSQL, MySQL)
|
311
|
+
|
312
|
+
**Installation:**
|
313
|
+
```bash
|
314
|
+
rails generate rails_pulse:install
|
315
|
+
rails db:migrate
|
316
|
+
```
|
285
317
|
|
286
|
-
|
318
|
+
### Option 2: Separate Database
|
287
319
|
|
320
|
+
Stores Rails Pulse data in a dedicated database, completely isolated from your main application.
|
321
|
+
|
322
|
+
**Use a separate database when you want:**
|
288
323
|
- **Isolating monitoring data** from your main application database
|
289
324
|
- **Using different database engines** optimized for time-series data
|
290
325
|
- **Scaling monitoring independently** from your application
|
291
326
|
- **Simplified backup strategies** with separate retention policies
|
292
327
|
|
293
|
-
**For shared database setup (default)**, no database configuration is needed - simply run `rails db:prepare` after installation.
|
294
|
-
|
295
328
|
### Configuration
|
296
329
|
|
297
|
-
To use a separate database, configure the `connects_to` option in your Rails Pulse initializer:
|
330
|
+
To use a separate database, install with the `--database=separate` flag, then configure the `connects_to` option in your Rails Pulse initializer:
|
298
331
|
|
299
332
|
```ruby
|
300
333
|
RailsPulse.configure do |config|
|
@@ -350,17 +383,30 @@ production:
|
|
350
383
|
pool: 5
|
351
384
|
```
|
352
385
|
|
353
|
-
###
|
386
|
+
### Installation Steps
|
354
387
|
|
355
|
-
|
388
|
+
**For separate database setup:**
|
356
389
|
|
357
|
-
|
358
|
-
|
359
|
-
|
390
|
+
1. **Generate installation files:**
|
391
|
+
```bash
|
392
|
+
rails generate rails_pulse:install --database=separate
|
393
|
+
```
|
394
|
+
|
395
|
+
2. **Configure `config/database.yml`** (see examples above)
|
396
|
+
|
397
|
+
3. **Create and load the schema:**
|
398
|
+
```bash
|
399
|
+
rails db:prepare
|
400
|
+
```
|
401
|
+
This automatically creates the database and loads the Rails Pulse schema.
|
402
|
+
|
403
|
+
**Schema Management:**
|
360
404
|
|
361
|
-
|
362
|
-
-
|
363
|
-
-
|
405
|
+
The schema file `db/rails_pulse_schema.rb` serves as your single source of truth for the database structure. It:
|
406
|
+
- Defines all Rails Pulse tables in one place
|
407
|
+
- Is loaded by the installation migration
|
408
|
+
- Should not be deleted or modified
|
409
|
+
- Future updates will provide migrations in `db/rails_pulse_migrate/`
|
364
410
|
|
365
411
|
## Testing
|
366
412
|
|
@@ -447,7 +493,7 @@ Test individual databases locally:
|
|
447
493
|
# Test with SQLite (default)
|
448
494
|
rails test:all
|
449
495
|
|
450
|
-
# Test with PostgreSQL
|
496
|
+
# Test with PostgreSQL
|
451
497
|
DB=postgresql FORCE_DB_CONFIG=true rails test:all
|
452
498
|
|
453
499
|
# Test with MySQL (local only)
|
data/Rakefile
CHANGED
@@ -1,109 +1,192 @@
|
|
1
1
|
require "bundler/setup"
|
2
|
+
require "bundler/gem_tasks"
|
2
3
|
|
3
4
|
# Load environment variables from .env file
|
4
5
|
require "dotenv/load" if File.exist?(".env")
|
5
6
|
|
6
7
|
APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
|
7
8
|
load "rails/tasks/engine.rake"
|
8
|
-
load "rails/tasks/statistics.rake"
|
9
9
|
|
10
|
-
|
10
|
+
desc "Setup database for testing"
|
11
|
+
task :test_setup do
|
12
|
+
database = ENV['DB'] || 'sqlite3'
|
13
|
+
|
14
|
+
puts "\n" + "=" * 50
|
15
|
+
puts "🛠️ Rails Pulse Test Setup"
|
16
|
+
puts "=" * 50
|
17
|
+
puts "Database: #{database.upcase}"
|
18
|
+
puts "=" * 50
|
19
|
+
puts
|
20
|
+
|
21
|
+
begin
|
22
|
+
# Remove schema.rb to ensure clean migration
|
23
|
+
schema_file = "test/dummy/db/schema.rb"
|
24
|
+
if File.exist?(schema_file)
|
25
|
+
puts "🧹 Removing existing schema.rb file..."
|
26
|
+
File.delete(schema_file)
|
27
|
+
end
|
11
28
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
29
|
+
case database.downcase
|
30
|
+
when 'sqlite3', 'sqlite'
|
31
|
+
puts "📦 Setting up SQLite database..."
|
32
|
+
sh "RAILS_ENV=test bin/rails db:drop db:create db:migrate"
|
33
|
+
|
34
|
+
when 'mysql2', 'mysql'
|
35
|
+
puts "🐬 Setting up MySQL database..."
|
36
|
+
sh "DB=mysql2 RAILS_ENV=test rails db:drop db:create db:migrate"
|
37
|
+
|
38
|
+
when 'postgresql', 'postgres'
|
39
|
+
puts "🐘 Setting up PostgreSQL database..."
|
40
|
+
sh "DB=postgresql RAILS_ENV=test rails db:drop db:create db:migrate"
|
41
|
+
|
42
|
+
else
|
43
|
+
puts "⚠️ Unknown database: #{database}"
|
44
|
+
puts "Supported databases: sqlite3, mysql2, postgresql"
|
45
|
+
exit 1
|
46
|
+
end
|
18
47
|
|
19
|
-
|
20
|
-
|
21
|
-
|
48
|
+
puts "\n✅ Database setup complete!"
|
49
|
+
puts "Ready to run: rake test"
|
50
|
+
|
51
|
+
rescue => e
|
52
|
+
puts "\n❌ Database setup failed!"
|
53
|
+
puts "Error: #{e.message}"
|
54
|
+
puts "\nTroubleshooting:"
|
55
|
+
puts "• Ensure #{database} is installed and running"
|
56
|
+
puts "• Check database credentials in test/dummy/config/database.yml"
|
57
|
+
puts "• Verify RAILS_ENV=test environment is configured"
|
58
|
+
exit 1
|
22
59
|
end
|
60
|
+
end
|
23
61
|
|
24
|
-
|
25
|
-
|
26
|
-
|
62
|
+
desc "Run test suite"
|
63
|
+
task :test do
|
64
|
+
database = ENV['DB'] || 'sqlite3'
|
65
|
+
|
66
|
+
# Get Rails version from Gemfile.lock or fallback
|
67
|
+
rails_version = begin
|
68
|
+
require 'rails'
|
69
|
+
Rails.version
|
70
|
+
rescue LoadError
|
71
|
+
# Try to get from Gemfile.lock
|
72
|
+
gemfile_lock = File.read('Gemfile.lock') rescue nil
|
73
|
+
if gemfile_lock && gemfile_lock.match(/rails \(([^)]+)\)/)
|
74
|
+
$1
|
75
|
+
else
|
76
|
+
'unknown'
|
77
|
+
end
|
27
78
|
end
|
28
79
|
|
29
|
-
|
30
|
-
|
31
|
-
|
80
|
+
puts "\n" + "=" * 50
|
81
|
+
puts "💛 Rails Pulse Test Suite"
|
82
|
+
puts "=" * 50
|
83
|
+
puts "Database: #{database.upcase}"
|
84
|
+
puts "Rails: #{rails_version}"
|
85
|
+
puts "=" * 50
|
86
|
+
puts
|
87
|
+
|
88
|
+
sh "rails test test/controllers test/helpers test/instrumentation test/jobs test/models test/services test/system"
|
89
|
+
end
|
90
|
+
|
91
|
+
desc "Setup database for specific Rails version and database"
|
92
|
+
task :test_setup_for_version, [ :database, :rails_version ] do |t, args|
|
93
|
+
database = args[:database] || ENV['DB'] || 'sqlite3'
|
94
|
+
rails_version = args[:rails_version] || 'rails-8-0'
|
95
|
+
|
96
|
+
puts "\n" + "=" * 50
|
97
|
+
puts "🛠️ Rails Pulse Test Setup"
|
98
|
+
puts "=" * 50
|
99
|
+
puts "Database: #{database.upcase}"
|
100
|
+
puts "Rails: #{rails_version.upcase.gsub('-', ' ')}"
|
101
|
+
puts "=" * 50
|
102
|
+
puts
|
103
|
+
|
104
|
+
begin
|
105
|
+
# Remove schema.rb to ensure clean migration
|
106
|
+
schema_file = "test/dummy/db/schema.rb"
|
107
|
+
if File.exist?(schema_file)
|
108
|
+
puts "🧹 Removing existing schema.rb file..."
|
109
|
+
File.delete(schema_file)
|
110
|
+
end
|
111
|
+
|
112
|
+
if rails_version == "rails-8-0" && database == "sqlite3"
|
113
|
+
# Use current default setup
|
114
|
+
puts "📦 Setting up #{database.upcase} database with Rails 8.0..."
|
115
|
+
sh "RAILS_ENV=test bin/rails db:drop db:create db:migrate"
|
116
|
+
else
|
117
|
+
# Use appraisal with specific database and Rails version
|
118
|
+
puts "📦 Setting up #{database.upcase} database with #{rails_version.upcase.gsub('-', ' ')}..."
|
119
|
+
sh "DB=#{database} bundle exec appraisal #{rails_version} rails db:drop db:create db:migrate RAILS_ENV=test"
|
120
|
+
end
|
121
|
+
|
122
|
+
puts "\n✅ Database setup complete for #{database.upcase} + #{rails_version.upcase.gsub('-', ' ')}!"
|
123
|
+
|
124
|
+
rescue => e
|
125
|
+
puts "\n❌ Database setup failed!"
|
126
|
+
puts "Error: #{e.message}"
|
127
|
+
exit 1
|
32
128
|
end
|
129
|
+
end
|
33
130
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
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}"
|
131
|
+
desc "Test all database and Rails version combinations"
|
132
|
+
task :test_matrix do
|
133
|
+
databases = %w[sqlite3 postgresql mysql2]
|
134
|
+
rails_versions = %w[rails-7-2 rails-8-0]
|
135
|
+
|
136
|
+
failed_combinations = []
|
137
|
+
total_combinations = databases.size * rails_versions.size
|
138
|
+
current = 0
|
139
|
+
|
140
|
+
puts "\n" + "=" * 60
|
141
|
+
puts "🚀 Rails Pulse Full Test Matrix"
|
142
|
+
puts "=" * 60
|
143
|
+
puts "Testing #{total_combinations} combinations..."
|
144
|
+
puts "=" * 60
|
145
|
+
|
146
|
+
databases.each do |database|
|
147
|
+
rails_versions.each do |rails_version|
|
148
|
+
current += 1
|
149
|
+
|
150
|
+
puts "\n[#{current}/#{total_combinations}] Testing: #{database.upcase} + #{rails_version.upcase.gsub('-', ' ')}"
|
151
|
+
puts "-" * 50
|
152
|
+
|
153
|
+
begin
|
154
|
+
# First setup the database for this specific combination
|
155
|
+
Rake::Task[:test_setup_for_version].reenable
|
156
|
+
Rake::Task[:test_setup_for_version].invoke(database, rails_version)
|
157
|
+
|
158
|
+
# Then run the tests
|
159
|
+
if rails_version == "rails-8-0" && database == "sqlite3"
|
160
|
+
# Current default setup
|
161
|
+
sh "bundle exec rake test"
|
162
|
+
else
|
163
|
+
# Use appraisal with specific database
|
164
|
+
sh "DB=#{database} bundle exec appraisal #{rails_version} rake test"
|
88
165
|
end
|
89
|
-
end
|
90
|
-
end
|
91
166
|
|
92
|
-
|
93
|
-
puts "🏁 Local Test Matrix Results"
|
94
|
-
puts "(CI automatically tests SQLite3 + PostgreSQL only)"
|
95
|
-
puts "=" * 80
|
167
|
+
puts "✅ PASSED: #{database} + #{rails_version}"
|
96
168
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
exit 1
|
169
|
+
rescue => e
|
170
|
+
puts "❌ FAILED: #{database} + #{rails_version}"
|
171
|
+
puts " Error: #{e.message}"
|
172
|
+
failed_combinations << "#{database} + #{rails_version}"
|
173
|
+
end
|
103
174
|
end
|
104
175
|
end
|
176
|
+
|
177
|
+
puts "\n" + "=" * 60
|
178
|
+
puts "🏁 Test Matrix Results"
|
179
|
+
puts "=" * 60
|
180
|
+
|
181
|
+
if failed_combinations.empty?
|
182
|
+
puts "🎉 All #{total_combinations} combinations passed!"
|
183
|
+
else
|
184
|
+
puts "✅ Passed: #{total_combinations - failed_combinations.size}/#{total_combinations}"
|
185
|
+
puts "❌ Failed combinations:"
|
186
|
+
failed_combinations.each { |combo| puts " • #{combo}" }
|
187
|
+
exit 1
|
188
|
+
end
|
105
189
|
end
|
106
190
|
|
107
|
-
|
108
|
-
|
109
|
-
task test: "test:all"
|
191
|
+
|
192
|
+
task default: :test
|
Binary file
|
Binary file
|
@@ -4,25 +4,48 @@
|
|
4
4
|
|
5
5
|
a {
|
6
6
|
text-decoration: underline;
|
7
|
-
color:
|
7
|
+
color: var(--color-link);
|
8
8
|
}
|
9
9
|
|
10
10
|
#header {
|
11
|
-
background-color:
|
11
|
+
background-color: var(--header-bg);
|
12
12
|
}
|
13
13
|
|
14
14
|
#header a {
|
15
|
-
color:
|
15
|
+
color: var(--header-link);
|
16
|
+
text-decoration: none;
|
16
17
|
}
|
17
18
|
|
18
19
|
#header a:hover {
|
19
|
-
background-color:
|
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:
|
92
|
+
top: 20px;
|
70
93
|
}
|
71
94
|
|
72
95
|
/* REQUEST OPERATIONS BAR */
|
@@ -56,3 +56,16 @@
|
|
56
56
|
--badge-border-color: transparent;
|
57
57
|
--badge-color: var(--color-negative);
|
58
58
|
}
|
59
|
+
|
60
|
+
/* Trend badge icon lightening (dark mode only) */
|
61
|
+
.badge--trend rails-pulse-icon { color: var(--badge-color, currentColor); }
|
62
|
+
html[data-color-scheme="dark"] .badge--trend rails-pulse-icon {
|
63
|
+
/* Lighten icon relative to badge text color for contrast */
|
64
|
+
color: color-mix(in srgb, var(--badge-color) 55%, white 45%);
|
65
|
+
}
|
66
|
+
|
67
|
+
/* Trend amount lightening (dark mode only) */
|
68
|
+
.badge--trend .badge__trend-amount { color: var(--badge-color, currentColor); }
|
69
|
+
html[data-color-scheme="dark"] .badge--trend .badge__trend-amount {
|
70
|
+
color: color-mix(in srgb, var(--badge-color) 55%, white 45%);
|
71
|
+
}
|
@@ -5,6 +5,10 @@
|
|
5
5
|
--color-text-reversed: white;
|
6
6
|
--color-text-subtle: var(--zinc-500);
|
7
7
|
--color-link: var(--blue-700);
|
8
|
+
/* Header tokens */
|
9
|
+
--header-bg: #ffc91f;
|
10
|
+
--header-link: black;
|
11
|
+
--header-link-hover-bg: #ffe284;
|
8
12
|
--color-border-light: var(--zinc-100);
|
9
13
|
--color-border: var(--zinc-200);
|
10
14
|
--color-border-dark: var(--zinc-400);
|
@@ -30,8 +34,9 @@ html[data-color-scheme="dark"] {
|
|
30
34
|
--color-bg: var(--zinc-800);
|
31
35
|
--color-text: white;
|
32
36
|
--color-text-reversed: black;
|
33
|
-
--color-text-subtle: var(--zinc-
|
34
|
-
|
37
|
+
--color-text-subtle: var(--zinc-300);
|
38
|
+
/* Use brand yellow for links in dark mode */
|
39
|
+
--color-link: #ffc91f;
|
35
40
|
--color-border-light: var(--zinc-900);
|
36
41
|
--color-border: var(--zinc-800);
|
37
42
|
--color-border-dark: var(--zinc-600);
|
@@ -39,6 +44,11 @@ html[data-color-scheme="dark"] {
|
|
39
44
|
--color-selected-dark: var(--blue-800);
|
40
45
|
--color-highlight: var(--yellow-900);
|
41
46
|
|
47
|
+
/* Header tokens */
|
48
|
+
--header-bg: rgb(32, 32, 32);
|
49
|
+
--header-link: #ffc91f;
|
50
|
+
--header-link-hover-bg: #ffe284; /* keep existing hover color */
|
51
|
+
|
42
52
|
/* Accent colors */
|
43
53
|
--color-primary: var(--zinc-50);
|
44
54
|
--color-secondary: var(--zinc-800);
|
@@ -0,0 +1,30 @@
|
|
1
|
+
.collapsible-code.collapsed pre {
|
2
|
+
max-height: 4.5em;
|
3
|
+
overflow: hidden;
|
4
|
+
position: relative;
|
5
|
+
}
|
6
|
+
|
7
|
+
.collapsible-code.collapsed pre::after {
|
8
|
+
content: '';
|
9
|
+
position: absolute;
|
10
|
+
bottom: 0;
|
11
|
+
left: 0;
|
12
|
+
right: 0;
|
13
|
+
height: 1em;
|
14
|
+
background: linear-gradient(transparent, var(--color-border-light));
|
15
|
+
pointer-events: none;
|
16
|
+
}
|
17
|
+
|
18
|
+
.collapsible-toggle {
|
19
|
+
margin-top: 0.5rem;
|
20
|
+
font-size: 0.875rem;
|
21
|
+
color: var(--color-link);
|
22
|
+
text-decoration: underline;
|
23
|
+
transform: lowercase;
|
24
|
+
cursor: pointer;
|
25
|
+
border: none;
|
26
|
+
background: none;
|
27
|
+
padding: 0;
|
28
|
+
font-weight: normal;
|
29
|
+
margin-left: 10px;
|
30
|
+
}
|
@@ -3,22 +3,74 @@
|
|
3
3
|
justify-content: space-between;
|
4
4
|
width: 100%;
|
5
5
|
gap: var(--column-gap, 0.5rem);
|
6
|
+
align-items: stretch;
|
6
7
|
}
|
7
8
|
|
8
9
|
.row > * {
|
9
10
|
flex: 1;
|
10
11
|
min-width: 0;
|
12
|
+
display: flex;
|
13
|
+
flex-direction: column;
|
11
14
|
}
|
12
15
|
|
13
|
-
/*
|
16
|
+
/* Ensure metric cards and their panels stretch to full height */
|
17
|
+
.row > .grid-item {
|
18
|
+
display: flex;
|
19
|
+
flex-direction: column;
|
20
|
+
}
|
21
|
+
|
22
|
+
.row > .grid-item > * {
|
23
|
+
flex: 1;
|
24
|
+
}
|
25
|
+
|
26
|
+
/* Responsive layout for screens smaller than 768px */
|
14
27
|
@media (max-width: 768px) {
|
15
28
|
.row {
|
16
|
-
|
29
|
+
display: flex;
|
30
|
+
flex-wrap: wrap;
|
31
|
+
justify-content: space-between;
|
17
32
|
gap: 0.5rem;
|
33
|
+
align-items: flex-start;
|
18
34
|
}
|
19
35
|
|
20
36
|
.row > * {
|
37
|
+
flex: 0 0 calc(50% - 0.25rem);
|
38
|
+
min-width: 0;
|
39
|
+
height: auto;
|
40
|
+
}
|
41
|
+
|
42
|
+
.row > .grid-item {
|
43
|
+
height: auto;
|
44
|
+
}
|
45
|
+
|
46
|
+
.row > .grid-item > * {
|
21
47
|
flex: none;
|
22
|
-
|
48
|
+
}
|
49
|
+
|
50
|
+
/* Tables should stack in single column on mobile */
|
51
|
+
.row:has(.table-container) > * {
|
52
|
+
flex: 0 0 100%;
|
53
|
+
}
|
54
|
+
|
55
|
+
/* Single column for very small screens */
|
56
|
+
@media (max-width: 480px) {
|
57
|
+
.row > * {
|
58
|
+
flex: 0 0 100%;
|
59
|
+
}
|
60
|
+
|
61
|
+
.row > .grid-item {
|
62
|
+
min-height: auto;
|
63
|
+
}
|
64
|
+
|
65
|
+
/* Make metric cards more compact on mobile */
|
66
|
+
.row > .grid-item .card {
|
67
|
+
padding: var(--size-3);
|
68
|
+
}
|
69
|
+
|
70
|
+
/* Make charts smaller on mobile */
|
71
|
+
.row > .grid-item .chart-container {
|
72
|
+
height: 60px !important;
|
73
|
+
max-height: 60px;
|
74
|
+
}
|
23
75
|
}
|
24
76
|
}
|