modaldiagrams 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +15 -0
- data/Gemfile.lock +110 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +20 -0
- data/Rakefile +45 -0
- data/TODO +2 -0
- data/VERSION +1 -0
- data/lib/modaldiagrams/modaldiagrams.rb +277 -0
- data/lib/modaldiagrams/parameters.rb +19 -0
- data/lib/modaldiagrams/tasks.rb +1 -0
- data/lib/modaldiagrams.rb +13 -0
- data/lib/tasks/diagrams.rake +15 -0
- data/lib/tasks/diagrams_pdf.rake +20 -0
- data/lib/tasks/diagrams_png.rake +20 -0
- data/lib/tasks/diagrams_ps.rake +19 -0
- data/lib/tasks.rb +1 -0
- data/modaldiagrams.gemspec +75 -0
- metadata +181 -0
data/Gemfile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
|
3
|
+
gem 'modalsettings'
|
4
|
+
gem 'modalsupport', '>= 0.9.2'
|
5
|
+
|
6
|
+
|
7
|
+
# Add dependencies to develop your gem here.
|
8
|
+
# Include everything needed to run rake, tests, features, etc.
|
9
|
+
group :development do
|
10
|
+
gem "shoulda", ">= 0"
|
11
|
+
gem "rdoc", "~> 3.12"
|
12
|
+
gem "bundler", "~> 1.1.0"
|
13
|
+
gem "jeweler", "~> 1.8.4"
|
14
|
+
gem 'modalfields', '>= 1.2.0'
|
15
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
actionmailer (3.2.8)
|
5
|
+
actionpack (= 3.2.8)
|
6
|
+
mail (~> 2.4.4)
|
7
|
+
actionpack (3.2.8)
|
8
|
+
activemodel (= 3.2.8)
|
9
|
+
activesupport (= 3.2.8)
|
10
|
+
builder (~> 3.0.0)
|
11
|
+
erubis (~> 2.7.0)
|
12
|
+
journey (~> 1.0.4)
|
13
|
+
rack (~> 1.4.0)
|
14
|
+
rack-cache (~> 1.2)
|
15
|
+
rack-test (~> 0.6.1)
|
16
|
+
sprockets (~> 2.1.3)
|
17
|
+
activemodel (3.2.8)
|
18
|
+
activesupport (= 3.2.8)
|
19
|
+
builder (~> 3.0.0)
|
20
|
+
activerecord (3.2.8)
|
21
|
+
activemodel (= 3.2.8)
|
22
|
+
activesupport (= 3.2.8)
|
23
|
+
arel (~> 3.0.2)
|
24
|
+
tzinfo (~> 0.3.29)
|
25
|
+
activeresource (3.2.8)
|
26
|
+
activemodel (= 3.2.8)
|
27
|
+
activesupport (= 3.2.8)
|
28
|
+
activesupport (3.2.8)
|
29
|
+
i18n (~> 0.6)
|
30
|
+
multi_json (~> 1.0)
|
31
|
+
arel (3.0.2)
|
32
|
+
builder (3.0.0)
|
33
|
+
erubis (2.7.0)
|
34
|
+
git (1.2.5)
|
35
|
+
hike (1.2.1)
|
36
|
+
i18n (0.6.1)
|
37
|
+
jeweler (1.8.4)
|
38
|
+
bundler (~> 1.0)
|
39
|
+
git (>= 1.2.5)
|
40
|
+
rake
|
41
|
+
rdoc
|
42
|
+
journey (1.0.4)
|
43
|
+
json (1.7.5)
|
44
|
+
mail (2.4.4)
|
45
|
+
i18n (>= 0.4.0)
|
46
|
+
mime-types (~> 1.16)
|
47
|
+
treetop (~> 1.4.8)
|
48
|
+
mime-types (1.19)
|
49
|
+
modalfields (1.2.0)
|
50
|
+
activerecord (>= 2.3.5)
|
51
|
+
activesupport (>= 2.3.5)
|
52
|
+
rails (>= 2.3.0)
|
53
|
+
modalsettings (1.0.0)
|
54
|
+
modalsupport (>= 0.8.1)
|
55
|
+
modalsupport (0.9.2)
|
56
|
+
multi_json (1.3.6)
|
57
|
+
polyglot (0.3.3)
|
58
|
+
rack (1.4.1)
|
59
|
+
rack-cache (1.2)
|
60
|
+
rack (>= 0.4)
|
61
|
+
rack-ssl (1.3.2)
|
62
|
+
rack
|
63
|
+
rack-test (0.6.1)
|
64
|
+
rack (>= 1.0)
|
65
|
+
rails (3.2.8)
|
66
|
+
actionmailer (= 3.2.8)
|
67
|
+
actionpack (= 3.2.8)
|
68
|
+
activerecord (= 3.2.8)
|
69
|
+
activeresource (= 3.2.8)
|
70
|
+
activesupport (= 3.2.8)
|
71
|
+
bundler (~> 1.0)
|
72
|
+
railties (= 3.2.8)
|
73
|
+
railties (3.2.8)
|
74
|
+
actionpack (= 3.2.8)
|
75
|
+
activesupport (= 3.2.8)
|
76
|
+
rack-ssl (~> 1.3.2)
|
77
|
+
rake (>= 0.8.7)
|
78
|
+
rdoc (~> 3.4)
|
79
|
+
thor (>= 0.14.6, < 2.0)
|
80
|
+
rake (0.9.2.2)
|
81
|
+
rdoc (3.12)
|
82
|
+
json (~> 1.4)
|
83
|
+
shoulda (3.1.1)
|
84
|
+
shoulda-context (~> 1.0)
|
85
|
+
shoulda-matchers (~> 1.2)
|
86
|
+
shoulda-context (1.0.0)
|
87
|
+
shoulda-matchers (1.3.0)
|
88
|
+
activesupport (>= 3.0.0)
|
89
|
+
sprockets (2.1.3)
|
90
|
+
hike (~> 1.2)
|
91
|
+
rack (~> 1.0)
|
92
|
+
tilt (~> 1.1, != 1.3.0)
|
93
|
+
thor (0.16.0)
|
94
|
+
tilt (1.3.3)
|
95
|
+
treetop (1.4.10)
|
96
|
+
polyglot
|
97
|
+
polyglot (>= 0.3.1)
|
98
|
+
tzinfo (0.3.33)
|
99
|
+
|
100
|
+
PLATFORMS
|
101
|
+
ruby
|
102
|
+
|
103
|
+
DEPENDENCIES
|
104
|
+
bundler (~> 1.1.0)
|
105
|
+
jeweler (~> 1.8.4)
|
106
|
+
modalfields (>= 1.2.0)
|
107
|
+
modalsettings
|
108
|
+
modalsupport (>= 0.9.2)
|
109
|
+
rdoc (~> 3.12)
|
110
|
+
shoulda
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2012 Javier Goizueta
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
= modaldiagrams
|
2
|
+
|
3
|
+
This gem provides Rake tasks for diagramming ActiveRecord databases.
|
4
|
+
The diagrams are generated as Graphviz dot files. Additional taks generate PostScript, PNG or PDF output using GhostView.
|
5
|
+
|
6
|
+
== Contributing to modaldiagrams
|
7
|
+
|
8
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
|
9
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
|
10
|
+
* Fork the project.
|
11
|
+
* Start a feature/bugfix branch.
|
12
|
+
* Commit and push until you are happy with your contribution.
|
13
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
14
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
15
|
+
|
16
|
+
== Copyright
|
17
|
+
|
18
|
+
Copyright (c) 2012 Javier Goizueta. See LICENSE.txt for
|
19
|
+
further details.
|
20
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
begin
|
6
|
+
Bundler.setup(:default, :development)
|
7
|
+
rescue Bundler::BundlerError => e
|
8
|
+
$stderr.puts e.message
|
9
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
+
exit e.status_code
|
11
|
+
end
|
12
|
+
require 'rake'
|
13
|
+
|
14
|
+
require 'jeweler'
|
15
|
+
Jeweler::Tasks.new do |gem|
|
16
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
17
|
+
gem.name = "modaldiagrams"
|
18
|
+
gem.homepage = "http://github.com/jgoizueta/modaldiagrams"
|
19
|
+
gem.license = "MIT"
|
20
|
+
gem.summary = %Q{DB diagramming tool fo Rails ActiveRecord applications}
|
21
|
+
gem.description = %Q{modaldiagrams provides Rake tasks for diagramming ActiveRecord databases. It generates Graphviz dot files.}
|
22
|
+
gem.email = "jgoizueta@gmail.com"
|
23
|
+
gem.authors = ["Javier Goizueta"]
|
24
|
+
# dependencies defined in Gemfile
|
25
|
+
end
|
26
|
+
Jeweler::RubygemsDotOrgTasks.new
|
27
|
+
|
28
|
+
require 'rake/testtask'
|
29
|
+
Rake::TestTask.new(:test) do |test|
|
30
|
+
test.libs << 'lib' << 'test'
|
31
|
+
test.pattern = 'test/**/test_*.rb'
|
32
|
+
test.verbose = true
|
33
|
+
end
|
34
|
+
|
35
|
+
task :default => :test
|
36
|
+
|
37
|
+
require 'rdoc/task'
|
38
|
+
Rake::RDocTask.new do |rdoc|
|
39
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
40
|
+
|
41
|
+
rdoc.rdoc_dir = 'rdoc'
|
42
|
+
rdoc.title = "modaldiagrams #{version}"
|
43
|
+
rdoc.rdoc_files.include('README*')
|
44
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
45
|
+
end
|
data/TODO
ADDED
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.0.0
|
@@ -0,0 +1,277 @@
|
|
1
|
+
# ActiveRecord DB Diagrams
|
2
|
+
# Configuration parameters can be changed by writing a file named config/db_diagram.yml
|
3
|
+
|
4
|
+
module ModalDiagrams
|
5
|
+
|
6
|
+
# Field type abbreviations
|
7
|
+
TYPE = {
|
8
|
+
:date=>'d',
|
9
|
+
:datetime=>'dt',
|
10
|
+
:timestamp=>'ts',
|
11
|
+
:boolean=>'b',
|
12
|
+
:integer=>'i',
|
13
|
+
:string=>'s',
|
14
|
+
:text=>'tx',
|
15
|
+
:float=>'f',
|
16
|
+
:decimal=>'d',
|
17
|
+
:geometry=>'g'
|
18
|
+
}
|
19
|
+
|
20
|
+
class <<self
|
21
|
+
|
22
|
+
def enable_clusters
|
23
|
+
if defined?(::Rails)
|
24
|
+
::ActiveRecord::Base.class_eval do
|
25
|
+
def self.cluster(name=nil)
|
26
|
+
if name
|
27
|
+
@cluster_name = name
|
28
|
+
else
|
29
|
+
@cluster_name
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def generate(cfg)
|
37
|
+
|
38
|
+
classes = {""=>[]} # assoc cluster name ("" for no cluster) to array of class definitions
|
39
|
+
relations = [] # array of relation defitions
|
40
|
+
n_m = [] # this is to avoid duplicating n:m relations
|
41
|
+
rep = [] # this is to avoid multiple relations between two models
|
42
|
+
# and the next is to draw separate diagrams per cluster
|
43
|
+
cluster_classes = {} # assoc cluster name to array of class names
|
44
|
+
relation_classes = [] # each element is the 2-element array of class names of the corresponding relation in relations
|
45
|
+
|
46
|
+
models = dbmodels
|
47
|
+
|
48
|
+
models.each do |cls|
|
49
|
+
if cls.respond_to?(:reflect_on_all_associations) && ActiveRecord::Base.connection.table_exists?(cls.table_name)
|
50
|
+
# Note: Don't use content_columns ignores columns ending with _id which I use for enum fields
|
51
|
+
columns = cls.columns.reject { |c| c.primary || c.name =~ /(_count)$/ || c.name == cls.inheritance_column || c.name =~ /^(created_at|updated_at)$/ }.map{|c| "#{c.name} : #{TYPE[c.type]}"}
|
52
|
+
columns_to_ignore = cls.reflect_on_all_associations.map{|a|
|
53
|
+
cols = []
|
54
|
+
if a.macro == :belongs_to
|
55
|
+
cols << assoc_foreign_key(a)
|
56
|
+
cols << assoc_foreign_type(a) if a.options[:polymorphic]
|
57
|
+
end
|
58
|
+
cols
|
59
|
+
}.flatten.compact.uniq
|
60
|
+
columns.reject!{|c| c.split(':').first.strip.in? columns_to_ignore}
|
61
|
+
if cfg.sti_fields && cls.respond_to?(:fields_info) && cls.fields_info!=:omitted
|
62
|
+
columns.reject!{|c| !c.split(':').first.strip.in? cls.fields_info.map{|c| c.name.to_s}}
|
63
|
+
end
|
64
|
+
columns = columns.to(cfg.max_attributes) + ['...'] if columns.size > cfg.max_attributes
|
65
|
+
# columns.reject! do |cname|
|
66
|
+
# cname.match(/_id\Z/) || cname.match(/_type\Z/)
|
67
|
+
# end
|
68
|
+
# arrowhead,arrowtail=none, normal, inv, dot, odot, invdot, invodot, tee,
|
69
|
+
# empty, invempty, open(2,3,n), halfopen, diamond, odiamond, box, obox, crow.
|
70
|
+
if cls.respond_to?(:cluster)
|
71
|
+
cluster = cls.cluster.to_s
|
72
|
+
classes[cluster] ||= []
|
73
|
+
else
|
74
|
+
cluster = ""
|
75
|
+
end
|
76
|
+
classes[cluster] << %{"#{cls}" [shape=Mrecord, label="{#{cls}|#{columns.join('\\l')}\\l}"]}
|
77
|
+
cluster_classes[cluster] ||= []
|
78
|
+
cluster_classes[cluster] << cls.to_s
|
79
|
+
cls.reflect_on_all_associations.each do |assoc|
|
80
|
+
target,type = nil,nil
|
81
|
+
case assoc.macro
|
82
|
+
when :has_many
|
83
|
+
unless assoc.options[:through]
|
84
|
+
target = assoc.class_name
|
85
|
+
type = :one_to_many
|
86
|
+
if !cfg.show_multiple && rep.include?([cls.to_s, target.to_s])
|
87
|
+
target = type = nil
|
88
|
+
else
|
89
|
+
rep << [cls.to_s,target.to_s]
|
90
|
+
end
|
91
|
+
end
|
92
|
+
when :has_one
|
93
|
+
target = assoc.class_name
|
94
|
+
type = :one_to_one
|
95
|
+
when :has_and_belongs_to_many
|
96
|
+
target = assoc.class_name
|
97
|
+
type = :many_to_many
|
98
|
+
if n_m.include?([target.to_s, cls.to_s])
|
99
|
+
target = type = nil
|
100
|
+
else
|
101
|
+
n_m << [cls.to_s,target.to_s]
|
102
|
+
end
|
103
|
+
end
|
104
|
+
if target
|
105
|
+
pk = assoc_foreign_key(assoc)
|
106
|
+
# detect specially named associations (usually polymorphi) and label it
|
107
|
+
if pk != cls.to_s.underscore+"_id"
|
108
|
+
label = pk.to_s
|
109
|
+
label = label[0..-4] if label[-3..-1]=='_id'
|
110
|
+
if label.size<=15
|
111
|
+
label = "headlabel=#{label}, "
|
112
|
+
else
|
113
|
+
label = nil
|
114
|
+
end
|
115
|
+
else
|
116
|
+
label = nil
|
117
|
+
end
|
118
|
+
# detect polymorphic associations to use a different symbol
|
119
|
+
poly = false
|
120
|
+
if iassoc = target.constantize.reflect_on_all_associations.detect{|a| !a.options[:through] && pk==assoc_foreign_key(a)}
|
121
|
+
if iassoc.options[:polymorphic]
|
122
|
+
poly = true
|
123
|
+
end
|
124
|
+
end
|
125
|
+
#puts "#{cls} -#{type}-> #{target}"
|
126
|
+
case type
|
127
|
+
when :one_to_one
|
128
|
+
tail = "none"
|
129
|
+
head = poly ? "obox" : "tee" # mark the end with the foreign key
|
130
|
+
when :one_to_many
|
131
|
+
tail = "none"
|
132
|
+
head = poly ? "odot" : "dot"
|
133
|
+
when :many_to_many
|
134
|
+
tail = "dot"
|
135
|
+
head = "dot"
|
136
|
+
end
|
137
|
+
samehead = (cfg.unified_polymorphic && label) ? %{, samehead="#{label}"} : ''
|
138
|
+
label = nil if cfg.no_association_labels
|
139
|
+
relations << %{"#{cls}" -> "#{target}" [arrowtail=#{tail}, arrowhead=#{head}#{samehead} #{label}dir=both]}
|
140
|
+
relation_classes << [cls.to_s, target.to_s]
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
if cfg.show_sti
|
147
|
+
sti_classes.each do |sti_class|
|
148
|
+
cls = sti_class.base_class
|
149
|
+
if cls.respond_to?(:cluster)
|
150
|
+
cluster = cls.cluster.to_s
|
151
|
+
else
|
152
|
+
cluster = ""
|
153
|
+
end
|
154
|
+
if cfg.sti_fields && sti_class.respond_to?(:fields_info) && sti_class.fields_info!=:omitted
|
155
|
+
columns = sti_class.fields_info.map{|c| "#{c.name} : #{TYPE[c.type]}"}
|
156
|
+
columns = columns.to(cfg.max_attributes) + ['...'] if columns.size > cfg.max_attributes
|
157
|
+
else
|
158
|
+
columns = nil
|
159
|
+
end
|
160
|
+
classes[cluster] ||= []
|
161
|
+
classes[cluster] << %{"#{sti_class}" [shape=Mrecord, label="{#{sti_class}\\l}"]}
|
162
|
+
cluster_classes[cluster] ||= []
|
163
|
+
cluster_classes[cluster] << sti_class.to_s
|
164
|
+
if columns
|
165
|
+
classes[cluster] << %{"#{sti_class}" [shape=Mrecord, label="{#{sti_class}|#{columns.join('\\l')}\\l}"]}
|
166
|
+
end
|
167
|
+
base_class = sti_class.superclass
|
168
|
+
relations << %{"#{base_class}" -> "#{sti_class}" [arrowtail=onormal, arrowhead=none, dir=both]}
|
169
|
+
relation_classes << [base_class.to_s, sti_class.to_s]
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
fn = Rails.root.join('db/diagrams/diagram.dot')
|
174
|
+
mkdir_p fn.dirname
|
175
|
+
File.open(fn,'w') do |f|
|
176
|
+
add_diagram_header f
|
177
|
+
cluster_id = 0
|
178
|
+
all_classes = []
|
179
|
+
classes.keys.each do |cluster|
|
180
|
+
next if cluster.in? cfg.clusters_not_shown_on_main_diagram
|
181
|
+
all_classes += cluster_classes[cluster]
|
182
|
+
cluster_id += 1
|
183
|
+
add_diagram_classes f, classes[cluster], cluster, cluster_id, cfg.show_cluster_boxes
|
184
|
+
end
|
185
|
+
add_diagram_relations f, relations, relation_classes, all_classes, cfg.show_external
|
186
|
+
add_diagram_footer f
|
187
|
+
end
|
188
|
+
classes.keys.each do |cluster|
|
189
|
+
#next if cluster.blank?
|
190
|
+
fn = "db/diagrams/diagram_#{cluster.downcase}.dot"
|
191
|
+
File.open(fn,'w') do |f|
|
192
|
+
add_diagram_header f
|
193
|
+
add_diagram_classes f, classes[cluster]
|
194
|
+
add_diagram_relations f, relations, relation_classes, cluster_classes[cluster], cfg.show_external
|
195
|
+
add_diagram_footer f
|
196
|
+
end
|
197
|
+
end
|
198
|
+
puts "The diagrams have been written to #{fn}"
|
199
|
+
|
200
|
+
end
|
201
|
+
|
202
|
+
|
203
|
+
private
|
204
|
+
|
205
|
+
# return ActiveRecord classes corresponding to tables, without STI derived classes, but including indirectly
|
206
|
+
# derived classes that do have their own tables (to achieve this we use the convention that in such cases
|
207
|
+
# the base class, directly derived from ActiveRecord::Base has a nil table_name)
|
208
|
+
def dbmodels
|
209
|
+
models = Dir.glob(File.join(Rails.root,"app/models/**/*.rb"))\
|
210
|
+
.map{|f| File.basename(f).chomp(".rb").camelize.constantize}\
|
211
|
+
.select{|c| has_table(c)}\
|
212
|
+
.reject{|c| has_table(c.superclass)}
|
213
|
+
models += ActiveRecord::Base.send(:subclasses).reject{|c| c.name.starts_with?('CGI::') || !has_table(c) || has_table(c.superclass)}
|
214
|
+
models.uniq
|
215
|
+
end
|
216
|
+
|
217
|
+
def sti_classes
|
218
|
+
models = Dir.glob(File.join(Rails.root,"app/models/**/*.rb"))\
|
219
|
+
.map{|f| File.basename(f).chomp(".rb").camelize.constantize}\
|
220
|
+
.select{|c| has_table(c) && c.base_class!=c}
|
221
|
+
models += ActiveRecord::Base.send(:subclasses).reject{|c| c.name.starts_with?('CGI::') || !has_table(c)}.select{|c| has_table(c) && c.base_class!=c}
|
222
|
+
models.uniq
|
223
|
+
end
|
224
|
+
|
225
|
+
def assoc_foreign_key(assoc)
|
226
|
+
# Up to ActiveRecord 3.1 we had primary_key_name in AssociationReflection; not it is foreign_key
|
227
|
+
assoc.respond_to?(:primary_key_name) ? assoc.primary_key_name : assoc.foreign_key
|
228
|
+
end
|
229
|
+
|
230
|
+
def assoc_foreign_type(assoc)
|
231
|
+
assoc.respond_to?(:foreign_type) ? assoc.foreign_type : assoc.options[:foreign_key]
|
232
|
+
end
|
233
|
+
|
234
|
+
def has_table(cls)
|
235
|
+
(cls!=ActiveRecord::Base) && cls.respond_to?(:table_name) && !cls.table_name.blank?
|
236
|
+
end
|
237
|
+
|
238
|
+
def add_diagram_header(f)
|
239
|
+
f.puts "digraph models_diagram {"
|
240
|
+
f.puts " graph[overlap=false, splines=true]"
|
241
|
+
f.puts " edge[labeldistance=2.5, labelfloat=true, labelfontname=Helvetica, decorate=false, labelangle=-30, fontsize=10, fontcolor=gray35]"
|
242
|
+
end
|
243
|
+
|
244
|
+
def add_diagram_classes(f, classes, cluster="", cluster_id=nil, show_cluster_boxes=false)
|
245
|
+
if cluster==""
|
246
|
+
f.puts classes.join("\n")
|
247
|
+
elsif cluster.downcase==cluster || !show_cluster_boxes
|
248
|
+
f.puts %<subgraph #{cluster_id} {\n #{classes.join("\n ")}\n}>
|
249
|
+
else
|
250
|
+
f.puts %<subgraph cluster_#{cluster_id} { label="#{cluster}"\n #{classes.join("\n ")}\n}>
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
def add_diagram_relations(f, relations, relation_classes, inner_classes, show_external)
|
255
|
+
relations.each_index do |i|
|
256
|
+
relation = relations[i]
|
257
|
+
is_internal = relation_classes[i].all?{|c| c.in? inner_classes}
|
258
|
+
is_external = relation_classes[i].any?{|c| c.in? inner_classes} && !is_internal
|
259
|
+
if show_external
|
260
|
+
show_relation = is_internal || is_external
|
261
|
+
unless is_internal
|
262
|
+
relation = relation.sub('arrowtail', 'color="gray", arrowtail')
|
263
|
+
end
|
264
|
+
else
|
265
|
+
show_relation = is_internal
|
266
|
+
end
|
267
|
+
f.puts relation if show_relation
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
def add_diagram_footer(f)
|
272
|
+
f.puts "}\n"
|
273
|
+
end
|
274
|
+
|
275
|
+
end
|
276
|
+
|
277
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module ModalDiagrams
|
2
|
+
|
3
|
+
def self.parameters
|
4
|
+
@settings ||= Settings[
|
5
|
+
# Default values
|
6
|
+
:max_attributes => 24, # maximum number of attributes shown in a class (table)
|
7
|
+
:clusters_not_shown_on_main_diagram => [], # clusters not shown in the main diagram
|
8
|
+
:show_external => true, # show associations to classes from other clusters in cluster diagrams
|
9
|
+
:show_cluster_boxes => false, # display cluster names capitalized inside a box (good for dot; bad for neato)
|
10
|
+
:show_multiple => true, # show multiple associations between two classes
|
11
|
+
:show_sti => true, # show STI classes
|
12
|
+
:sti_fields => true, # show fields of STI classes where declared if using modalfields
|
13
|
+
:unified_polymorphic => false, # unify the circled end of polymorphic associations
|
14
|
+
:no_association_labels=>false, # don't show association labels
|
15
|
+
:output_tools => %w{dot fdp neato} # graphviz styles (tools) used whe generating output (ps/png/pdf)
|
16
|
+
].merge Settings.load(Rails.root.join('config','db_diagram.yml'))
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
Dir[File.join(File.dirname(__FILE__), '..', 'tasks', '**/*.rake')].each { |f| load f }
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'modaldiagrams/parameters'
|
2
|
+
require 'modaldiagrams/modaldiagrams'
|
3
|
+
|
4
|
+
if defined?(Rails) && Rails.respond_to?(:version)
|
5
|
+
if Rails.version.split('.').first.to_i > 2
|
6
|
+
class BackupTask < Rails::Railtie
|
7
|
+
rake_tasks do
|
8
|
+
Dir[File.join(File.dirname(__FILE__), 'tasks', '**/*.rake')].each { |f| load f }
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
ModalDiagrams.enable_clusters if defined?(ActiveRecord::Base)
|
13
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
namespace :db do
|
2
|
+
|
3
|
+
desc "Generate DB diagrams (in dot format)"
|
4
|
+
task :diagrams => [:environment] do
|
5
|
+
|
6
|
+
cfg = ModalDiagrams.parameters
|
7
|
+
puts "Using parameters:"
|
8
|
+
puts cfg.to_yaml + "\n"
|
9
|
+
puts "To change the parameters create or edit a file named config/modal_diagrams.yml"
|
10
|
+
|
11
|
+
ModalDiagrams.generate cfg
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
namespace :db do
|
2
|
+
namespace :diagrams do
|
3
|
+
|
4
|
+
desc "Generate DB diagrams and convert to PDF with Graphviz and Ghostview"
|
5
|
+
task :pdf => 'db:diagrams:ps' do
|
6
|
+
ModalDiagrams.parameters.output_tools.each do |cmd|
|
7
|
+
in_dir = Rails.root.join("db/diagrams/#{cmd}_ps")
|
8
|
+
out_dir = Rails.root.join("db/diagrams/#{cmd}_pdf")
|
9
|
+
mkdir_p out_dir
|
10
|
+
Dir[in_dir.join('*.ps')].each do |fn|
|
11
|
+
fn = Pathname(fn)
|
12
|
+
out_fn = out_dir.join(fn.basename.sub_ext('.pdf'))
|
13
|
+
`ps2pdf #{fn} #{out_fn}`
|
14
|
+
end
|
15
|
+
puts "Output has been generated in #{out_dir}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
namespace :db do
|
2
|
+
namespace :diagrams do
|
3
|
+
|
4
|
+
desc "Generate DB diagrams and convert to PNG with Graphviz and Ghostview"
|
5
|
+
task :png => 'db:diagrams:ps' do
|
6
|
+
ModalDiagrams.parameters.output_tools.each do |cmd|
|
7
|
+
in_dir = Rails.root.join("db/diagrams/#{cmd}_ps")
|
8
|
+
out_dir = Rails.root.join("db/diagrams/#{cmd}_png")
|
9
|
+
mkdir_p out_dir
|
10
|
+
Dir[in_dir.join('*.ps')].each do |fn|
|
11
|
+
fn = Pathname(fn)
|
12
|
+
out_fn = out_dir.join(fn.basename.sub_ext('.png'))
|
13
|
+
`gs -q -dNOPAUSE -dBATCH -dTextAlphaBits=4 -dGraphicsAlphaBits=4 -sDEVICE=png16m -sOutputFile=#{out_fn} #{fn}`
|
14
|
+
end
|
15
|
+
puts "Output has been generated in #{out_dir}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
namespace :db do
|
2
|
+
namespace :diagrams do
|
3
|
+
|
4
|
+
desc "Generate DB diagrams and convert to PS with Graphviz"
|
5
|
+
task :ps => 'db:diagrams' do
|
6
|
+
ModalDiagrams.parameters.output_tools.each do |cmd|
|
7
|
+
out_dir = Rails.root.join("db/diagrams/#{cmd}_ps")
|
8
|
+
mkdir_p out_dir
|
9
|
+
Dir[Rails.root.join('db/diagrams/*.dot')].each do |fn|
|
10
|
+
fn = Pathname(fn)
|
11
|
+
out_fn = out_dir.join(fn.basename.sub_ext('.ps'))
|
12
|
+
`#{cmd} #{fn} -o#{out_fn} -Tps2`
|
13
|
+
end
|
14
|
+
puts "Output has been generated in #{out_dir}"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
data/lib/tasks.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
Dir[File.join(File.dirname(__FILE__), '..', 'tasks', '**/*.rake')].each { |f| load f }
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "modaldiagrams"
|
8
|
+
s.version = "1.0.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Javier Goizueta"]
|
12
|
+
s.date = "2012-09-05"
|
13
|
+
s.description = "modaldiagrams provides Rake tasks for diagramming ActiveRecord databases. It generates Graphviz dot files."
|
14
|
+
s.email = "jgoizueta@gmail.com"
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE.txt",
|
17
|
+
"README.rdoc",
|
18
|
+
"TODO"
|
19
|
+
]
|
20
|
+
s.files = [
|
21
|
+
"Gemfile",
|
22
|
+
"Gemfile.lock",
|
23
|
+
"LICENSE.txt",
|
24
|
+
"README.rdoc",
|
25
|
+
"Rakefile",
|
26
|
+
"TODO",
|
27
|
+
"VERSION",
|
28
|
+
"lib/modaldiagrams.rb",
|
29
|
+
"lib/modaldiagrams/modaldiagrams.rb",
|
30
|
+
"lib/modaldiagrams/parameters.rb",
|
31
|
+
"lib/modaldiagrams/tasks.rb",
|
32
|
+
"lib/tasks.rb",
|
33
|
+
"lib/tasks/diagrams.rake",
|
34
|
+
"lib/tasks/diagrams_pdf.rake",
|
35
|
+
"lib/tasks/diagrams_png.rake",
|
36
|
+
"lib/tasks/diagrams_ps.rake",
|
37
|
+
"modaldiagrams.gemspec"
|
38
|
+
]
|
39
|
+
s.homepage = "http://github.com/jgoizueta/modaldiagrams"
|
40
|
+
s.licenses = ["MIT"]
|
41
|
+
s.require_paths = ["lib"]
|
42
|
+
s.rubygems_version = "1.8.23"
|
43
|
+
s.summary = "DB diagramming tool fo Rails ActiveRecord applications"
|
44
|
+
|
45
|
+
if s.respond_to? :specification_version then
|
46
|
+
s.specification_version = 3
|
47
|
+
|
48
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
49
|
+
s.add_runtime_dependency(%q<modalsettings>, [">= 0"])
|
50
|
+
s.add_runtime_dependency(%q<modalsupport>, [">= 0.9.2"])
|
51
|
+
s.add_development_dependency(%q<shoulda>, [">= 0"])
|
52
|
+
s.add_development_dependency(%q<rdoc>, ["~> 3.12"])
|
53
|
+
s.add_development_dependency(%q<bundler>, ["~> 1.1.0"])
|
54
|
+
s.add_development_dependency(%q<jeweler>, ["~> 1.8.4"])
|
55
|
+
s.add_development_dependency(%q<modalfields>, [">= 1.2.0"])
|
56
|
+
else
|
57
|
+
s.add_dependency(%q<modalsettings>, [">= 0"])
|
58
|
+
s.add_dependency(%q<modalsupport>, [">= 0.9.2"])
|
59
|
+
s.add_dependency(%q<shoulda>, [">= 0"])
|
60
|
+
s.add_dependency(%q<rdoc>, ["~> 3.12"])
|
61
|
+
s.add_dependency(%q<bundler>, ["~> 1.1.0"])
|
62
|
+
s.add_dependency(%q<jeweler>, ["~> 1.8.4"])
|
63
|
+
s.add_dependency(%q<modalfields>, [">= 1.2.0"])
|
64
|
+
end
|
65
|
+
else
|
66
|
+
s.add_dependency(%q<modalsettings>, [">= 0"])
|
67
|
+
s.add_dependency(%q<modalsupport>, [">= 0.9.2"])
|
68
|
+
s.add_dependency(%q<shoulda>, [">= 0"])
|
69
|
+
s.add_dependency(%q<rdoc>, ["~> 3.12"])
|
70
|
+
s.add_dependency(%q<bundler>, ["~> 1.1.0"])
|
71
|
+
s.add_dependency(%q<jeweler>, ["~> 1.8.4"])
|
72
|
+
s.add_dependency(%q<modalfields>, [">= 1.2.0"])
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
metadata
ADDED
@@ -0,0 +1,181 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: modaldiagrams
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Javier Goizueta
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-09-05 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: modalsettings
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: modalsupport
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 0.9.2
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 0.9.2
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: shoulda
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: rdoc
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '3.12'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '3.12'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: bundler
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ~>
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: 1.1.0
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ~>
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: 1.1.0
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: jeweler
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ~>
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: 1.8.4
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ~>
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: 1.8.4
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: modalfields
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ! '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: 1.2.0
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ! '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: 1.2.0
|
126
|
+
description: modaldiagrams provides Rake tasks for diagramming ActiveRecord databases.
|
127
|
+
It generates Graphviz dot files.
|
128
|
+
email: jgoizueta@gmail.com
|
129
|
+
executables: []
|
130
|
+
extensions: []
|
131
|
+
extra_rdoc_files:
|
132
|
+
- LICENSE.txt
|
133
|
+
- README.rdoc
|
134
|
+
- TODO
|
135
|
+
files:
|
136
|
+
- Gemfile
|
137
|
+
- Gemfile.lock
|
138
|
+
- LICENSE.txt
|
139
|
+
- README.rdoc
|
140
|
+
- Rakefile
|
141
|
+
- TODO
|
142
|
+
- VERSION
|
143
|
+
- lib/modaldiagrams.rb
|
144
|
+
- lib/modaldiagrams/modaldiagrams.rb
|
145
|
+
- lib/modaldiagrams/parameters.rb
|
146
|
+
- lib/modaldiagrams/tasks.rb
|
147
|
+
- lib/tasks.rb
|
148
|
+
- lib/tasks/diagrams.rake
|
149
|
+
- lib/tasks/diagrams_pdf.rake
|
150
|
+
- lib/tasks/diagrams_png.rake
|
151
|
+
- lib/tasks/diagrams_ps.rake
|
152
|
+
- modaldiagrams.gemspec
|
153
|
+
homepage: http://github.com/jgoizueta/modaldiagrams
|
154
|
+
licenses:
|
155
|
+
- MIT
|
156
|
+
post_install_message:
|
157
|
+
rdoc_options: []
|
158
|
+
require_paths:
|
159
|
+
- lib
|
160
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
161
|
+
none: false
|
162
|
+
requirements:
|
163
|
+
- - ! '>='
|
164
|
+
- !ruby/object:Gem::Version
|
165
|
+
version: '0'
|
166
|
+
segments:
|
167
|
+
- 0
|
168
|
+
hash: -3401625637363987072
|
169
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
170
|
+
none: false
|
171
|
+
requirements:
|
172
|
+
- - ! '>='
|
173
|
+
- !ruby/object:Gem::Version
|
174
|
+
version: '0'
|
175
|
+
requirements: []
|
176
|
+
rubyforge_project:
|
177
|
+
rubygems_version: 1.8.23
|
178
|
+
signing_key:
|
179
|
+
specification_version: 3
|
180
|
+
summary: DB diagramming tool fo Rails ActiveRecord applications
|
181
|
+
test_files: []
|