RailsVisualizer

Gem Version Ruby Rails License: MIT

One command. A full interactive dashboard for your Rails app.

RailsVisualizer introspects your Rails application and generates a self-contained interactive HTML dashboard — no server, no config, no Node.js required. Run one Rake task and explore your models, associations, routes, controllers, jobs, mailers, migrations, and gems through a polished 8-tab UI with search, filters, an ER diagram canvas, and built-in health checks.

The generated file contains all JavaScript and CSS inline — it works offline, loads instantly, and can be shared as a single file with zero external dependencies.


Table of Contents


Installation

Add rails_visualizer to your Gemfile. Since it only introspects the app and is never needed in production, place it in the development group:

group :development do
  gem 'rails_visualizer'
end

Then run:

bundle install

That's it — no generators, no migrations, no configuration files needed.

Quick Start

bundle exec rake rails_visualizer

This single command will:

  1. Eager-load your Rails application.
  2. Introspect models, routes, controllers, jobs, mailers, migrations, and gems.
  3. Generate a self-contained HTML file at tmp/rails_visualizer/index.html.
  4. Open it in your default browser automatically.

Example output:

RailsVisualizer: Introspecting app...
  ✓ 47 models
  ✓ 128 routes
  ✓ 12 jobs
  ⚠ 31 controllers (3 actions without route)
  ⚠ 64 migrations (2 pending migrations)
  ✓ 4 mailers
RailsVisualizer: Rendering...
RailsVisualizer: Done → tmp/rails_visualizer/index.html

The generated HTML file is completely standalone — email it, commit it, drop it in Slack, or open it on any machine. No server required.

Dashboard Tabs

Overview

The landing tab gives you a bird's-eye view of your entire application:

  • App header — displays your app name, page title (extracted from the layout), favicon (auto-detected from public/), and version badges for Rails, Ruby, your database (PostgreSQL, MySQL, or SQLite), and background job processor (Sidekiq, GoodJob, Solid Queue, etc.).
  • Stats grid — clickable cards for each area (Gems, Routes, Controllers, Models, Migrations, Jobs, Mailers) showing counts and sub-stats like HTTP verb breakdown, column counts, queue counts, and more. Warning badges appear on cards that have issues.
  • Health checks — a prioritized checklist that surfaces problems across your codebase, with "Go to" links that navigate directly to the relevant tab. See Health Checks for the full list.

Gems

Displays every dependency from your Gemfile:

  • Name, version, and version constraint (e.g. ~> 7.0).
  • Summary pulled from the gem's metadata.
  • Environment groups — color-coded chips for Common, Development, Test, and Production.
  • Source tags — shows non-standard sources like github: owner/repo, branch: main, path: engines/erp, or require: false.
  • Homepage link — external link icon for each gem.
  • Filters — filter by environment group (Common, Development, Test, Production).
  • Search — search across gem name, summary, and version.

Routes

Shows every route defined in config/routes.rb:

  • HTTP verb — color-coded badge (GET green, POST blue, PATCH/PUT amber, DELETE red).
  • Path — with :param segments highlighted and (format) segments dimmed.
  • Controller#action — linked pair showing the target.
  • Route name — the named helper (e.g. edit_user).
  • Constraints — any route constraints displayed as chips.
  • Orphaned route detection — routes pointing to non-existent controllers are flagged with a red "Orphaned" badge.
  • Filters — toggle by HTTP verb (GET, POST, PATCH, PUT, DELETE) and an "Issues only" toggle to show only orphaned routes.
  • Copy and Open in editor buttons for path and controller file.

Controllers

Namespace-grouped tree view of all your ActionController classes:

  • Actions — each public action is displayed as a pill, with RESTful actions (index, show, new, create, edit, update, destroy) getting a CRUD badge and tooltip description.
  • Unrouted action warnings — actions without a matching route get an amber "No route" badge. If the action is a template method routed via child controllers, it shows "Routed via ChildController" instead.
  • Callbacksbefore_action, after_action, and around_action with full metadata:
    • only: and except: restrictions
    • if: and unless: conditions
    • Inherited callback tracking (shows which parent class defined it)
    • Custom Proc condition indicator
  • Public helpers — non-routable public methods (ending in ? or =) that might need visibility changes.
  • Superclass shown for each controller.
  • Issues only toggle to filter down to controllers with unrouted actions.

Models

The richest tab, with two view modes:

List View

