rails-erd 2.0.2 → 2.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 +4 -4
- data/CHANGES.md +335 -0
- data/LICENSE.md +19 -0
- data/README.md +193 -5
- data/Rakefile +1 -1
- data/lib/rails_erd/cli.rb +11 -9
- data/lib/rails_erd/diagram/mermaid.rb +64 -0
- data/lib/rails_erd/diagram/tbls.rb +168 -0
- data/lib/rails_erd/diagram.rb +71 -9
- data/lib/rails_erd/domain.rb +38 -4
- data/lib/rails_erd/version.rb +1 -1
- data/test/unit/diagram_test.rb +109 -1
- data/test/unit/domain_test.rb +104 -0
- data/test/unit/mermaid_test.rb +124 -2
- data/test/unit/tbls_test.rb +152 -0
- metadata +7 -2
data/test/unit/domain_test.rb
CHANGED
|
@@ -151,6 +151,23 @@ class DomainTest < ActiveSupport::TestCase
|
|
|
151
151
|
assert_equal ["Many", "More"], [relationship.source.name, relationship.destination.name]
|
|
152
152
|
end
|
|
153
153
|
|
|
154
|
+
test "relationships should be deterministic regardless of model order" do
|
|
155
|
+
create_model "Author"
|
|
156
|
+
create_model "Book", :author => :references do
|
|
157
|
+
belongs_to :author
|
|
158
|
+
has_many :reviews
|
|
159
|
+
end
|
|
160
|
+
create_model "Review", :book => :references do
|
|
161
|
+
belongs_to :book
|
|
162
|
+
end
|
|
163
|
+
pairs = lambda do |models|
|
|
164
|
+
Domain.new(models).relationships.collect { |r| [r.source.name, r.destination.name] }
|
|
165
|
+
end
|
|
166
|
+
models = [Author, Book, Review]
|
|
167
|
+
# Order and source/destination direction must not depend on the input model order.
|
|
168
|
+
assert_equal pairs.call(models), pairs.call(models.reverse)
|
|
169
|
+
end
|
|
170
|
+
|
|
154
171
|
# Specialization processing ================================================
|
|
155
172
|
test "specializations should return empty array for empty domain" do
|
|
156
173
|
assert_equal [], Domain.generate.specializations
|
|
@@ -301,4 +318,91 @@ class DomainTest < ActiveSupport::TestCase
|
|
|
301
318
|
end
|
|
302
319
|
assert_equal "", output
|
|
303
320
|
end
|
|
321
|
+
|
|
322
|
+
# Pattern matching for exclude/only ==========================================
|
|
323
|
+
test "matches_pattern? should match exact string" do
|
|
324
|
+
domain = Domain.new([])
|
|
325
|
+
assert domain.send(:matches_pattern?, "Foo", "Foo")
|
|
326
|
+
refute domain.send(:matches_pattern?, "Foo", "Bar")
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
test "matches_pattern? should match glob pattern with asterisk" do
|
|
330
|
+
domain = Domain.new([])
|
|
331
|
+
assert domain.send(:matches_pattern?, "SolidQueue::*", "SolidQueue::Job")
|
|
332
|
+
assert domain.send(:matches_pattern?, "SolidQueue::*", "SolidQueue::Process")
|
|
333
|
+
refute domain.send(:matches_pattern?, "SolidQueue::*", "SolidCache::Entry")
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
test "matches_pattern? should match glob pattern without namespace separator" do
|
|
337
|
+
domain = Domain.new([])
|
|
338
|
+
assert domain.send(:matches_pattern?, "ActiveStorage*", "ActiveStorageBlob")
|
|
339
|
+
assert domain.send(:matches_pattern?, "ActiveStorage*", "ActiveStorage::Blob")
|
|
340
|
+
refute domain.send(:matches_pattern?, "ActiveStorage*", "ActionMailbox::InboundEmail")
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
test "matches_pattern? should match regex pattern" do
|
|
344
|
+
domain = Domain.new([])
|
|
345
|
+
assert domain.send(:matches_pattern?, "/^Active/", "ActiveRecord")
|
|
346
|
+
assert domain.send(:matches_pattern?, "/^Active/", "ActiveStorage::Blob")
|
|
347
|
+
refute domain.send(:matches_pattern?, "/^Active/", "SolidQueue::Job")
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
test "matches_pattern? should match regex pattern with flags" do
|
|
351
|
+
domain = Domain.new([])
|
|
352
|
+
assert domain.send(:matches_pattern?, "/queue/i", "SolidQueue::Job")
|
|
353
|
+
refute domain.send(:matches_pattern?, "/queue/", "SolidQueue::Job")
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
test "excluded_model? should match glob patterns" do
|
|
357
|
+
create_module_model "SolidQueue::Job"
|
|
358
|
+
create_module_model "SolidQueue::Process"
|
|
359
|
+
create_model "User"
|
|
360
|
+
|
|
361
|
+
domain = Domain.generate(:exclude => ["SolidQueue::*"])
|
|
362
|
+
|
|
363
|
+
# excluded_model? returns true for matching patterns
|
|
364
|
+
assert domain.send(:excluded_model?, SolidQueue::Job)
|
|
365
|
+
assert domain.send(:excluded_model?, SolidQueue::Process)
|
|
366
|
+
refute domain.send(:excluded_model?, User)
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
test "excluded_model? should match regex patterns" do
|
|
370
|
+
create_module_model "SolidQueue::Job"
|
|
371
|
+
create_model "User"
|
|
372
|
+
|
|
373
|
+
domain = Domain.generate(:exclude => ["/^Solid/"])
|
|
374
|
+
assert domain.send(:excluded_model?, SolidQueue::Job)
|
|
375
|
+
refute domain.send(:excluded_model?, User)
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
test "excluded_model? should still match exact names for backward compatibility" do
|
|
379
|
+
create_model "Foo"
|
|
380
|
+
create_model "Bar"
|
|
381
|
+
|
|
382
|
+
domain = Domain.generate(:exclude => ["Foo"])
|
|
383
|
+
assert domain.send(:excluded_model?, Foo)
|
|
384
|
+
refute domain.send(:excluded_model?, Bar)
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
test "excluded_association? should match glob patterns for source model" do
|
|
388
|
+
create_module_model "SolidQueue::Job" do
|
|
389
|
+
has_many :executions
|
|
390
|
+
end
|
|
391
|
+
create_model "User"
|
|
392
|
+
|
|
393
|
+
domain = Domain.generate(:exclude => ["SolidQueue::*"], :warn => false)
|
|
394
|
+
association = SolidQueue::Job.reflect_on_association(:executions)
|
|
395
|
+
assert domain.send(:excluded_association?, association)
|
|
396
|
+
end
|
|
397
|
+
|
|
398
|
+
test "excluded_association? should match glob patterns for target model" do
|
|
399
|
+
create_module_model "SolidQueue::Execution"
|
|
400
|
+
create_model "Task" do
|
|
401
|
+
has_many :executions, :class_name => "SolidQueue::Execution"
|
|
402
|
+
end
|
|
403
|
+
|
|
404
|
+
domain = Domain.generate(:exclude => ["SolidQueue::*"], :warn => false)
|
|
405
|
+
association = Task.reflect_on_association(:executions)
|
|
406
|
+
assert domain.send(:excluded_association?, association)
|
|
407
|
+
end
|
|
304
408
|
end
|
data/test/unit/mermaid_test.rb
CHANGED
|
@@ -230,9 +230,9 @@ class MermaidTest < ActiveSupport::TestCase
|
|
|
230
230
|
"\tclass `Bar`",
|
|
231
231
|
"\tclass `Baz`",
|
|
232
232
|
"\tclass `Foo`",
|
|
233
|
-
"\t`Foo` --> `Baz`",
|
|
234
233
|
"\t`Foo` --> `Bar`",
|
|
235
|
-
"\t`Bar` ..> `Baz`"
|
|
234
|
+
"\t`Bar` ..> `Baz`",
|
|
235
|
+
"\t`Foo` --> `Baz`"
|
|
236
236
|
]
|
|
237
237
|
|
|
238
238
|
assert_equal expected, diagram.graph.uniq
|
|
@@ -437,4 +437,126 @@ class MermaidTest < ActiveSupport::TestCase
|
|
|
437
437
|
# Verify the specialization relationship line also quotes it
|
|
438
438
|
assert result.match?(/Vehicle.*--.*"Transport::Car"/), "Specialization relationship should quote namespaced entity"
|
|
439
439
|
end
|
|
440
|
+
|
|
441
|
+
# Namespace clustering tests (Issue #479) =====================================
|
|
442
|
+
|
|
443
|
+
test "classDiagram with cluster should group entities by namespace" do
|
|
444
|
+
create_model "Post"
|
|
445
|
+
create_module_model "Admin::Author", :post => :references do
|
|
446
|
+
belongs_to :post
|
|
447
|
+
end
|
|
448
|
+
create_module_model "Admin::Role"
|
|
449
|
+
|
|
450
|
+
result = diagram(:cluster => true).graph.join("\n")
|
|
451
|
+
|
|
452
|
+
assert result.include?("classDiagram")
|
|
453
|
+
# Should have namespace block for Admin
|
|
454
|
+
assert result.include?("namespace Admin {"), "Should have namespace block for Admin"
|
|
455
|
+
# Author and Role should be inside the Admin namespace
|
|
456
|
+
assert result.match?(/namespace Admin \{[^}]*class `Author`/m), "Author should be inside Admin namespace"
|
|
457
|
+
assert result.match?(/namespace Admin \{[^}]*class `Role`/m), "Role should be inside Admin namespace"
|
|
458
|
+
end
|
|
459
|
+
|
|
460
|
+
test "classDiagram with cluster should place entities without namespace outside blocks" do
|
|
461
|
+
create_model "Post"
|
|
462
|
+
create_module_model "Admin::Author", :post => :references do
|
|
463
|
+
belongs_to :post
|
|
464
|
+
end
|
|
465
|
+
|
|
466
|
+
result = diagram(:cluster => true).graph.join("\n")
|
|
467
|
+
|
|
468
|
+
# Post should appear before any namespace block
|
|
469
|
+
post_index = result.index("class `Post`")
|
|
470
|
+
namespace_index = result.index("namespace Admin {")
|
|
471
|
+
assert post_index < namespace_index, "Entities without namespace should appear before namespace blocks"
|
|
472
|
+
end
|
|
473
|
+
|
|
474
|
+
test "classDiagram with cluster should handle nested namespaces" do
|
|
475
|
+
create_model "Post"
|
|
476
|
+
create_module_model "Admin::Users::Role", :post => :references do
|
|
477
|
+
belongs_to :post
|
|
478
|
+
end
|
|
479
|
+
|
|
480
|
+
result = diagram(:cluster => true).graph.join("\n")
|
|
481
|
+
|
|
482
|
+
# Nested namespace should use dot notation (Admin.Users)
|
|
483
|
+
assert result.include?("namespace Admin.Users {"), "Should convert :: to . in namespace names"
|
|
484
|
+
assert result.match?(/namespace Admin\.Users \{[^}]*class `Role`/m), "Role should be inside Admin.Users namespace"
|
|
485
|
+
end
|
|
486
|
+
|
|
487
|
+
test "classDiagram with cluster should render relationships outside namespace blocks" do
|
|
488
|
+
create_model "Post"
|
|
489
|
+
create_module_model "Admin::Author", :post => :references do
|
|
490
|
+
belongs_to :post
|
|
491
|
+
end
|
|
492
|
+
|
|
493
|
+
result = diagram(:cluster => true).graph.join("\n")
|
|
494
|
+
|
|
495
|
+
# Relationship should appear after the namespace block closes
|
|
496
|
+
namespace_close_index = result.index("}")
|
|
497
|
+
relationship_index = result.index("`Post` --> `Admin::Author`")
|
|
498
|
+
assert relationship_index > namespace_close_index, "Relationships should appear after namespace blocks"
|
|
499
|
+
end
|
|
500
|
+
|
|
501
|
+
test "erDiagram with cluster should emit warning and render flat" do
|
|
502
|
+
create_model "Post"
|
|
503
|
+
create_module_model "Admin::Author", :post => :references do
|
|
504
|
+
belongs_to :post
|
|
505
|
+
end
|
|
506
|
+
|
|
507
|
+
test_diagram = nil
|
|
508
|
+
|
|
509
|
+
warning_output = collect_stdout do
|
|
510
|
+
domain = Domain.generate
|
|
511
|
+
test_diagram = Diagram::Mermaid.new(domain, :cluster => true, :mermaid_style => :erdiagram, :warn => true)
|
|
512
|
+
test_diagram.generate
|
|
513
|
+
end
|
|
514
|
+
|
|
515
|
+
result = test_diagram.graph.join("\n")
|
|
516
|
+
|
|
517
|
+
# Should emit warning about clustering not supported
|
|
518
|
+
assert warning_output.include?("Clustering is not supported"), "Should warn about clustering not supported in erDiagram"
|
|
519
|
+
# Should still render the diagram (flat, no namespace blocks)
|
|
520
|
+
assert result.include?("erDiagram")
|
|
521
|
+
refute result.include?("namespace"), "erDiagram should not have namespace blocks"
|
|
522
|
+
end
|
|
523
|
+
|
|
524
|
+
test "classDiagram with cluster false should not group entities" do
|
|
525
|
+
create_model "Post"
|
|
526
|
+
create_module_model "Admin::Author", :post => :references do
|
|
527
|
+
belongs_to :post
|
|
528
|
+
end
|
|
529
|
+
|
|
530
|
+
result = diagram(:cluster => false).graph.join("\n")
|
|
531
|
+
|
|
532
|
+
assert result.include?("classDiagram")
|
|
533
|
+
# Should NOT have namespace blocks
|
|
534
|
+
refute result.include?("namespace"), "cluster: false should not create namespace blocks"
|
|
535
|
+
# Should use full entity names
|
|
536
|
+
assert result.include?("class `Admin::Author`"), "Should use full entity name without clustering"
|
|
537
|
+
end
|
|
538
|
+
|
|
539
|
+
test "classDiagram with cluster and inheritance should emit entities before specializations" do
|
|
540
|
+
create_model "Vehicle", :type => :string
|
|
541
|
+
create_module_model "Transport::Car", Vehicle
|
|
542
|
+
|
|
543
|
+
result = diagram(:cluster => true, :inheritance => true).graph.join("\n")
|
|
544
|
+
|
|
545
|
+
# Entity class definitions should appear BEFORE specialization lines
|
|
546
|
+
vehicle_class_index = result.index("class `Vehicle`")
|
|
547
|
+
polymorphic_index = result.index("<<polymorphic>>")
|
|
548
|
+
inheritance_index = result.index("<|--")
|
|
549
|
+
|
|
550
|
+
assert vehicle_class_index, "Should have Vehicle class definition.\nGot:\n#{result}"
|
|
551
|
+
assert polymorphic_index, "Should have polymorphic marker.\nGot:\n#{result}"
|
|
552
|
+
assert inheritance_index, "Should have inheritance relationship.\nGot:\n#{result}"
|
|
553
|
+
|
|
554
|
+
# The key assertion: class definitions must come BEFORE specializations
|
|
555
|
+
assert vehicle_class_index < polymorphic_index,
|
|
556
|
+
"Class definitions should appear before polymorphic markers.\n" \
|
|
557
|
+
"Got:\n#{result}"
|
|
558
|
+
assert vehicle_class_index < inheritance_index,
|
|
559
|
+
"Class definitions should appear before inheritance relationships.\n" \
|
|
560
|
+
"Got:\n#{result}"
|
|
561
|
+
end
|
|
440
562
|
end
|
|
@@ -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.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Rolf Timmermans
|
|
@@ -118,6 +118,8 @@ executables:
|
|
|
118
118
|
extensions: []
|
|
119
119
|
extra_rdoc_files: []
|
|
120
120
|
files:
|
|
121
|
+
- CHANGES.md
|
|
122
|
+
- LICENSE.md
|
|
121
123
|
- README.md
|
|
122
124
|
- Rakefile
|
|
123
125
|
- bin/erd
|
|
@@ -131,6 +133,7 @@ files:
|
|
|
131
133
|
- lib/rails_erd/diagram.rb
|
|
132
134
|
- lib/rails_erd/diagram/graphviz.rb
|
|
133
135
|
- lib/rails_erd/diagram/mermaid.rb
|
|
136
|
+
- lib/rails_erd/diagram/tbls.rb
|
|
134
137
|
- lib/rails_erd/diagram/templates/node.html.erb
|
|
135
138
|
- lib/rails_erd/diagram/templates/node.record.erb
|
|
136
139
|
- lib/rails_erd/domain.rb
|
|
@@ -160,6 +163,7 @@ files:
|
|
|
160
163
|
- test/unit/rake_task_test.rb
|
|
161
164
|
- test/unit/relationship_test.rb
|
|
162
165
|
- test/unit/specialization_test.rb
|
|
166
|
+
- test/unit/tbls_test.rb
|
|
163
167
|
homepage: https://github.com/voormedia/rails-erd
|
|
164
168
|
licenses:
|
|
165
169
|
- MIT
|
|
@@ -178,7 +182,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
178
182
|
- !ruby/object:Gem::Version
|
|
179
183
|
version: '0'
|
|
180
184
|
requirements: []
|
|
181
|
-
rubygems_version: 4.0.
|
|
185
|
+
rubygems_version: 4.0.10
|
|
182
186
|
specification_version: 4
|
|
183
187
|
summary: Entity-relationship diagram for your Rails models.
|
|
184
188
|
test_files:
|
|
@@ -199,3 +203,4 @@ test_files:
|
|
|
199
203
|
- test/unit/rake_task_test.rb
|
|
200
204
|
- test/unit/relationship_test.rb
|
|
201
205
|
- test/unit/specialization_test.rb
|
|
206
|
+
- test/unit/tbls_test.rb
|