rails-schema 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/CHANGELOG.md +23 -0
- data/CLAUDE.md +107 -0
- data/README.md +29 -5
- data/lib/rails/schema/assets/app.js +237 -13
- data/lib/rails/schema/assets/style.css +55 -3
- data/lib/rails/schema/assets/template.html.erb +3 -1
- data/lib/rails/schema/extractor/model_scanner.rb +5 -5
- data/lib/rails/schema/extractor/mongoid/association_reader.rb +59 -0
- data/lib/rails/schema/extractor/mongoid/column_reader.rb +71 -0
- data/lib/rails/schema/extractor/mongoid/model_adapter.rb +37 -0
- data/lib/rails/schema/extractor/mongoid/model_scanner.rb +135 -0
- data/lib/rails/schema/extractor/structure_sql_parser.rb +6 -4
- data/lib/rails/schema/transformer/graph_builder.rb +22 -9
- data/lib/rails/schema/version.rb +1 -1
- data/lib/rails/schema.rb +44 -1
- metadata +34 -13
- data/PROJECT.md +0 -348
metadata
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rails-schema
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Andrei Kislichenko
|
|
8
|
+
autorequire:
|
|
8
9
|
bindir: exe
|
|
9
10
|
cert_chain: []
|
|
10
|
-
date:
|
|
11
|
+
date: 2026-03-08 00:00:00.000000000 Z
|
|
11
12
|
dependencies:
|
|
12
13
|
- !ruby/object:Gem::Dependency
|
|
13
14
|
name: activerecord
|
|
@@ -15,28 +16,42 @@ dependencies:
|
|
|
15
16
|
requirements:
|
|
16
17
|
- - ">="
|
|
17
18
|
- !ruby/object:Gem::Version
|
|
18
|
-
version: '
|
|
19
|
+
version: '5.2'
|
|
19
20
|
type: :runtime
|
|
20
21
|
prerelease: false
|
|
21
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
22
23
|
requirements:
|
|
23
24
|
- - ">="
|
|
24
25
|
- !ruby/object:Gem::Version
|
|
25
|
-
version: '
|
|
26
|
+
version: '5.2'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: psych
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '4.0'
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '4.0'
|
|
26
41
|
- !ruby/object:Gem::Dependency
|
|
27
42
|
name: railties
|
|
28
43
|
requirement: !ruby/object:Gem::Requirement
|
|
29
44
|
requirements:
|
|
30
45
|
- - ">="
|
|
31
46
|
- !ruby/object:Gem::Version
|
|
32
|
-
version: '
|
|
47
|
+
version: '5.2'
|
|
33
48
|
type: :runtime
|
|
34
49
|
prerelease: false
|
|
35
50
|
version_requirements: !ruby/object:Gem::Requirement
|
|
36
51
|
requirements:
|
|
37
52
|
- - ">="
|
|
38
53
|
- !ruby/object:Gem::Version
|
|
39
|
-
version: '
|
|
54
|
+
version: '5.2'
|
|
40
55
|
description: Introspects a Rails app's models, associations, and columns, then generates
|
|
41
56
|
a single self-contained HTML file with an interactive entity-relationship diagram.
|
|
42
57
|
email:
|
|
@@ -46,8 +61,8 @@ extensions: []
|
|
|
46
61
|
extra_rdoc_files: []
|
|
47
62
|
files:
|
|
48
63
|
- CHANGELOG.md
|
|
64
|
+
- CLAUDE.md
|
|
49
65
|
- LICENSE.txt
|
|
50
|
-
- PROJECT.md
|
|
51
66
|
- README.md
|
|
52
67
|
- Rakefile
|
|
53
68
|
- docs/index.html
|
|
@@ -61,6 +76,10 @@ files:
|
|
|
61
76
|
- lib/rails/schema/extractor/association_reader.rb
|
|
62
77
|
- lib/rails/schema/extractor/column_reader.rb
|
|
63
78
|
- lib/rails/schema/extractor/model_scanner.rb
|
|
79
|
+
- lib/rails/schema/extractor/mongoid/association_reader.rb
|
|
80
|
+
- lib/rails/schema/extractor/mongoid/column_reader.rb
|
|
81
|
+
- lib/rails/schema/extractor/mongoid/model_adapter.rb
|
|
82
|
+
- lib/rails/schema/extractor/mongoid/model_scanner.rb
|
|
64
83
|
- lib/rails/schema/extractor/schema_file_parser.rb
|
|
65
84
|
- lib/rails/schema/extractor/structure_sql_parser.rb
|
|
66
85
|
- lib/rails/schema/railtie.rb
|
|
@@ -70,14 +89,15 @@ files:
|
|
|
70
89
|
- lib/rails/schema/transformer/node.rb
|
|
71
90
|
- lib/rails/schema/version.rb
|
|
72
91
|
- sig/rails/schema.rbs
|
|
73
|
-
homepage: https://github.com/
|
|
92
|
+
homepage: https://github.com/andrew2net/rails-schema
|
|
74
93
|
licenses:
|
|
75
94
|
- MIT
|
|
76
95
|
metadata:
|
|
77
|
-
homepage_uri: https://github.com/
|
|
78
|
-
source_code_uri: https://github.com/
|
|
79
|
-
changelog_uri: https://github.com/
|
|
96
|
+
homepage_uri: https://github.com/andrew2net/rails-schema
|
|
97
|
+
source_code_uri: https://github.com/andrew2net/rails-schema
|
|
98
|
+
changelog_uri: https://github.com/andrew2net/rails-schema/blob/main/CHANGELOG.md
|
|
80
99
|
rubygems_mfa_required: 'true'
|
|
100
|
+
post_install_message:
|
|
81
101
|
rdoc_options: []
|
|
82
102
|
require_paths:
|
|
83
103
|
- lib
|
|
@@ -85,14 +105,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
85
105
|
requirements:
|
|
86
106
|
- - ">="
|
|
87
107
|
- !ruby/object:Gem::Version
|
|
88
|
-
version:
|
|
108
|
+
version: 2.7.0
|
|
89
109
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
90
110
|
requirements:
|
|
91
111
|
- - ">="
|
|
92
112
|
- !ruby/object:Gem::Version
|
|
93
113
|
version: '0'
|
|
94
114
|
requirements: []
|
|
95
|
-
rubygems_version: 3.6
|
|
115
|
+
rubygems_version: 3.1.6
|
|
116
|
+
signing_key:
|
|
96
117
|
specification_version: 4
|
|
97
118
|
summary: Interactive HTML visualization of your Rails database schema
|
|
98
119
|
test_files: []
|
data/PROJECT.md
DELETED
|
@@ -1,348 +0,0 @@
|
|
|
1
|
-
# Rails::Schema — Project Design
|
|
2
|
-
|
|
3
|
-
**A Ruby gem that generates an interactive HTML/JS/CSS page to visualize the database schema of a Rails application.**
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## 1. Gem Overview
|
|
8
|
-
|
|
9
|
-
**Name:** `rails-schema`
|
|
10
|
-
**Module:** `Rails::Schema`
|
|
11
|
-
**Version:** `0.1.2`
|
|
12
|
-
|
|
13
|
-
Rails::Schema introspects a Rails app's models, associations, and database columns at runtime, then generates a single self-contained HTML file with an interactive, explorable entity-relationship diagram. No external server, no SaaS dependency — just one command and a browser.
|
|
14
|
-
|
|
15
|
-
```bash
|
|
16
|
-
# Rake task
|
|
17
|
-
rake rails_schema:generate
|
|
18
|
-
|
|
19
|
-
# Programmatic
|
|
20
|
-
Rails::Schema.generate(output: "docs/schema.html")
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
---
|
|
24
|
-
|
|
25
|
-
## 2. Architecture
|
|
26
|
-
|
|
27
|
-
```
|
|
28
|
-
┌──────────────────────────────────────────────────────┐
|
|
29
|
-
│ rails-schema gem │
|
|
30
|
-
├──────────────┬──────────────┬────────────────────────┤
|
|
31
|
-
│ Extractor │ Transformer │ Renderer │
|
|
32
|
-
│ (Ruby) │ (Ruby) │ (ERB → HTML/JS/CSS) │
|
|
33
|
-
├──────────────┼──────────────┼────────────────────────┤
|
|
34
|
-
│ Reads Rails │ Builds a │ Produces a single │
|
|
35
|
-
│ models, │ normalized │ self-contained .html │
|
|
36
|
-
│ reflections, │ graph JSON │ file with embedded │
|
|
37
|
-
│ schema.rb, │ structure │ JS app + CSS │
|
|
38
|
-
│ columns │ │ │
|
|
39
|
-
└──────────────┴──────────────┴────────────────────────┘
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
### 2.1 Layer Breakdown
|
|
43
|
-
|
|
44
|
-
| Layer | Responsibility | Key Classes |
|
|
45
|
-
|---|---|---|
|
|
46
|
-
| **Extractor** | Introspects Rails environment; collects models, columns, associations | `Rails::Schema::Extractor::ModelScanner`, `ColumnReader`, `AssociationReader`, `SchemaFileParser`, `StructureSqlParser` |
|
|
47
|
-
| **Transformer** | Normalizes extracted data into a serializable graph structure (nodes + edges + metadata) | `Rails::Schema::Transformer::GraphBuilder`, `Node`, `Edge` |
|
|
48
|
-
| **Renderer** | Takes the graph data and injects it into an HTML/JS/CSS template using ERB | `Rails::Schema::Renderer::HtmlGenerator` |
|
|
49
|
-
| **Railtie** | Provides the `rails_schema:generate` rake task | `Rails::Schema::Railtie` |
|
|
50
|
-
|
|
51
|
-
### 2.2 Generation Pipeline
|
|
52
|
-
|
|
53
|
-
```ruby
|
|
54
|
-
def generate(output: nil)
|
|
55
|
-
schema_data = parse_schema
|
|
56
|
-
models = Extractor::ModelScanner.new(schema_data: schema_data).scan
|
|
57
|
-
column_reader = Extractor::ColumnReader.new(schema_data: schema_data)
|
|
58
|
-
graph_data = Transformer::GraphBuilder.new(column_reader: column_reader).build(models)
|
|
59
|
-
generator = Renderer::HtmlGenerator.new(graph_data: graph_data)
|
|
60
|
-
generator.render_to_file(output)
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
def parse_schema
|
|
64
|
-
case configuration.schema_format
|
|
65
|
-
when :ruby then Extractor::SchemaFileParser.new.parse
|
|
66
|
-
when :sql then Extractor::StructureSqlParser.new.parse
|
|
67
|
-
when :auto
|
|
68
|
-
data = Extractor::SchemaFileParser.new.parse
|
|
69
|
-
data.empty? ? Extractor::StructureSqlParser.new.parse : data
|
|
70
|
-
end
|
|
71
|
-
end
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
---
|
|
75
|
-
|
|
76
|
-
## 3. Data Extraction Strategy
|
|
77
|
-
|
|
78
|
-
### 3.1 Sources of Truth
|
|
79
|
-
|
|
80
|
-
1. **`db/schema.rb` parsing** — `SchemaFileParser` parses the schema file line-by-line with regex to extract table names, column definitions (name, type, nullable, default), and primary key info. This is attempted first and used as a fast, database-free source.
|
|
81
|
-
2. **`db/structure.sql` parsing** — `StructureSqlParser` parses SQL `CREATE TABLE` statements for projects using `config.active_record.schema_format = :sql`. Maps SQL types to Rails-friendly types, detects `NOT NULL`, `DEFAULT` values, and primary keys. Handles schema-qualified names (`public.users`), timestamp precision (`timestamp(6)`), and both quoted and unquoted identifiers.
|
|
82
|
-
3. **ActiveRecord reflection API** — `AssociationReader` uses `Model.reflect_on_all_associations` for associations (`has_many`, `belongs_to`, `has_one`, `has_and_belongs_to_many`), including `:through` and `:polymorphic`.
|
|
83
|
-
4. **`Model.columns`** — `ColumnReader` falls back to `model.columns` via ActiveRecord when a table is not found in schema_data.
|
|
84
|
-
|
|
85
|
-
### 3.2 Model Discovery
|
|
86
|
-
|
|
87
|
-
`ModelScanner` discovers models by:
|
|
88
|
-
|
|
89
|
-
1. Calling `Rails.application.eager_load!` (with Zeitwerk support and multiple fallback strategies)
|
|
90
|
-
2. Collecting `ActiveRecord::Base.descendants`
|
|
91
|
-
3. Filtering out abstract classes, anonymous classes, and models without known tables
|
|
92
|
-
4. Applying `exclude_models` configuration (supports wildcard prefix matching like `"ActiveStorage::*"`)
|
|
93
|
-
5. Returning models sorted by name
|
|
94
|
-
|
|
95
|
-
When `schema_data` is available, table existence is checked against parsed schema data instead of hitting the database.
|
|
96
|
-
|
|
97
|
-
### 3.3 Schema File Parser
|
|
98
|
-
|
|
99
|
-
`SchemaFileParser` provides database-free column extraction:
|
|
100
|
-
|
|
101
|
-
- Parses `create_table` blocks from `db/schema.rb`
|
|
102
|
-
- Extracts column types, names, nullability, and defaults (string, numeric, boolean)
|
|
103
|
-
- Handles custom primary key types (`id: :uuid`, `id: :bigint`) and `id: false`
|
|
104
|
-
- Skips index definitions
|
|
105
|
-
|
|
106
|
-
### 3.4 Structure SQL Parser
|
|
107
|
-
|
|
108
|
-
`StructureSqlParser` provides database-free column extraction from SQL dumps:
|
|
109
|
-
|
|
110
|
-
- Parses `CREATE TABLE` statements from `db/structure.sql`
|
|
111
|
-
- Maps SQL types to Rails types (e.g. `character varying` → `string`, `bigint` → `bigint`, `timestamp without time zone` → `datetime`)
|
|
112
|
-
- Handles schema-qualified table names (`public.users` → `users`)
|
|
113
|
-
- Handles timestamp precision (`timestamp(6) without time zone`)
|
|
114
|
-
- Detects primary keys from `CONSTRAINT ... PRIMARY KEY` and inline `PRIMARY KEY`
|
|
115
|
-
- Extracts `NOT NULL`, `DEFAULT` values (strings, numbers, booleans)
|
|
116
|
-
- Skips constraint lines (`CONSTRAINT`, `UNIQUE`, `CHECK`, `FOREIGN KEY`, etc.)
|
|
117
|
-
|
|
118
|
-
### 3.5 Intermediate Data Format (JSON Graph)
|
|
119
|
-
|
|
120
|
-
```json
|
|
121
|
-
{
|
|
122
|
-
"nodes": [
|
|
123
|
-
{
|
|
124
|
-
"id": "User",
|
|
125
|
-
"table": "users",
|
|
126
|
-
"columns": [
|
|
127
|
-
{ "name": "id", "type": "bigint", "primary": true },
|
|
128
|
-
{ "name": "email", "type": "string", "nullable": false },
|
|
129
|
-
{ "name": "name", "type": "string", "nullable": true }
|
|
130
|
-
]
|
|
131
|
-
}
|
|
132
|
-
],
|
|
133
|
-
"edges": [
|
|
134
|
-
{
|
|
135
|
-
"from": "User",
|
|
136
|
-
"to": "Post",
|
|
137
|
-
"type": "has_many",
|
|
138
|
-
"through": null,
|
|
139
|
-
"foreign_key": "user_id",
|
|
140
|
-
"polymorphic": false,
|
|
141
|
-
"label": "posts"
|
|
142
|
-
}
|
|
143
|
-
],
|
|
144
|
-
"metadata": {
|
|
145
|
-
"rails_version": "7.2.0",
|
|
146
|
-
"generated_at": "2026-02-15T12:00:00Z",
|
|
147
|
-
"model_count": 42
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
---
|
|
153
|
-
|
|
154
|
-
## 4. Interactive Frontend Design
|
|
155
|
-
|
|
156
|
-
The generated HTML file is a **single self-contained file** — no CDN dependencies, no network requests. All JS and CSS are inlined. The JSON graph is embedded as a `<script>` tag.
|
|
157
|
-
|
|
158
|
-
### 4.1 Technology Choices
|
|
159
|
-
|
|
160
|
-
| Concern | Choice | Rationale |
|
|
161
|
-
|---|---|---|
|
|
162
|
-
| Graph rendering | **SVG + d3-force** (vendored/minified) | DOM-level interactivity, good for typical schema sizes |
|
|
163
|
-
| Layout algorithm | Force-directed (d3-force) | Natural clustering of related models |
|
|
164
|
-
| UI framework | Vanilla JS | Zero dependencies, small file size |
|
|
165
|
-
| Styling | CSS custom properties + embedded stylesheet | Theming support, dark/light mode |
|
|
166
|
-
|
|
167
|
-
### 4.2 Implemented Interactive Features
|
|
168
|
-
|
|
169
|
-
#### A. Model Selector Panel (left sidebar, 280px)
|
|
170
|
-
|
|
171
|
-
- Searchable list of all models with filtering
|
|
172
|
-
- Multi-select checkboxes to toggle visibility
|
|
173
|
-
- Model count display
|
|
174
|
-
|
|
175
|
-
#### B. Canvas / Diagram Area (center)
|
|
176
|
-
|
|
177
|
-
- **Nodes** = model cards showing:
|
|
178
|
-
- Model name (bold header)
|
|
179
|
-
- Column list (expandable/collapsible — collapsed by default)
|
|
180
|
-
- Primary key highlighted
|
|
181
|
-
- Column types shown in a muted typeface
|
|
182
|
-
- **Edges** = association lines:
|
|
183
|
-
- Color-coded by association type
|
|
184
|
-
- Labels on hover (association name + foreign key)
|
|
185
|
-
- **Force-directed layout** that stabilizes, then allows manual drag-and-drop
|
|
186
|
-
|
|
187
|
-
#### C. Zoom & Navigation
|
|
188
|
-
|
|
189
|
-
- Scroll-wheel zoom with smooth interpolation
|
|
190
|
-
- Pinch-to-zoom on trackpads
|
|
191
|
-
- Fit-to-screen button
|
|
192
|
-
- Zoom-to-selection (click a model in sidebar to center on it)
|
|
193
|
-
|
|
194
|
-
#### D. Focus Mode
|
|
195
|
-
|
|
196
|
-
When a user clicks on a model node:
|
|
197
|
-
|
|
198
|
-
1. The selected model and its directly associated models are highlighted
|
|
199
|
-
2. All other nodes and edges fade to reduced opacity
|
|
200
|
-
3. A detail panel (right sidebar, 320px) shows full column/association info
|
|
201
|
-
4. Press `Esc` or click background to exit
|
|
202
|
-
|
|
203
|
-
#### E. Toolbar (48px)
|
|
204
|
-
|
|
205
|
-
- Dark / Light theme toggle (respects `prefers-color-scheme`)
|
|
206
|
-
- Fit-to-screen button
|
|
207
|
-
- Keyboard shortcuts: `/` to focus search, `Esc` to deselect
|
|
208
|
-
|
|
209
|
-
---
|
|
210
|
-
|
|
211
|
-
## 5. Configuration
|
|
212
|
-
|
|
213
|
-
```ruby
|
|
214
|
-
# config/initializers/rails_schema.rb
|
|
215
|
-
Rails::Schema.configure do |config|
|
|
216
|
-
config.output_path = "docs/schema.html" # Output file location
|
|
217
|
-
config.exclude_models = [] # Models to exclude (supports "Namespace::*" wildcards)
|
|
218
|
-
config.title = "Database Schema" # Page title
|
|
219
|
-
config.theme = :auto # :light, :dark, :auto
|
|
220
|
-
config.expand_columns = false # Start with columns expanded
|
|
221
|
-
config.schema_format = :auto # :auto, :ruby, or :sql
|
|
222
|
-
end
|
|
223
|
-
```
|
|
224
|
-
|
|
225
|
-
---
|
|
226
|
-
|
|
227
|
-
## 6. Gem Structure
|
|
228
|
-
|
|
229
|
-
```
|
|
230
|
-
rails-schema/
|
|
231
|
-
├── lib/
|
|
232
|
-
│ ├── rails/schema.rb # Entry point, configuration DSL, generate method
|
|
233
|
-
│ └── rails/schema/
|
|
234
|
-
│ ├── version.rb # VERSION = "0.1.2"
|
|
235
|
-
│ ├── configuration.rb # Config object (6 attributes)
|
|
236
|
-
│ ├── railtie.rb # Rails integration, rake task
|
|
237
|
-
│ ├── extractor/
|
|
238
|
-
│ │ ├── model_scanner.rb # Discovers AR models
|
|
239
|
-
│ │ ├── association_reader.rb # Reads reflections
|
|
240
|
-
│ │ ├── column_reader.rb # Reads columns (schema_data or AR)
|
|
241
|
-
│ │ ├── schema_file_parser.rb # Parses db/schema.rb
|
|
242
|
-
│ │ └── structure_sql_parser.rb # Parses db/structure.sql
|
|
243
|
-
│ ├── transformer/
|
|
244
|
-
│ │ ├── graph_builder.rb # Builds node/edge graph
|
|
245
|
-
│ │ ├── node.rb # Value object
|
|
246
|
-
│ │ └── edge.rb # Value object
|
|
247
|
-
│ ├── renderer/
|
|
248
|
-
│ │ └── html_generator.rb # ERB rendering, asset inlining
|
|
249
|
-
│ └── assets/
|
|
250
|
-
│ ├── template.html.erb # Main HTML template
|
|
251
|
-
│ ├── app.js # Interactive frontend (vanilla JS)
|
|
252
|
-
│ ├── style.css # Stylesheet with CSS custom properties
|
|
253
|
-
│ └── vendor/
|
|
254
|
-
│ └── d3.min.js # Vendored d3 library
|
|
255
|
-
├── spec/
|
|
256
|
-
│ ├── spec_helper.rb
|
|
257
|
-
│ ├── support/
|
|
258
|
-
│ │ └── test_models.rb # User, Post, Comment, Tag models
|
|
259
|
-
│ └── rails/schema/
|
|
260
|
-
│ ├── rails_schema_spec.rb
|
|
261
|
-
│ ├── configuration_spec.rb
|
|
262
|
-
│ ├── extractor/
|
|
263
|
-
│ │ ├── model_scanner_spec.rb
|
|
264
|
-
│ │ ├── column_reader_spec.rb
|
|
265
|
-
│ │ ├── association_reader_spec.rb
|
|
266
|
-
│ │ ├── schema_file_parser_spec.rb
|
|
267
|
-
│ │ └── structure_sql_parser_spec.rb
|
|
268
|
-
│ ├── transformer/
|
|
269
|
-
│ │ └── graph_builder_spec.rb
|
|
270
|
-
│ └── renderer/
|
|
271
|
-
│ └── html_generator_spec.rb
|
|
272
|
-
├── Gemfile
|
|
273
|
-
├── rails-schema.gemspec
|
|
274
|
-
├── LICENSE.txt
|
|
275
|
-
└── README.md
|
|
276
|
-
```
|
|
277
|
-
|
|
278
|
-
---
|
|
279
|
-
|
|
280
|
-
## 7. Key Design Decisions
|
|
281
|
-
|
|
282
|
-
### Why a single HTML file?
|
|
283
|
-
|
|
284
|
-
- **Zero deployment friction** — open in any browser, share via Slack/email, commit to repo
|
|
285
|
-
- **Offline-first** — works on airplane mode, no CDN failures
|
|
286
|
-
- **Portable** — CI can generate it, GitHub Pages can host it, anyone can view it
|
|
287
|
-
|
|
288
|
-
### Why not a mounted Rails engine?
|
|
289
|
-
|
|
290
|
-
A mounted engine requires a running server. A static file can be generated in CI, committed to the repo, and opened by anyone — including non-developers looking at a data model.
|
|
291
|
-
|
|
292
|
-
### Why parse schema.rb / structure.sql?
|
|
293
|
-
|
|
294
|
-
Parsing `db/schema.rb` or `db/structure.sql` allows column extraction without a database connection. This means the gem can work in CI environments or development setups where the database isn't running. It also avoids eager-loading the entire app just to read column metadata. The `schema_format: :auto` default tries `schema.rb` first, then falls back to `structure.sql`, so the gem works out of the box regardless of which format a project uses.
|
|
295
|
-
|
|
296
|
-
### Why force-directed layout?
|
|
297
|
-
|
|
298
|
-
It handles unknown schemas gracefully — you don't need to pre-define positions. Combined with drag-and-drop repositioning, it gives the best default experience.
|
|
299
|
-
|
|
300
|
-
---
|
|
301
|
-
|
|
302
|
-
## 8. Dependencies
|
|
303
|
-
|
|
304
|
-
```ruby
|
|
305
|
-
# rails-schema.gemspec
|
|
306
|
-
spec.add_dependency "activerecord", ">= 6.0"
|
|
307
|
-
spec.add_dependency "railties", ">= 6.0"
|
|
308
|
-
|
|
309
|
-
# Development
|
|
310
|
-
# rspec (~> 3.0), rubocop (~> 1.21), sqlite3
|
|
311
|
-
```
|
|
312
|
-
|
|
313
|
-
**Zero runtime JS dependencies shipped to the user** — d3 is vendored and minified into the template. The HTML file has no external requests.
|
|
314
|
-
|
|
315
|
-
---
|
|
316
|
-
|
|
317
|
-
## 9. Testing Strategy
|
|
318
|
-
|
|
319
|
-
| Layer | Approach |
|
|
320
|
-
|---|---|
|
|
321
|
-
| Extractor | Unit tests with in-memory SQLite models (User, Post, Comment, Tag); rescue-path warnings tested via `output(...).to_stderr` |
|
|
322
|
-
| Transformer | Pure Ruby unit tests — graph building, edge filtering |
|
|
323
|
-
| Renderer | Output tests — verify HTML structure, embedded data, script injection safety |
|
|
324
|
-
| Configuration | Unit tests for defaults and attribute setting |
|
|
325
|
-
|
|
326
|
-
**108 tests, all passing.** Run with `bundle exec rspec`.
|
|
327
|
-
|
|
328
|
-
---
|
|
329
|
-
|
|
330
|
-
## 10. Future Enhancements (Roadmap)
|
|
331
|
-
|
|
332
|
-
1. **CLI executable** — `bundle exec rails_schema` binary for standalone usage
|
|
333
|
-
2. **Live mode** — a mounted Rails engine with hot-reload when migrations run
|
|
334
|
-
3. **Additional layout modes** — hierarchical, circular, grid
|
|
335
|
-
4. **Validation extraction** — read `Model.validators` for presence, uniqueness constraints
|
|
336
|
-
5. **STI handling** — group models sharing a table, show children as badges
|
|
337
|
-
6. **Concern extraction** — display included modules on model nodes
|
|
338
|
-
7. **Export options** — PNG, SVG, Mermaid ER diagram, raw JSON
|
|
339
|
-
8. **Schema diff** — compare two generated JSONs and highlight changes
|
|
340
|
-
9. **Multi-database support** — Rails 6+ multi-DB configs
|
|
341
|
-
10. **Minimap** — thumbnail overview for large schemas
|
|
342
|
-
11. **Permalink / State URL** — encode view state in URL hash for sharing
|
|
343
|
-
12. **Advanced filtering** — `include_only`, namespace grouping, tag-based filters
|
|
344
|
-
13. **Custom CSS/JS injection** — user-provided assets inlined into output
|
|
345
|
-
|
|
346
|
-
---
|
|
347
|
-
|
|
348
|
-
*Document reflects the current implementation (v0.1.2). Future enhancements are aspirational and subject to refinement.*
|