rails-erd 2.0.0 → 2.0.2
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 +27 -0
- data/lib/rails_erd/cli.rb +5 -0
- data/lib/rails_erd/config.rb +5 -0
- data/lib/rails_erd/diagram/mermaid.rb +17 -5
- data/lib/rails_erd/diagram.rb +68 -0
- data/lib/rails_erd/domain.rb +6 -4
- data/lib/rails_erd/version.rb +1 -1
- data/lib/rails_erd.rb +1 -0
- data/test/test_helper.rb +12 -5
- data/test/unit/config_test.rb +11 -0
- data/test/unit/diagram_test.rb +30 -0
- data/test/unit/domain_test.rb +13 -0
- data/test/unit/graphviz_test.rb +2 -9
- data/test/unit/mermaid_test.rb +62 -10
- data/test/unit/rake_task_test.rb +0 -4
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4d47dd705b783be29e06d39d8bea92d6c5ac8019fba658e0279287b2197938d3
|
|
4
|
+
data.tar.gz: 1417646a7d2731ad2964e9cbac400cb3b2325f765ba1db610b44f28ffe03f699
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: cde6a9b96a0d3e5c03ccbed1c3e04e37d27efb3a37d2a09e1a5ff298cbb4e9ec3148065c551efe8da349315d7601420b3b98bccf049f4c83b782863a6a13f495
|
|
7
|
+
data.tar.gz: 5d53400f939b341b02becb90a5b4b413d4556a0fc36ef856d4c831d13ccc343ab0fd48772c8245ebedcdcdfee028068d51b1a09027ae809614c568bf19506505
|
data/README.md
CHANGED
|
@@ -70,6 +70,7 @@ sort: true
|
|
|
70
70
|
warn: true
|
|
71
71
|
title: true
|
|
72
72
|
exclude: null
|
|
73
|
+
exclude_attributes: null
|
|
73
74
|
only: null
|
|
74
75
|
only_recursion_depth: null
|
|
75
76
|
prepend_primary: false
|
|
@@ -77,6 +78,32 @@ cluster: false
|
|
|
77
78
|
splines: spline
|
|
78
79
|
```
|
|
79
80
|
|
|
81
|
+
### Hiding attributes for specific models
|
|
82
|
+
|
|
83
|
+
While `attributes: false` hides attributes for *every* model, `exclude_attributes`
|
|
84
|
+
lets you hide attributes for individual models without affecting the others. Map a
|
|
85
|
+
model name to `true` to hide all of its attributes, or to a list of attribute names
|
|
86
|
+
to hide only those:
|
|
87
|
+
|
|
88
|
+
```yaml
|
|
89
|
+
exclude_attributes:
|
|
90
|
+
BigTable: true # hide all attributes for BigTable
|
|
91
|
+
User: # hide only these attributes for User
|
|
92
|
+
- password_digest
|
|
93
|
+
- remember_token
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
From the command line, pass a comma separated list where each entry is either
|
|
97
|
+
`Model` (hide all of its attributes) or `Model.attribute` (hide a single one):
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
# rake task (bare key=value)
|
|
101
|
+
bundle exec rake erd exclude_attributes="BigTable,User.password_digest,User.remember_token"
|
|
102
|
+
|
|
103
|
+
# erd binary (flags require the -- prefix)
|
|
104
|
+
bundle exec erd --exclude_attributes="BigTable,User.password_digest,User.remember_token"
|
|
105
|
+
```
|
|
106
|
+
|
|
80
107
|
You can also customize fonts (useful if the defaults aren't available on your system):
|
|
81
108
|
|
|
82
109
|
```yaml
|
data/lib/rails_erd/cli.rb
CHANGED
|
@@ -72,6 +72,11 @@ Choice.options do
|
|
|
72
72
|
desc "Filter to exclude listed models in diagram."
|
|
73
73
|
end
|
|
74
74
|
|
|
75
|
+
option :exclude_attributes do
|
|
76
|
+
long "--exclude_attributes=MODEL[.ATTRIBUTE],..."
|
|
77
|
+
desc "Hide attributes per model. Use Model to hide all its attributes or Model.attribute to hide one, e.g. BigTable,User.password_digest."
|
|
78
|
+
end
|
|
79
|
+
|
|
75
80
|
option :sort do
|
|
76
81
|
long "--sort=BOOLEAN"
|
|
77
82
|
desc "Sort attribute list alphabetically"
|
data/lib/rails_erd/config.rb
CHANGED
|
@@ -73,6 +73,11 @@ module RailsERD
|
|
|
73
73
|
when :only, :exclude
|
|
74
74
|
Array(value).join(",").split(",").map { |v| v.strip }
|
|
75
75
|
|
|
76
|
+
# nil | { <string> => true | [<string>] }
|
|
77
|
+
when :exclude_attributes
|
|
78
|
+
require "rails_erd/diagram"
|
|
79
|
+
RailsERD::Diagram.normalize_exclude_attributes(value)
|
|
80
|
+
|
|
76
81
|
# true | false
|
|
77
82
|
when :disconnected, :indirect, :inheritance, :markup, :polymorphism,
|
|
78
83
|
:warn, :cluster
|
|
@@ -20,7 +20,8 @@ module RailsERD
|
|
|
20
20
|
each_entity do |entity, attributes|
|
|
21
21
|
if er_diagram?
|
|
22
22
|
# Build entity block as a single string to avoid uniq issues with closing braces
|
|
23
|
-
|
|
23
|
+
quoted_entity = quote_entity_name(entity)
|
|
24
|
+
entity_lines = ["\t#{quoted_entity} {"]
|
|
24
25
|
attributes.each do |attr|
|
|
25
26
|
key_marker = attribute_key_marker(attr)
|
|
26
27
|
entity_lines << "\t\t#{attr.type} #{attr.name}#{key_marker}"
|
|
@@ -39,7 +40,7 @@ module RailsERD
|
|
|
39
40
|
from, to = specialization.generalized, specialization.specialized
|
|
40
41
|
if er_diagram?
|
|
41
42
|
# erDiagram doesn't have a direct polymorphic notation, use inheritance-like
|
|
42
|
-
graph << "\t#{from.name} ||--o{ #{to.name} : \"specializes\""
|
|
43
|
+
graph << "\t#{quote_entity_name(from.name)} ||--o{ #{quote_entity_name(to.name)} : \"specializes\""
|
|
43
44
|
else
|
|
44
45
|
graph << "\t<<polymorphic>> `#{specialization.generalized}`"
|
|
45
46
|
graph << "\t #{from.name} <|-- #{to.name}"
|
|
@@ -51,14 +52,14 @@ module RailsERD
|
|
|
51
52
|
next unless from && to
|
|
52
53
|
|
|
53
54
|
if er_diagram?
|
|
54
|
-
graph << "\t#{from.name} #{er_relation_notation(relationship)} #{to.name} : \"\""
|
|
55
|
+
graph << "\t#{quote_entity_name(from.name)} #{er_relation_notation(relationship)} #{quote_entity_name(to.name)} : \"\""
|
|
55
56
|
|
|
56
57
|
from.children.each do |child|
|
|
57
|
-
graph << "\t#{child.name} #{er_relation_notation(relationship)} #{to.name} : \"\""
|
|
58
|
+
graph << "\t#{quote_entity_name(child.name)} #{er_relation_notation(relationship)} #{quote_entity_name(to.name)} : \"\""
|
|
58
59
|
end
|
|
59
60
|
|
|
60
61
|
to.children.each do |child|
|
|
61
|
-
graph << "\t#{from.name} #{er_relation_notation(relationship)} #{child.name} : \"\""
|
|
62
|
+
graph << "\t#{quote_entity_name(from.name)} #{er_relation_notation(relationship)} #{quote_entity_name(child.name)} : \"\""
|
|
62
63
|
end
|
|
63
64
|
else
|
|
64
65
|
graph << "\t`#{from.name}` #{relation_arrow(relationship)} `#{to.name}`"
|
|
@@ -92,6 +93,17 @@ module RailsERD
|
|
|
92
93
|
options[:mermaid_style] == :erdiagram || options[:mermaid_style] == :er
|
|
93
94
|
end
|
|
94
95
|
|
|
96
|
+
# Quote entity names that contain special characters (like :: for namespaces)
|
|
97
|
+
# Mermaid erDiagram requires double quotes for names with special characters
|
|
98
|
+
def quote_entity_name(name)
|
|
99
|
+
name = name.to_s
|
|
100
|
+
if name.include?("::")
|
|
101
|
+
%("#{name}")
|
|
102
|
+
else
|
|
103
|
+
name
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
95
107
|
# erDiagram relationship notation using crow's foot
|
|
96
108
|
# Format: left_cardinality--right_cardinality
|
|
97
109
|
# Cardinality markers:
|
data/lib/rails_erd/diagram.rb
CHANGED
|
@@ -55,6 +55,14 @@ module RailsERD
|
|
|
55
55
|
# attributes:: Selects which attributes to display. Can be any combination of
|
|
56
56
|
# +:content+, +:primary_keys+, +:foreign_keys+, +:timestamps+, or
|
|
57
57
|
# +:inheritance+.
|
|
58
|
+
# exclude_attributes:: Hides attributes on a per-model basis, without affecting
|
|
59
|
+
# other models. Accepts a hash that maps model names to
|
|
60
|
+
# either +true+ (hide all attributes for that model) or a
|
|
61
|
+
# list of attribute names to hide. From the command line it
|
|
62
|
+
# can also be given as a comma separated string where each
|
|
63
|
+
# entry is either +Model+ (hide all attributes) or
|
|
64
|
+
# +Model.attribute+ (hide a single attribute), for example
|
|
65
|
+
# <tt>exclude_attributes="BigTable,User.password_digest"</tt>.
|
|
58
66
|
# disconnected:: Set to +false+ to exclude entities that are not connected to other
|
|
59
67
|
# entities. Defaults to +false+.
|
|
60
68
|
# indirect:: Set to +false+ to exclude relationships that are indirect.
|
|
@@ -76,6 +84,38 @@ module RailsERD
|
|
|
76
84
|
new(Domain.generate(options), options).create
|
|
77
85
|
end
|
|
78
86
|
|
|
87
|
+
# Canonicalises the +exclude_attributes+ option into a hash that maps
|
|
88
|
+
# model names (as strings) to either +true+ (hide all attributes) or an
|
|
89
|
+
# array of attribute names to hide. Accepts several input shapes:
|
|
90
|
+
#
|
|
91
|
+
# * a hash, e.g. <tt>{ "BigTable" => true, "User" => ["password_digest"] }</tt>
|
|
92
|
+
# (values may be +true+/+"all"+ to hide every attribute, +false+/+nil+
|
|
93
|
+
# to hide none, or a comma separated string / array of names);
|
|
94
|
+
# * a string or array of <tt>Model</tt> / <tt>Model.attribute</tt>
|
|
95
|
+
# entries, e.g. <tt>"BigTable,User.password_digest"</tt>.
|
|
96
|
+
def normalize_exclude_attributes(value)
|
|
97
|
+
return {} if value.nil? || value == false
|
|
98
|
+
|
|
99
|
+
if value.is_a?(Hash)
|
|
100
|
+
value.each_with_object({}) do |(model, attrs), result|
|
|
101
|
+
result[model.to_s] = normalize_exclude_attribute_value(attrs)
|
|
102
|
+
end
|
|
103
|
+
else
|
|
104
|
+
entries = Array(value).flat_map { |entry| entry.to_s.split(",") }
|
|
105
|
+
entries.each_with_object({}) do |entry, result|
|
|
106
|
+
model, attribute = entry.split(".", 2)
|
|
107
|
+
model = model.to_s.strip
|
|
108
|
+
next if model.empty?
|
|
109
|
+
|
|
110
|
+
if attribute.nil?
|
|
111
|
+
result[model] = true
|
|
112
|
+
elsif result[model] != true
|
|
113
|
+
(result[model] ||= []) << attribute.strip
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
79
119
|
protected
|
|
80
120
|
|
|
81
121
|
def setup(&block)
|
|
@@ -103,6 +143,16 @@ module RailsERD
|
|
|
103
143
|
def callbacks
|
|
104
144
|
@callbacks ||= Hash.new { proc {} }
|
|
105
145
|
end
|
|
146
|
+
|
|
147
|
+
def normalize_exclude_attribute_value(attrs)
|
|
148
|
+
case attrs
|
|
149
|
+
when true then true
|
|
150
|
+
when false, nil then []
|
|
151
|
+
when "all", :all, "true" then true
|
|
152
|
+
else
|
|
153
|
+
Array(attrs).flat_map { |attr| attr.to_s.split(",") }.map(&:strip).reject(&:empty?)
|
|
154
|
+
end
|
|
155
|
+
end
|
|
106
156
|
end
|
|
107
157
|
|
|
108
158
|
# The options that are used to create this diagram.
|
|
@@ -205,13 +255,31 @@ module RailsERD
|
|
|
205
255
|
end
|
|
206
256
|
|
|
207
257
|
def filtered_attributes(entity)
|
|
258
|
+
excluded = excluded_attributes_for(entity)
|
|
259
|
+
return [] if excluded == :all
|
|
260
|
+
|
|
208
261
|
entity.attributes.reject { |attribute|
|
|
262
|
+
# Hide attributes excluded for this specific model.
|
|
263
|
+
excluded.include?(attribute.name) or
|
|
209
264
|
# Select attributes that satisfy the conditions in the :attributes option.
|
|
210
265
|
!options.attributes or entity.specialized? or
|
|
211
266
|
[*options.attributes].none? { |type| attribute.send(:"#{type.to_s.chomp('s')}?") }
|
|
212
267
|
}
|
|
213
268
|
end
|
|
214
269
|
|
|
270
|
+
# Returns the attribute exclusions configured for the given entity through
|
|
271
|
+
# the +exclude_attributes+ option. Returns +:all+ when every attribute
|
|
272
|
+
# should be hidden, or an array of attribute names otherwise.
|
|
273
|
+
def excluded_attributes_for(entity)
|
|
274
|
+
spec = normalized_exclude_attributes[entity.name]
|
|
275
|
+
return :all if spec == true
|
|
276
|
+
Array(spec)
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
def normalized_exclude_attributes
|
|
280
|
+
@normalized_exclude_attributes ||= self.class.normalize_exclude_attributes(options.exclude_attributes)
|
|
281
|
+
end
|
|
282
|
+
|
|
215
283
|
def warn(message)
|
|
216
284
|
puts "Warning: #{message}" if options.warn
|
|
217
285
|
end
|
data/lib/rails_erd/domain.rb
CHANGED
|
@@ -210,11 +210,13 @@ module RailsERD
|
|
|
210
210
|
excluded_names = [options.exclude].flatten.map(&:to_sym)
|
|
211
211
|
|
|
212
212
|
# Suppress warning if either the source model or target model is excluded
|
|
213
|
-
excluded_names.include?(association.active_record.name.to_sym)
|
|
214
|
-
|
|
213
|
+
return true if excluded_names.include?(association.active_record.name.to_sym)
|
|
214
|
+
|
|
215
|
+
target_name = association.options[:polymorphic] ? association.class_name : association.klass.name
|
|
216
|
+
target_name && excluded_names.include?(target_name.to_sym)
|
|
215
217
|
rescue NameError
|
|
216
|
-
# If we can't determine the target class,
|
|
217
|
-
|
|
218
|
+
# If we can't determine the target class, the source model was already checked above
|
|
219
|
+
false
|
|
218
220
|
end
|
|
219
221
|
|
|
220
222
|
def check_polymorphic_association_validity(association)
|
data/lib/rails_erd/version.rb
CHANGED
data/lib/rails_erd.rb
CHANGED
data/test/test_helper.rb
CHANGED
|
@@ -2,6 +2,7 @@ require "rubygems"
|
|
|
2
2
|
require "bundler/setup"
|
|
3
3
|
require 'pry'
|
|
4
4
|
require 'pry-nav'
|
|
5
|
+
require 'tmpdir'
|
|
5
6
|
|
|
6
7
|
require "active_record"
|
|
7
8
|
|
|
@@ -60,14 +61,17 @@ class ActiveSupport::TestCase
|
|
|
60
61
|
superklass = args.first.kind_of?(Class) ? args.shift : ActiveRecord::Base
|
|
61
62
|
|
|
62
63
|
names = full_name.split('::')
|
|
64
|
+
name = names.pop # Remove and store the model name
|
|
63
65
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
+
# Create namespace modules (all parts except the model name)
|
|
67
|
+
parent_module = names.inject(Object) do |parent, child|
|
|
68
|
+
if parent.const_defined?(child.to_sym, false)
|
|
69
|
+
parent.const_get(child.to_sym)
|
|
70
|
+
else
|
|
71
|
+
parent.const_set(child.to_sym, Module.new)
|
|
72
|
+
end
|
|
66
73
|
end
|
|
67
74
|
|
|
68
|
-
parent_module ||= Object
|
|
69
|
-
name = names.last
|
|
70
|
-
|
|
71
75
|
columns = args.first || {}
|
|
72
76
|
klass = parent_module.const_set name.to_sym, Class.new(superklass)
|
|
73
77
|
konstant = parent_module.const_get(name.to_sym)
|
|
@@ -173,6 +177,9 @@ class ActiveSupport::TestCase
|
|
|
173
177
|
File.expand_path("../../examples/erdconfig.not_exists", __FILE__)
|
|
174
178
|
|
|
175
179
|
RailsERD.options = RailsERD.default_options.merge(Config.load)
|
|
180
|
+
|
|
181
|
+
# Use temp directory for test output to avoid polluting the project root
|
|
182
|
+
RailsERD.options.filename = File.join(Dir.tmpdir, "rails_erd_test_erd")
|
|
176
183
|
end
|
|
177
184
|
|
|
178
185
|
def name_to_object_symbol_pairs(name)
|
data/test/unit/config_test.rb
CHANGED
|
@@ -122,6 +122,17 @@ class ConfigTest < ActiveSupport::TestCase
|
|
|
122
122
|
assert_equal [:content, :primary_keys], normalize_value(:attributes, ["content", "primary_keys"])
|
|
123
123
|
end
|
|
124
124
|
|
|
125
|
+
test "normalize_value should canonicalize a hash when key is :exclude_attributes." do
|
|
126
|
+
value = { "BigTable" => true, "User" => ["password_digest"] }
|
|
127
|
+
expected = { "BigTable" => true, "User" => ["password_digest"] }
|
|
128
|
+
assert_equal expected, normalize_value(:exclude_attributes, value)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
test "normalize_value should parse a string when key is :exclude_attributes." do
|
|
132
|
+
expected = { "BigTable" => true, "User" => ["password_digest"] }
|
|
133
|
+
assert_equal expected, normalize_value(:exclude_attributes, "BigTable,User.password_digest")
|
|
134
|
+
end
|
|
135
|
+
|
|
125
136
|
test "normalize_value should return hash with symbol keys when key is :fonts and value is a hash." do
|
|
126
137
|
fonts_value = { "normal" => "Arial", "bold" => "Arial Bold", "italic" => "Arial Italic" }
|
|
127
138
|
expected = {:normal => "Arial", :bold => "Arial Bold", :italic => "Arial Italic"}
|
data/test/unit/diagram_test.rb
CHANGED
|
@@ -358,4 +358,34 @@ class DiagramTest < ActiveSupport::TestCase
|
|
|
358
358
|
Object.const_set :Whisky, Class.new(Beverage)
|
|
359
359
|
assert_equal [], retrieve_attribute_lists(:inheritance => true)[Whisky].map(&:name)
|
|
360
360
|
end
|
|
361
|
+
|
|
362
|
+
test "generate should hide all attributes for a model excluded with true" do
|
|
363
|
+
create_model "Book", :title => :string, :pages => :integer
|
|
364
|
+
create_model "Author", :name => :string
|
|
365
|
+
attribute_lists = retrieve_attribute_lists(:exclude_attributes => { "Book" => true })
|
|
366
|
+
assert_equal [], attribute_lists[Book].map(&:name)
|
|
367
|
+
assert_equal %w{name}, attribute_lists[Author].map(&:name)
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
test "generate should hide only listed attributes for a model" do
|
|
371
|
+
create_model "Book", :title => :string, :subtitle => :string, :pages => :integer
|
|
372
|
+
attribute_lists = retrieve_attribute_lists(:exclude_attributes => { "Book" => ["subtitle"] })
|
|
373
|
+
assert_equal %w{pages title}, attribute_lists[Book].map(&:name).sort
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
test "generate should hide attributes for the listed model only" do
|
|
377
|
+
create_model "Book", :title => :string
|
|
378
|
+
create_model "Author", :name => :string
|
|
379
|
+
attribute_lists = retrieve_attribute_lists(:exclude_attributes => { "Book" => ["title"] })
|
|
380
|
+
assert_equal [], attribute_lists[Book].map(&:name)
|
|
381
|
+
assert_equal %w{name}, attribute_lists[Author].map(&:name)
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
test "generate should accept exclude_attributes as a string" do
|
|
385
|
+
create_model "Book", :title => :string, :subtitle => :string
|
|
386
|
+
create_model "Author", :name => :string
|
|
387
|
+
attribute_lists = retrieve_attribute_lists(:exclude_attributes => "Book.subtitle,Author")
|
|
388
|
+
assert_equal %w{title}, attribute_lists[Book].map(&:name)
|
|
389
|
+
assert_equal [], attribute_lists[Author].map(&:name)
|
|
390
|
+
end
|
|
361
391
|
end
|
data/test/unit/domain_test.rb
CHANGED
|
@@ -255,6 +255,19 @@ class DomainTest < ActiveSupport::TestCase
|
|
|
255
255
|
assert_match(/polymorphic interface FooBar does not exist/, output)
|
|
256
256
|
end
|
|
257
257
|
|
|
258
|
+
test "relationships should not crash when exclude is present and a polymorphic association's interface does not exist" do
|
|
259
|
+
create_model "Foo" do
|
|
260
|
+
has_many :bars, :as => :foo
|
|
261
|
+
end
|
|
262
|
+
create_model "Bar", :foobar => :references do
|
|
263
|
+
belongs_to :foo_bar, :polymorphic => true
|
|
264
|
+
end
|
|
265
|
+
output = collect_stdout do
|
|
266
|
+
Domain.generate(:exclude => ["Qux"]).relationships
|
|
267
|
+
end
|
|
268
|
+
assert_match(/polymorphic interface FooBar does not exist/, output)
|
|
269
|
+
end
|
|
270
|
+
|
|
258
271
|
test "relationships should not warn when a bad association is encountered if warnings are disabled" do
|
|
259
272
|
create_model "Foo" do
|
|
260
273
|
has_many :flabs
|
data/test/unit/graphviz_test.rb
CHANGED
|
@@ -7,10 +7,6 @@ class GraphvizTest < ActiveSupport::TestCase
|
|
|
7
7
|
RailsERD.options.warn = false
|
|
8
8
|
end
|
|
9
9
|
|
|
10
|
-
def teardown
|
|
11
|
-
FileUtils.rm Dir["erd*.*"] rescue nil
|
|
12
|
-
end
|
|
13
|
-
|
|
14
10
|
def diagram(options = {})
|
|
15
11
|
@diagram ||= Diagram::Graphviz.new(Domain.generate(options), options).tap do |diagram|
|
|
16
12
|
diagram.generate
|
|
@@ -52,11 +48,8 @@ class GraphvizTest < ActiveSupport::TestCase
|
|
|
52
48
|
# Diagram properties =======================================================
|
|
53
49
|
test "file name should depend on file type" do
|
|
54
50
|
create_simple_domain
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
ensure
|
|
58
|
-
FileUtils.rm "erd.svg" rescue nil
|
|
59
|
-
end
|
|
51
|
+
result = Diagram::Graphviz.create(:filetype => :svg)
|
|
52
|
+
assert result.end_with?(".svg"), "Expected filename to end with .svg, got: #{result}"
|
|
60
53
|
end
|
|
61
54
|
|
|
62
55
|
test "rank direction should be tb for horizontal orientation" do
|
data/test/unit/mermaid_test.rb
CHANGED
|
@@ -8,10 +8,6 @@ class MermaidTest < ActiveSupport::TestCase
|
|
|
8
8
|
RailsERD.options.mermaid_style = :classdiagram
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
-
def teardown
|
|
12
|
-
FileUtils.rm Dir["erd*.*"] rescue nil
|
|
13
|
-
end
|
|
14
|
-
|
|
15
11
|
def diagram(options = {})
|
|
16
12
|
@diagram ||= Diagram::Mermaid.new(Domain.generate(options), options).tap do |diagram|
|
|
17
13
|
diagram.generate
|
|
@@ -27,13 +23,10 @@ class MermaidTest < ActiveSupport::TestCase
|
|
|
27
23
|
end
|
|
28
24
|
|
|
29
25
|
# Diagram properties =======================================================
|
|
30
|
-
test "file name should
|
|
26
|
+
test "file name should have mmd extension" do
|
|
31
27
|
create_simple_domain
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
ensure
|
|
35
|
-
FileUtils.rm "erd.mmd" rescue nil
|
|
36
|
-
end
|
|
28
|
+
result = Diagram::Mermaid.create
|
|
29
|
+
assert result.end_with?(".mmd"), "Expected filename to end with .mmd, got: #{result}"
|
|
37
30
|
end
|
|
38
31
|
|
|
39
32
|
test "direction should be top to bottom by default" do
|
|
@@ -385,4 +378,63 @@ class MermaidTest < ActiveSupport::TestCase
|
|
|
385
378
|
|
|
386
379
|
assert result.include?("erDiagram")
|
|
387
380
|
end
|
|
381
|
+
|
|
382
|
+
# Namespace tests (Issue #450) ================================================
|
|
383
|
+
|
|
384
|
+
test "classDiagram should handle namespaced model names" do
|
|
385
|
+
create_model "Post"
|
|
386
|
+
create_module_model "Admin::Author", :post => :references do
|
|
387
|
+
belongs_to :post
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
result = diagram.graph
|
|
391
|
+
|
|
392
|
+
assert result.include?("classDiagram")
|
|
393
|
+
# Backticks should properly escape the namespace
|
|
394
|
+
assert result.any? { |line| line.include?("`Admin::Author`") }, "Should wrap namespaced entity in backticks"
|
|
395
|
+
assert result.any? { |line| line.include?("`Post`") && line.include?("`Admin::Author`") }, "Relationship should use backticks for both entities"
|
|
396
|
+
end
|
|
397
|
+
|
|
398
|
+
test "erDiagram should wrap namespaced model names in double quotes" do
|
|
399
|
+
create_model "Post"
|
|
400
|
+
create_module_model "Admin::Author", :post => :references do
|
|
401
|
+
belongs_to :post
|
|
402
|
+
end
|
|
403
|
+
|
|
404
|
+
result = diagram(:mermaid_style => :erdiagram).graph.join("\n")
|
|
405
|
+
|
|
406
|
+
assert result.include?("erDiagram")
|
|
407
|
+
# Double quotes are required for entity names containing ::
|
|
408
|
+
assert result.include?('"Admin::Author"'), "Should wrap namespaced entity in double quotes, got: #{result}"
|
|
409
|
+
# Relationship line should also quote the namespaced entity (order may vary)
|
|
410
|
+
assert result.match?(/("Admin::Author".*--.*Post|Post.*--.*"Admin::Author")/), "Relationship should quote namespaced entity"
|
|
411
|
+
end
|
|
412
|
+
|
|
413
|
+
test "erDiagram should handle multiple namespaced models in relationships" do
|
|
414
|
+
create_module_model "Admin::User" do
|
|
415
|
+
has_many :posts, class_name: "Blog::Post"
|
|
416
|
+
end
|
|
417
|
+
create_module_model "Blog::Post", :admin_user => :references do
|
|
418
|
+
belongs_to :admin_user, class_name: "Admin::User"
|
|
419
|
+
end
|
|
420
|
+
|
|
421
|
+
result = diagram(:mermaid_style => :erdiagram).graph.join("\n")
|
|
422
|
+
|
|
423
|
+
assert result.include?('"Admin::User"'), "Should quote Admin::User"
|
|
424
|
+
assert result.include?('"Blog::Post"'), "Should quote Blog::Post"
|
|
425
|
+
end
|
|
426
|
+
|
|
427
|
+
test "erDiagram should quote namespaced entities in specialization relationships" do
|
|
428
|
+
create_model "Vehicle", :type => :string
|
|
429
|
+
# Create a namespaced subclass (STI)
|
|
430
|
+
create_module_model "Transport::Car", Vehicle
|
|
431
|
+
|
|
432
|
+
# STI requires inheritance: true option
|
|
433
|
+
result = diagram(:mermaid_style => :erdiagram, :inheritance => true).graph.join("\n")
|
|
434
|
+
|
|
435
|
+
# The specialization relationship should quote the namespaced entity
|
|
436
|
+
assert result.include?('"Transport::Car"'), "Should quote namespaced specialized entity"
|
|
437
|
+
# Verify the specialization relationship line also quotes it
|
|
438
|
+
assert result.match?(/Vehicle.*--.*"Transport::Car"/), "Specialization relationship should quote namespaced entity"
|
|
439
|
+
end
|
|
388
440
|
end
|
data/test/unit/rake_task_test.rb
CHANGED
|
@@ -14,10 +14,6 @@ class RakeTaskTest < ActiveSupport::TestCase
|
|
|
14
14
|
Rake.application.options.silent = true
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
-
def teardown
|
|
18
|
-
FileUtils.rm "erd.dot" rescue nil
|
|
19
|
-
end
|
|
20
|
-
|
|
21
17
|
define_method :create_app do
|
|
22
18
|
Object::Quux = Module.new
|
|
23
19
|
Object::Quux::Application = Class.new
|
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.0.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Rolf Timmermans
|
|
@@ -178,7 +178,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
178
178
|
- !ruby/object:Gem::Version
|
|
179
179
|
version: '0'
|
|
180
180
|
requirements: []
|
|
181
|
-
rubygems_version: 4.0.
|
|
181
|
+
rubygems_version: 4.0.13
|
|
182
182
|
specification_version: 4
|
|
183
183
|
summary: Entity-relationship diagram for your Rails models.
|
|
184
184
|
test_files:
|