mermaid_rails_erd 1.0.1
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 +7 -0
- data/LICENSE +21 -0
- data/README.md +190 -0
- data/Rakefile +12 -0
- data/bin/console +10 -0
- data/bin/release +152 -0
- data/bin/setup +8 -0
- data/lib/mermaid_rails_erd/association_resolver.rb +32 -0
- data/lib/mermaid_rails_erd/column_info.rb +15 -0
- data/lib/mermaid_rails_erd/generator.rb +71 -0
- data/lib/mermaid_rails_erd/mermaid_emitter.rb +34 -0
- data/lib/mermaid_rails_erd/model_data_collector.rb +157 -0
- data/lib/mermaid_rails_erd/model_loader.rb +12 -0
- data/lib/mermaid_rails_erd/parsed_data.rb +51 -0
- data/lib/mermaid_rails_erd/polymorphic_targets_resolver.rb +37 -0
- data/lib/mermaid_rails_erd/railtie.rb +9 -0
- data/lib/mermaid_rails_erd/relationship.rb +41 -0
- data/lib/mermaid_rails_erd/relationship_builders/base_relationship_builder.rb +91 -0
- data/lib/mermaid_rails_erd/relationship_builders/belongs_to_relationship_builder.rb +39 -0
- data/lib/mermaid_rails_erd/relationship_builders/habtm_relationship_builder.rb +81 -0
- data/lib/mermaid_rails_erd/relationship_builders/has_many_relationship_builder.rb +36 -0
- data/lib/mermaid_rails_erd/relationship_builders/has_one_relationship_builder.rb +39 -0
- data/lib/mermaid_rails_erd/relationship_registry.rb +85 -0
- data/lib/mermaid_rails_erd/relationship_symbol_mapper.rb +13 -0
- data/lib/mermaid_rails_erd/version.rb +5 -0
- data/lib/mermaid_rails_erd.rb +35 -0
- data/lib/tasks/mermaid_rails_erd.rake +36 -0
- data/mermaid_rails_erd.gemspec +46 -0
- metadata +205 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 54915650b33e2be8a680722f466382e0f2671a4c9a1d023e18a26fea744fb03b
|
4
|
+
data.tar.gz: 78f1a7ec38a563280a5b714bd14c6d18b6819c24a89b0ffd4015a744a87e356d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a9e598ba9b70ae93b98dfbdb60b1b6f5d4ef5e706b13c5f6cb7ac4e0a14c6adfc622952916b10a514d830c42f0f00dc57e05e817f3e5d3143607c26b3d95f841
|
7
|
+
data.tar.gz: 6c5e1e893393a4a52c9c25c8ba9295ef97ff268690298d649ba2231955ea6a81509604064896e5891705a488c6e4f1db394a221477815a5e88726bdb6f0e4b28
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2023 Yang Liu
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,190 @@
|
|
1
|
+
# Mermaid Rails ERD
|
2
|
+
|
3
|
+
[](https://badge.fury.io/rb/mermaid_rails_erd)
|
4
|
+
[](https://github.com/delexw/mermaid_rails_erd/actions)
|
5
|
+
|
6
|
+
A Ruby gem that generates [Mermaid.js](https://mermaid.js.org/) Entity Relationship Diagrams (ERD) from ActiveRecord models in Rails applications.
|
7
|
+
|
8
|
+
## Features
|
9
|
+
|
10
|
+
- 🔍 **ActiveRecord Introspection**: Automatically discovers your Rails models and their relationships
|
11
|
+
- 📊 **Mermaid ERD Generation**: Outputs clean, readable Mermaid.js ERD syntax
|
12
|
+
- 🚀 **Simple Integration**: Easy-to-use Rake task for generating diagrams
|
13
|
+
- 🔗 **Relationship Mapping**: Supports `belongs_to`, `has_one`, `has_many`, and `has_and_belongs_to_many` associations
|
14
|
+
- 💫 **Polymorphic Support**: Accurately discovers and maps polymorphic relationships
|
15
|
+
|
16
|
+
## Installation
|
17
|
+
|
18
|
+
Add this line to your application's Gemfile:
|
19
|
+
|
20
|
+
```ruby
|
21
|
+
gem 'mermaid_rails_erd', group: :development
|
22
|
+
```
|
23
|
+
|
24
|
+
And then execute:
|
25
|
+
|
26
|
+
```bash
|
27
|
+
$ bundle install
|
28
|
+
```
|
29
|
+
|
30
|
+
Or install it yourself as:
|
31
|
+
|
32
|
+
```bash
|
33
|
+
$ gem install mermaid_rails_erd
|
34
|
+
```
|
35
|
+
|
36
|
+
## Usage
|
37
|
+
|
38
|
+
### Generate ERD with Rake Task
|
39
|
+
|
40
|
+
The simplest way to generate your ERD is using the provided Rake task:
|
41
|
+
|
42
|
+
```bash
|
43
|
+
$ bundle exec rails mermaid_rails_erd:generate
|
44
|
+
```
|
45
|
+
|
46
|
+
This will:
|
47
|
+
1. Analyze all your ActiveRecord models
|
48
|
+
2. Generate a Mermaid ERD file at `tmp/erd.mmd`
|
49
|
+
3. Provide instructions on how to view the diagram
|
50
|
+
|
51
|
+
|
52
|
+
### Advanced Usage: Model Data Interface
|
53
|
+
|
54
|
+
You can access the collected model and relationship data directly without generating a diagram:
|
55
|
+
|
56
|
+
#### Simple Data Collection
|
57
|
+
|
58
|
+
```ruby
|
59
|
+
# Get all collected data in a structured format
|
60
|
+
data = MermaidRailsErd.build.parsed_data
|
61
|
+
|
62
|
+
# Access collected data
|
63
|
+
models_data = data.models_data # Hash of models having table keyed by model name
|
64
|
+
models = data.models # Array of all loaded models
|
65
|
+
models_no_tables = data.models_no_tables # Array of models missing tables
|
66
|
+
relationships = data.relationships # Array of relationship objects
|
67
|
+
invalid_associations = data.invalid_associations # Array of associations missing associated table
|
68
|
+
polymorphic_associations = data.polymorphic_associations # Array of polymorphic associations
|
69
|
+
regular_associations = data.regular_associations # Array of regular (non-polymorphic) associations
|
70
|
+
|
71
|
+
```
|
72
|
+
|
73
|
+
## Viewing the Generated ERD
|
74
|
+
|
75
|
+
Once you have generated the `.mmd` file, you can view it using:
|
76
|
+
|
77
|
+
- **Mermaid ERD Visulizer**: https://github.com/delexw/mermaid-erd-visualizer
|
78
|
+
|
79
|
+
|
80
|
+
## Example Output
|
81
|
+
|
82
|
+
Given Rails models like:
|
83
|
+
|
84
|
+
```ruby
|
85
|
+
class User < ActiveRecord::Base
|
86
|
+
has_many :posts
|
87
|
+
has_one :profile
|
88
|
+
end
|
89
|
+
|
90
|
+
class Post < ActiveRecord::Base
|
91
|
+
belongs_to :user
|
92
|
+
has_many :comments
|
93
|
+
end
|
94
|
+
|
95
|
+
class Comment < ActiveRecord::Base
|
96
|
+
belongs_to :post
|
97
|
+
end
|
98
|
+
|
99
|
+
class Profile < ActiveRecord::Base
|
100
|
+
belongs_to :user
|
101
|
+
end
|
102
|
+
```
|
103
|
+
|
104
|
+
The gem will generate:
|
105
|
+
|
106
|
+
```mermaid
|
107
|
+
erDiagram
|
108
|
+
users {
|
109
|
+
bigint id PK
|
110
|
+
varchar email
|
111
|
+
varchar name
|
112
|
+
timestamp created_at
|
113
|
+
timestamp updated_at
|
114
|
+
}
|
115
|
+
|
116
|
+
posts {
|
117
|
+
bigint id PK
|
118
|
+
bigint user_id
|
119
|
+
varchar title
|
120
|
+
text content
|
121
|
+
timestamp created_at
|
122
|
+
timestamp updated_at
|
123
|
+
}
|
124
|
+
|
125
|
+
comments {
|
126
|
+
bigint id PK
|
127
|
+
bigint post_id
|
128
|
+
text content
|
129
|
+
timestamp created_at
|
130
|
+
timestamp updated_at
|
131
|
+
}
|
132
|
+
|
133
|
+
profiles {
|
134
|
+
bigint id PK
|
135
|
+
bigint user_id
|
136
|
+
text bio
|
137
|
+
timestamp created_at
|
138
|
+
timestamp updated_at
|
139
|
+
}
|
140
|
+
|
141
|
+
users ||--o{ posts : "user_id"
|
142
|
+
posts ||--o{ comments : "post_id"
|
143
|
+
users ||--|| profiles : "user_id"
|
144
|
+
```
|
145
|
+
|
146
|
+
## Polymorphic Associations
|
147
|
+
|
148
|
+
The gem automatically detects and properly maps polymorphic associations in your models. For example:
|
149
|
+
|
150
|
+
```ruby
|
151
|
+
class Comment < ActiveRecord::Base
|
152
|
+
belongs_to :commentable, polymorphic: true
|
153
|
+
end
|
154
|
+
|
155
|
+
class Post < ActiveRecord::Base
|
156
|
+
has_many :comments, as: :commentable
|
157
|
+
end
|
158
|
+
|
159
|
+
class Photo < ActiveRecord::Base
|
160
|
+
has_many :comments, as: :commentable
|
161
|
+
end
|
162
|
+
```
|
163
|
+
|
164
|
+
The ERD will correctly show relationships between `comments` and both `posts` and `photos` tables.
|
165
|
+
|
166
|
+
## Supported ActiveRecord Associations
|
167
|
+
|
168
|
+
- **belongs_to**: Generates one-to-many relationships
|
169
|
+
- **has_one**: Generates one-to-one relationships
|
170
|
+
- **has_many**: Covered by the corresponding belongs_to
|
171
|
+
- **has_and_belongs_to_many**: Generates many-to-many relationships
|
172
|
+
- **polymorphic**: Discovers and maps all polymorphic interfaces
|
173
|
+
|
174
|
+
## Development
|
175
|
+
|
176
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
177
|
+
|
178
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
179
|
+
|
180
|
+
## Contributing
|
181
|
+
|
182
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/delexw/mermaid_rails_erd.
|
183
|
+
|
184
|
+
## License
|
185
|
+
|
186
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
187
|
+
|
188
|
+
## Changelog
|
189
|
+
|
190
|
+
See [CHANGELOG.md](CHANGELOG.md) for details about changes in each version.
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
# This file allows you to interactively experiment with your gem using `bin/console`
|
5
|
+
require "bundler/setup"
|
6
|
+
require "rails_mermaid_erd"
|
7
|
+
require "irb"
|
8
|
+
|
9
|
+
puts "Loaded rails_mermaid_erd. You can now use the gem in this IRB session."
|
10
|
+
IRB.start
|
data/bin/release
ADDED
@@ -0,0 +1,152 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "optparse"
|
5
|
+
require "fileutils"
|
6
|
+
|
7
|
+
class ReleaseManager
|
8
|
+
VERSION_FILE = "lib/rails_mermaid_erd/version.rb"
|
9
|
+
CHANGELOG_FILE = "CHANGELOG.md"
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@options = {}
|
13
|
+
parse_options
|
14
|
+
end
|
15
|
+
|
16
|
+
def run
|
17
|
+
case @options[:action]
|
18
|
+
when "bump"
|
19
|
+
bump_version(@options[:version_type])
|
20
|
+
when "tag"
|
21
|
+
create_tag
|
22
|
+
when "release"
|
23
|
+
full_release(@options[:version_type])
|
24
|
+
else
|
25
|
+
puts "Unknown action: #{@options[:action]}"
|
26
|
+
exit 1
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def parse_options
|
33
|
+
OptionParser.new do |opts|
|
34
|
+
opts.banner = "Usage: #{$PROGRAM_NAME} [options]"
|
35
|
+
|
36
|
+
opts.on("-a", "--action ACTION", "Action to perform (bump, tag, release)") do |action|
|
37
|
+
@options[:action] = action
|
38
|
+
end
|
39
|
+
|
40
|
+
opts.on("-t", "--type TYPE", "Version type (major, minor, patch)") do |type|
|
41
|
+
@options[:version_type] = type
|
42
|
+
end
|
43
|
+
|
44
|
+
opts.on("-h", "--help", "Show this help") do
|
45
|
+
puts opts
|
46
|
+
exit
|
47
|
+
end
|
48
|
+
end.parse!
|
49
|
+
|
50
|
+
return unless @options[:action].nil?
|
51
|
+
|
52
|
+
puts "Action required. Use -h for help."
|
53
|
+
exit 1
|
54
|
+
end
|
55
|
+
|
56
|
+
def current_version
|
57
|
+
version_content = File.read(VERSION_FILE)
|
58
|
+
version_content.match(/VERSION = ["']([^"']+)["']/)[1]
|
59
|
+
end
|
60
|
+
|
61
|
+
def bump_version(type)
|
62
|
+
current = current_version
|
63
|
+
parts = current.split(".").map(&:to_i)
|
64
|
+
|
65
|
+
case type
|
66
|
+
when "major"
|
67
|
+
parts[0] += 1
|
68
|
+
parts[1] = 0
|
69
|
+
parts[2] = 0
|
70
|
+
when "minor"
|
71
|
+
parts[1] += 1
|
72
|
+
parts[2] = 0
|
73
|
+
when "patch"
|
74
|
+
parts[2] += 1
|
75
|
+
else
|
76
|
+
puts "Invalid version type: #{type}. Use major, minor, or patch."
|
77
|
+
exit 1
|
78
|
+
end
|
79
|
+
|
80
|
+
new_version = parts.join(".")
|
81
|
+
|
82
|
+
# Update version file
|
83
|
+
version_content = File.read(VERSION_FILE)
|
84
|
+
new_content = version_content.gsub(/VERSION = ["'][^"']+["']/, "VERSION = \"#{new_version}\"")
|
85
|
+
File.write(VERSION_FILE, new_content)
|
86
|
+
|
87
|
+
puts "Version bumped from #{current} to #{new_version}"
|
88
|
+
new_version
|
89
|
+
end
|
90
|
+
|
91
|
+
def create_tag
|
92
|
+
version = current_version
|
93
|
+
|
94
|
+
# Create git tag
|
95
|
+
system("git add -A")
|
96
|
+
system("git commit -m 'Release v#{version}'")
|
97
|
+
system("git tag -a v#{version} -m 'Release v#{version}'")
|
98
|
+
|
99
|
+
puts "Created tag v#{version}"
|
100
|
+
puts "Push with: git push origin main --tags"
|
101
|
+
end
|
102
|
+
|
103
|
+
def full_release(type)
|
104
|
+
puts "Starting full release process..."
|
105
|
+
|
106
|
+
# Check if working directory is clean
|
107
|
+
unless system("git diff --quiet && git diff --cached --quiet")
|
108
|
+
puts "Working directory is not clean. Please commit or stash changes first."
|
109
|
+
exit 1
|
110
|
+
end
|
111
|
+
|
112
|
+
# Run tests
|
113
|
+
puts "Running tests..."
|
114
|
+
unless system("bundle exec rake")
|
115
|
+
puts "Tests failed. Aborting release."
|
116
|
+
exit 1
|
117
|
+
end
|
118
|
+
|
119
|
+
# Bump version
|
120
|
+
new_version = bump_version(type)
|
121
|
+
|
122
|
+
# Update changelog
|
123
|
+
update_changelog(new_version)
|
124
|
+
|
125
|
+
# Create tag
|
126
|
+
create_tag
|
127
|
+
|
128
|
+
puts "\nRelease v#{new_version} prepared!"
|
129
|
+
puts "To complete the release:"
|
130
|
+
puts "1. Review the changes: git log --oneline -10"
|
131
|
+
puts "2. Push to GitHub: git push origin main --tags"
|
132
|
+
puts "3. Create a GitHub release at: https://github.com/delexw/rails_mermaid_erd/releases/new"
|
133
|
+
end
|
134
|
+
|
135
|
+
def update_changelog(version)
|
136
|
+
return unless File.exist?(CHANGELOG_FILE)
|
137
|
+
|
138
|
+
date = Time.now.strftime("%Y-%m-%d")
|
139
|
+
changelog_content = File.read(CHANGELOG_FILE)
|
140
|
+
|
141
|
+
# Find the unreleased section and replace it
|
142
|
+
new_content = changelog_content.gsub(
|
143
|
+
"## [Unreleased]",
|
144
|
+
"## [Unreleased]\n\n## [#{version}] - #{date}",
|
145
|
+
)
|
146
|
+
|
147
|
+
File.write(CHANGELOG_FILE, new_content)
|
148
|
+
puts "Updated CHANGELOG.md with version #{version}"
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
ReleaseManager.new.run if __FILE__ == $PROGRAM_NAME
|
data/bin/setup
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MermaidRailsErd
|
4
|
+
class AssociationResolver
|
5
|
+
def resolve(assoc)
|
6
|
+
# Try direct table_name access if available
|
7
|
+
if assoc.respond_to?(:table_name)
|
8
|
+
begin
|
9
|
+
table_name = assoc.table_name
|
10
|
+
rescue StandardError
|
11
|
+
table_name = nil
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# Determine table name from options or plural_name if not already set
|
16
|
+
table_name ||= if assoc.options[:table_name]
|
17
|
+
assoc.options[:table_name].to_s
|
18
|
+
else
|
19
|
+
assoc.plural_name.to_s
|
20
|
+
end
|
21
|
+
|
22
|
+
# Check if table exists
|
23
|
+
return nil unless ActiveRecord::Base.connection.table_exists?(table_name)
|
24
|
+
|
25
|
+
# Return a hash with necessary information
|
26
|
+
{
|
27
|
+
table_name: table_name,
|
28
|
+
primary_key: ActiveRecord::Base.connection.primary_key(table_name),
|
29
|
+
}
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MermaidRailsErd
|
4
|
+
class ColumnInfo
|
5
|
+
attr_reader :name, :annotations, :raw_sql_type, :activerecord_type, :isNullable
|
6
|
+
|
7
|
+
def initialize(name, annotations = [], raw_sql_type = nil, activerecord_type = nil, isNullable = nil)
|
8
|
+
@name = name
|
9
|
+
@annotations = annotations
|
10
|
+
@raw_sql_type = raw_sql_type
|
11
|
+
@activerecord_type = activerecord_type
|
12
|
+
@isNullable = isNullable
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "relationship"
|
4
|
+
require_relative "mermaid_emitter"
|
5
|
+
require_relative "column_info"
|
6
|
+
require_relative "model_loader"
|
7
|
+
require_relative "association_resolver"
|
8
|
+
require_relative "polymorphic_targets_resolver"
|
9
|
+
require_relative "relationship_symbol_mapper"
|
10
|
+
require_relative "relationship_registry"
|
11
|
+
require_relative "model_data_collector"
|
12
|
+
require_relative "parsed_data"
|
13
|
+
|
14
|
+
module MermaidRailsErd
|
15
|
+
class Generator
|
16
|
+
def initialize(output: nil)
|
17
|
+
@output = output
|
18
|
+
@printed_tables = Set.new
|
19
|
+
@printed_relationships = Set.new
|
20
|
+
@relationships = []
|
21
|
+
|
22
|
+
@model_loader = ModelLoader.new
|
23
|
+
@association_resolver = AssociationResolver.new
|
24
|
+
@symbol_mapper = RelationshipSymbolMapper.new
|
25
|
+
@model_data_collector = ModelDataCollector.new(@model_loader)
|
26
|
+
@polymorphic_resolver = PolymorphicTargetsResolver.new(@model_data_collector)
|
27
|
+
@relationship_registry = RelationshipRegistry.new(
|
28
|
+
symbol_mapper: @symbol_mapper,
|
29
|
+
association_resolver: @association_resolver,
|
30
|
+
polymorphic_resolver: @polymorphic_resolver,
|
31
|
+
printed_tables: @printed_tables,
|
32
|
+
model_data_collector: @model_data_collector,
|
33
|
+
)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Build and collect data from models
|
37
|
+
# @return [self]
|
38
|
+
def build
|
39
|
+
@model_data_collector.collect
|
40
|
+
|
41
|
+
# Build all relationships with polymorphic handling first
|
42
|
+
begin
|
43
|
+
@relationships = @relationship_registry.build_all_relationships
|
44
|
+
rescue StandardError => e
|
45
|
+
puts "ERROR building relationships: #{e.class} - #{e.message}"
|
46
|
+
puts e.backtrace.join("\n")
|
47
|
+
@relationships = []
|
48
|
+
end
|
49
|
+
|
50
|
+
# Update table definitions with FK annotations
|
51
|
+
@tables = @model_data_collector.update_foreign_keys(@relationships)
|
52
|
+
|
53
|
+
self
|
54
|
+
end
|
55
|
+
|
56
|
+
# Get parsed data as a structured object
|
57
|
+
# @return [ParsedData] Struct containing relationships, tables, and delegated model data
|
58
|
+
def parsed_data
|
59
|
+
ParsedData.new(@relationships, @tables, @model_data_collector)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Generate and emit the ERD diagram
|
63
|
+
# @param output [IO] Output stream to write the ERD to (defaults to the one provided in initialize)
|
64
|
+
# @return [void]
|
65
|
+
def emit(output: nil)
|
66
|
+
output ||= @output
|
67
|
+
output ||= $stdout
|
68
|
+
MermaidEmitter.new(output, @tables, @relationships).emit
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MermaidRailsErd
|
4
|
+
class MermaidEmitter
|
5
|
+
def initialize(output, tables, relationships)
|
6
|
+
@output = output
|
7
|
+
@tables = tables
|
8
|
+
@relationships = relationships
|
9
|
+
end
|
10
|
+
|
11
|
+
def emit
|
12
|
+
@output.puts "erDiagram"
|
13
|
+
|
14
|
+
@tables.each do |table_name, columns|
|
15
|
+
@output.puts " #{table_name} {"
|
16
|
+
columns.each do |col|
|
17
|
+
annotations = col.annotations.empty? ? "" : " #{col.annotations.join(' ')}"
|
18
|
+
@output.puts " #{col.activerecord_type} #{col.name}#{annotations}"
|
19
|
+
end
|
20
|
+
@output.puts " }"
|
21
|
+
end
|
22
|
+
|
23
|
+
@output.puts
|
24
|
+
|
25
|
+
emitted = Set.new
|
26
|
+
@relationships.each do |rel|
|
27
|
+
next if emitted.include?(rel.key)
|
28
|
+
|
29
|
+
emitted << rel.key
|
30
|
+
@output.puts " #{rel.from_table} #{rel.relationship_type} #{rel.to_table} : \"#{rel.label}\""
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|