rails-erd 1.7.2 → 2.0.0
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/README.md +112 -16
- data/lib/generators/erd/install_generator.rb +2 -0
- data/lib/generators/erd/templates/auto_generate_diagram.rake +1 -1
- data/lib/rails-erd.rb +2 -0
- data/lib/rails_erd/cli.rb +47 -5
- data/lib/rails_erd/config.rb +7 -2
- data/lib/rails_erd/diagram/graphviz.rb +28 -15
- data/lib/rails_erd/diagram/mermaid.rb +152 -0
- data/lib/rails_erd/diagram/templates/node.html.erb +1 -1
- data/lib/rails_erd/diagram/templates/node.record.erb +2 -2
- data/lib/rails_erd/diagram.rb +5 -1
- data/lib/rails_erd/domain/attribute.rb +15 -5
- data/lib/rails_erd/domain/entity.rb +6 -0
- data/lib/rails_erd/domain/relationship/cardinality.rb +2 -0
- data/lib/rails_erd/domain/relationship.rb +2 -0
- data/lib/rails_erd/domain/specialization.rb +2 -0
- data/lib/rails_erd/domain.rb +43 -6
- data/lib/rails_erd/railtie.rb +3 -1
- data/lib/rails_erd/tasks.rake +24 -9
- data/lib/rails_erd/version.rb +3 -1
- data/lib/rails_erd.rb +7 -1
- data/test/support_files/erdconfig.empty +0 -0
- data/test/unit/cli_test.rb +75 -0
- data/test/unit/config_test.rb +7 -0
- data/test/unit/domain_test.rb +8 -13
- data/test/unit/graphviz_test.rb +11 -14
- data/test/unit/mermaid_test.rb +388 -0
- data/test/unit/rake_task_test.rb +23 -2
- metadata +36 -18
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9dadf622a2d2bcae2e4961d23b1f86647b7eaf4e68ae062dad5a18050b2fc935
|
|
4
|
+
data.tar.gz: 72115211691f90710ccdfea75c6b8456bae393cc1794b73727006ee3ec7475af
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f19813191fc9a1258053cab237535fb5358e2a804af4bf394bcae0eb1e2b743c1d48c284d100b2044bed76aa57e8f95cd998379e84fc16de9cecbf452d29d816
|
|
7
|
+
data.tar.gz: b77b4b3e57436b9a32fe92cfec304aa35c8f094fc0c00d615f01c394fae6680f899eab1f548d50986a61534372b48b661d656638502ad1bdad5bc4da3757c139
|
data/README.md
CHANGED
|
@@ -6,7 +6,7 @@ Rails ERD - Generate Entity-Relationship Diagrams for Rails applications
|
|
|
6
6
|
|
|
7
7
|
The second goal of Rails ERD is to provide you with a tool to inspect your application's domain model. If you don't like the default output, it is very easy to use the API to build your own diagrams.
|
|
8
8
|
|
|
9
|
-
Rails ERD was created specifically for Rails and works on versions
|
|
9
|
+
Rails ERD was created specifically for Rails and works on versions 6.0 and later (including Rails 7.x and 8.x). It uses Active Record's built-in reflection capabilities to figure out how your models are associated.
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
Preview
|
|
@@ -22,62 +22,158 @@ Browse the [gallery](https://voormedia.github.io/rails-erd/gallery.html) for mor
|
|
|
22
22
|
Requirements
|
|
23
23
|
---------------
|
|
24
24
|
|
|
25
|
-
* Ruby 1
|
|
26
|
-
* ActiveRecord
|
|
25
|
+
* Ruby 3.1+
|
|
26
|
+
* ActiveRecord 7.0+
|
|
27
|
+
* Graphviz 2.22+ (optional - only needed for PDF/PNG output)
|
|
27
28
|
|
|
28
29
|
Getting started
|
|
29
30
|
---------------
|
|
30
31
|
|
|
31
32
|
See the [installation instructions](https://voormedia.github.io/rails-erd/install.html) for a complete description of how to install Rails ERD. Here's a summary:
|
|
32
33
|
|
|
33
|
-
* Install Graphviz 2.22+ ([how?](https://voormedia.github.io/rails-erd/install.html)). On macOS with Homebrew run `brew install graphviz`.
|
|
34
|
-
|
|
35
|
-
* on linux - `sudo apt-get install graphviz`
|
|
36
|
-
|
|
37
34
|
* Add <tt>gem 'rails-erd', group: :development</tt> to your application's Gemfile
|
|
38
35
|
|
|
39
36
|
* Run <tt>bundle exec erd</tt>
|
|
40
37
|
|
|
38
|
+
This generates a Mermaid diagram (`erd.mmd`) by default. Mermaid diagrams render natively in GitHub, GitLab, and many documentation tools.
|
|
39
|
+
|
|
40
|
+
**For PDF/PNG output (optional):**
|
|
41
|
+
|
|
42
|
+
* Install Graphviz 2.22+ ([how?](https://voormedia.github.io/rails-erd/install.html)). On macOS with Homebrew run `brew install graphviz`, on Linux run `sudo apt-get install graphviz`.
|
|
43
|
+
|
|
44
|
+
* Add `gem 'ruby-graphviz'` to your Gemfile
|
|
45
|
+
|
|
46
|
+
* Run `bundle exec erd generator=graphviz filetype=pdf`
|
|
47
|
+
|
|
41
48
|
### Configuration
|
|
42
49
|
|
|
43
50
|
|
|
44
|
-
Rails ERD has the ability to be configured via the command line or through the use of a YAML file with configuration options set. It will look for this file first at `~/.erdconfig` and then `./.erdconfig` (which will override any settings in `~/.erdconfig`).
|
|
51
|
+
Rails ERD has the ability to be configured via the command line or through the use of a YAML file with configuration options set. It will look for this file first at `~/.erdconfig` and then `./.erdconfig` (which will override any settings in `~/.erdconfig`). More information on [customization options](https://voormedia.github.io/rails-erd/customise.html) can be found in Rails ERD's project documentation.
|
|
52
|
+
|
|
53
|
+
Here is an example `.erdconfig` showing the default values:
|
|
45
54
|
|
|
46
55
|
```yaml
|
|
47
56
|
attributes:
|
|
48
57
|
- content
|
|
49
|
-
- foreign_keys
|
|
50
|
-
- inheritance
|
|
51
58
|
disconnected: true
|
|
52
59
|
filename: erd
|
|
53
|
-
filetype:
|
|
60
|
+
filetype: mmd
|
|
61
|
+
generator: mermaid
|
|
54
62
|
indirect: true
|
|
55
63
|
inheritance: false
|
|
56
64
|
markup: true
|
|
65
|
+
mermaid_style: erdiagram
|
|
57
66
|
notation: simple
|
|
58
67
|
orientation: horizontal
|
|
59
68
|
polymorphism: false
|
|
60
69
|
sort: true
|
|
61
70
|
warn: true
|
|
62
|
-
title:
|
|
71
|
+
title: true
|
|
63
72
|
exclude: null
|
|
64
73
|
only: null
|
|
65
74
|
only_recursion_depth: null
|
|
66
75
|
prepend_primary: false
|
|
67
76
|
cluster: false
|
|
68
77
|
splines: spline
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
You can also customize fonts (useful if the defaults aren't available on your system):
|
|
81
|
+
|
|
82
|
+
```yaml
|
|
69
83
|
fonts:
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
84
|
+
normal: "Arial"
|
|
85
|
+
bold: "Arial Bold"
|
|
86
|
+
italic: "Arial Italic"
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
**Note:** The `filename` option can include a path to output the diagram to a specific directory:
|
|
90
|
+
|
|
91
|
+
```yaml
|
|
92
|
+
filename: docs/erd
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Or via command line:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
bundle exec erd filename="docs/erd"
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Mermaid output (default)
|
|
102
|
+
------------------------
|
|
103
|
+
|
|
104
|
+
Rails ERD generates [Mermaid](https://mermaid.js.org/) diagrams by default. Mermaid is a text-based diagramming format that renders natively in GitHub, GitLab, Notion, and many other tools.
|
|
105
|
+
|
|
106
|
+
By default, Mermaid output uses `erDiagram` syntax with crow's foot notation, PK/FK markers, and proper cardinality. You can switch to `classDiagram` syntax if preferred:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
bundle exec erd mermaid_style=classdiagram
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Or in your `.erdconfig`:
|
|
113
|
+
|
|
114
|
+
```yaml
|
|
115
|
+
mermaid_style: classdiagram
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
The default `erDiagram` style produces output like:
|
|
119
|
+
|
|
120
|
+
```mermaid
|
|
121
|
+
erDiagram
|
|
122
|
+
User {
|
|
123
|
+
integer id PK
|
|
124
|
+
string name
|
|
125
|
+
integer organization_id FK
|
|
126
|
+
}
|
|
127
|
+
Organization {
|
|
128
|
+
integer id PK
|
|
129
|
+
string name
|
|
130
|
+
}
|
|
131
|
+
Organization ||--o{ User : ""
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Graphviz output
|
|
135
|
+
---------------
|
|
136
|
+
|
|
137
|
+
For PDF, PNG, or SVG output, you can use the Graphviz generator:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
bundle exec erd generator=graphviz filetype=pdf
|
|
73
141
|
```
|
|
74
142
|
|
|
143
|
+
This requires:
|
|
144
|
+
* The `ruby-graphviz` gem in your Gemfile
|
|
145
|
+
* Graphviz installed on your system (`brew install graphviz` or `apt-get install graphviz`)
|
|
146
|
+
|
|
75
147
|
Auto generation
|
|
76
148
|
---------------
|
|
77
149
|
|
|
78
150
|
* Run <tt>bundle exec rails g erd:install</tt>
|
|
79
151
|
* Run <tt>bundle exec rails db:migrate</tt>, then the diagram is generated
|
|
80
152
|
|
|
153
|
+
Using in a gem (without Rails)
|
|
154
|
+
------------------------------
|
|
155
|
+
|
|
156
|
+
If you want to use Rails ERD in a gem that defines ActiveRecord models but doesn't include Rails/Railties, you can use the API directly:
|
|
157
|
+
|
|
158
|
+
```ruby
|
|
159
|
+
require 'rails_erd/diagram/mermaid'
|
|
160
|
+
|
|
161
|
+
# Ensure your models are loaded
|
|
162
|
+
require_relative 'lib/my_gem/models'
|
|
163
|
+
|
|
164
|
+
# Generate the diagram
|
|
165
|
+
RailsERD::Diagram::Mermaid.create
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
For Graphviz output:
|
|
169
|
+
|
|
170
|
+
```ruby
|
|
171
|
+
require 'rails_erd/diagram/graphviz'
|
|
172
|
+
RailsERD::Diagram::Graphviz.create
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
You'll need to ensure your database connection is established and models are loaded before generating the diagram.
|
|
176
|
+
|
|
81
177
|
Learn more
|
|
82
178
|
----------
|
|
83
179
|
|
|
@@ -91,7 +187,7 @@ About Rails ERD
|
|
|
91
187
|
|
|
92
188
|
Rails ERD was created by Rolf Timmermans (r.timmermans *at* voormedia.com)
|
|
93
189
|
|
|
94
|
-
Copyright 2010-
|
|
190
|
+
Copyright 2010-2026 Voormedia - [www.voormedia.com](http://www.voormedia.com/)
|
|
95
191
|
|
|
96
192
|
|
|
97
193
|
License
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# NOTE: only doing this in development as some production environments (Heroku)
|
|
2
2
|
# NOTE: are sensitive to local FS writes, and besides -- it's just not proper
|
|
3
3
|
# NOTE: to have a dev-mode tool do its thing in production.
|
|
4
|
-
if
|
|
4
|
+
if defined? RailsERD
|
|
5
5
|
RailsERD.load_tasks
|
|
6
6
|
end
|
data/lib/rails-erd.rb
CHANGED
data/lib/rails_erd/cli.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require "rails_erd"
|
|
2
4
|
require "choice"
|
|
3
5
|
|
|
@@ -5,6 +7,16 @@ Choice.options do
|
|
|
5
7
|
separator ""
|
|
6
8
|
separator "Diagram options:"
|
|
7
9
|
|
|
10
|
+
option :generator do
|
|
11
|
+
long "--generator=Generator"
|
|
12
|
+
desc "Generator to use (mermaid or graphviz). Defaults to mermaid."
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
option :mermaid_style do
|
|
16
|
+
long "--mermaid_style=STYLE"
|
|
17
|
+
desc "Mermaid diagram style: classdiagram (default) or erdiagram. Only applies when generator is mermaid."
|
|
18
|
+
end
|
|
19
|
+
|
|
8
20
|
option :title do
|
|
9
21
|
long "--title=TITLE"
|
|
10
22
|
desc "Replace default diagram title with a custom one."
|
|
@@ -12,7 +24,7 @@ Choice.options do
|
|
|
12
24
|
|
|
13
25
|
option :notation do
|
|
14
26
|
long "--notation=STYLE"
|
|
15
|
-
desc "Diagram notation style, one of simple, bachman, uml or crowsfoot."
|
|
27
|
+
desc "Diagram notation style, one of simple, bachman, uml or crowsfoot (avaiable only with Graphviz engine)."
|
|
16
28
|
end
|
|
17
29
|
|
|
18
30
|
option :attributes do
|
|
@@ -80,6 +92,16 @@ Choice.options do
|
|
|
80
92
|
desc "Control how edges are represented. See http://www.graphviz.org/doc/info/attrs.html#d:splines for values."
|
|
81
93
|
end
|
|
82
94
|
|
|
95
|
+
option :table_names do
|
|
96
|
+
long "--table_names=BOOLEAN"
|
|
97
|
+
desc "Label each entity with the table name instead of the ActiveRecord class name."
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
option :native_types do
|
|
101
|
+
long "--native_types=BOOLEAN"
|
|
102
|
+
desc "Display the db-native types instead of the Rails migration ones."
|
|
103
|
+
end
|
|
104
|
+
|
|
83
105
|
separator ""
|
|
84
106
|
separator "Output options:"
|
|
85
107
|
|
|
@@ -138,16 +160,22 @@ module RailsERD
|
|
|
138
160
|
class CLI
|
|
139
161
|
attr_reader :path, :options
|
|
140
162
|
|
|
163
|
+
# Options that should be converted to symbols
|
|
164
|
+
SYMBOL_OPTIONS = %i[generator mermaid_style filetype notation orientation].freeze
|
|
165
|
+
|
|
141
166
|
class << self
|
|
142
167
|
def start
|
|
143
168
|
path = Choice.rest.first || Dir.pwd
|
|
144
169
|
options = Choice.choices.each_with_object({}) do |(key, value), opts|
|
|
170
|
+
key_sym = key.to_sym
|
|
145
171
|
if key.start_with? "no_"
|
|
146
172
|
opts[key.gsub("no_", "").to_sym] = !value
|
|
147
173
|
elsif value.to_s.include? ","
|
|
148
|
-
opts[
|
|
174
|
+
opts[key_sym] = value.split(",").map(&:to_s)
|
|
175
|
+
elsif SYMBOL_OPTIONS.include?(key_sym) && value.is_a?(String)
|
|
176
|
+
opts[key_sym] = value.to_sym
|
|
149
177
|
else
|
|
150
|
-
opts[
|
|
178
|
+
opts[key_sym] = value
|
|
151
179
|
end
|
|
152
180
|
end
|
|
153
181
|
if options[:config_file] && options[:config_file] != ''
|
|
@@ -159,7 +187,12 @@ module RailsERD
|
|
|
159
187
|
|
|
160
188
|
def initialize(path, options)
|
|
161
189
|
@path, @options = path, options
|
|
162
|
-
|
|
190
|
+
generator = options[:generator] || RailsERD.options[:generator]
|
|
191
|
+
if generator == :mermaid
|
|
192
|
+
require "rails_erd/diagram/mermaid"
|
|
193
|
+
else
|
|
194
|
+
require "rails_erd/diagram/graphviz"
|
|
195
|
+
end
|
|
163
196
|
end
|
|
164
197
|
|
|
165
198
|
def start
|
|
@@ -196,9 +229,18 @@ module RailsERD
|
|
|
196
229
|
rescue TypeError
|
|
197
230
|
end
|
|
198
231
|
|
|
232
|
+
def generator
|
|
233
|
+
generator_type = options[:generator] || RailsERD.options[:generator]
|
|
234
|
+
if generator_type == :mermaid
|
|
235
|
+
RailsERD::Diagram::Mermaid
|
|
236
|
+
else
|
|
237
|
+
RailsERD::Diagram::Graphviz
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
|
|
199
241
|
def create_diagram
|
|
200
242
|
$stderr.puts "Generating entity-relationship diagram for #{ActiveRecord::Base.descendants.length} models..."
|
|
201
|
-
file =
|
|
243
|
+
file = generator.create(options)
|
|
202
244
|
$stderr.puts "Diagram saved to '#{file}'."
|
|
203
245
|
`open #{file}` if options[:open]
|
|
204
246
|
end
|
data/lib/rails_erd/config.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require "yaml"
|
|
2
4
|
|
|
3
5
|
module RailsERD
|
|
@@ -42,7 +44,10 @@ module RailsERD
|
|
|
42
44
|
|
|
43
45
|
def load_file(path)
|
|
44
46
|
if File.exist?(path)
|
|
45
|
-
YAML.load_file(path)
|
|
47
|
+
config = YAML.load_file(path)
|
|
48
|
+
return unless config.is_a?(Hash)
|
|
49
|
+
|
|
50
|
+
config.each do |key, value|
|
|
46
51
|
key = key.to_sym
|
|
47
52
|
@options[key] = normalize_value(key, value)
|
|
48
53
|
end
|
|
@@ -61,7 +66,7 @@ module RailsERD
|
|
|
61
66
|
end
|
|
62
67
|
|
|
63
68
|
# <symbol>
|
|
64
|
-
when :filetype, :notation
|
|
69
|
+
when :filetype, :notation, :generator, :mermaid_style
|
|
65
70
|
value.to_sym
|
|
66
71
|
|
|
67
72
|
# [<string>]
|
|
@@ -1,19 +1,25 @@
|
|
|
1
|
-
#
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
2
3
|
require "rails_erd/diagram"
|
|
3
|
-
require "graphviz"
|
|
4
4
|
require "erb"
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
@data
|
|
6
|
+
begin
|
|
7
|
+
require "graphviz"
|
|
8
|
+
|
|
9
|
+
# Fix bad RegEx test in Ruby-Graphviz.
|
|
10
|
+
GraphViz::Types::LblString.class_eval do
|
|
11
|
+
def output # @private :nodoc:
|
|
12
|
+
if /^<.*>$/m =~ @data
|
|
13
|
+
@data
|
|
14
|
+
else
|
|
15
|
+
@data.to_s.inspect.gsub("\\\\", "\\")
|
|
16
|
+
end
|
|
13
17
|
end
|
|
18
|
+
alias_method :to_gv, :output
|
|
19
|
+
alias_method :to_s, :output
|
|
14
20
|
end
|
|
15
|
-
|
|
16
|
-
|
|
21
|
+
rescue LoadError
|
|
22
|
+
# ruby-graphviz is optional as of v2.0 - only needed when using generator: graphviz
|
|
17
23
|
end
|
|
18
24
|
|
|
19
25
|
module RailsERD
|
|
@@ -123,8 +129,8 @@ module RailsERD
|
|
|
123
129
|
options[:style] = :dotted if relationship.indirect?
|
|
124
130
|
|
|
125
131
|
# Cardinality is "look-across".
|
|
126
|
-
dst = relationship.to_many? ? "crow" : "tee"
|
|
127
|
-
src = relationship.many_to? ? "crow" : "tee"
|
|
132
|
+
dst = +(relationship.to_many? ? "crow" : "tee")
|
|
133
|
+
src = +(relationship.many_to? ? "crow" : "tee")
|
|
128
134
|
|
|
129
135
|
# Participation is "look-across".
|
|
130
136
|
dst << (relationship.destination_optional? ? "odot" : "tee")
|
|
@@ -143,8 +149,8 @@ module RailsERD
|
|
|
143
149
|
options[:style] = :dotted if relationship.indirect?
|
|
144
150
|
|
|
145
151
|
# Participation is "look-here".
|
|
146
|
-
dst = relationship.source_optional? ? "odot" : "dot"
|
|
147
|
-
src = relationship.destination_optional? ? "odot" : "dot"
|
|
152
|
+
dst = +(relationship.source_optional? ? "odot" : "dot")
|
|
153
|
+
src = +(relationship.destination_optional? ? "odot" : "dot")
|
|
148
154
|
|
|
149
155
|
# Cardinality is "look-across".
|
|
150
156
|
dst << "normal" if relationship.to_many?
|
|
@@ -181,6 +187,11 @@ module RailsERD
|
|
|
181
187
|
attr_accessor :graph
|
|
182
188
|
|
|
183
189
|
setup do
|
|
190
|
+
unless defined?(GraphViz)
|
|
191
|
+
raise LoadError, "The ruby-graphviz gem is required for Graphviz output. " \
|
|
192
|
+
"Add `gem 'ruby-graphviz'` to your Gemfile, or use `generator: mermaid` instead."
|
|
193
|
+
end
|
|
194
|
+
|
|
184
195
|
self.graph = GraphViz.digraph(domain.name)
|
|
185
196
|
|
|
186
197
|
# Set all default attributes.
|
|
@@ -238,6 +249,8 @@ module RailsERD
|
|
|
238
249
|
|
|
239
250
|
each_relationship do |relationship|
|
|
240
251
|
from, to = relationship.source, relationship.destination
|
|
252
|
+
next unless from && to
|
|
253
|
+
|
|
241
254
|
unless draw_edge from.name, to.name, relationship_options(relationship)
|
|
242
255
|
from.children.each do |child|
|
|
243
256
|
draw_edge child.name, to.name, relationship_options(relationship)
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails_erd/diagram"
|
|
4
|
+
require "erb"
|
|
5
|
+
|
|
6
|
+
module RailsERD
|
|
7
|
+
class Diagram
|
|
8
|
+
class Mermaid < Diagram
|
|
9
|
+
|
|
10
|
+
attr_accessor :graph
|
|
11
|
+
|
|
12
|
+
setup do
|
|
13
|
+
self.graph = [diagram_type]
|
|
14
|
+
|
|
15
|
+
# Respect orientation option: horizontal = TB (top-down), vertical = LR (left-right)
|
|
16
|
+
direction = (options.orientation.to_s == "vertical") ? "LR" : "TB"
|
|
17
|
+
self.graph << "\tdirection #{direction}"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
each_entity do |entity, attributes|
|
|
21
|
+
if er_diagram?
|
|
22
|
+
# Build entity block as a single string to avoid uniq issues with closing braces
|
|
23
|
+
entity_lines = ["\t#{entity} {"]
|
|
24
|
+
attributes.each do |attr|
|
|
25
|
+
key_marker = attribute_key_marker(attr)
|
|
26
|
+
entity_lines << "\t\t#{attr.type} #{attr.name}#{key_marker}"
|
|
27
|
+
end
|
|
28
|
+
entity_lines << "\t}"
|
|
29
|
+
graph << entity_lines.join("\n")
|
|
30
|
+
else
|
|
31
|
+
graph << "\tclass `#{entity}`"
|
|
32
|
+
attributes.each do |attr|
|
|
33
|
+
graph << "\t`#{entity}` : +#{attr.type} #{attr.name}"
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
each_specialization do |specialization|
|
|
39
|
+
from, to = specialization.generalized, specialization.specialized
|
|
40
|
+
if er_diagram?
|
|
41
|
+
# erDiagram doesn't have a direct polymorphic notation, use inheritance-like
|
|
42
|
+
graph << "\t#{from.name} ||--o{ #{to.name} : \"specializes\""
|
|
43
|
+
else
|
|
44
|
+
graph << "\t<<polymorphic>> `#{specialization.generalized}`"
|
|
45
|
+
graph << "\t #{from.name} <|-- #{to.name}"
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
each_relationship do |relationship|
|
|
50
|
+
from, to = relationship.source, relationship.destination
|
|
51
|
+
next unless from && to
|
|
52
|
+
|
|
53
|
+
if er_diagram?
|
|
54
|
+
graph << "\t#{from.name} #{er_relation_notation(relationship)} #{to.name} : \"\""
|
|
55
|
+
|
|
56
|
+
from.children.each do |child|
|
|
57
|
+
graph << "\t#{child.name} #{er_relation_notation(relationship)} #{to.name} : \"\""
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
to.children.each do |child|
|
|
61
|
+
graph << "\t#{from.name} #{er_relation_notation(relationship)} #{child.name} : \"\""
|
|
62
|
+
end
|
|
63
|
+
else
|
|
64
|
+
graph << "\t`#{from.name}` #{relation_arrow(relationship)} `#{to.name}`"
|
|
65
|
+
|
|
66
|
+
from.children.each do |child|
|
|
67
|
+
graph << "\t`#{child.name}` #{relation_arrow(relationship)} `#{to.name}`"
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
to.children.each do |child|
|
|
71
|
+
graph << "\t`#{from.name}` #{relation_arrow(relationship)} `#{child.name}`"
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
save do
|
|
77
|
+
raise "Saving diagram failed!\nOutput directory '#{File.dirname(filename)}' does not exist." unless File.directory?(File.dirname(filename))
|
|
78
|
+
|
|
79
|
+
File.write(filename.gsub(/\s/,"_"), graph.uniq.join("\n"))
|
|
80
|
+
filename
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def filename
|
|
84
|
+
"#{options.filename}.mmd"
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def diagram_type
|
|
88
|
+
er_diagram? ? "erDiagram" : "classDiagram"
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def er_diagram?
|
|
92
|
+
options[:mermaid_style] == :erdiagram || options[:mermaid_style] == :er
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# erDiagram relationship notation using crow's foot
|
|
96
|
+
# Format: left_cardinality--right_cardinality
|
|
97
|
+
# Cardinality markers:
|
|
98
|
+
# || = exactly one
|
|
99
|
+
# o| = zero or one
|
|
100
|
+
# }| = one or more
|
|
101
|
+
# }o = zero or more
|
|
102
|
+
def er_relation_notation(relationship)
|
|
103
|
+
line = relationship.indirect? ? ".." : "--"
|
|
104
|
+
|
|
105
|
+
# Left side (source cardinality - how many source entities relate to one destination)
|
|
106
|
+
left = if relationship.many_to?
|
|
107
|
+
relationship.source_optional? ? "o{" : "|{"
|
|
108
|
+
else
|
|
109
|
+
relationship.source_optional? ? "o|" : "||"
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Right side (destination cardinality - how many destination entities relate to one source)
|
|
113
|
+
right = if relationship.to_many?
|
|
114
|
+
relationship.destination_optional? ? "}o" : "}|"
|
|
115
|
+
else
|
|
116
|
+
relationship.destination_optional? ? "|o" : "||"
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
"#{left}#{line}#{right}"
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def attribute_key_marker(attr)
|
|
123
|
+
markers = []
|
|
124
|
+
markers << "PK" if attr.primary_key?
|
|
125
|
+
markers << "FK" if attr.foreign_key?
|
|
126
|
+
markers.empty? ? "" : " #{markers.join(',')}"
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# classDiagram relationship arrows (legacy)
|
|
130
|
+
def relation_arrow(relationship)
|
|
131
|
+
arrow_body = arrow_body relationship
|
|
132
|
+
arrow_head = arrow_head relationship
|
|
133
|
+
arrow_tail = arrow_tail relationship
|
|
134
|
+
|
|
135
|
+
"#{arrow_tail}#{arrow_body}#{arrow_head}"
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def arrow_body(relationship)
|
|
139
|
+
relationship.indirect? ? ".." : "--"
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def arrow_head(relationship)
|
|
143
|
+
relationship.to_many? ? ">" : ""
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def arrow_tail(relationship)
|
|
147
|
+
relationship.many_to? ? "<" : ""
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<% if options.orientation == :horizontal %>{<% end %>
|
|
2
2
|
<table border="0" align="center" cellspacing="0.5" cellpadding="0" width="<%= NODE_WIDTH + 4 %>">
|
|
3
|
-
<tr><td align="center" valign="bottom" width="<%= NODE_WIDTH %>"><font face="<%= FONTS[:bold] %>" point-size="11"><%= entity.
|
|
3
|
+
<tr><td align="center" valign="bottom" width="<%= NODE_WIDTH %>"><font face="<%= FONTS[:bold] %>" point-size="11"><%= entity.label %></font></td></tr>
|
|
4
4
|
</table>
|
|
5
5
|
<% if attributes.any? %>
|
|
6
6
|
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<% if options.orientation == :horizontal %>{<% end %><%= entity.
|
|
1
|
+
<% if options.orientation == :horizontal %>{<% end %><%= entity.label %><% if attributes.any? %>
|
|
2
2
|
|<% attributes.each do |attribute| %><%=
|
|
3
3
|
attribute %> (<%= attribute.type_description %>)
|
|
4
|
-
<% end %><% end %><% if options.orientation == :horizontal %>}<% end %>
|
|
4
|
+
<% end %><% end %><% if options.orientation == :horizontal %>}<% end %>
|
data/lib/rails_erd/diagram.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require "rails_erd/domain"
|
|
2
4
|
|
|
3
5
|
module RailsERD
|
|
@@ -126,8 +128,10 @@ module RailsERD
|
|
|
126
128
|
instance_eval(&callbacks[:setup])
|
|
127
129
|
if options.only_recursion_depth.present?
|
|
128
130
|
depth = options.only_recursion_depth.to_s.to_i
|
|
131
|
+
# Ensure options[:only] is an array (CLI may pass a single string)
|
|
132
|
+
options[:only] = [options[:only]].flatten
|
|
129
133
|
options[:only].dup.each do |class_name|
|
|
130
|
-
options[:only]+= recurse_into_relationships(@domain.entity_by_name(class_name), depth)
|
|
134
|
+
options[:only] += recurse_into_relationships(@domain.entity_by_name(class_name), depth)
|
|
131
135
|
end
|
|
132
136
|
options[:only].uniq!
|
|
133
137
|
end
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#
|
|
1
|
+
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
#--
|
|
4
4
|
module RailsERD
|
|
@@ -49,7 +49,7 @@ module RailsERD
|
|
|
49
49
|
# The type of the attribute, equal to the Rails migration type. Can be any
|
|
50
50
|
# of +:string+, +:integer+, +:boolean+, +:text+, etc.
|
|
51
51
|
def type
|
|
52
|
-
column.type or column.sql_type.downcase.to_sym
|
|
52
|
+
!RailsERD.options[:native_types] and column.type or column.sql_type.downcase.to_sym
|
|
53
53
|
end
|
|
54
54
|
|
|
55
55
|
# Returns +true+ if this attribute is a content column, that is, if it
|
|
@@ -77,9 +77,18 @@ module RailsERD
|
|
|
77
77
|
# Returns +true+ if this attribute is used as a foreign key for any
|
|
78
78
|
# relationship.
|
|
79
79
|
def foreign_key?
|
|
80
|
-
@domain
|
|
81
|
-
|
|
82
|
-
|
|
80
|
+
@domain
|
|
81
|
+
.relationships_by_entity_name(@model.name)
|
|
82
|
+
.map(&:associations)
|
|
83
|
+
.flatten
|
|
84
|
+
.map do |associaton|
|
|
85
|
+
result = associaton.send(Domain.foreign_key_method_name)
|
|
86
|
+
if result.respond_to?(:to_ary)
|
|
87
|
+
result.join("_").to_sym
|
|
88
|
+
else
|
|
89
|
+
result.to_sym
|
|
90
|
+
end
|
|
91
|
+
end.include?(name.to_sym)
|
|
83
92
|
end
|
|
84
93
|
|
|
85
94
|
# Returns +true+ if this attribute is used for single table inheritance.
|
|
@@ -120,6 +129,7 @@ module RailsERD
|
|
|
120
129
|
# <tt>:boolean, :null => false</tt>:: boolean *
|
|
121
130
|
def type_description
|
|
122
131
|
type.to_s.dup.tap do |desc|
|
|
132
|
+
desc << "[]" if column.respond_to?(:array?) && column.array?
|
|
123
133
|
desc << " #{limit_description}" if limit_description
|
|
124
134
|
desc << " ∗" if mandatory? && !primary_key? # Add a hair space + low asterisk (Unicode characters)
|
|
125
135
|
desc << " U" if unique? && !primary_key? && !foreign_key? # Add U if unique but non-key
|