rails-erd 1.4.7 → 1.6.1
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 +19 -11
- data/lib/generators/erd/templates/auto_generate_diagram.rake +1 -1
- data/lib/rails_erd.rb +13 -1
- data/lib/rails_erd/cli.rb +36 -3
- data/lib/rails_erd/config.rb +10 -4
- data/lib/rails_erd/diagram.rb +30 -3
- data/lib/rails_erd/diagram/graphviz.rb +32 -5
- data/lib/rails_erd/diagram/templates/node.html.erb +2 -2
- data/lib/rails_erd/diagram/templates/node.record.erb +2 -2
- data/lib/rails_erd/domain.rb +19 -3
- data/lib/rails_erd/domain/entity.rb +4 -0
- data/lib/rails_erd/domain/relationship.rb +1 -2
- data/lib/rails_erd/domain/specialization.rb +1 -1
- data/lib/rails_erd/tasks.rake +22 -2
- data/lib/rails_erd/version.rb +1 -1
- data/test/test_helper.rb +85 -10
- data/test/unit/attribute_test.rb +20 -19
- data/test/unit/config_test.rb +23 -0
- data/test/unit/diagram_test.rb +9 -0
- data/test/unit/domain_test.rb +27 -10
- data/test/unit/entity_test.rb +26 -0
- data/test/unit/graphviz_test.rb +39 -19
- data/test/unit/rake_task_test.rb +22 -2
- metadata +42 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 16bfef1b79087e3017df7723bef8cf286df5a812
|
4
|
+
data.tar.gz: 7162f90b6dce224a0ec89693e7d307fbc15a9b45
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 34e2582512bfe6313f626d4de969eebc328415c97456b1cd151df3e19d75e55cc0b65a9ded7ba0c363d3708072d001266d96657e2a8669ee6a7c4ca7b0cc3ee0
|
7
|
+
data.tar.gz: 16ddf4ba4c7d640a4c139905fe6f238f76c70cf20712c467ebb9bf9075f0ee6603c0c532114b86bd0e66f7d275f5ae0200802e91c8978a469c6e39208dd3c709
|
data/README.md
CHANGED
@@ -2,11 +2,11 @@ Rails ERD - Generate Entity-Relationship Diagrams for Rails applications
|
|
2
2
|
========================================================================
|
3
3
|
[](https://travis-ci.org/voormedia/rails-erd) [](https://codeclimate.com/github/voormedia/rails-erd)
|
4
4
|
|
5
|
-
[Rails ERD](
|
5
|
+
[Rails ERD](https://voormedia.github.io/rails-erd/) is a gem that allows you to easily generate a diagram based on your application's Active Record models. The diagram gives an overview of how your models are related. Having a diagram that describes your models is perfect documentation for your application.
|
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 3.0-
|
9
|
+
Rails ERD was created specifically for Rails and works on versions 3.0-5.0. It uses Active Record's built-in reflection capabilities to figure out how your models are associated.
|
10
10
|
|
11
11
|
|
12
12
|
Preview
|
@@ -14,34 +14,34 @@ Preview
|
|
14
14
|
|
15
15
|
Here's an example entity-relationship diagram that was generated by Rails ERD:
|
16
16
|
|
17
|
-

|
18
18
|
|
19
|
-
Browse the [gallery](
|
19
|
+
Browse the [gallery](https://voormedia.github.io/rails-erd/gallery.html) for more example diagrams.
|
20
20
|
|
21
21
|
|
22
22
|
Requirements
|
23
23
|
---------------
|
24
24
|
|
25
25
|
* Ruby 1.9.3+
|
26
|
-
* ActiveRecord 3.x
|
26
|
+
* ActiveRecord 3.x - 5.0.x
|
27
27
|
|
28
28
|
Getting started
|
29
29
|
---------------
|
30
30
|
|
31
|
-
See the [installation instructions](
|
31
|
+
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
32
|
|
33
|
-
* Install Graphviz 2.22+ ([how?](
|
33
|
+
* Install Graphviz 2.22+ ([how?](https://voormedia.github.io/rails-erd/install.html)). On macOS with Homebrew run `brew install graphviz`.
|
34
34
|
|
35
|
-
* Add <tt>gem
|
35
|
+
* Add <tt>gem 'rails-erd', group: :development</tt> to your application's Gemfile
|
36
36
|
|
37
37
|
* Run <tt>bundle exec erd</tt>
|
38
38
|
|
39
39
|
### Configuration
|
40
40
|
|
41
41
|
|
42
|
-
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`). The format of the file is as follows (shown here with the default settings used if no `.erdconfig` is found). More information on [customization options](
|
42
|
+
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`). The format of the file is as follows (shown here with the default settings used if no `.erdconfig` is found). More information on [customization options](https://voormedia.github.io/rails-erd/customise.html) can be found in Rails ERD's project documentation.
|
43
43
|
|
44
|
-
```
|
44
|
+
```yaml
|
45
45
|
attributes:
|
46
46
|
- content
|
47
47
|
- foreign_key
|
@@ -60,14 +60,22 @@ warn: true
|
|
60
60
|
title: sample title
|
61
61
|
exclude: null
|
62
62
|
only: null
|
63
|
+
only_recursion_depth: null
|
63
64
|
prepend_primary: false
|
65
|
+
cluster: false
|
66
|
+
splines: spline
|
64
67
|
```
|
65
68
|
|
69
|
+
Auto generation
|
70
|
+
---------------
|
71
|
+
|
72
|
+
* Run <tt>bundle exec rails g erd:install</tt>
|
73
|
+
* Run <tt>bundle exec rails db:migrate</tt>, then the diagram is generated
|
66
74
|
|
67
75
|
Learn more
|
68
76
|
----------
|
69
77
|
|
70
|
-
More information can be found on [Rails ERD's project homepage](
|
78
|
+
More information can be found on [Rails ERD's project homepage](https://voormedia.github.io/rails-erd/).
|
71
79
|
|
72
80
|
If you wish to extend or customise Rails ERD, take a look at the [API documentation](http://rubydoc.info/github/voormedia/rails-erd/frames).
|
73
81
|
|
data/lib/rails_erd.rb
CHANGED
@@ -51,9 +51,21 @@ module RailsERD
|
|
51
51
|
:title, true,
|
52
52
|
:exclude, nil,
|
53
53
|
:only, nil,
|
54
|
-
:
|
54
|
+
:only_recursion_depth, nil,
|
55
|
+
:prepend_primary, false,
|
56
|
+
:cluster, false,
|
55
57
|
]
|
56
58
|
end
|
59
|
+
|
60
|
+
def loaded_tasks=(val); @loaded_tasks = val; end
|
61
|
+
def loaded_tasks; return @loaded_tasks; end
|
62
|
+
|
63
|
+
def load_tasks
|
64
|
+
return if(self.loaded_tasks)
|
65
|
+
self.loaded_tasks = true
|
66
|
+
|
67
|
+
Dir[File.join(File.dirname(__FILE__), 'tasks', '**/*.rake')].each { |rake| load rake }
|
68
|
+
end
|
57
69
|
end
|
58
70
|
|
59
71
|
module Inspectable # @private :nodoc:
|
data/lib/rails_erd/cli.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require "rails_erd"
|
1
2
|
require "choice"
|
2
3
|
|
3
4
|
Choice.options do
|
@@ -49,6 +50,11 @@ Choice.options do
|
|
49
50
|
desc "Filter to only include listed models in diagram."
|
50
51
|
end
|
51
52
|
|
53
|
+
option :only_recursion_depth do
|
54
|
+
long "--only_recursion_depth=INTEGER"
|
55
|
+
desc "Recurses into relations specified by --only upto a depth N."
|
56
|
+
end
|
57
|
+
|
52
58
|
option :exclude do
|
53
59
|
long "--exclude"
|
54
60
|
desc "Filter to exclude listed models in diagram."
|
@@ -64,6 +70,16 @@ Choice.options do
|
|
64
70
|
desc "Ensure primary key is at start of attribute list"
|
65
71
|
end
|
66
72
|
|
73
|
+
option :cluster do
|
74
|
+
long "--cluster"
|
75
|
+
desc "Display models in subgraphs based on their namespace."
|
76
|
+
end
|
77
|
+
|
78
|
+
option :splines do
|
79
|
+
long "--splines=SPLINE_TYPE"
|
80
|
+
desc "Control how edges are represented. See http://www.graphviz.org/doc/info/attrs.html#d:splines for values."
|
81
|
+
end
|
82
|
+
|
67
83
|
separator ""
|
68
84
|
separator "Output options:"
|
69
85
|
|
@@ -110,6 +126,12 @@ Choice.options do
|
|
110
126
|
exit
|
111
127
|
end
|
112
128
|
end
|
129
|
+
|
130
|
+
option :config_file do
|
131
|
+
short "-c"
|
132
|
+
long "--config=FILENAME"
|
133
|
+
desc "Configuration file to use"
|
134
|
+
end
|
113
135
|
end
|
114
136
|
|
115
137
|
module RailsERD
|
@@ -128,6 +150,9 @@ module RailsERD
|
|
128
150
|
opts[key.to_sym] = value
|
129
151
|
end
|
130
152
|
end
|
153
|
+
if options[:config_file] && options[:config_file] != ''
|
154
|
+
RailsERD.options = RailsERD.default_options.merge(Config.load(options[:config_file]))
|
155
|
+
end
|
131
156
|
new(path, options).start
|
132
157
|
end
|
133
158
|
end
|
@@ -149,9 +174,17 @@ module RailsERD
|
|
149
174
|
|
150
175
|
def load_application
|
151
176
|
$stderr.puts "Loading application in '#{File.basename(path)}'..."
|
152
|
-
|
153
|
-
|
154
|
-
|
177
|
+
begin
|
178
|
+
environment_path = "#{path}/config/environment.rb"
|
179
|
+
require environment_path
|
180
|
+
rescue ::LoadError
|
181
|
+
puts "Please create a file in '#{environment_path}' that loads your application environment."
|
182
|
+
raise
|
183
|
+
end
|
184
|
+
if defined? Rails
|
185
|
+
Rails.application.eager_load!
|
186
|
+
Rails.application.config.eager_load_namespaces.each(&:eager_load!) if Rails.application.config.respond_to?(:eager_load_namespaces)
|
187
|
+
end
|
155
188
|
rescue TypeError
|
156
189
|
end
|
157
190
|
|
data/lib/rails_erd/config.rb
CHANGED
@@ -7,17 +7,21 @@ module RailsERD
|
|
7
7
|
|
8
8
|
attr_reader :options
|
9
9
|
|
10
|
-
def self.load
|
11
|
-
new.load
|
10
|
+
def self.load(extra_config_file=nil)
|
11
|
+
new.load extra_config_file
|
12
12
|
end
|
13
13
|
|
14
14
|
def initialize
|
15
15
|
@options = {}
|
16
16
|
end
|
17
17
|
|
18
|
-
def load
|
18
|
+
def load(extra_config_file=nil)
|
19
19
|
load_file(USER_WIDE_CONFIG_FILE)
|
20
20
|
load_file(CURRENT_CONFIG_FILE)
|
21
|
+
if extra_config_file
|
22
|
+
extra_config_path = File.expand_path(extra_config_file, Dir.pwd)
|
23
|
+
load_file(extra_config_path) if File.exist?(extra_config_path)
|
24
|
+
end
|
21
25
|
|
22
26
|
@options
|
23
27
|
end
|
@@ -63,8 +67,10 @@ module RailsERD
|
|
63
67
|
# [<string>]
|
64
68
|
when :only, :exclude
|
65
69
|
Array(value).join(",").split(",").map { |v| v.strip }
|
70
|
+
|
66
71
|
# true | false
|
67
|
-
when :disconnected, :indirect, :inheritance, :markup, :polymorphism,
|
72
|
+
when :disconnected, :indirect, :inheritance, :markup, :polymorphism,
|
73
|
+
:warn, :cluster
|
68
74
|
!!value
|
69
75
|
|
70
76
|
# nil | <string>
|
data/lib/rails_erd/diagram.rb
CHANGED
@@ -7,7 +7,7 @@ module RailsERD
|
|
7
7
|
# and (optionally) +save+.
|
8
8
|
#
|
9
9
|
# As an example, a diagram class that generates code that can be used with
|
10
|
-
# yUML (
|
10
|
+
# yUML (https://yuml.me) can be as simple as:
|
11
11
|
#
|
12
12
|
# require "rails_erd/diagram"
|
13
13
|
#
|
@@ -124,6 +124,13 @@ module RailsERD
|
|
124
124
|
# internally by Diagram#create.
|
125
125
|
def generate
|
126
126
|
instance_eval(&callbacks[:setup])
|
127
|
+
if options.only_recursion_depth.present?
|
128
|
+
depth = options.only_recursion_depth.to_s.to_i
|
129
|
+
options[:only].dup.each do |class_name|
|
130
|
+
options[:only]+= recurse_into_relationships(@domain.entity_by_name(class_name), depth)
|
131
|
+
end
|
132
|
+
options[:only].uniq!
|
133
|
+
end
|
127
134
|
|
128
135
|
filtered_entities.each do |entity|
|
129
136
|
instance_exec entity, filtered_attributes(entity), &callbacks[:each_entity]
|
@@ -138,6 +145,26 @@ module RailsERD
|
|
138
145
|
end
|
139
146
|
end
|
140
147
|
|
148
|
+
def recurse_into_relationships(entity, max_level, current_level = 0)
|
149
|
+
return [] unless entity
|
150
|
+
return [] if max_level == current_level
|
151
|
+
|
152
|
+
relationships = entity.relationships.reject{|r| r.indirect? || r.recursive?}
|
153
|
+
|
154
|
+
relationships.map do |relationship|
|
155
|
+
other_entitiy = if relationship.source == entity
|
156
|
+
relationship.destination
|
157
|
+
else
|
158
|
+
relationship.source
|
159
|
+
end
|
160
|
+
if other_entitiy and !other_entitiy.generalized?
|
161
|
+
[other_entitiy.name] + recurse_into_relationships(other_entitiy, max_level, current_level + 1)
|
162
|
+
else
|
163
|
+
[]
|
164
|
+
end
|
165
|
+
end.flatten.uniq
|
166
|
+
end
|
167
|
+
|
141
168
|
def save
|
142
169
|
instance_eval(&callbacks[:save])
|
143
170
|
end
|
@@ -150,8 +177,8 @@ module RailsERD
|
|
150
177
|
|
151
178
|
def filtered_entities
|
152
179
|
@domain.entities.reject { |entity|
|
153
|
-
options.exclude.present? &&
|
154
|
-
options
|
180
|
+
options.exclude.present? && [options.exclude].flatten.map(&:to_sym).include?(entity.name.to_sym) or
|
181
|
+
options[:only].present? && entity.model && ![options[:only]].flatten.map(&:to_sym).include?(entity.name.to_sym) or
|
155
182
|
!options.inheritance && entity.specialized? or
|
156
183
|
!options.polymorphism && entity.generalized? or
|
157
184
|
!options.disconnected && entity.disconnected?
|
@@ -62,7 +62,8 @@ module RailsERD
|
|
62
62
|
concentrate: true,
|
63
63
|
labelloc: :t,
|
64
64
|
fontsize: 13,
|
65
|
-
fontname: FONTS[:bold]
|
65
|
+
fontname: FONTS[:bold],
|
66
|
+
splines: 'spline'
|
66
67
|
}
|
67
68
|
|
68
69
|
# Default node attributes.
|
@@ -85,6 +86,11 @@ module RailsERD
|
|
85
86
|
labeldistance: 1.8,
|
86
87
|
}
|
87
88
|
|
89
|
+
# Default cluster attributes.
|
90
|
+
CLUSTER_ATTRIBUTES = {
|
91
|
+
margin: "10,10"
|
92
|
+
}
|
93
|
+
|
88
94
|
module Simple
|
89
95
|
def entity_style(entity, attributes)
|
90
96
|
{}.tap do |options|
|
@@ -188,6 +194,9 @@ module RailsERD
|
|
188
194
|
# Title of the graph itself.
|
189
195
|
graph[:label] = "#{title}\\n\\n" if title
|
190
196
|
|
197
|
+
# Style of splines
|
198
|
+
graph[:splines] = options.splines unless options.splines.nil?
|
199
|
+
|
191
200
|
# Setup notation options.
|
192
201
|
extend self.class.const_get(options.notation.to_s.capitalize.to_sym)
|
193
202
|
end
|
@@ -210,7 +219,16 @@ module RailsERD
|
|
210
219
|
end
|
211
220
|
|
212
221
|
each_entity do |entity, attributes|
|
213
|
-
|
222
|
+
if options[:cluster] && entity.namespace
|
223
|
+
cluster_name = "cluster_#{entity.namespace}"
|
224
|
+
cluster_options = CLUSTER_ATTRIBUTES.merge(label: entity.namespace)
|
225
|
+
cluster = graph.get_graph(cluster_name) ||
|
226
|
+
graph.add_graph(cluster_name, cluster_options)
|
227
|
+
|
228
|
+
draw_cluster_node cluster, entity.name, entity_options(entity, attributes)
|
229
|
+
else
|
230
|
+
draw_node entity.name, entity_options(entity, attributes)
|
231
|
+
end
|
214
232
|
end
|
215
233
|
|
216
234
|
each_specialization do |specialization|
|
@@ -233,15 +251,19 @@ module RailsERD
|
|
233
251
|
private
|
234
252
|
|
235
253
|
def node_exists?(name)
|
236
|
-
!!graph.
|
254
|
+
!!graph.search_node(escape_name(name))
|
237
255
|
end
|
238
256
|
|
239
257
|
def draw_node(name, options)
|
240
258
|
graph.add_nodes escape_name(name), options
|
241
259
|
end
|
242
260
|
|
261
|
+
def draw_cluster_node(cluster, name, options)
|
262
|
+
cluster.add_nodes escape_name(name), options
|
263
|
+
end
|
264
|
+
|
243
265
|
def draw_edge(from, to, options)
|
244
|
-
graph.add_edges graph.
|
266
|
+
graph.add_edges graph.search_node(escape_name(from)), graph.search_node(escape_name(to)), options if node_exists?(from) and node_exists?(to)
|
245
267
|
end
|
246
268
|
|
247
269
|
def escape_name(name)
|
@@ -288,7 +310,12 @@ module RailsERD
|
|
288
310
|
end
|
289
311
|
|
290
312
|
def read_template(type)
|
291
|
-
|
313
|
+
template_text = File.read(File.expand_path("templates/#{NODE_LABEL_TEMPLATES[type]}", File.dirname(__FILE__)))
|
314
|
+
if ERB.instance_method(:initialize).parameters.assoc(:key) # Ruby 2.6+
|
315
|
+
ERB.new(template_text, trim_mode: "<>")
|
316
|
+
else
|
317
|
+
ERB.new(template_text, nil, "<>")
|
318
|
+
end
|
292
319
|
end
|
293
320
|
end
|
294
321
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
<% if options.orientation == :
|
1
|
+
<% if options.orientation == :horizontal %>{<% end %>
|
2
2
|
<table border="0" align="center" cellspacing="0.5" cellpadding="0" width="<%= NODE_WIDTH + 4 %>">
|
3
3
|
<tr><td align="center" valign="bottom" width="<%= NODE_WIDTH %>"><font face="<%= FONTS[:bold] %>" point-size="11"><%= entity.name %></font></td></tr>
|
4
4
|
</table>
|
@@ -11,4 +11,4 @@
|
|
11
11
|
</table>
|
12
12
|
<% else %>
|
13
13
|
<% end %>
|
14
|
-
<% if options.orientation == :
|
14
|
+
<% if options.orientation == :horizontal %>}<% end %>
|
@@ -1,4 +1,4 @@
|
|
1
|
-
<% if options.orientation == :
|
1
|
+
<% if options.orientation == :horizontal %>{<% end %><%= entity.name %><% if attributes.any? %>
|
2
2
|
|<% attributes.each do |attribute| %><%=
|
3
3
|
attribute %> (<%= attribute.type_description %>)
|
4
|
-
<% end %><% end %><% if options.orientation == :
|
4
|
+
<% end %><% end %><% if options.orientation == :horizontal %>}<% end %>
|
data/lib/rails_erd/domain.rb
CHANGED
@@ -49,7 +49,13 @@ module RailsERD
|
|
49
49
|
# Returns the domain model name, which is the name of your Rails
|
50
50
|
# application or +nil+ outside of Rails.
|
51
51
|
def name
|
52
|
-
defined?
|
52
|
+
return unless defined?(Rails) && Rails.application
|
53
|
+
|
54
|
+
if Rails.application.class.respond_to?(:module_parent)
|
55
|
+
Rails.application.class.module_parent.name
|
56
|
+
else
|
57
|
+
Rails.application.class.parent.name
|
58
|
+
end
|
53
59
|
end
|
54
60
|
|
55
61
|
# Returns all entities of your domain model.
|
@@ -140,8 +146,7 @@ module RailsERD
|
|
140
146
|
association.check_validity!
|
141
147
|
|
142
148
|
if association.options[:polymorphic]
|
143
|
-
|
144
|
-
entity_by_name(entity_name) or raise "polymorphic interface #{entity_name} does not exist"
|
149
|
+
check_polymorphic_association_validity(association)
|
145
150
|
else
|
146
151
|
entity_name = association.klass.name # Raises NameError if the associated class cannot be found.
|
147
152
|
entity_by_name(entity_name) or raise "model #{entity_name} exists, but is not included in domain"
|
@@ -150,6 +155,17 @@ module RailsERD
|
|
150
155
|
warn "Ignoring invalid association #{association_description(association)} (#{e.message})"
|
151
156
|
end
|
152
157
|
|
158
|
+
def check_polymorphic_association_validity(association)
|
159
|
+
entity_name = association.class_name
|
160
|
+
entity = entity_by_name(entity_name)
|
161
|
+
|
162
|
+
if entity || (entity && entity.generalized?)
|
163
|
+
return entity
|
164
|
+
else
|
165
|
+
raise("polymorphic interface #{entity_name} does not exist")
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
153
169
|
def association_description(association)
|
154
170
|
"#{association.name.inspect} on #{association.active_record}"
|
155
171
|
end
|
@@ -21,8 +21,7 @@ module RailsERD
|
|
21
21
|
private
|
22
22
|
|
23
23
|
def association_identity(association)
|
24
|
-
|
25
|
-
Set[identifier, association_owner(association), association_target(association)]
|
24
|
+
Set[association_owner(association), association_target(association)]
|
26
25
|
end
|
27
26
|
|
28
27
|
def association_identifier(association)
|
@@ -30,7 +30,7 @@ module RailsERD
|
|
30
30
|
end
|
31
31
|
|
32
32
|
def abstract_from_models(domain, models)
|
33
|
-
models.select(&:abstract_class?).collect(&:
|
33
|
+
models.select(&:abstract_class?).collect(&:direct_descendants).flatten.collect { |model|
|
34
34
|
new(domain, domain.entity_by_name(model.superclass.name), domain.entity_by_name(model.name))
|
35
35
|
}
|
36
36
|
end
|
data/lib/rails_erd/tasks.rake
CHANGED
@@ -1,15 +1,31 @@
|
|
1
|
+
require 'graphviz/utils'
|
2
|
+
|
1
3
|
def say(message)
|
2
4
|
puts message unless Rake.application.options.silent
|
3
5
|
end
|
4
6
|
|
5
7
|
namespace :erd do
|
8
|
+
task :check_dependencies do
|
9
|
+
include GraphViz::Utils
|
10
|
+
unless find_executable("dot", nil)
|
11
|
+
raise "Unable to find GraphViz's \"dot\" executable. Please " \
|
12
|
+
"visit https://voormedia.github.io/rails-erd/install.html for installation instructions."
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
6
16
|
task :options do
|
7
17
|
(RailsERD.options.keys.map(&:to_s) & ENV.keys).each do |option|
|
8
18
|
RailsERD.options[option.to_sym] = case ENV[option]
|
9
19
|
when "true", "yes" then true
|
10
20
|
when "false", "no" then false
|
11
21
|
when /,/ then ENV[option].split(/\s*,\s*/)
|
12
|
-
|
22
|
+
when /^\d+$/ then ENV[option].to_i
|
23
|
+
else
|
24
|
+
if option == 'only'
|
25
|
+
[ENV[option]]
|
26
|
+
else
|
27
|
+
ENV[option].to_sym
|
28
|
+
end
|
13
29
|
end
|
14
30
|
end
|
15
31
|
end
|
@@ -21,6 +37,10 @@ namespace :erd do
|
|
21
37
|
say "Loading code in search of Active Record models..."
|
22
38
|
begin
|
23
39
|
Rails.application.eager_load!
|
40
|
+
|
41
|
+
if Rails.application.respond_to?(:config) && !Rails.application.config.nil?
|
42
|
+
Rails.application.config.eager_load_namespaces.each(&:eager_load!) if Rails.application.config.respond_to?(:eager_load_namespaces)
|
43
|
+
end
|
24
44
|
rescue Exception => err
|
25
45
|
if Rake.application.options.trace
|
26
46
|
raise
|
@@ -34,7 +54,7 @@ namespace :erd do
|
|
34
54
|
raise "Active Record was not loaded." unless defined? ActiveRecord
|
35
55
|
end
|
36
56
|
|
37
|
-
task :generate => [:options, :load_models] do
|
57
|
+
task :generate => [:check_dependencies, :options, :load_models] do
|
38
58
|
say "Generating Entity-Relationship Diagram for #{ActiveRecord::Base.descendants.length} models..."
|
39
59
|
|
40
60
|
require "rails_erd/diagram/graphviz"
|
data/lib/rails_erd/version.rb
CHANGED
data/test/test_helper.rb
CHANGED
@@ -1,15 +1,12 @@
|
|
1
1
|
require "rubygems"
|
2
2
|
require "bundler/setup"
|
3
|
+
require 'pry'
|
4
|
+
require 'pry-nav'
|
3
5
|
|
4
6
|
require "active_record"
|
5
7
|
|
6
|
-
|
7
|
-
|
8
|
-
require 'mocha/mini_test'
|
9
|
-
else
|
10
|
-
require "test/unit"
|
11
|
-
require 'mocha/test_unit'
|
12
|
-
end
|
8
|
+
require "minitest/autorun"
|
9
|
+
require 'mocha/minitest'
|
13
10
|
|
14
11
|
require "rails_erd/domain"
|
15
12
|
|
@@ -19,6 +16,14 @@ if ActiveSupport::TestCase.respond_to?(:test_order=)
|
|
19
16
|
ActiveSupport::TestCase.test_order = :random
|
20
17
|
end
|
21
18
|
|
19
|
+
# Patch to make Rails 6.1 work.
|
20
|
+
module Kernel
|
21
|
+
# class_eval on an object acts like singleton_class.class_eval.
|
22
|
+
def class_eval(*args, &block)
|
23
|
+
singleton_class.class_eval(*args, &block)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
22
27
|
class ActiveSupport::TestCase
|
23
28
|
include RailsERD
|
24
29
|
|
@@ -48,10 +53,34 @@ class ActiveSupport::TestCase
|
|
48
53
|
ActiveRecord::Base.clear_cache!
|
49
54
|
end
|
50
55
|
|
56
|
+
def create_module_model(full_name,*args,&block)
|
57
|
+
superklass = args.first.kind_of?(Class) ? args.shift : ActiveRecord::Base
|
58
|
+
|
59
|
+
names = full_name.split('::')
|
60
|
+
|
61
|
+
parent_module = names[0..-1].inject(Object) do |parent,child|
|
62
|
+
parent = parent.const_set(child.to_sym, Module.new)
|
63
|
+
end
|
64
|
+
|
65
|
+
parent_module ||= Object
|
66
|
+
name = names.last
|
67
|
+
|
68
|
+
columns = args.first || {}
|
69
|
+
klass = parent_module.const_set name.to_sym, Class.new(superklass)
|
70
|
+
konstant = parent_module.const_get(name.to_sym)
|
71
|
+
|
72
|
+
if superklass == ActiveRecord::Base || superklass.abstract_class?
|
73
|
+
create_table konstant.table_name, columns, konstant.primary_key rescue nil
|
74
|
+
end
|
75
|
+
klass.class_eval(&block) if block_given?
|
76
|
+
konstant
|
77
|
+
end
|
78
|
+
|
51
79
|
def create_model(name, *args, &block)
|
52
80
|
superklass = args.first.kind_of?(Class) ? args.shift : ActiveRecord::Base
|
53
81
|
columns = args.first || {}
|
54
82
|
klass = Object.const_set name.to_sym, Class.new(superklass)
|
83
|
+
|
55
84
|
if superklass == ActiveRecord::Base || superklass.abstract_class?
|
56
85
|
create_table Object.const_get(name.to_sym).table_name, columns, Object.const_get(name.to_sym).primary_key rescue nil
|
57
86
|
end
|
@@ -143,18 +172,64 @@ class ActiveSupport::TestCase
|
|
143
172
|
RailsERD.options = RailsERD.default_options.merge(Config.load)
|
144
173
|
end
|
145
174
|
|
175
|
+
def name_to_object_symbol_pairs(name)
|
176
|
+
parts = name.to_s.split('::')
|
177
|
+
|
178
|
+
return [] if parts.first == '' || parts.count == 0
|
179
|
+
|
180
|
+
parts[1..-1].inject([[Object, parts.first.to_sym]]) do |pairs,string|
|
181
|
+
last_parent, last_child = pairs.last
|
182
|
+
# Fixes for Rails 6. No idea if this is actually correct as I can't decipher what the heck is going on in this
|
183
|
+
# code.
|
184
|
+
if last_child == :ActiveRecord || last_child == :primary
|
185
|
+
break []
|
186
|
+
end
|
187
|
+
|
188
|
+
break pairs unless last_parent.const_defined?(last_child)
|
189
|
+
|
190
|
+
next_parent = last_parent.const_get(last_child)
|
191
|
+
next_child = string.to_sym
|
192
|
+
pairs << [next_parent, next_child]
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
def remove_fully_qualified_constant(name)
|
197
|
+
pairs = name_to_object_symbol_pairs(name)
|
198
|
+
pairs.reverse.each do |parent, child|
|
199
|
+
parent.send(:remove_const,child) if parent.const_defined?(child)
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
146
203
|
def reset_domain
|
147
204
|
if defined? ActiveRecord
|
148
205
|
ActiveRecord::Base.descendants.each do |model|
|
206
|
+
next if model.name == "ActiveRecord::InternalMetadata"
|
149
207
|
model.reset_column_information
|
150
|
-
|
208
|
+
remove_fully_qualified_constant(model.name)
|
151
209
|
end
|
152
|
-
|
210
|
+
|
211
|
+
tables_and_views.each do |table|
|
153
212
|
ActiveRecord::Base.connection.drop_table table
|
154
213
|
end
|
155
|
-
|
214
|
+
|
215
|
+
if ActiveRecord.version >= Gem::Version.new("6.0.0.rc1")
|
216
|
+
cv = ActiveSupport::DescendantsTracker.class_variable_get(:@@direct_descendants)
|
217
|
+
cv.delete(ActiveRecord::Base)
|
218
|
+
ActiveSupport::DescendantsTracker.class_variable_set(:@@direct_descendants, cv)
|
219
|
+
else
|
220
|
+
ActiveRecord::Base.direct_descendants.clear
|
221
|
+
end
|
222
|
+
|
156
223
|
ActiveSupport::Dependencies::Reference.clear!
|
157
224
|
ActiveRecord::Base.clear_cache!
|
158
225
|
end
|
159
226
|
end
|
227
|
+
|
228
|
+
def tables_and_views
|
229
|
+
if ActiveRecord::VERSION::MAJOR >= 5
|
230
|
+
ActiveRecord::Base.connection.data_sources
|
231
|
+
else
|
232
|
+
ActiveRecord::Base.connection.tables
|
233
|
+
end
|
234
|
+
end
|
160
235
|
end
|
data/test/unit/attribute_test.rb
CHANGED
@@ -3,8 +3,9 @@ require File.expand_path("../test_helper", File.dirname(__FILE__))
|
|
3
3
|
|
4
4
|
class AttributeTest < ActiveSupport::TestCase
|
5
5
|
def with_native_limit(type, new_limit)
|
6
|
-
ActiveRecord::Base.connection.class_eval do
|
7
|
-
|
6
|
+
ActiveRecord::Base.connection.singleton_class.class_eval do
|
7
|
+
undef :native_database_types
|
8
|
+
define_method(:native_database_types) do
|
8
9
|
super().tap do |types|
|
9
10
|
types[type][:limit] = new_limit
|
10
11
|
end
|
@@ -12,8 +13,9 @@ class AttributeTest < ActiveSupport::TestCase
|
|
12
13
|
end
|
13
14
|
yield
|
14
15
|
ensure
|
15
|
-
ActiveRecord::Base.connection.class_eval do
|
16
|
-
|
16
|
+
ActiveRecord::Base.connection.singleton_class.class_eval do
|
17
|
+
undef :native_database_types
|
18
|
+
define_method(:native_database_types) do
|
17
19
|
super()
|
18
20
|
end
|
19
21
|
end
|
@@ -222,14 +224,14 @@ class AttributeTest < ActiveSupport::TestCase
|
|
222
224
|
test "limit should return nil if there is no limit" do
|
223
225
|
create_model "Foo"
|
224
226
|
add_column :foos, :my_txt, :text
|
225
|
-
|
227
|
+
assert_nil create_attribute(Foo, "my_txt").limit
|
226
228
|
end
|
227
229
|
|
228
230
|
test "limit should return nil if equal to standard database limit" do
|
229
231
|
with_native_limit :string, 456 do
|
230
232
|
create_model "Foo"
|
231
233
|
add_column :foos, :my_str, :string, :limit => 456
|
232
|
-
|
234
|
+
assert_nil create_attribute(Foo, "my_str").limit
|
233
235
|
end
|
234
236
|
end
|
235
237
|
|
@@ -250,7 +252,7 @@ class AttributeTest < ActiveSupport::TestCase
|
|
250
252
|
test "limit should return nil for decimal columns if equal to standard database limit" do
|
251
253
|
create_model "Foo"
|
252
254
|
add_column :foos, :num, :decimal
|
253
|
-
|
255
|
+
assert_nil create_attribute(Foo, "num").limit
|
254
256
|
end
|
255
257
|
|
256
258
|
test "limit should return nil if type is unsupported by rails" do
|
@@ -260,19 +262,19 @@ class AttributeTest < ActiveSupport::TestCase
|
|
260
262
|
add_column "foos", "a", "REAL"
|
261
263
|
end
|
262
264
|
end
|
263
|
-
|
265
|
+
assert_nil create_attribute(Foo, "a").limit
|
264
266
|
end
|
265
267
|
|
266
268
|
test "limit should return nil for oddball column types that misuse the limit attribute" do
|
267
|
-
create_model "Business", :location => :integer
|
268
|
-
|
269
|
-
attribute.column.class_eval do
|
270
|
-
define_method :limit do
|
269
|
+
create_model "Business", :location => :integer do
|
270
|
+
define_singleton_method :limit do
|
271
271
|
# https://github.com/voormedia/rails-erd/issues/21
|
272
272
|
{ :srid => 4326, :type => "point", :geographic => true }
|
273
273
|
end
|
274
274
|
end
|
275
|
-
|
275
|
+
|
276
|
+
attribute = create_attribute(Business, "location")
|
277
|
+
assert_nil attribute.limit
|
276
278
|
end
|
277
279
|
|
278
280
|
test "scale should return scale for decimal columns if nonstandard" do
|
@@ -284,7 +286,7 @@ class AttributeTest < ActiveSupport::TestCase
|
|
284
286
|
test "scale should return nil for decimal columns if equal to standard database limit" do
|
285
287
|
create_model "Foo"
|
286
288
|
add_column :foos, :num, :decimal
|
287
|
-
|
289
|
+
assert_nil create_attribute(Foo, "num").scale
|
288
290
|
end
|
289
291
|
|
290
292
|
test "scale should return zero for decimal columns if left to default setting when specifying precision" do
|
@@ -300,17 +302,16 @@ class AttributeTest < ActiveSupport::TestCase
|
|
300
302
|
add_column "foos", "a", "REAL"
|
301
303
|
end
|
302
304
|
end
|
303
|
-
|
305
|
+
assert_nil create_attribute(Foo, "a").scale
|
304
306
|
end
|
305
307
|
|
306
308
|
test "scale should return nil for oddball column types that misuse the scale attribute" do
|
307
|
-
create_model "Kobold", :size => :integer
|
308
|
-
attribute = create_attribute(Kobold, "size")
|
309
|
-
attribute.column.class_eval do
|
309
|
+
create_model "Kobold", :size => :integer do
|
310
310
|
define_method :scale do
|
311
311
|
1..5
|
312
312
|
end
|
313
313
|
end
|
314
|
-
|
314
|
+
attribute = create_attribute(Kobold, "size")
|
315
|
+
assert_nil attribute.scale
|
315
316
|
end
|
316
317
|
end
|
data/test/unit/config_test.rb
CHANGED
@@ -61,6 +61,29 @@ class ConfigTest < ActiveSupport::TestCase
|
|
61
61
|
assert_equal expected_hash, RailsERD::Config.load
|
62
62
|
end
|
63
63
|
|
64
|
+
test "load_config_file should return a hash from the configured config file when a new config file is given as an argument" do
|
65
|
+
set_local_config_file_to("erdconfig.another_example")
|
66
|
+
|
67
|
+
expected_hash = {
|
68
|
+
attributes: [:content, :foreign_key, :inheritance, :false],
|
69
|
+
disconnected: true,
|
70
|
+
filename: "erd",
|
71
|
+
filetype: :pdf,
|
72
|
+
indirect: true,
|
73
|
+
inheritance: false,
|
74
|
+
markup: true,
|
75
|
+
notation: :simple,
|
76
|
+
orientation: "horizontal",
|
77
|
+
polymorphism: false,
|
78
|
+
warn: true,
|
79
|
+
title: "sample title",
|
80
|
+
exclude: [],
|
81
|
+
only: []
|
82
|
+
}
|
83
|
+
|
84
|
+
assert_equal expected_hash, RailsERD::Config.load("test/support_files/erdconfig.example")
|
85
|
+
end
|
86
|
+
|
64
87
|
test "load_config_gile should return a hash from CURRENT_CONFIG_FILE overriding USER_WIDE_CONFIG_FILE when both of them exist." do
|
65
88
|
set_user_config_file_to("erdconfig.example")
|
66
89
|
set_local_config_file_to("erdconfig.another_example")
|
data/test/unit/diagram_test.rb
CHANGED
@@ -136,6 +136,15 @@ class DiagramTest < ActiveSupport::TestCase
|
|
136
136
|
assert_equal [Book], retrieve_entities(:exclude => [:Author, :Editor]).map(&:model)
|
137
137
|
end
|
138
138
|
|
139
|
+
test "generate should filter excluded polymorphic entities" do
|
140
|
+
create_model "Cannon"
|
141
|
+
create_model "Galleon" do
|
142
|
+
has_many :cannons, as: :defensible
|
143
|
+
end
|
144
|
+
assert_equal ["Cannon", "Galleon"], retrieve_entities(polymorphism: true, exclude: :Defensible).map(&:name)
|
145
|
+
end
|
146
|
+
|
147
|
+
|
139
148
|
test "generate should include only specified entity" do
|
140
149
|
create_model "Book"
|
141
150
|
create_model "Author"
|
data/test/unit/domain_test.rb
CHANGED
@@ -127,13 +127,21 @@ class DomainTest < ActiveSupport::TestCase
|
|
127
127
|
end
|
128
128
|
|
129
129
|
test "relationships should count relationship between same models with distinct foreign key seperately" do
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
130
|
+
# TODO: Once we drop Rails 3.2 support, we _should_ be able to drop the
|
131
|
+
# :respond_to? check
|
132
|
+
#
|
133
|
+
if respond_to? :skip
|
134
|
+
skip("multiple edges between the same objects can cause segfaults in some versions of Graphviz")
|
135
|
+
|
136
|
+
create_model "Foo", :bar => :references, :special_bar => :references do
|
137
|
+
belongs_to :bar
|
138
|
+
end
|
139
|
+
create_model "Bar" do
|
140
|
+
has_many :foos, :foreign_key => :special_bar_id
|
141
|
+
end
|
142
|
+
|
143
|
+
assert_equal [Domain::Relationship] * 2, Domain.generate.relationships.collect(&:class)
|
135
144
|
end
|
136
|
-
assert_equal [Domain::Relationship] * 2, Domain.generate.relationships.collect(&:class)
|
137
145
|
end
|
138
146
|
|
139
147
|
test "relationships should use model name first in alphabet as source for many to many relationships" do
|
@@ -186,6 +194,15 @@ class DomainTest < ActiveSupport::TestCase
|
|
186
194
|
assert_equal [Domain::Specialization] * 3, Domain.generate.specializations.collect(&:class)
|
187
195
|
end
|
188
196
|
|
197
|
+
test "specializations should return specializations in domain model once for descendants of abstract class" do
|
198
|
+
create_model "Thing" do
|
199
|
+
self.abstract_class = true
|
200
|
+
end
|
201
|
+
create_model "Beverage", Thing, :type => :string
|
202
|
+
create_model "Beer", Beverage
|
203
|
+
assert_equal [Domain::Specialization], Domain.generate.specializations.collect(&:class)
|
204
|
+
end
|
205
|
+
|
189
206
|
# Erroneous associations ===================================================
|
190
207
|
test "relationships should omit bad has_many associations" do
|
191
208
|
create_model "Foo" do
|
@@ -216,7 +233,7 @@ class DomainTest < ActiveSupport::TestCase
|
|
216
233
|
output = collect_stdout do
|
217
234
|
Domain.generate.relationships
|
218
235
|
end
|
219
|
-
assert_match
|
236
|
+
assert_match(/Ignoring invalid association :flabs on Foo/, output)
|
220
237
|
end
|
221
238
|
|
222
239
|
test "relationships should output a warning when an association to model outside domain is encountered" do
|
@@ -227,7 +244,7 @@ class DomainTest < ActiveSupport::TestCase
|
|
227
244
|
output = collect_stdout do
|
228
245
|
Domain.new([Foo]).relationships
|
229
246
|
end
|
230
|
-
assert_match
|
247
|
+
assert_match(/model Bar exists, but is not included in domain/, output)
|
231
248
|
end
|
232
249
|
|
233
250
|
test "relationships should output a warning when an association to a non existent generalization is encountered" do
|
@@ -240,7 +257,7 @@ class DomainTest < ActiveSupport::TestCase
|
|
240
257
|
output = collect_stdout do
|
241
258
|
Domain.generate.relationships
|
242
259
|
end
|
243
|
-
assert_match
|
260
|
+
assert_match(/polymorphic interface FooBar does not exist/, output)
|
244
261
|
end
|
245
262
|
|
246
263
|
test "relationships should not warn when a bad association is encountered if warnings are disabled" do
|
@@ -264,6 +281,6 @@ class DomainTest < ActiveSupport::TestCase
|
|
264
281
|
output = collect_stdout do
|
265
282
|
Domain.generate.entities
|
266
283
|
end
|
267
|
-
assert_match
|
284
|
+
assert_match(/Ignoring invalid model Foo \(table foos does not exist\)/, output)
|
268
285
|
end
|
269
286
|
end
|
data/test/unit/entity_test.rb
CHANGED
@@ -249,4 +249,30 @@ class EntityTest < ActiveSupport::TestCase
|
|
249
249
|
assert_equal [domain.entity_by_name("Galleon"), domain.entity_by_name("Stronghold")],
|
250
250
|
domain.entity_by_name("Defensible").children
|
251
251
|
end
|
252
|
+
|
253
|
+
# Namespace ===================================================================
|
254
|
+
test "namespace should return nil for models outside modules" do
|
255
|
+
create_module_model "Plane"
|
256
|
+
assert_nil create_entity(Plane).namespace
|
257
|
+
end
|
258
|
+
|
259
|
+
test "namespace should return the module name for single-module models" do
|
260
|
+
create_module_model "Saw::Plane"
|
261
|
+
assert_equal "Saw", create_entity(Saw::Plane).namespace
|
262
|
+
end
|
263
|
+
|
264
|
+
test "namespace should return the module path if more than one module" do
|
265
|
+
create_module_model "Augur::Chisel::Saw::Plane"
|
266
|
+
assert_equal "Augur::Chisel::Saw", create_entity(Augur::Chisel::Saw::Plane).namespace
|
267
|
+
end
|
268
|
+
|
269
|
+
test "namespace defaults to nil" do
|
270
|
+
create_model "Foo"
|
271
|
+
assert_nil create_entity(Foo).namespace
|
272
|
+
end
|
273
|
+
|
274
|
+
test "namespace returns appropriate modules" do
|
275
|
+
entity = Domain::Entity.new(Domain.new, "Foo::Bar::Qux")
|
276
|
+
assert_equal "Foo::Bar", entity.namespace
|
277
|
+
end
|
252
278
|
end
|
data/test/unit/graphviz_test.rb
CHANGED
@@ -106,6 +106,7 @@ class GraphvizTest < ActiveSupport::TestCase
|
|
106
106
|
begin
|
107
107
|
GraphViz.class_eval do
|
108
108
|
alias_method :old_output_and_errors_from_command, :output_and_errors_from_command
|
109
|
+
undef :output_and_errors_from_command
|
109
110
|
def output_and_errors_from_command(*args); raise end
|
110
111
|
end
|
111
112
|
assert_nothing_raised do
|
@@ -113,6 +114,7 @@ class GraphvizTest < ActiveSupport::TestCase
|
|
113
114
|
end
|
114
115
|
ensure
|
115
116
|
GraphViz.class_eval do
|
117
|
+
undef :output_and_errors_from_command
|
116
118
|
alias_method :output_and_errors_from_command, :old_output_and_errors_from_command
|
117
119
|
end
|
118
120
|
end
|
@@ -137,7 +139,7 @@ class GraphvizTest < ActiveSupport::TestCase
|
|
137
139
|
|
138
140
|
test "create should not create output if there are no connected models" do
|
139
141
|
Diagram::Graphviz.create rescue nil
|
140
|
-
assert !File.
|
142
|
+
assert !File.exist?("erd.png")
|
141
143
|
end
|
142
144
|
|
143
145
|
test "create should abort and complain if there are no connected models" do
|
@@ -147,7 +149,7 @@ class GraphvizTest < ActiveSupport::TestCase
|
|
147
149
|
rescue => e
|
148
150
|
message = e.message
|
149
151
|
end
|
150
|
-
assert_match
|
152
|
+
assert_match(/No entities found/, message)
|
151
153
|
end
|
152
154
|
|
153
155
|
test "create should abort and complain if output directory does not exist" do
|
@@ -160,7 +162,7 @@ class GraphvizTest < ActiveSupport::TestCase
|
|
160
162
|
message = e.message
|
161
163
|
end
|
162
164
|
|
163
|
-
assert_match
|
165
|
+
assert_match(/Output directory 'does_not_exist' does not exist/, message)
|
164
166
|
end
|
165
167
|
|
166
168
|
test "create should not fail when reserved words are used as node names" do
|
@@ -184,6 +186,16 @@ class GraphvizTest < ActiveSupport::TestCase
|
|
184
186
|
assert_equal '"Domain model\n\n"', diagram.graph.graph[:label].to_s
|
185
187
|
end
|
186
188
|
|
189
|
+
test "generate should add default value for splines attribute" do
|
190
|
+
create_simple_domain
|
191
|
+
assert_equal '"spline"', diagram.graph.graph[:splines].to_s
|
192
|
+
end
|
193
|
+
|
194
|
+
test "generate should add set value for splines attribute" do
|
195
|
+
create_simple_domain
|
196
|
+
assert_equal '"ortho"', diagram(splines: 'ortho').graph.graph[:splines].to_s
|
197
|
+
end
|
198
|
+
|
187
199
|
test "generate should add title with application name to graph" do
|
188
200
|
begin
|
189
201
|
Object::Quux = Module.new
|
@@ -226,7 +238,7 @@ class GraphvizTest < ActiveSupport::TestCase
|
|
226
238
|
belongs_to :bar
|
227
239
|
end
|
228
240
|
create_model "Bar"
|
229
|
-
assert_equal %Q("Bar"), find_dot_node(diagram, "m_Bar")[:label].to_gv
|
241
|
+
assert_equal %Q("{Bar}"), find_dot_node(diagram, "m_Bar")[:label].to_gv
|
230
242
|
end
|
231
243
|
|
232
244
|
test "generate should add attributes to entity html labels" do
|
@@ -244,7 +256,7 @@ class GraphvizTest < ActiveSupport::TestCase
|
|
244
256
|
belongs_to :bar
|
245
257
|
end
|
246
258
|
create_model "Bar", :column => :string, :column_two => :boolean
|
247
|
-
assert_equal %Q("Bar|column (string)\\ncolumn_two (boolean)\\n"), find_dot_node(diagram, "m_Bar")[:label].to_gv
|
259
|
+
assert_equal %Q("{Bar|column (string)\\ncolumn_two (boolean)\\n}"), find_dot_node(diagram, "m_Bar")[:label].to_gv
|
248
260
|
end
|
249
261
|
|
250
262
|
test "generate should not add any attributes to entity labels if attributes is set to false" do
|
@@ -255,50 +267,58 @@ class GraphvizTest < ActiveSupport::TestCase
|
|
255
267
|
assert_no_match %r{contents}, find_dot_node(diagram(:attributes => false), "m_Jar")[:label].to_gv
|
256
268
|
end
|
257
269
|
|
258
|
-
test "node html labels should have direction reversing braces for
|
270
|
+
test "node html labels should have direction reversing braces for horizontal orientation" do
|
259
271
|
RailsERD.options.markup = true
|
260
272
|
create_model "Book", :author => :references do
|
261
273
|
belongs_to :author
|
262
274
|
end
|
263
275
|
create_model "Author", :name => :string
|
264
|
-
assert_match %r(\A<\{\s*<.*\|.*>\s*\}>\Z)m, find_dot_node(diagram(:orientation => :
|
276
|
+
assert_match %r(\A<\{\s*<.*\|.*>\s*\}>\Z)m, find_dot_node(diagram(:orientation => :horizontal), "m_Author")[:label].to_gv
|
265
277
|
end
|
266
278
|
|
267
|
-
test "node html labels should not have direction reversing braces for
|
279
|
+
test "node html labels should not have direction reversing braces for vertical orientation" do
|
268
280
|
RailsERD.options.markup = true
|
269
281
|
create_model "Book", :author => :references do
|
270
282
|
belongs_to :author
|
271
283
|
end
|
272
284
|
create_model "Author", :name => :string
|
273
|
-
assert_match %r(\A<\s*<.*\|.*>\s*>\Z)m, find_dot_node(diagram(:orientation => :
|
285
|
+
assert_match %r(\A<\s*<.*\|.*>\s*>\Z)m, find_dot_node(diagram(:orientation => :vertical), "m_Author")[:label].to_gv
|
274
286
|
end
|
275
287
|
|
276
|
-
test "node record labels should have direction reversing braces for
|
288
|
+
test "node record labels should have direction reversing braces for horizontal orientation" do
|
277
289
|
RailsERD.options.markup = false
|
278
290
|
create_model "Book", :author => :references do
|
279
291
|
belongs_to :author
|
280
292
|
end
|
281
293
|
create_model "Author", :name => :string
|
282
|
-
assert_match %r(\A"\{\w
|
294
|
+
assert_match %r(\A"\{\w+\|.*\}"\Z)m, find_dot_node(diagram(:orientation => :horizontal), "m_Author")[:label].to_gv
|
283
295
|
end
|
284
296
|
|
285
|
-
test "node record labels should not have direction reversing braces for
|
297
|
+
test "node record labels should not have direction reversing braces for vertical orientation" do
|
286
298
|
RailsERD.options.markup = false
|
287
299
|
create_model "Book", :author => :references do
|
288
300
|
belongs_to :author
|
289
301
|
end
|
290
302
|
create_model "Author", :name => :string
|
291
|
-
assert_match %r(\A"\w
|
303
|
+
assert_match %r(\A"\w+\|.*"\Z)m, find_dot_node(diagram(:orientation => :vertical), "m_Author")[:label].to_gv
|
292
304
|
end
|
293
305
|
|
294
306
|
test "generate should create edge for each relationship" do
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
307
|
+
# TODO: Once we drop Rails 3.2 support, we _should_ be able to drop the
|
308
|
+
# :respond_to? check
|
309
|
+
#
|
310
|
+
if respond_to? :skip
|
311
|
+
skip("multiple edges between the same objects can cause segfaults in some versions of Graphviz")
|
312
|
+
|
313
|
+
create_model "Foo", :bar => :references do
|
314
|
+
belongs_to :bar
|
315
|
+
end
|
316
|
+
create_model "Bar", :foo => :references do
|
317
|
+
belongs_to :foo
|
318
|
+
end
|
319
|
+
|
320
|
+
assert_equal [["m_Bar", "m_Foo"], ["m_Foo", "m_Bar"]], find_dot_node_pairs(diagram).sort
|
300
321
|
end
|
301
|
-
assert_equal [["m_Bar", "m_Foo"], ["m_Foo", "m_Bar"]], find_dot_node_pairs(diagram).sort
|
302
322
|
end
|
303
323
|
|
304
324
|
test "generate should create edge to polymorphic entity if polymorphism is true" do
|
data/test/unit/rake_task_test.rb
CHANGED
@@ -20,6 +20,7 @@ class RakeTaskTest < ActiveSupport::TestCase
|
|
20
20
|
Object::Quux = Module.new
|
21
21
|
Object::Quux::Application = Class.new
|
22
22
|
Object::Rails = Struct.new(:application).new(Object::Quux::Application.new)
|
23
|
+
|
23
24
|
Rails.class_eval do
|
24
25
|
define_method :backtrace_cleaner do
|
25
26
|
ActiveSupport::BacktraceCleaner.new.tap do |cleaner|
|
@@ -40,7 +41,7 @@ class RakeTaskTest < ActiveSupport::TestCase
|
|
40
41
|
|
41
42
|
test "generate task should not create output if there are no connected models" do
|
42
43
|
Rake::Task["erd:generate"].execute rescue nil
|
43
|
-
assert !File.
|
44
|
+
assert !File.exist?("erd.dot")
|
44
45
|
end
|
45
46
|
|
46
47
|
test "generate task should eager load application environment" do
|
@@ -100,12 +101,13 @@ class RakeTaskTest < ActiveSupport::TestCase
|
|
100
101
|
rescue => e
|
101
102
|
message = e.message
|
102
103
|
end
|
103
|
-
assert_match
|
104
|
+
assert_match(/#{Regexp.escape(<<-MSG.strip).gsub("xxx", ".*?")}/, message
|
104
105
|
Loading models failed!
|
105
106
|
Error occurred while loading application: FooBar (RuntimeError)
|
106
107
|
test/unit/rake_task_test.rb:#{l1}:in `xxx'
|
107
108
|
test/unit/rake_task_test.rb:#{l2}:in `xxx'
|
108
109
|
MSG
|
110
|
+
)
|
109
111
|
end
|
110
112
|
|
111
113
|
test "generate task should reraise if application could not be loaded and trace option is enabled" do
|
@@ -171,4 +173,22 @@ Error occurred while loading application: FooBar (RuntimeError)
|
|
171
173
|
Rake::Task["erd:options"].execute
|
172
174
|
assert_equal %w[content timestamps], RailsERD.options.attributes
|
173
175
|
end
|
176
|
+
|
177
|
+
test "options task should set known integer command line options when value is only digits" do
|
178
|
+
ENV["only_recursion_depth"] = "2"
|
179
|
+
Rake::Task["erd:options"].execute
|
180
|
+
assert_equal 2, RailsERD.options.only_recursion_depth
|
181
|
+
end
|
182
|
+
|
183
|
+
test "options task sets known command line options as symbols when not boolean or numeric" do
|
184
|
+
ENV["only_recursion_depth"] = "test"
|
185
|
+
Rake::Task["erd:options"].execute
|
186
|
+
assert_equal :test, RailsERD.options.only_recursion_depth
|
187
|
+
end
|
188
|
+
|
189
|
+
test "options task should set single parameter to only as array xxx" do
|
190
|
+
ENV["only"] = "model"
|
191
|
+
Rake::Task["erd:options"].execute
|
192
|
+
assert_equal ["model"], RailsERD.options.only
|
193
|
+
end
|
174
194
|
end
|
metadata
CHANGED
@@ -1,14 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rails-erd
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.6.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rolf Timmermans
|
8
|
-
|
8
|
+
- Kerri Miller
|
9
|
+
autorequire:
|
9
10
|
bindir: bin
|
10
11
|
cert_chain: []
|
11
|
-
date:
|
12
|
+
date: 2021-02-16 00:00:00.000000000 Z
|
12
13
|
dependencies:
|
13
14
|
- !ruby/object:Gem::Dependency
|
14
15
|
name: activerecord
|
@@ -16,28 +17,28 @@ dependencies:
|
|
16
17
|
requirements:
|
17
18
|
- - ">="
|
18
19
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
20
|
+
version: '4.2'
|
20
21
|
type: :runtime
|
21
22
|
prerelease: false
|
22
23
|
version_requirements: !ruby/object:Gem::Requirement
|
23
24
|
requirements:
|
24
25
|
- - ">="
|
25
26
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
27
|
+
version: '4.2'
|
27
28
|
- !ruby/object:Gem::Dependency
|
28
29
|
name: activesupport
|
29
30
|
requirement: !ruby/object:Gem::Requirement
|
30
31
|
requirements:
|
31
32
|
- - ">="
|
32
33
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
34
|
+
version: '4.2'
|
34
35
|
type: :runtime
|
35
36
|
prerelease: false
|
36
37
|
version_requirements: !ruby/object:Gem::Requirement
|
37
38
|
requirements:
|
38
39
|
- - ">="
|
39
40
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
41
|
+
version: '4.2'
|
41
42
|
- !ruby/object:Gem::Dependency
|
42
43
|
name: ruby-graphviz
|
43
44
|
requirement: !ruby/object:Gem::Requirement
|
@@ -66,10 +67,39 @@ dependencies:
|
|
66
67
|
- - "~>"
|
67
68
|
- !ruby/object:Gem::Version
|
68
69
|
version: 0.2.0
|
70
|
+
- !ruby/object:Gem::Dependency
|
71
|
+
name: pry
|
72
|
+
requirement: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
type: :development
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
84
|
+
- !ruby/object:Gem::Dependency
|
85
|
+
name: pry-nav
|
86
|
+
requirement: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0'
|
91
|
+
type: :development
|
92
|
+
prerelease: false
|
93
|
+
version_requirements: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - ">="
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: '0'
|
69
98
|
description: Automatically generate an entity-relationship diagram (ERD) for your
|
70
99
|
Rails models.
|
71
100
|
email:
|
72
101
|
- r.timmermans@voormedia.com
|
102
|
+
- kerrizor@kerrizor.com
|
73
103
|
executables:
|
74
104
|
- erd
|
75
105
|
extensions: []
|
@@ -117,7 +147,7 @@ homepage: https://github.com/voormedia/rails-erd
|
|
117
147
|
licenses:
|
118
148
|
- MIT
|
119
149
|
metadata: {}
|
120
|
-
post_install_message:
|
150
|
+
post_install_message:
|
121
151
|
rdoc_options: []
|
122
152
|
require_paths:
|
123
153
|
- lib
|
@@ -125,16 +155,16 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
125
155
|
requirements:
|
126
156
|
- - ">="
|
127
157
|
- !ruby/object:Gem::Version
|
128
|
-
version:
|
158
|
+
version: '2.2'
|
129
159
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
130
160
|
requirements:
|
131
161
|
- - ">="
|
132
162
|
- !ruby/object:Gem::Version
|
133
163
|
version: '0'
|
134
164
|
requirements: []
|
135
|
-
rubyforge_project:
|
136
|
-
rubygems_version: 2.
|
137
|
-
signing_key:
|
165
|
+
rubyforge_project:
|
166
|
+
rubygems_version: 2.6.14
|
167
|
+
signing_key:
|
138
168
|
specification_version: 4
|
139
169
|
summary: Entity-relationship diagram for your Rails models.
|
140
170
|
test_files:
|
@@ -152,4 +182,3 @@ test_files:
|
|
152
182
|
- test/unit/rake_task_test.rb
|
153
183
|
- test/unit/relationship_test.rb
|
154
184
|
- test/unit/specialization_test.rb
|
155
|
-
has_rdoc:
|