rails-schema 0.1.3 → 0.1.5
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/CHANGELOG.md +40 -0
- data/CLAUDE.md +64 -3
- data/README.md +16 -2
- data/docs/index.html +544 -54
- data/docs/screenshot.png +0 -0
- data/lib/rails/schema/assets/app.js +334 -23
- data/lib/rails/schema/assets/style.css +70 -7
- data/lib/rails/schema/assets/template.html.erb +6 -1
- data/lib/rails/schema/configuration.rb +2 -1
- data/lib/rails/schema/extractor/model_scanner.rb +7 -5
- data/lib/rails/schema/extractor/mongoid/model_adapter.rb +2 -2
- data/lib/rails/schema/extractor/mongoid/model_scanner.rb +4 -2
- data/lib/rails/schema/extractor/structure_sql_parser.rb +6 -4
- data/lib/rails/schema/transformer/edge.rb +6 -1
- data/lib/rails/schema/transformer/graph_builder.rb +70 -9
- data/lib/rails/schema/version.rb +1 -1
- metadata +29 -13
- data/PROJECT.md +0 -367
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 14f99b5a4a676074862ccab2a47f79ebb68595632cf6b3770e9383d14ab02202
|
|
4
|
+
data.tar.gz: e145477c9d42ac72e16972eed1ef0d2dd3d325cd4a9fcf1ba0bf595beceb2d4d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c756f2e042f3b2958357eeb3048f900ea695139f4163ebe50770926dd1e597eda5695f96aa4763e7a401ef5b5f5d767e5306bd595528534cffb08601e20cda4a
|
|
7
|
+
data.tar.gz: 3a7734bc958fdac6246e35585fb66ed62609c1bbfa30dd5fa8640dcb36d7817d774321dd439bb51589eef657cdd8b7324699babf20e34ecf49a3367da6140a51
|
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,46 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/), and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
6
6
|
|
|
7
|
+
## [0.1.5] - 2026-03-14
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- `exclude_model_if` configuration option: provide a proc/lambda to dynamically exclude models based on arbitrary conditions, works with both ActiveRecord and Mongoid pipelines (#16)
|
|
12
|
+
- Mermaid ER diagram export (`.mmd`) — respects sidebar visibility filters so you can export a subset of models (#23)
|
|
13
|
+
- Double-click a model node to isolate its neighborhood (#20)
|
|
14
|
+
- Shift-click range selection for sidebar checkboxes (#24)
|
|
15
|
+
- Smart "Select All" toggle — when all models are selected and a search filter is active, narrows to only filtered models (#24)
|
|
16
|
+
- Search clear button in the sidebar (#24)
|
|
17
|
+
- Color-coded edge labels by association type (#19)
|
|
18
|
+
- Edge deduplication for `has_many`/`belongs_to` pairs — reciprocal associations are merged into a single edge with dual labels, each colored by its own association type
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
|
|
22
|
+
- Refactored text measurement and truncation functions for cleaner rendering
|
|
23
|
+
|
|
24
|
+
## [0.1.4] - 2026-03-08
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
|
|
28
|
+
- Support for Ruby 2.7+ and Rails 5.2+ (improved compatibility with older versions)
|
|
29
|
+
|
|
30
|
+
### Changed
|
|
31
|
+
|
|
32
|
+
- Self-referential-only models (all edges point to themselves) are now placed in a vertical column to the left of the main graph instead of floating in the force simulation
|
|
33
|
+
- True orphan models (zero edges) continue to appear in rows above the diagram
|
|
34
|
+
- Improved class names and table names visibility
|
|
35
|
+
|
|
36
|
+
## [0.1.3] - 2026-03-01
|
|
37
|
+
|
|
38
|
+
### Added
|
|
39
|
+
|
|
40
|
+
- Mongoid support: visualize MongoDB-backed models without a schema file (`schema_format: :mongoid`)
|
|
41
|
+
- Auto-detection of Mongoid when `schema_format: :auto` and `Mongoid::Document` is defined
|
|
42
|
+
- Mongoid extractors: `ModelScanner`, `ModelAdapter`, `ColumnReader`, `AssociationReader`
|
|
43
|
+
- Support for all Mongoid association types: `has_many`, `has_one`, `belongs_to`, `has_and_belongs_to_many`, `embeds_many`, `embeds_one`, `embedded_in`
|
|
44
|
+
- Embedded document styling in the frontend (dashed borders for embed associations)
|
|
45
|
+
- Engine model eager-loading for Mongoid apps
|
|
46
|
+
|
|
7
47
|
## [0.1.2] - 2026-02-22
|
|
8
48
|
|
|
9
49
|
### Added
|
data/CLAUDE.md
CHANGED
|
@@ -16,16 +16,58 @@ bundle exec rspec spec/rails/schema/extractor/mongoid/ # Mongoid tests only
|
|
|
16
16
|
|
|
17
17
|
Three-layer pipeline: **Extractor → Transformer → Renderer**
|
|
18
18
|
|
|
19
|
+
| Layer | Responsibility | Key Classes |
|
|
20
|
+
|---|---|---|
|
|
21
|
+
| **Extractor** | Introspects Rails environment; collects models, columns, associations | `ModelScanner`, `ColumnReader`, `AssociationReader`, `SchemaFileParser`, `StructureSqlParser`, plus `Mongoid::ModelScanner`, `Mongoid::ModelAdapter`, `Mongoid::ColumnReader`, `Mongoid::AssociationReader` |
|
|
22
|
+
| **Transformer** | Normalizes extracted data into a serializable graph (nodes + edges + metadata) | `GraphBuilder`, `Node`, `Edge` |
|
|
23
|
+
| **Renderer** | Injects graph data into an HTML/JS/CSS template via ERB | `HtmlGenerator` |
|
|
24
|
+
| **Railtie** | Provides the `rails_schema:generate` rake task | `Railtie` |
|
|
25
|
+
|
|
26
|
+
### Key Paths
|
|
27
|
+
|
|
19
28
|
- `lib/rails/schema.rb` — entry point, `generate` dispatches to ActiveRecord or Mongoid pipeline
|
|
20
29
|
- `lib/rails/schema/extractor/` — model discovery, column/association reading, schema file parsing
|
|
21
|
-
- `lib/rails/schema/extractor/mongoid/` — Mongoid-specific extractors
|
|
22
|
-
- `lib/rails/schema/transformer/` — builds normalized graph JSON
|
|
30
|
+
- `lib/rails/schema/extractor/mongoid/` — Mongoid-specific extractors
|
|
31
|
+
- `lib/rails/schema/transformer/` — builds normalized graph JSON
|
|
23
32
|
- `lib/rails/schema/renderer/` — ERB-based HTML generation with inlined JS/CSS/data
|
|
24
33
|
- `lib/rails/schema/assets/` — frontend (vanilla JS + d3-force, CSS, HTML template)
|
|
25
34
|
|
|
35
|
+
### Generation Pipeline
|
|
36
|
+
|
|
37
|
+
```ruby
|
|
38
|
+
def generate(output: nil)
|
|
39
|
+
schema_data = parse_schema
|
|
40
|
+
models = Extractor::ModelScanner.new(schema_data: schema_data).scan
|
|
41
|
+
column_reader = Extractor::ColumnReader.new(schema_data: schema_data)
|
|
42
|
+
graph_data = Transformer::GraphBuilder.new(column_reader: column_reader).build(models)
|
|
43
|
+
Renderer::HtmlGenerator.new(graph_data: graph_data).render_to_file(output)
|
|
44
|
+
end
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Data Extraction Strategy
|
|
48
|
+
|
|
49
|
+
1. **`db/schema.rb`** — `SchemaFileParser` parses with regex (table names, columns, types, nullable, defaults, PKs)
|
|
50
|
+
2. **`db/structure.sql`** — `StructureSqlParser` parses SQL `CREATE TABLE` statements, maps SQL types to Rails types
|
|
51
|
+
3. **ActiveRecord reflection API** — `AssociationReader` uses `reflect_on_all_associations` for associations
|
|
52
|
+
4. **`Model.columns`** — `ColumnReader` falls back to this when table not found in schema_data
|
|
53
|
+
|
|
54
|
+
### Model Discovery
|
|
55
|
+
|
|
56
|
+
`ModelScanner` (ActiveRecord):
|
|
57
|
+
1. Calls `Rails.application.eager_load!` (Zeitwerk support, multiple fallback strategies including `LoadError` rescue)
|
|
58
|
+
2. Collects `ActiveRecord::Base.descendants`
|
|
59
|
+
3. Filters: abstract classes, anonymous classes, models without known tables
|
|
60
|
+
4. Applies `exclude_models` config (supports wildcard prefix like `"ActiveStorage::*"`) and `exclude_model_if` proc
|
|
61
|
+
|
|
62
|
+
`Mongoid::ModelScanner`:
|
|
63
|
+
1. Eager-loads via Zeitwerk or file glob with fallbacks
|
|
64
|
+
2. Scans `ObjectSpace` for `Mongoid::Document` includers
|
|
65
|
+
3. Also loads models from mounted Rails engines
|
|
66
|
+
4. Returns models wrapped in `ModelAdapter` for GraphBuilder compatibility
|
|
67
|
+
|
|
26
68
|
## Key Conventions
|
|
27
69
|
|
|
28
|
-
- Ruby >=
|
|
70
|
+
- Ruby >= 2.7, Rails >= 5.2
|
|
29
71
|
- Double quotes for strings (RuboCop enforced)
|
|
30
72
|
- RuboCop max method length: 15 lines, default ABC/complexity limits
|
|
31
73
|
- No `Style/Documentation` required
|
|
@@ -44,5 +86,24 @@ Three-layer pipeline: **Extractor → Transformer → Renderer**
|
|
|
44
86
|
## Testing Notes
|
|
45
87
|
|
|
46
88
|
- Always run `bundle exec rubocop` before committing — CI checks both tests and linting
|
|
89
|
+
- CI matrix: Ruby 2.7, 3.0, 3.1, 3.2, 3.3, 3.4
|
|
47
90
|
- Mongoid specs stub `Rails::Engine` and `Rails::Application` since they may not exist in test env
|
|
48
91
|
- SimpleCov is enabled; coverage report goes to `coverage/`
|
|
92
|
+
|
|
93
|
+
## Frontend
|
|
94
|
+
|
|
95
|
+
Single self-contained HTML file — no CDN, no network requests. D3 is vendored/minified.
|
|
96
|
+
|
|
97
|
+
- **Vanilla JS + d3-force** for graph rendering
|
|
98
|
+
- **CSS custom properties** for dark/light theming
|
|
99
|
+
- Features: searchable sidebar, click-to-focus, double-click to isolate neighborhood, detail panel, zoom/pan, keyboard shortcuts (`/` search, `Esc` deselect, `+/-` zoom, `F` fit), Mermaid ER diagram export (`.mmd`) filtered by sidebar visibility
|
|
100
|
+
- **Select All smart toggle** — when all models are already selected and a search filter is active, "Select All" narrows to only filtered models (acts as "select only these"); otherwise it adds filtered models to the current selection
|
|
101
|
+
|
|
102
|
+
## Design Decisions
|
|
103
|
+
|
|
104
|
+
- **Single HTML file** — zero deployment friction, offline-first, portable (CI, GitHub Pages, email)
|
|
105
|
+
- **Not a mounted engine** — static file works without a running server
|
|
106
|
+
- **Parse schema files** — works without DB connection (CI environments, no local DB)
|
|
107
|
+
- **Force-directed layout** — handles unknown schemas gracefully without pre-defined positions
|
|
108
|
+
- **Node layout categories** — three-way partition in `app.js`: connected nodes use force simulation, self-ref-only models (all edges point to themselves) are placed in a vertical left column via `layoutSelfRefNodes()`, true orphans (zero edges) are placed in rows above the diagram via `layoutOrphans()`
|
|
109
|
+
- **Edge deduplication** — `GraphBuilder.deduplicate_edges` chains two passes: (1) HABTM dedup merges symmetric `has_and_belongs_to_many` pairs into one edge with `reverse_label`, (2) has_many/belongs_to dedup merges matching pairs (same foreign_key, reversed endpoints) into one edge where the has_many/has_one side is kept and the belongs_to label becomes `reverse_label` with its own `reverse_association_type`. The frontend uses `reverse_association_type` to color each label by its own association type.
|
data/README.md
CHANGED
|
@@ -4,6 +4,13 @@ Interactive HTML visualization of your Rails database schema. Introspects your a
|
|
|
4
4
|
|
|
5
5
|
No external server, no CDN — just one command and a browser.
|
|
6
6
|
|
|
7
|
+
## Compatibility
|
|
8
|
+
|
|
9
|
+
| Dependency | Required version |
|
|
10
|
+
|-----------|-----------------|
|
|
11
|
+
| Ruby | >= 2.7 |
|
|
12
|
+
| Rails | >= 5.2 |
|
|
13
|
+
|
|
7
14
|
**[Live example](https://andrew2net.github.io/rails-schema/)** — generated from [Fizzy](https://www.fizzy.do), a modern spin on kanban for tracking just about anything, created by [37signals](https://37signals.com).
|
|
8
15
|
|
|
9
16
|

|
|
@@ -54,6 +61,7 @@ Rails::Schema.configure do |config|
|
|
|
54
61
|
"ActiveStorage::Attachment",
|
|
55
62
|
"ActionMailbox::*" # wildcard prefix matching
|
|
56
63
|
]
|
|
64
|
+
config.exclude_model_if = ->(model) { model.table_name.start_with?("_") }
|
|
57
65
|
end
|
|
58
66
|
```
|
|
59
67
|
|
|
@@ -65,6 +73,7 @@ end
|
|
|
65
73
|
| `expand_columns` | `false` | Whether model nodes start with columns expanded |
|
|
66
74
|
| `schema_format` | `:auto` | Schema source — `:auto`, `:ruby`, `:sql`, or `:mongoid` (see below) |
|
|
67
75
|
| `exclude_models` | `[]` | Models to hide; supports exact names and wildcard prefixes (`"ActionMailbox::*"`) |
|
|
76
|
+
| `exclude_model_if` | `nil` | A proc/lambda that receives a model class and returns `true` to exclude it |
|
|
68
77
|
|
|
69
78
|
### Schema format
|
|
70
79
|
|
|
@@ -100,13 +109,18 @@ For Mongoid apps, the gem introspects model classes at runtime to read field def
|
|
|
100
109
|
## Features
|
|
101
110
|
|
|
102
111
|
- **No database required** — reads from `db/schema.rb`, `db/structure.sql`, or Mongoid model introspection
|
|
103
|
-
- **Force-directed layout** — models cluster naturally by association density
|
|
104
|
-
- **Searchable sidebar** — filter models by name or table
|
|
112
|
+
- **Force-directed layout** — models cluster naturally by association density; self-referential-only models are placed in a left column, true orphans in rows above
|
|
113
|
+
- **Searchable sidebar** — filter models by name or table, with a clear button to reset
|
|
114
|
+
- **Select/Deselect All** — operates on filtered (visible) models only, so you can search and bulk-toggle a subset; when all models are selected and a search filter is active, "Select All" narrows the selection to only the filtered models
|
|
115
|
+
- **Shift-click range selection** — hold Shift and click checkboxes to toggle a range at once
|
|
105
116
|
- **Click-to-focus** — click a model to highlight its neighborhood, fading unrelated models
|
|
117
|
+
- **Double-click to isolate** — double-click a model to filter the view to only that model and its direct neighbors
|
|
106
118
|
- **Detail panel** — full column list and associations for the selected model
|
|
107
119
|
- **Dark/light theme** — toggle or auto-detect from system preference
|
|
108
120
|
- **Zoom & pan** — scroll wheel, pinch, or buttons
|
|
109
121
|
- **Keyboard shortcuts** — `/` search, `Esc` deselect, `+/-` zoom, `F` fit to screen
|
|
122
|
+
- **Export to Mermaid** — download the diagram as a `.mmd` file for use in Markdown, GitHub, or other tools that render Mermaid ER diagrams; respects sidebar visibility filters so you can export a subset of models
|
|
123
|
+
- **Deduplicated edges** — reciprocal associations (e.g. `has_many :posts` / `belongs_to :user`, or symmetric HABTM) are merged into a single edge with dual labels, each colored by its own association type
|
|
110
124
|
- **Self-contained** — single HTML file with all CSS, JS, and data inlined
|
|
111
125
|
|
|
112
126
|
## License
|