rails-erd 2.0.2 → 2.2.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/CHANGES.md +351 -0
- data/LICENSE.md +19 -0
- data/README.md +232 -12
- data/Rakefile +1 -1
- data/lib/rails_erd/cli.rb +21 -9
- data/lib/rails_erd/config.rb +6 -1
- data/lib/rails_erd/diagram/mermaid.rb +86 -21
- data/lib/rails_erd/diagram/tbls.rb +168 -0
- data/lib/rails_erd/diagram.rb +134 -17
- data/lib/rails_erd/domain.rb +38 -4
- data/lib/rails_erd/version.rb +1 -1
- data/lib/rails_erd.rb +2 -0
- data/test/test_helper.rb +1 -1
- data/test/unit/config_test.rb +11 -0
- data/test/unit/diagram_test.rb +191 -1
- data/test/unit/domain_test.rb +104 -0
- data/test/unit/mermaid_test.rb +142 -3
- data/test/unit/tbls_test.rb +152 -0
- metadata +8 -17
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require File.expand_path("../test_helper", File.dirname(__FILE__))
|
|
4
|
+
require "rails_erd/diagram/tbls"
|
|
5
|
+
require "json"
|
|
6
|
+
|
|
7
|
+
class TblsTest < ActiveSupport::TestCase
|
|
8
|
+
def setup
|
|
9
|
+
RailsERD.options.warn = false
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def diagram(options = {})
|
|
13
|
+
Diagram::Tbls.new(Domain.generate(options), options).tap(&:generate)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def payload(options = {})
|
|
17
|
+
diagram(options).send(:schema_payload)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def table(payload, name)
|
|
21
|
+
payload[:tables].find { |t| t[:name] == name }
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def constraint(table, type)
|
|
25
|
+
table[:constraints].find { |c| c[:type] == type }
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# File output ==============================================================
|
|
29
|
+
test "filename has json extension" do
|
|
30
|
+
create_simple_domain
|
|
31
|
+
result = Diagram::Tbls.create
|
|
32
|
+
assert result.end_with?(".json"), "Expected .json, got #{result}"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
test "written file is valid JSON parseable by tbls schema shape" do
|
|
36
|
+
create_simple_domain
|
|
37
|
+
file = Diagram::Tbls.create
|
|
38
|
+
parsed = JSON.parse(File.read(file))
|
|
39
|
+
|
|
40
|
+
assert parsed.key?("tables")
|
|
41
|
+
assert parsed["tables"].is_a?(Array)
|
|
42
|
+
assert parsed["tables"].all? { |t| t.key?("name") && t.key?("columns") }
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Tables ===================================================================
|
|
46
|
+
test "emits one table per concrete entity using db table name" do
|
|
47
|
+
create_simple_domain
|
|
48
|
+
p = payload
|
|
49
|
+
|
|
50
|
+
names = p[:tables].map { |t| t[:name] }.sort
|
|
51
|
+
assert_equal %w[bars beers], names
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
test "columns carry name, type, nullable; default and comment only when set" do
|
|
55
|
+
create_model "Widget", name: :string, qty: :integer do
|
|
56
|
+
belongs_to :gizmo, optional: true
|
|
57
|
+
end
|
|
58
|
+
create_model "Gizmo"
|
|
59
|
+
|
|
60
|
+
widgets = table(payload, "widgets")
|
|
61
|
+
name_col = widgets[:columns].find { |c| c[:name] == "name" }
|
|
62
|
+
|
|
63
|
+
assert name_col[:type].start_with?("varchar"), "Expected varchar type, got #{name_col[:type]}"
|
|
64
|
+
assert_equal true, name_col[:nullable]
|
|
65
|
+
# tbls schema rejects null comments/defaults — they must be omitted, not nulled.
|
|
66
|
+
refute_includes name_col.keys, :default
|
|
67
|
+
refute_includes name_col.keys, :comment
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Primary keys =============================================================
|
|
71
|
+
test "every table gets a PRIMARY KEY constraint" do
|
|
72
|
+
create_simple_domain
|
|
73
|
+
|
|
74
|
+
table(payload, "beers")[:constraints].tap do |constraints|
|
|
75
|
+
pk = constraints.find { |c| c[:type] == "PRIMARY KEY" }
|
|
76
|
+
assert pk, "expected a PRIMARY KEY constraint"
|
|
77
|
+
assert_equal ["id"], pk[:columns]
|
|
78
|
+
assert_equal "beers_pkey", pk[:name]
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Foreign keys =============================================================
|
|
83
|
+
test "belongs_to becomes a FOREIGN KEY constraint on the owning table" do
|
|
84
|
+
create_simple_domain # Beer belongs_to :bar
|
|
85
|
+
|
|
86
|
+
beers = table(payload, "beers")
|
|
87
|
+
fk = constraint(beers, "FOREIGN KEY")
|
|
88
|
+
|
|
89
|
+
assert fk, "expected a FOREIGN KEY constraint on beers"
|
|
90
|
+
assert_equal ["bar_id"], fk[:columns]
|
|
91
|
+
assert_equal "bars", fk[:referenced_table]
|
|
92
|
+
assert_equal ["id"], fk[:referenced_columns]
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
test "FK def string is parseable by tbls parser" do
|
|
96
|
+
create_simple_domain
|
|
97
|
+
fk = constraint(table(payload, "beers"), "FOREIGN KEY")
|
|
98
|
+
|
|
99
|
+
assert_equal "FOREIGN KEY (bar_id) REFERENCES bars(id)", fk[:def]
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
test "has_many side does not get an FK; only the belongs_to side does" do
|
|
103
|
+
create_one_to_many_assoc_domain # One has_many :many; Many belongs_to :one
|
|
104
|
+
|
|
105
|
+
ones = table(payload, "ones")
|
|
106
|
+
manies = table(payload, "manies")
|
|
107
|
+
|
|
108
|
+
assert_nil constraint(ones, "FOREIGN KEY"), "owner side must not have an FK"
|
|
109
|
+
assert constraint(manies, "FOREIGN KEY"), "belongs_to side must have an FK"
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
test "polymorphic associations are skipped (no synthetic FK)" do
|
|
113
|
+
create_polymorphic_generalization # Cannon belongs_to :defensible, polymorphic; Galleon has_many :cannons, as: :defensible
|
|
114
|
+
|
|
115
|
+
cannons = table(payload(polymorphism: true), "cannons")
|
|
116
|
+
assert_nil constraint(cannons, "FOREIGN KEY"),
|
|
117
|
+
"polymorphic belongs_to has no concrete target — must not emit FK"
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
test "indirect (has_many :through) relationships do not produce extra constraints" do
|
|
121
|
+
create_model "Author" do
|
|
122
|
+
has_many :authorships
|
|
123
|
+
has_many :books, through: :authorships
|
|
124
|
+
end
|
|
125
|
+
create_model "Book" do
|
|
126
|
+
has_many :authorships
|
|
127
|
+
has_many :authors, through: :authorships
|
|
128
|
+
end
|
|
129
|
+
create_model "Authorship", author: :references, book: :references do
|
|
130
|
+
belongs_to :author
|
|
131
|
+
belongs_to :book
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
authorships = table(payload, "authorships")
|
|
135
|
+
fk_targets = authorships[:constraints]
|
|
136
|
+
.select { |c| c[:type] == "FOREIGN KEY" }
|
|
137
|
+
.map { |c| c[:referenced_table] }
|
|
138
|
+
.sort
|
|
139
|
+
|
|
140
|
+
# Exactly two FKs — author + book — not duplicated by the indirect relationship.
|
|
141
|
+
assert_equal %w[authors books], fk_targets
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# Schema-level payload =====================================================
|
|
145
|
+
test "top-level payload has a name and a tables array" do
|
|
146
|
+
create_simple_domain
|
|
147
|
+
p = payload
|
|
148
|
+
|
|
149
|
+
assert p[:name].is_a?(String) && !p[:name].empty?
|
|
150
|
+
assert p[:tables].is_a?(Array)
|
|
151
|
+
end
|
|
152
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rails-erd
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.0
|
|
4
|
+
version: 2.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Rolf Timmermans
|
|
@@ -52,20 +52,6 @@ dependencies:
|
|
|
52
52
|
- - "~>"
|
|
53
53
|
- !ruby/object:Gem::Version
|
|
54
54
|
version: 0.2.0
|
|
55
|
-
- !ruby/object:Gem::Dependency
|
|
56
|
-
name: ostruct
|
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
|
58
|
-
requirements:
|
|
59
|
-
- - ">="
|
|
60
|
-
- !ruby/object:Gem::Version
|
|
61
|
-
version: '0'
|
|
62
|
-
type: :runtime
|
|
63
|
-
prerelease: false
|
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
-
requirements:
|
|
66
|
-
- - ">="
|
|
67
|
-
- !ruby/object:Gem::Version
|
|
68
|
-
version: '0'
|
|
69
55
|
- !ruby/object:Gem::Dependency
|
|
70
56
|
name: ruby-graphviz
|
|
71
57
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -95,7 +81,7 @@ dependencies:
|
|
|
95
81
|
- !ruby/object:Gem::Version
|
|
96
82
|
version: '0'
|
|
97
83
|
- !ruby/object:Gem::Dependency
|
|
98
|
-
name: pry-
|
|
84
|
+
name: pry-byebug
|
|
99
85
|
requirement: !ruby/object:Gem::Requirement
|
|
100
86
|
requirements:
|
|
101
87
|
- - ">="
|
|
@@ -118,6 +104,8 @@ executables:
|
|
|
118
104
|
extensions: []
|
|
119
105
|
extra_rdoc_files: []
|
|
120
106
|
files:
|
|
107
|
+
- CHANGES.md
|
|
108
|
+
- LICENSE.md
|
|
121
109
|
- README.md
|
|
122
110
|
- Rakefile
|
|
123
111
|
- bin/erd
|
|
@@ -131,6 +119,7 @@ files:
|
|
|
131
119
|
- lib/rails_erd/diagram.rb
|
|
132
120
|
- lib/rails_erd/diagram/graphviz.rb
|
|
133
121
|
- lib/rails_erd/diagram/mermaid.rb
|
|
122
|
+
- lib/rails_erd/diagram/tbls.rb
|
|
134
123
|
- lib/rails_erd/diagram/templates/node.html.erb
|
|
135
124
|
- lib/rails_erd/diagram/templates/node.record.erb
|
|
136
125
|
- lib/rails_erd/domain.rb
|
|
@@ -160,6 +149,7 @@ files:
|
|
|
160
149
|
- test/unit/rake_task_test.rb
|
|
161
150
|
- test/unit/relationship_test.rb
|
|
162
151
|
- test/unit/specialization_test.rb
|
|
152
|
+
- test/unit/tbls_test.rb
|
|
163
153
|
homepage: https://github.com/voormedia/rails-erd
|
|
164
154
|
licenses:
|
|
165
155
|
- MIT
|
|
@@ -178,7 +168,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
178
168
|
- !ruby/object:Gem::Version
|
|
179
169
|
version: '0'
|
|
180
170
|
requirements: []
|
|
181
|
-
rubygems_version: 4.0.
|
|
171
|
+
rubygems_version: 4.0.10
|
|
182
172
|
specification_version: 4
|
|
183
173
|
summary: Entity-relationship diagram for your Rails models.
|
|
184
174
|
test_files:
|
|
@@ -199,3 +189,4 @@ test_files:
|
|
|
199
189
|
- test/unit/rake_task_test.rb
|
|
200
190
|
- test/unit/relationship_test.rb
|
|
201
191
|
- test/unit/specialization_test.rb
|
|
192
|
+
- test/unit/tbls_test.rb
|