A namespace-grouped tree of all ActiveRecord models. Click any model to open a detail sidebar showing:

  • Columns — name, type, nullability, default value, index info, and enum values.
  • Associationshas_many, belongs_to, has_one, has_many :through, and has_and_belongs_to_many with class name, foreign key, dependent:, as: (polymorphic), and through: details.
  • Validations — each validator with its kind, attributes, options, and validator class.
  • Enums — enum name and all possible values.
  • Scopes — named scopes defined in the model file.
  • Callbacks — lifecycle callbacks (before_save, after_create, around_destroy, etc.) with the owning module/concern if inherited.

Canvas View (ER Diagram)

An interactive React Flow canvas that renders your schema as an entity-relationship diagram:

  • Model nodes — cards showing the model name and its columns with types and constraints.
  • Association edges — labeled, color-coded lines between related models.
  • Automatic layout — powered by Dagre for clean graph arrangement.
  • Zoom, pan, and minimap — navigate large schemas with ease.
  • Click-to-select — click any model node to open the detail sidebar.

Both views support:

  • Search — filter models by name or table name.
  • Issues only toggle — filter to models without any validations.

Migrations

Lists every migration file in db/migrate/:

  • Status — green "Applied" badge or red "Pending" badge.
  • Human-readable name — e.g. "Create Users" from 20240101_create_users.rb.
  • Version timestamp — parsed into a readable date.
  • Current version indicator on the latest applied migration.
  • Filter — by status (Applied, Pending) and search by name or version.
  • Open in editor button for each migration file.

Jobs

Covers both ActiveJob classes and Sidekiq workers:

  • Namespace tree — jobs grouped by :: module separators into a collapsible tree.
  • Queue — color-coded badge per queue name.
  • ActiveJob details — queue name, priority, retry_on handlers, and discard_on configuration.
  • Sidekiq details — queue, retry setting, and uniqueness configuration. Workers with retries disabled get an amber warning.
  • Adapter info — shows the configured queue adapter (Sidekiq, GoodJob, Solid Queue, etc.) with a description.
  • Filter — by queue name, and an "Issues only" toggle for Sidekiq workers with retries disabled.

Mailers

Namespace tree of all ActionMailer classes:

  • Email actions — each mailer method displayed as a pill.
  • Inherited actions — collapsible section showing email methods inherited from parent mailers, grouped by the defining class.
  • Default from — the configured default from: address. Mailers without one are flagged.
  • Default reply-to — if configured.
  • Layout — the mailer layout name.
  • Callbacksbefore_action, after_action, and around_action on the mailer.
  • Abstract detection — mailers with no own email methods that serve as base classes are marked as abstract.
  • Issues only toggle to filter to mailers without a default_from.

Configuration

Create an initializer to customize any defaults. All settings are optional:

# config/initializers/rails_visualizer.rb
RailsVisualizer.configure do |config|
  # Exclude specific model class names from introspection.
  # Useful for internal/legacy models you don't want in the dashboard.
  config.excluded_models = ['AuditLog', 'InternalSession']

  # Exclude specific controller class names.
  config.excluded_controllers = ['HealthCheckController', 'Admin::InternalController']

  # Exclude specific job class names.
  config.excluded_jobs = ['LegacyCleanupJob']

  # Exclude specific mailer class names.
  config.excluded_mailers = ['InternalNotifier']

  # Exclude routes whose paths start with these prefixes.
  # Handy for mounted engines like Sidekiq Web or ActiveAdmin.
  config.excluded_route_paths = ['/admin', '/sidekiq']

  # Directory where the HTML file is written (relative to Rails.root).
  config.output_path = 'tmp/rails_visualizer'

  # Output filename.
  config.filename = 'index.html'

  # Automatically open the browser after generation. Set to false for CI or scripts.
  config.open_browser = true

  # Default color theme for the dashboard. Users can change it in the UI.
  # :light or :dark
  config.theme = :light

  # Use fork-based parallel workers for model inspection on large apps.
  # Disable if forking causes issues with your database driver or environment.
  config.parallel = true
end

Configuration Options Reference

Option Type Default Description
excluded_models Array<String> [] Model class names to exclude
excluded_controllers Array<String> [] Controller class names to exclude
excluded_jobs Array<String> [] Job class names to exclude
excluded_mailers Array<String> [] Mailer class names to exclude
excluded_route_paths Array<String> [] Route path prefixes to exclude
output_path String 'tmp/rails_visualizer' Output directory (relative to Rails.root)
filename String 'index.html' Output file name
open_browser Boolean true Auto-open browser after generation
theme Symbol :light Default theme (:light or :dark)
parallel Boolean true Enable fork-based parallel model inspection

