arerd 0.1.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 +7 -0
- data/.devcontainer/Dockerfile +22 -0
- data/.devcontainer/compose.yaml +18 -0
- data/.devcontainer/devcontainer.json +37 -0
- data/.devcontainer/fish/conf.d/abbrs.fish +3 -0
- data/.devcontainer/fish/functions/git.fish +12 -0
- data/.devcontainer/post_create.fish +1 -0
- data/.editorconfig +12 -0
- data/.standard.yml +2 -0
- data/.tool-versions +1 -0
- data/Gemfile +13 -0
- data/Gemfile.lock +248 -0
- data/LICENSE +21 -0
- data/README.md +111 -0
- data/Rakefile +10 -0
- data/lib/arerd/association.rb +109 -0
- data/lib/arerd/erd_generator.rb +32 -0
- data/lib/arerd/railtie.rb +9 -0
- data/lib/arerd/tasks/erd.rake +18 -0
- data/lib/arerd/templates/erd.md.erb +3 -0
- data/lib/arerd/templates/erd.mmd.erb +43 -0
- data/lib/arerd/version.rb +5 -0
- data/lib/arerd.rb +11 -0
- metadata +96 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 9a3364c03a39be5bf9ac0727652516999efb340d3208fd87621556021c28de20
|
|
4
|
+
data.tar.gz: c84c46b6e3e2fec66857e74c087e5ba6f03b2fe4cc42c3ed99d9997dc5f69038
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 8a634f01c34f49fe14acfcfc9e9d37407723852857b05ab16ecef35ac5f5dfb420a6eeaf5695e156d0ca44e998a8f8df7032d8e3f987ab7b51b77b66c4035dc4
|
|
7
|
+
data.tar.gz: 46d6acb3f3be56edb6630df0c97ff1a318d73b6aba02b2e009272ae0761bc616896040f6dd12ebc7beb8918845632274f7df9f70044f6ba8f4b5822f8305b1ee
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
FROM mcr.microsoft.com/devcontainers/base:1-bookworm
|
|
2
|
+
|
|
3
|
+
RUN apt-get update && \
|
|
4
|
+
export DEBIAN_FRONTEND=noninteractive && \
|
|
5
|
+
apt-get -y install --no-install-recommends \
|
|
6
|
+
fish \
|
|
7
|
+
build-essential autoconf libssl-dev libyaml-dev zlib1g-dev libffi-dev libgmp-dev rustc && \
|
|
8
|
+
rm -rf /var/lib/apt/lists/* /var/cache/apt/archives/*
|
|
9
|
+
|
|
10
|
+
# Install asdf
|
|
11
|
+
RUN curl -L 'https://github.com/asdf-vm/asdf/releases/download/v0.18.0/asdf-v0.18.0-linux-amd64.tar.gz' | tar -xz -C /usr/local/bin && \
|
|
12
|
+
mkdir -p /home/vscode/.config/fish/conf.d && \
|
|
13
|
+
curl -L 'https://raw.githubusercontent.com/asdf-vm/asdf/refs/tags/v0.18.0/asdf.fish' > /home/vscode/.config/fish/conf.d/asdf.fish && \
|
|
14
|
+
chown -R vscode:vscode /home/vscode/.config
|
|
15
|
+
|
|
16
|
+
# Install asdf plugins and specified runtimes
|
|
17
|
+
USER vscode
|
|
18
|
+
WORKDIR /home/vscode
|
|
19
|
+
COPY --chown=vscode:vscode .tool-versions /home/vscode/.tool-versions
|
|
20
|
+
RUN asdf plugin add ruby && \
|
|
21
|
+
asdf install && \
|
|
22
|
+
asdf reshim
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
name: "arerd"
|
|
2
|
+
|
|
3
|
+
services:
|
|
4
|
+
arerd:
|
|
5
|
+
build:
|
|
6
|
+
context: ..
|
|
7
|
+
dockerfile: .devcontainer/Dockerfile
|
|
8
|
+
|
|
9
|
+
volumes:
|
|
10
|
+
- ../..:/workspaces:cached
|
|
11
|
+
- ./fish/conf.d/abbrs.fish:/home/vscode/.config/fish/conf.d/abbrs.fish
|
|
12
|
+
- ./fish/functions/git.fish:/home/vscode/.config/fish/conf.d/git.fish
|
|
13
|
+
|
|
14
|
+
# Overrides default command so things don't shut down after the process ends.
|
|
15
|
+
command: sleep infinity
|
|
16
|
+
|
|
17
|
+
# Uncomment the next line to use a non-root user for all processes.
|
|
18
|
+
user: vscode
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// For format details, see https://containers.dev/implementors/json_reference/.
|
|
2
|
+
// For config options, see the README at: https://github.com/devcontainers/templates/tree/main/src/ruby
|
|
3
|
+
{
|
|
4
|
+
"name": "arerd",
|
|
5
|
+
"dockerComposeFile": "compose.yaml",
|
|
6
|
+
"service": "arerd",
|
|
7
|
+
"workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}",
|
|
8
|
+
|
|
9
|
+
// Features to add to the dev container. More info: https://containers.dev/features.
|
|
10
|
+
"features": {
|
|
11
|
+
"ghcr.io/devcontainers/features/github-cli:1": {}
|
|
12
|
+
},
|
|
13
|
+
|
|
14
|
+
"containerEnv": {},
|
|
15
|
+
|
|
16
|
+
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
|
17
|
+
"forwardPorts": [],
|
|
18
|
+
|
|
19
|
+
// Configure tool-specific properties.
|
|
20
|
+
"customizations": {
|
|
21
|
+
"vscode": {
|
|
22
|
+
"settings": {
|
|
23
|
+
"terminal.integrated.defaultProfile.linux": "fish"
|
|
24
|
+
},
|
|
25
|
+
"extensions": [
|
|
26
|
+
"EditorConfig.EditorConfig",
|
|
27
|
+
"Shopify.ruby-lsp",
|
|
28
|
+
]
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
// Uncomment to connect as root instead. More info: https://containers.dev/implementors/json_reference/#remoteUser.
|
|
33
|
+
// "remoteUser": "root",
|
|
34
|
+
|
|
35
|
+
// Use 'postCreateCommand' to run commands after the container is created.
|
|
36
|
+
"postCreateCommand": "./.devcontainer/post_create.fish"
|
|
37
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/bin/fish
|
data/.editorconfig
ADDED
data/.standard.yml
ADDED
data/.tool-versions
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ruby 3.1.6
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
PATH
|
|
2
|
+
remote: .
|
|
3
|
+
specs:
|
|
4
|
+
arerd (0.1.0)
|
|
5
|
+
rails (>= 6.0)
|
|
6
|
+
|
|
7
|
+
GEM
|
|
8
|
+
remote: https://rubygems.org/
|
|
9
|
+
specs:
|
|
10
|
+
actioncable (7.2.2.1)
|
|
11
|
+
actionpack (= 7.2.2.1)
|
|
12
|
+
activesupport (= 7.2.2.1)
|
|
13
|
+
nio4r (~> 2.0)
|
|
14
|
+
websocket-driver (>= 0.6.1)
|
|
15
|
+
zeitwerk (~> 2.6)
|
|
16
|
+
actionmailbox (7.2.2.1)
|
|
17
|
+
actionpack (= 7.2.2.1)
|
|
18
|
+
activejob (= 7.2.2.1)
|
|
19
|
+
activerecord (= 7.2.2.1)
|
|
20
|
+
activestorage (= 7.2.2.1)
|
|
21
|
+
activesupport (= 7.2.2.1)
|
|
22
|
+
mail (>= 2.8.0)
|
|
23
|
+
actionmailer (7.2.2.1)
|
|
24
|
+
actionpack (= 7.2.2.1)
|
|
25
|
+
actionview (= 7.2.2.1)
|
|
26
|
+
activejob (= 7.2.2.1)
|
|
27
|
+
activesupport (= 7.2.2.1)
|
|
28
|
+
mail (>= 2.8.0)
|
|
29
|
+
rails-dom-testing (~> 2.2)
|
|
30
|
+
actionpack (7.2.2.1)
|
|
31
|
+
actionview (= 7.2.2.1)
|
|
32
|
+
activesupport (= 7.2.2.1)
|
|
33
|
+
nokogiri (>= 1.8.5)
|
|
34
|
+
racc
|
|
35
|
+
rack (>= 2.2.4, < 3.2)
|
|
36
|
+
rack-session (>= 1.0.1)
|
|
37
|
+
rack-test (>= 0.6.3)
|
|
38
|
+
rails-dom-testing (~> 2.2)
|
|
39
|
+
rails-html-sanitizer (~> 1.6)
|
|
40
|
+
useragent (~> 0.16)
|
|
41
|
+
actiontext (7.2.2.1)
|
|
42
|
+
actionpack (= 7.2.2.1)
|
|
43
|
+
activerecord (= 7.2.2.1)
|
|
44
|
+
activestorage (= 7.2.2.1)
|
|
45
|
+
activesupport (= 7.2.2.1)
|
|
46
|
+
globalid (>= 0.6.0)
|
|
47
|
+
nokogiri (>= 1.8.5)
|
|
48
|
+
actionview (7.2.2.1)
|
|
49
|
+
activesupport (= 7.2.2.1)
|
|
50
|
+
builder (~> 3.1)
|
|
51
|
+
erubi (~> 1.11)
|
|
52
|
+
rails-dom-testing (~> 2.2)
|
|
53
|
+
rails-html-sanitizer (~> 1.6)
|
|
54
|
+
activejob (7.2.2.1)
|
|
55
|
+
activesupport (= 7.2.2.1)
|
|
56
|
+
globalid (>= 0.3.6)
|
|
57
|
+
activemodel (7.2.2.1)
|
|
58
|
+
activesupport (= 7.2.2.1)
|
|
59
|
+
activerecord (7.2.2.1)
|
|
60
|
+
activemodel (= 7.2.2.1)
|
|
61
|
+
activesupport (= 7.2.2.1)
|
|
62
|
+
timeout (>= 0.4.0)
|
|
63
|
+
activestorage (7.2.2.1)
|
|
64
|
+
actionpack (= 7.2.2.1)
|
|
65
|
+
activejob (= 7.2.2.1)
|
|
66
|
+
activerecord (= 7.2.2.1)
|
|
67
|
+
activesupport (= 7.2.2.1)
|
|
68
|
+
marcel (~> 1.0)
|
|
69
|
+
activesupport (7.2.2.1)
|
|
70
|
+
base64
|
|
71
|
+
benchmark (>= 0.3)
|
|
72
|
+
bigdecimal
|
|
73
|
+
concurrent-ruby (~> 1.0, >= 1.3.1)
|
|
74
|
+
connection_pool (>= 2.2.5)
|
|
75
|
+
drb
|
|
76
|
+
i18n (>= 1.6, < 2)
|
|
77
|
+
logger (>= 1.4.2)
|
|
78
|
+
minitest (>= 5.1)
|
|
79
|
+
securerandom (>= 0.3)
|
|
80
|
+
tzinfo (~> 2.0, >= 2.0.5)
|
|
81
|
+
ast (2.4.3)
|
|
82
|
+
base64 (0.3.0)
|
|
83
|
+
benchmark (0.4.1)
|
|
84
|
+
bigdecimal (3.2.2)
|
|
85
|
+
builder (3.3.0)
|
|
86
|
+
cgi (0.5.0)
|
|
87
|
+
concurrent-ruby (1.3.5)
|
|
88
|
+
connection_pool (2.5.3)
|
|
89
|
+
crass (1.0.6)
|
|
90
|
+
date (3.4.1)
|
|
91
|
+
drb (2.2.3)
|
|
92
|
+
erb (4.0.4)
|
|
93
|
+
cgi (>= 0.3.3)
|
|
94
|
+
erubi (1.13.1)
|
|
95
|
+
globalid (1.2.1)
|
|
96
|
+
activesupport (>= 6.1)
|
|
97
|
+
i18n (1.14.7)
|
|
98
|
+
concurrent-ruby (~> 1.0)
|
|
99
|
+
io-console (0.8.0)
|
|
100
|
+
irb (1.15.2)
|
|
101
|
+
pp (>= 0.6.0)
|
|
102
|
+
rdoc (>= 4.0.0)
|
|
103
|
+
reline (>= 0.4.2)
|
|
104
|
+
json (2.12.2)
|
|
105
|
+
language_server-protocol (3.17.0.5)
|
|
106
|
+
lint_roller (1.1.0)
|
|
107
|
+
logger (1.7.0)
|
|
108
|
+
loofah (2.24.1)
|
|
109
|
+
crass (~> 1.0.2)
|
|
110
|
+
nokogiri (>= 1.12.0)
|
|
111
|
+
mail (2.8.1)
|
|
112
|
+
mini_mime (>= 0.1.1)
|
|
113
|
+
net-imap
|
|
114
|
+
net-pop
|
|
115
|
+
net-smtp
|
|
116
|
+
marcel (1.0.4)
|
|
117
|
+
mini_mime (1.1.5)
|
|
118
|
+
minitest (5.25.5)
|
|
119
|
+
net-imap (0.5.9)
|
|
120
|
+
date
|
|
121
|
+
net-protocol
|
|
122
|
+
net-pop (0.1.2)
|
|
123
|
+
net-protocol
|
|
124
|
+
net-protocol (0.2.2)
|
|
125
|
+
timeout
|
|
126
|
+
net-smtp (0.5.1)
|
|
127
|
+
net-protocol
|
|
128
|
+
nio4r (2.7.4)
|
|
129
|
+
nokogiri (1.18.8-x86_64-linux-gnu)
|
|
130
|
+
racc (~> 1.4)
|
|
131
|
+
parallel (1.27.0)
|
|
132
|
+
parser (3.3.8.0)
|
|
133
|
+
ast (~> 2.4.1)
|
|
134
|
+
racc
|
|
135
|
+
pp (0.6.2)
|
|
136
|
+
prettyprint
|
|
137
|
+
prettyprint (0.2.0)
|
|
138
|
+
prism (1.4.0)
|
|
139
|
+
psych (5.2.6)
|
|
140
|
+
date
|
|
141
|
+
stringio
|
|
142
|
+
racc (1.8.1)
|
|
143
|
+
rack (3.1.16)
|
|
144
|
+
rack-session (2.1.1)
|
|
145
|
+
base64 (>= 0.1.0)
|
|
146
|
+
rack (>= 3.0.0)
|
|
147
|
+
rack-test (2.2.0)
|
|
148
|
+
rack (>= 1.3)
|
|
149
|
+
rackup (2.2.1)
|
|
150
|
+
rack (>= 3)
|
|
151
|
+
rails (7.2.2.1)
|
|
152
|
+
actioncable (= 7.2.2.1)
|
|
153
|
+
actionmailbox (= 7.2.2.1)
|
|
154
|
+
actionmailer (= 7.2.2.1)
|
|
155
|
+
actionpack (= 7.2.2.1)
|
|
156
|
+
actiontext (= 7.2.2.1)
|
|
157
|
+
actionview (= 7.2.2.1)
|
|
158
|
+
activejob (= 7.2.2.1)
|
|
159
|
+
activemodel (= 7.2.2.1)
|
|
160
|
+
activerecord (= 7.2.2.1)
|
|
161
|
+
activestorage (= 7.2.2.1)
|
|
162
|
+
activesupport (= 7.2.2.1)
|
|
163
|
+
bundler (>= 1.15.0)
|
|
164
|
+
railties (= 7.2.2.1)
|
|
165
|
+
rails-dom-testing (2.3.0)
|
|
166
|
+
activesupport (>= 5.0.0)
|
|
167
|
+
minitest
|
|
168
|
+
nokogiri (>= 1.6)
|
|
169
|
+
rails-html-sanitizer (1.6.2)
|
|
170
|
+
loofah (~> 2.21)
|
|
171
|
+
nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
|
|
172
|
+
railties (7.2.2.1)
|
|
173
|
+
actionpack (= 7.2.2.1)
|
|
174
|
+
activesupport (= 7.2.2.1)
|
|
175
|
+
irb (~> 1.13)
|
|
176
|
+
rackup (>= 1.0.0)
|
|
177
|
+
rake (>= 12.2)
|
|
178
|
+
thor (~> 1.0, >= 1.2.2)
|
|
179
|
+
zeitwerk (~> 2.6)
|
|
180
|
+
rainbow (3.1.1)
|
|
181
|
+
rake (13.3.0)
|
|
182
|
+
rdoc (6.14.2)
|
|
183
|
+
erb
|
|
184
|
+
psych (>= 4.0.0)
|
|
185
|
+
regexp_parser (2.10.0)
|
|
186
|
+
reline (0.6.1)
|
|
187
|
+
io-console (~> 0.5)
|
|
188
|
+
rubocop (1.75.8)
|
|
189
|
+
json (~> 2.3)
|
|
190
|
+
language_server-protocol (~> 3.17.0.2)
|
|
191
|
+
lint_roller (~> 1.1.0)
|
|
192
|
+
parallel (~> 1.10)
|
|
193
|
+
parser (>= 3.3.0.2)
|
|
194
|
+
rainbow (>= 2.2.2, < 4.0)
|
|
195
|
+
regexp_parser (>= 2.9.3, < 3.0)
|
|
196
|
+
rubocop-ast (>= 1.44.0, < 2.0)
|
|
197
|
+
ruby-progressbar (~> 1.7)
|
|
198
|
+
unicode-display_width (>= 2.4.0, < 4.0)
|
|
199
|
+
rubocop-ast (1.45.1)
|
|
200
|
+
parser (>= 3.3.7.2)
|
|
201
|
+
prism (~> 1.4)
|
|
202
|
+
rubocop-performance (1.25.0)
|
|
203
|
+
lint_roller (~> 1.1)
|
|
204
|
+
rubocop (>= 1.75.0, < 2.0)
|
|
205
|
+
rubocop-ast (>= 1.38.0, < 2.0)
|
|
206
|
+
ruby-progressbar (1.13.0)
|
|
207
|
+
securerandom (0.4.1)
|
|
208
|
+
sqlite3 (2.7.2-x86_64-linux-gnu)
|
|
209
|
+
standard (1.50.0)
|
|
210
|
+
language_server-protocol (~> 3.17.0.2)
|
|
211
|
+
lint_roller (~> 1.0)
|
|
212
|
+
rubocop (~> 1.75.5)
|
|
213
|
+
standard-custom (~> 1.0.0)
|
|
214
|
+
standard-performance (~> 1.8)
|
|
215
|
+
standard-custom (1.0.2)
|
|
216
|
+
lint_roller (~> 1.0)
|
|
217
|
+
rubocop (~> 1.50)
|
|
218
|
+
standard-performance (1.8.0)
|
|
219
|
+
lint_roller (~> 1.1)
|
|
220
|
+
rubocop-performance (~> 1.25.0)
|
|
221
|
+
stringio (3.1.7)
|
|
222
|
+
thor (1.4.0)
|
|
223
|
+
timeout (0.4.3)
|
|
224
|
+
tzinfo (2.0.6)
|
|
225
|
+
concurrent-ruby (~> 1.0)
|
|
226
|
+
unicode-display_width (3.1.4)
|
|
227
|
+
unicode-emoji (~> 4.0, >= 4.0.4)
|
|
228
|
+
unicode-emoji (4.0.4)
|
|
229
|
+
useragent (0.16.11)
|
|
230
|
+
websocket-driver (0.8.0)
|
|
231
|
+
base64
|
|
232
|
+
websocket-extensions (>= 0.1.0)
|
|
233
|
+
websocket-extensions (0.1.5)
|
|
234
|
+
zeitwerk (2.6.18)
|
|
235
|
+
|
|
236
|
+
PLATFORMS
|
|
237
|
+
x86_64-linux
|
|
238
|
+
|
|
239
|
+
DEPENDENCIES
|
|
240
|
+
arerd!
|
|
241
|
+
irb
|
|
242
|
+
minitest
|
|
243
|
+
rake
|
|
244
|
+
sqlite3
|
|
245
|
+
standard
|
|
246
|
+
|
|
247
|
+
BUNDLED WITH
|
|
248
|
+
2.3.27
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Shuhei YOSHIDA
|
|
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
|
|
13
|
+
all 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
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# Arerd
|
|
2
|
+
|
|
3
|
+
**Arerd** is a Ruby gem that extracts Entity-Relationship (ER) information from your ActiveRecord models and generates clear, visual ER diagrams in [Mermaid](https://mermaid-js.github.io/) format.
|
|
4
|
+
|
|
5
|
+
Once integrated into your Rails project, Arerd provides a convenient Rake task (`db:erd:mermaid` or `db:erd:markdown`) that outputs a Mermaid-formatted ER diagram directly to your terminal.
|
|
6
|
+
|
|
7
|
+
You can also automate ER diagram generation in your CI pipeline by outputting the diagram in Markdown format. This ensures your ER diagram documentation is always up to date and easy to maintain.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
Add Arerd to your Rails application's Gemfile:
|
|
12
|
+
|
|
13
|
+
```ruby
|
|
14
|
+
gem "arerd"
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Then install the gem:
|
|
18
|
+
|
|
19
|
+
```shell
|
|
20
|
+
bundle install
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
### Generate a Mermaid ER Diagram
|
|
26
|
+
|
|
27
|
+
Run the following command to create a Mermaid ER diagram:
|
|
28
|
+
|
|
29
|
+
```shell
|
|
30
|
+
bin/rails db:erd:mermaid
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
This will output the diagram in Mermaid's `erDiagram` format to standard output. You can copy and paste the result into the [Mermaid Live Editor](https://mermaid.live/) or any Mermaid-compatible tool to visualize your ER diagram.
|
|
34
|
+
|
|
35
|
+
### Generate a Markdown ER Diagram
|
|
36
|
+
|
|
37
|
+
To output the ER diagram in Markdown format:
|
|
38
|
+
|
|
39
|
+
```shell
|
|
40
|
+
bin/rails db:erd:markdown
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
This command prints the diagram wrapped in triple backticks and tagged as `mermaid`, allowing you to preview it directly in supported Markdown editors or viewers.
|
|
44
|
+
|
|
45
|
+
### Internationalization (I18n) Support
|
|
46
|
+
|
|
47
|
+
Table and column names in diagrams are automatically translated using your Rails application's locale files. Arerd leverages Rails' I18n system to provide localized names for entities and attributes, making diagrams more accessible for international teams.
|
|
48
|
+
|
|
49
|
+
### Notes
|
|
50
|
+
|
|
51
|
+
* **All associations must specify the `inverse_of` option.**
|
|
52
|
+
* Associations using `has_many :through` are ignored.
|
|
53
|
+
* Polymorphic associations (e.g., `belongs_to :taggable, polymorphic: true`) are not supported.
|
|
54
|
+
|
|
55
|
+
### Example Output
|
|
56
|
+
|
|
57
|
+
```mermaid
|
|
58
|
+
erDiagram
|
|
59
|
+
User["User (ユーザー)"] {
|
|
60
|
+
integer id PK "Id"
|
|
61
|
+
string name UK "名前 (indexed)"
|
|
62
|
+
datetime created_at "作成日時"
|
|
63
|
+
datetime updated_at "更新日時"
|
|
64
|
+
}
|
|
65
|
+
Profile["Profile (プロフィール)"] {
|
|
66
|
+
integer id PK "Id"
|
|
67
|
+
integer user_id FK "ユーザーID (indexed)"
|
|
68
|
+
string bio "自己紹介 (nullable)"
|
|
69
|
+
datetime created_at "作成日時"
|
|
70
|
+
datetime updated_at "更新日時"
|
|
71
|
+
}
|
|
72
|
+
Post["Post (投稿)"] {
|
|
73
|
+
integer id PK "Id"
|
|
74
|
+
string title "タイトル"
|
|
75
|
+
text body "本文"
|
|
76
|
+
integer user_id FK "ユーザーID (indexed)"
|
|
77
|
+
datetime created_at "作成日時"
|
|
78
|
+
datetime updated_at "更新日時"
|
|
79
|
+
}
|
|
80
|
+
Notification["Notification (通知)"] {
|
|
81
|
+
integer id PK "Id"
|
|
82
|
+
integer user_id FK "ユーザーID (indexed)"
|
|
83
|
+
integer sender_id FK "送信者ID (nullable, indexed)"
|
|
84
|
+
string message "メッセージ"
|
|
85
|
+
datetime created_at "作成日時"
|
|
86
|
+
datetime updated_at "更新日時"
|
|
87
|
+
}
|
|
88
|
+
Follow["Follow (フォロー)"] {
|
|
89
|
+
integer id PK "Id"
|
|
90
|
+
integer follower_id FK "フォロワーID (indexed)"
|
|
91
|
+
integer followed_id UK "フォロー対象ID (indexed)"
|
|
92
|
+
datetime created_at "作成日時"
|
|
93
|
+
datetime updated_at "更新日時"
|
|
94
|
+
}
|
|
95
|
+
Community["Community (コミュニティ)"] {
|
|
96
|
+
integer id PK "Id"
|
|
97
|
+
string name "名前"
|
|
98
|
+
datetime created_at "作成日時"
|
|
99
|
+
datetime updated_at "更新日時"
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
User ||--o{ Post : "posts / user"
|
|
103
|
+
User ||--o| Profile : "profile / user"
|
|
104
|
+
User ||--o{ Follow : "follows / follower"
|
|
105
|
+
User ||--o{ User : "followees / followers"
|
|
106
|
+
User ||--o{ Follow : "reverse_follows / followee"
|
|
107
|
+
User ||--o{ User : "followers / followees"
|
|
108
|
+
User ||--o{ Notification : "notifications / user"
|
|
109
|
+
User |o--o{ Notification : "sent_notifications / sender"
|
|
110
|
+
Community }o--o{ User : "users / communities"
|
|
111
|
+
```
|
data/Rakefile
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Arerd
|
|
4
|
+
class Association
|
|
5
|
+
attr_reader :left_model, :left_key, :left_association_name,
|
|
6
|
+
:right_model, :right_key, :right_association_name,
|
|
7
|
+
:left_side_multiplicity, :right_side_multiplicity
|
|
8
|
+
|
|
9
|
+
def initialize(
|
|
10
|
+
left_model:,
|
|
11
|
+
left_key:,
|
|
12
|
+
left_association_name:,
|
|
13
|
+
right_model:,
|
|
14
|
+
right_key:,
|
|
15
|
+
right_association_name:,
|
|
16
|
+
left_side_multiplicity:,
|
|
17
|
+
right_side_multiplicity:
|
|
18
|
+
)
|
|
19
|
+
@left_model = left_model
|
|
20
|
+
@left_key = left_key
|
|
21
|
+
@left_association_name = left_association_name
|
|
22
|
+
@right_model = right_model
|
|
23
|
+
@right_key = right_key
|
|
24
|
+
@right_association_name = right_association_name
|
|
25
|
+
@left_side_multiplicity = left_side_multiplicity
|
|
26
|
+
@right_side_multiplicity = right_side_multiplicity
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def self.build(association)
|
|
30
|
+
case association.macro
|
|
31
|
+
when :has_many
|
|
32
|
+
return if association.options[:through]
|
|
33
|
+
|
|
34
|
+
if association.inverse_of.nil?
|
|
35
|
+
warn "Association #{association.name} has no inverse association. Skipping association."
|
|
36
|
+
|
|
37
|
+
return
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
new(
|
|
41
|
+
left_model: association.active_record,
|
|
42
|
+
left_key: association.active_record_primary_key,
|
|
43
|
+
left_association_name: association.name,
|
|
44
|
+
right_model: association.class_name.constantize,
|
|
45
|
+
right_key: association.foreign_key,
|
|
46
|
+
right_association_name: association.inverse_of.name,
|
|
47
|
+
left_side_multiplicity: association.inverse_of.options[:optional] ? :optional_one : :one,
|
|
48
|
+
right_side_multiplicity: :optional_many
|
|
49
|
+
)
|
|
50
|
+
when :has_one
|
|
51
|
+
return if association.options[:through]
|
|
52
|
+
|
|
53
|
+
if association.inverse_of.nil?
|
|
54
|
+
warn "Association #{association.name} has no inverse association. Skipping association."
|
|
55
|
+
|
|
56
|
+
return
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
new(
|
|
60
|
+
left_model: association.active_record,
|
|
61
|
+
left_key: association.active_record_primary_key,
|
|
62
|
+
left_association_name: association.name,
|
|
63
|
+
right_model: association.class_name.constantize,
|
|
64
|
+
right_key: association.foreign_key,
|
|
65
|
+
right_association_name: association.inverse_of.name,
|
|
66
|
+
left_side_multiplicity: association.inverse_of.options[:optional] ? :optional_one : :one,
|
|
67
|
+
right_side_multiplicity: :optional_one
|
|
68
|
+
)
|
|
69
|
+
when :has_and_belongs_to_many
|
|
70
|
+
if association.inverse_of.nil?
|
|
71
|
+
warn "Association #{association.name} has no inverse association. Skipping association."
|
|
72
|
+
|
|
73
|
+
return
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
return if association.active_record.name > association.class_name # Avoid duplicate associations
|
|
77
|
+
|
|
78
|
+
new(
|
|
79
|
+
left_model: association.active_record,
|
|
80
|
+
left_key: association.active_record_primary_key,
|
|
81
|
+
left_association_name: association.name,
|
|
82
|
+
right_model: association.class_name.constantize,
|
|
83
|
+
right_key: association.foreign_key,
|
|
84
|
+
right_association_name: association.inverse_of.name,
|
|
85
|
+
left_side_multiplicity: :optional_many,
|
|
86
|
+
right_side_multiplicity: :optional_many
|
|
87
|
+
)
|
|
88
|
+
when :belongs_to
|
|
89
|
+
if association.options[:polymorphic]
|
|
90
|
+
warn "Polymorphic associations are not supported yet. Skipping association #{association.name}."
|
|
91
|
+
|
|
92
|
+
return
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
if association.inverse_of.nil?
|
|
96
|
+
warn "Association #{association.name} has no inverse association."
|
|
97
|
+
end
|
|
98
|
+
else
|
|
99
|
+
warn "Unknown association type: #{association.macro} for #{association.name} in #{left_model.name}"
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def self.build_associations_from_models(models)
|
|
104
|
+
models.flat_map(&:reflect_on_all_associations).map { |association|
|
|
105
|
+
build(association)
|
|
106
|
+
}.compact
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "erb"
|
|
4
|
+
|
|
5
|
+
module Arerd
|
|
6
|
+
class ErdGenerator
|
|
7
|
+
MERMAID_TEMPLATE_PATH = File.expand_path("./templates/erd.mmd.erb", __dir__)
|
|
8
|
+
MARKDOWN_TEMPLATE_PATH = File.expand_path("./templates/erd.md.erb", __dir__)
|
|
9
|
+
|
|
10
|
+
def self.generate_markdown(models:, associations:)
|
|
11
|
+
mermaid = generate_mermaid(models:, associations:)
|
|
12
|
+
|
|
13
|
+
template = File.read(MARKDOWN_TEMPLATE_PATH)
|
|
14
|
+
ERB.new(template, trim_mode: "-").result_with_hash(mermaid:)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def self.generate_mermaid(models:, associations:)
|
|
18
|
+
template = File.read(MERMAID_TEMPLATE_PATH)
|
|
19
|
+
ERB.new(template, trim_mode: "-").result_with_hash(models:, associations:)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def self.collect_models_and_associations
|
|
23
|
+
Rails.application.eager_load!
|
|
24
|
+
|
|
25
|
+
models = ApplicationRecord.descendants
|
|
26
|
+
|
|
27
|
+
associations = Arerd::Association.build_associations_from_models(models)
|
|
28
|
+
|
|
29
|
+
{models:, associations:}
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "arerd/erd_generator"
|
|
4
|
+
|
|
5
|
+
namespace :db do
|
|
6
|
+
desc "Output ERD as Mermaid to STDOUT"
|
|
7
|
+
namespace :erd do
|
|
8
|
+
task mermaid: :environment do
|
|
9
|
+
models, associations = Arerd::ErdGenerator.collect_models_and_associations.values_at(:models, :associations)
|
|
10
|
+
puts Arerd::ErdGenerator.generate_mermaid(models:, associations:)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
task markdown: :environment do
|
|
14
|
+
models, associations = Arerd::ErdGenerator.collect_models_and_associations.values_at(:models, :associations)
|
|
15
|
+
puts Arerd::ErdGenerator.generate_markdown(models:, associations:)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
erDiagram
|
|
2
|
+
<%- models.each do |model| -%>
|
|
3
|
+
<%- foreign_keys = model.reflect_on_all_associations.map(&:foreign_key).uniq -%>
|
|
4
|
+
<%- indexes = (model.connection.indexes(model.table_name) rescue []) -%>
|
|
5
|
+
<%- unique_columns = (indexes.select(&:unique).flat_map(&:columns) rescue []) -%>
|
|
6
|
+
<%= model.name %>["<%= [model.name, "(#{model.model_name.human})"].compact.join(" ") %>"] {
|
|
7
|
+
<%- model.columns.each do |column| -%>
|
|
8
|
+
<%= [
|
|
9
|
+
column.type,
|
|
10
|
+
column.name,
|
|
11
|
+
if model.primary_key == column.name
|
|
12
|
+
"PK"
|
|
13
|
+
elsif foreign_keys.include?(column.name)
|
|
14
|
+
"FK"
|
|
15
|
+
elsif unique_columns.include?(column.name)
|
|
16
|
+
"UK"
|
|
17
|
+
end,
|
|
18
|
+
"\"#{
|
|
19
|
+
model.human_attribute_name(column.name)
|
|
20
|
+
}#{
|
|
21
|
+
[
|
|
22
|
+
column.null ? "nullable" : nil,
|
|
23
|
+
(indexes.any? { |idx| idx.columns.include?(column.name) } ? "indexed" : nil),
|
|
24
|
+
].compact.join(", ").then { |c| c == "" ? "" : " (#{c})" }
|
|
25
|
+
}\"",
|
|
26
|
+
].compact.join(" ") %>
|
|
27
|
+
<%- end -%>
|
|
28
|
+
}
|
|
29
|
+
<%- end -%>
|
|
30
|
+
|
|
31
|
+
<%- associations.each do |association| -%>
|
|
32
|
+
<%=
|
|
33
|
+
association.left_model.name
|
|
34
|
+
%> <%=
|
|
35
|
+
{ one: "||", many: "}o", optional_one: "|o", optional_many: "}o" }[association.left_side_multiplicity]
|
|
36
|
+
%>--<%=
|
|
37
|
+
{ one: "||", many: "o{", optional_one: "o|", optional_many: "o{" }[association.right_side_multiplicity]
|
|
38
|
+
%> <%=
|
|
39
|
+
association.right_model.name
|
|
40
|
+
%> : "<%=
|
|
41
|
+
"#{association.left_association_name} / #{association.right_association_name}"
|
|
42
|
+
%>"
|
|
43
|
+
<%- end -%>
|
data/lib/arerd.rb
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "arerd/association"
|
|
4
|
+
require_relative "arerd/erd_generator"
|
|
5
|
+
require_relative "arerd/version"
|
|
6
|
+
|
|
7
|
+
module Arerd
|
|
8
|
+
class Error < StandardError; end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
require "arerd/railtie" if defined?(Rails::Railtie)
|
metadata
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: arerd
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Shuhei YOSHIDA
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: exe
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2025-07-21 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: rails
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '6.0'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ">="
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '6.0'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: sqlite3
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - ">="
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '0'
|
|
34
|
+
type: :development
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - ">="
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '0'
|
|
41
|
+
description: Provides a Rake task (db:erd) that extracts Entity-Relationship information
|
|
42
|
+
from ActiveRecord and outputs an E-R diagram in Mermaid notation.
|
|
43
|
+
email:
|
|
44
|
+
- contact@yantene.net
|
|
45
|
+
executables: []
|
|
46
|
+
extensions: []
|
|
47
|
+
extra_rdoc_files: []
|
|
48
|
+
files:
|
|
49
|
+
- ".devcontainer/Dockerfile"
|
|
50
|
+
- ".devcontainer/compose.yaml"
|
|
51
|
+
- ".devcontainer/devcontainer.json"
|
|
52
|
+
- ".devcontainer/fish/conf.d/abbrs.fish"
|
|
53
|
+
- ".devcontainer/fish/functions/git.fish"
|
|
54
|
+
- ".devcontainer/post_create.fish"
|
|
55
|
+
- ".editorconfig"
|
|
56
|
+
- ".standard.yml"
|
|
57
|
+
- ".tool-versions"
|
|
58
|
+
- Gemfile
|
|
59
|
+
- Gemfile.lock
|
|
60
|
+
- LICENSE
|
|
61
|
+
- README.md
|
|
62
|
+
- Rakefile
|
|
63
|
+
- lib/arerd.rb
|
|
64
|
+
- lib/arerd/association.rb
|
|
65
|
+
- lib/arerd/erd_generator.rb
|
|
66
|
+
- lib/arerd/railtie.rb
|
|
67
|
+
- lib/arerd/tasks/erd.rake
|
|
68
|
+
- lib/arerd/templates/erd.md.erb
|
|
69
|
+
- lib/arerd/templates/erd.mmd.erb
|
|
70
|
+
- lib/arerd/version.rb
|
|
71
|
+
homepage: https://github.com/yantene/arerd
|
|
72
|
+
licenses: []
|
|
73
|
+
metadata:
|
|
74
|
+
allowed_push_host: https://rubygems.org
|
|
75
|
+
homepage_uri: https://github.com/yantene/arerd
|
|
76
|
+
source_code_uri: https://github.com/yantene/arerd
|
|
77
|
+
post_install_message:
|
|
78
|
+
rdoc_options: []
|
|
79
|
+
require_paths:
|
|
80
|
+
- lib
|
|
81
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
82
|
+
requirements:
|
|
83
|
+
- - ">="
|
|
84
|
+
- !ruby/object:Gem::Version
|
|
85
|
+
version: 3.1.0
|
|
86
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
87
|
+
requirements:
|
|
88
|
+
- - ">="
|
|
89
|
+
- !ruby/object:Gem::Version
|
|
90
|
+
version: '0'
|
|
91
|
+
requirements: []
|
|
92
|
+
rubygems_version: 3.3.27
|
|
93
|
+
signing_key:
|
|
94
|
+
specification_version: 4
|
|
95
|
+
summary: Rails gem for generating ERD from ActiveRecord in Mermaid format
|
|
96
|
+
test_files: []
|