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.
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.2
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: 1980-01-02 00:00:00.000000000 Z
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: '6.0'
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: '6.0'
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: '6.0'
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: '6.0'
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/nicholaides/rails-schema
92
+ homepage: https://github.com/andrew2net/rails-schema
74
93
  licenses:
75
94
  - MIT
76
95
  metadata:
77
- homepage_uri: https://github.com/nicholaides/rails-schema
78
- source_code_uri: https://github.com/nicholaides/rails-schema
79
- changelog_uri: https://github.com/nicholaides/rails-schema/blob/main/CHANGELOG.md
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: 3.2.0
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.9
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.*