Disabling Browser Auto-Open

If you're running in CI, scripting, or just want the file without opening a browser:

RailsVisualizer.configure do |config|
  config.open_browser = false
end

Excluding Mounted Engine Routes

Many apps mount engines like Sidekiq Web, ActiveAdmin, or Letter Opener that add many routes you may not want to visualize:

RailsVisualizer.configure do |config|
  config.excluded_route_paths = ['/admin', '/sidekiq', '/letter_opener']
end

Health Checks

The Overview tab runs automated health checks across your application and surfaces issues at a glance. Each check has a severity level:

Check Severity Passes when
Orphaned routes Error All routes point to a known controller class
Unrouted actions Warning All public controller actions have a matching route
Models without validations Warning All models have at least one validation
Pending migrations Error All migrations have been applied
Sidekiq retries disabled Warning All Sidekiq workers have retries enabled
Mailers without default_from Warning All mailers have a default from: address

Each failing check has a "Go to" button that navigates to the relevant tab where you can see the full details.

How It Works

Rake task
  └─ Introspector
       ├─ eager_load! (load all classes)
       ├─ 5 parallel Threads (routes, controllers, jobs, mailers, gems)
       ├─ Schema::Cache (bulk DB metadata in 2 SQL queries on PG)
       ├─ MigrationsInspector
       └─ ModelInspector (fork-based workers for 50+ models)
            └─ per-model: columns, associations, validations, enums, scopes, callbacks
  └─ Serializer (Ruby Hash → camelCase JSON)
  └─ Renderer (inject JSON into pre-built React HTML)
  └─ Write tmp/rails_visualizer/index.html
  └─ Open browser

Step by Step

  1. Eager-loadRails.application.eager_load! ensures all classes are defined before introspection begins.

  2. Parallel introspection — five inspectors run in parallel threads: RoutesInspector, ControllersInspector, JobsInspector, MailersInspector, and GemsInspector. Since these are I/O-bound (filesystem reads, route table traversal), threads give a meaningful speedup even with the GIL.

  3. Database metadataSchema::Cache bulk-loads all column and index metadata. On PostgreSQL, this runs two parallel SQL queries against information_schema.columns and pg_index with a SQL-level table filter for maximum speed. Other adapters use a per-table fallback through the ActiveRecord API.

  4. Model inspectionSchema::ModelInspector inspects each model for columns, associations, validations, enums, scopes, and callbacks. On large apps (50+ models), inspection runs in fork-based parallel workers (up to 8 processes) for significant speedup on multi-core machines. Results are passed back via Marshal.dump/load through temp files.

  5. SerializationSerializer converts the Ruby hash to JSON, recursively transforming all snake_case keys to camelCase with memoized key conversion.

  6. RenderingRenderer reads the pre-built React HTML template and injects the JSON via a placeholder <script> tag, producing a single self-contained HTML file.

  7. Output — the file is written to disk and optionally opened in the browser.

Error Resilience

Every inspector follows the same safety pattern:

def call
  # ... introspection logic
rescue StandardError
  []  # safe default — never crash the host app
end

Individual model/controller/job failures are caught and skipped. Warnings are printed via warn "[RailsVisualizer] ..." but never raise.

Performance

RailsVisualizer is designed to be fast even on large codebases:

  • Parallel threads for I/O-bound inspectors (routes, controllers, jobs, mailers, gems).
  • Fork-based workers for CPU-bound model inspection when you have 50+ models (automatically scales up to 8 processes based on CPU count).
  • Bulk SQL on PostgreSQL — two queries total for all column and index metadata, instead of per-table queries.
  • Shared caches — file content, method source locations, and enum lookups are cached across models to avoid redundant work.
  • Memoized key conversion — the Serializer reuses the same ~30 camelCase key transforms across thousands of objects.

If you experience issues with fork-based parallelism (e.g. database driver compatibility), disable it:

RailsVisualizer.configure do |config|
  config.parallel = false
end

Compatibility

  • Ruby >= 3.0
  • Rails >= 7.0 (tested on 7.0, 7.1, 7.2, 8.0+)
  • PostgreSQL — optimized with bulk-loaded parallel SQL queries for maximum speed
  • MySQL — full support via per-table ActiveRecord API fallback
  • SQLite — full support via per-table ActiveRecord API fallback
  • Any other ActiveRecord adapter — per-table fallback with identical output
  • Zero runtime dependencies — the gem adds nothing to your production bundle
  • ActionController::API — API-only apps are fully supported alongside traditional apps
  • Sidekiq — Sidekiq workers are detected alongside ActiveJob classes

FAQ

Does this affect my production app? No. The gem should be in the :development group and is only activated when you explicitly run the Rake task.

Do I need Node.js? No. The frontend is pre-built into a single HTML file and committed to the gem. End users never need Node.js.

Can I share the generated HTML file? Yes. The file is completely self-contained — all JavaScript, CSS, and data are inlined. Drop it in Slack, attach it to a PR, or email it. It works offline on any machine with a browser.

Does it work with API-only Rails apps? Yes. Both ActionController::Base and ActionController::API controllers are introspected.

What about STI / abstract models? Abstract models are excluded from introspection by default. STI models are included and shown with their own columns and associations.

My app has 500+ models — will it be slow? The gem automatically uses fork-based parallel workers for large apps (50+ models, up to 8 workers). PostgreSQL users benefit from bulk SQL queries. A 500-model app typically generates in under 10 seconds.

Can I customize the output path? Yes. See the Configuration section. You can change both the directory and filename.

The browser didn't open automatically. Set config.open_browser = true (the default) and make sure your system has a default browser configured. On Linux, the open command is replaced by xdg-open — if that's not working, set open_browser to false and open the file manually.

How do I regenerate after code changes? Just run bundle exec rake rails_visualizer again. The file is overwritten each time.

Does it detect issues? Yes. The Overview tab includes health checks for orphaned routes, unrouted controller actions, models without validations, pending migrations, Sidekiq workers with retries disabled, and mailers without a default_from. See Health Checks.

Development

Clone the repo and install dependencies:

git clone https://github.com/rajkamallashkari/rails_visualizer.git
cd rails_visualizer
bundle install

Running Tests

bundle exec rspec

The test suite boots a minimal dummy Rails app with an in-memory SQLite database, so no external database is needed.

Running RuboCop

bundle exec rubocop

Frontend Development

The frontend is a Vite + React 18 + TypeScript + Tailwind CSS app. To work on it:

cd frontend
npm install
npm run dev    # Start Vite dev server with hot reload

During frontend development, the app renders with an empty data payload. To test with real data, generate a dashboard from a Rails app and copy the JSON payload from the resulting HTML.

Building the Frontend

cd frontend
npm run build

This builds a single self-contained HTML file (via vite-plugin-singlefile) to lib/rails_visualizer/assets/dist/index.html. This built file is committed to the repository so end users never need Node.js.

Project Structure

lib/
  rails_visualizer.rb                    # Entry point
  rails_visualizer/
    version.rb                           # VERSION constant
    configuration.rb                     # All configurable options
    railtie.rb                           # Loads rake task into Rails
    path_helper.rb                       # Shared source file path resolution
    introspector.rb                      # Orchestrator: parallel + fork-based
    routes_inspector.rb                  # Route introspection
    controllers_inspector.rb             # Controller introspection
    jobs_inspector.rb                    # ActiveJob + Sidekiq introspection
    mailers_inspector.rb                 # Mailer introspection
    migrations_inspector.rb              # Migration status introspection
    gems_inspector.rb                    # Bundler dependency introspection
    serializer.rb                        # Ruby Hash → camelCase JSON
    renderer.rb                          # JSON injection into HTML template
    schema/
      cache.rb                           # Bulk DB metadata (PG-optimized)
      index_inspector.rb                 # Per-model index lookup
      model_inspector.rb                 # Full model introspection
    assets/dist/index.html               # Pre-built frontend (committed)
  tasks/
    rails_visualizer.rake                # The rake task

frontend/                                # React source (dev only, not shipped)
  src/
    main.tsx                             # App bootstrap
    App.tsx                              # Tab bar + tab routing
    types.ts                             # TypeScript interfaces
    hooks/useTheme.ts                    # Theme persistence
    components/                          # One component per tab + shared UI
    utils/                               # Layout, search, tree, dead code detection

spec/                                    # RSpec test suite
  dummy/                                 # Minimal Rails app for testing
  rails_visualizer/                      # Inspector specs

Contributing

  1. Fork the repository.
  2. Create a feature branch (git checkout -b feature/my-feature).
  3. Make your changes with full test coverage and no RuboCop offenses.
  4. Run the test suite and linter:
   bundle exec rspec
   bundle exec rubocop
  1. Open a pull request.

Please keep each PR focused. Bug fixes and well-scoped features are most likely to be merged quickly.

License

MIT License © 2026 Raj Kamal Lashkari