ddollar-railroad 0.7.1.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.
- data/COPYING +340 -0
- data/ChangeLog +85 -0
- data/History.txt +23 -0
- data/Manifest.txt +17 -0
- data/README.txt +138 -0
- data/Rakefile +15 -0
- data/bin/railroad +45 -0
- data/init.rb +0 -0
- data/lib/railroad/aasm_diagram.rb +79 -0
- data/lib/railroad/app_diagram.rb +86 -0
- data/lib/railroad/controllers_diagram.rb +81 -0
- data/lib/railroad/diagram_graph.rb +133 -0
- data/lib/railroad/models_diagram.rb +190 -0
- data/lib/railroad/options_struct.rb +177 -0
- data/lib/railroad.rb +11 -0
- data/tasks/diagrams.rake +35 -0
- metadata +85 -0
@@ -0,0 +1,79 @@
|
|
1
|
+
# RailRoad - RoR diagrams generator
|
2
|
+
# http://railroad.rubyforge.org
|
3
|
+
#
|
4
|
+
# Copyright 2007-2008 - Javier Smaldone (http://www.smaldone.com.ar)
|
5
|
+
# See COPYING for more details
|
6
|
+
|
7
|
+
# AASM code provided by Ana Nelson (http://ananelson.com/)
|
8
|
+
|
9
|
+
# Diagram for Acts As State Machine
|
10
|
+
class AasmDiagram < AppDiagram
|
11
|
+
|
12
|
+
def initialize(options)
|
13
|
+
#options.exclude.map! {|e| e = "app/models/" + e}
|
14
|
+
super options
|
15
|
+
@graph.diagram_type = 'Models'
|
16
|
+
# Processed habtm associations
|
17
|
+
@habtm = []
|
18
|
+
end
|
19
|
+
|
20
|
+
# Process model files
|
21
|
+
def generate
|
22
|
+
STDERR.print "Generating AASM diagram\n" if @options.verbose
|
23
|
+
files = Dir.glob("app/models/*.rb")
|
24
|
+
files += Dir.glob("vendor/plugins/**/app/models/*.rb") if @options.plugins_models
|
25
|
+
files -= @options.exclude
|
26
|
+
files.each do |f|
|
27
|
+
process_class extract_class_name('app/models/', f).constantize
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
# Load model classes
|
34
|
+
def load_classes
|
35
|
+
begin
|
36
|
+
disable_stdout
|
37
|
+
files = Dir.glob("app/models/**/*.rb")
|
38
|
+
files += Dir.glob("vendor/plugins/**/app/models/*.rb") if @options.plugins_models
|
39
|
+
files -= @options.exclude
|
40
|
+
files.each {|m| require m }
|
41
|
+
enable_stdout
|
42
|
+
rescue LoadError
|
43
|
+
enable_stdout
|
44
|
+
print_error "model classes"
|
45
|
+
raise
|
46
|
+
end
|
47
|
+
end # load_classes
|
48
|
+
|
49
|
+
# Process a model class
|
50
|
+
def process_class(current_class)
|
51
|
+
|
52
|
+
STDERR.print "\tProcessing #{current_class}\n" if @options.verbose
|
53
|
+
|
54
|
+
# Only interested in acts_as_state_machine models.
|
55
|
+
return unless current_class.respond_to?'states'
|
56
|
+
|
57
|
+
node_attribs = []
|
58
|
+
node_type = 'aasm'
|
59
|
+
|
60
|
+
current_class.states.each do |state_name|
|
61
|
+
state = current_class.read_inheritable_attribute(:states)[state_name]
|
62
|
+
node_shape = (current_class.initial_state === state_name) ? ", peripheries = 2" : ""
|
63
|
+
node_attribs << "#{current_class.name.downcase}_#{state_name} [label=#{state_name} #{node_shape}];"
|
64
|
+
end
|
65
|
+
@graph.add_node [node_type, current_class.name, node_attribs]
|
66
|
+
|
67
|
+
current_class.read_inheritable_attribute(:transition_table).each do |event_name, event|
|
68
|
+
event.each do |transition|
|
69
|
+
@graph.add_edge [
|
70
|
+
'event',
|
71
|
+
current_class.name.downcase + "_" + transition.from.to_s,
|
72
|
+
current_class.name.downcase + "_" + transition.to.to_s,
|
73
|
+
event_name.to_s
|
74
|
+
]
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end # process_class
|
78
|
+
|
79
|
+
end # class AasmDiagram
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# RailRoad - RoR diagrams generator
|
2
|
+
# http://railroad.rubyforge.org
|
3
|
+
#
|
4
|
+
# Copyright 2007-2008 - Javier Smaldone (http://www.smaldone.com.ar)
|
5
|
+
# See COPYING for more details
|
6
|
+
|
7
|
+
# Root class for RailRoad diagrams
|
8
|
+
class AppDiagram
|
9
|
+
|
10
|
+
def initialize(options)
|
11
|
+
@options = options
|
12
|
+
@graph = DiagramGraph.new
|
13
|
+
@graph.show_label = @options.label
|
14
|
+
|
15
|
+
STDERR.print "Loading application environment\n" if @options.verbose
|
16
|
+
load_environment
|
17
|
+
|
18
|
+
STDERR.print "Loading application classes\n" if @options.verbose
|
19
|
+
load_classes
|
20
|
+
end
|
21
|
+
|
22
|
+
# Print diagram
|
23
|
+
def print
|
24
|
+
if @options.output
|
25
|
+
old_stdout = STDOUT.dup
|
26
|
+
begin
|
27
|
+
STDOUT.reopen(@options.output)
|
28
|
+
rescue
|
29
|
+
STDERR.print "Error: Cannot write diagram to #{@options.output}\n\n"
|
30
|
+
exit 2
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
if @options.xmi
|
35
|
+
STDERR.print "Generating XMI diagram\n" if @options.verbose
|
36
|
+
STDOUT.print @graph.to_xmi
|
37
|
+
else
|
38
|
+
STDERR.print "Generating DOT graph\n" if @options.verbose
|
39
|
+
STDOUT.print @graph.to_dot
|
40
|
+
end
|
41
|
+
|
42
|
+
if @options.output
|
43
|
+
STDOUT.reopen(old_stdout)
|
44
|
+
end
|
45
|
+
end # print
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
# Prevents Rails application from writing to STDOUT
|
50
|
+
def disable_stdout
|
51
|
+
@old_stdout = STDOUT.dup
|
52
|
+
STDOUT.reopen(PLATFORM =~ /mswin/ ? "NUL" : "/dev/null")
|
53
|
+
end
|
54
|
+
|
55
|
+
# Restore STDOUT
|
56
|
+
def enable_stdout
|
57
|
+
STDOUT.reopen(@old_stdout)
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
# Print error when loading Rails application
|
62
|
+
def print_error(type)
|
63
|
+
STDERR.print "Error loading #{type}.\n (Are you running " +
|
64
|
+
"#{APP_NAME} on the aplication's root directory?)\n\n"
|
65
|
+
end
|
66
|
+
|
67
|
+
# Load Rails application's environment
|
68
|
+
def load_environment
|
69
|
+
begin
|
70
|
+
disable_stdout
|
71
|
+
require "config/environment"
|
72
|
+
enable_stdout
|
73
|
+
rescue LoadError
|
74
|
+
enable_stdout
|
75
|
+
print_error "application environment"
|
76
|
+
raise
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# Extract class name from filename
|
81
|
+
def extract_class_name(base, filename)
|
82
|
+
# this is will handle directory names as namespace names
|
83
|
+
filename.reverse.chomp(base.reverse).reverse.chomp(".rb").camelize
|
84
|
+
end
|
85
|
+
|
86
|
+
end # class AppDiagram
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# RailRoad - RoR diagrams generator
|
2
|
+
# http://railroad.rubyforge.org
|
3
|
+
#
|
4
|
+
# Copyright 2007-2008 - Javier Smaldone (http://www.smaldone.com.ar)
|
5
|
+
# See COPYING for more details
|
6
|
+
|
7
|
+
# RailRoad controllers diagram
|
8
|
+
class ControllersDiagram < AppDiagram
|
9
|
+
|
10
|
+
def initialize(options)
|
11
|
+
#options.exclude.map! {|e| "app/controllers/" + e}
|
12
|
+
super options
|
13
|
+
@graph.diagram_type = 'Controllers'
|
14
|
+
end
|
15
|
+
|
16
|
+
# Process controller files
|
17
|
+
def generate
|
18
|
+
STDERR.print "Generating controllers diagram\n" if @options.verbose
|
19
|
+
|
20
|
+
files = Dir.glob("app/controllers/**/*_controller.rb") - @options.exclude
|
21
|
+
files << 'app/controllers/application.rb'
|
22
|
+
files.each do |f|
|
23
|
+
class_name = extract_class_name('app/controllers/', f)
|
24
|
+
# ApplicationController's file is 'application.rb'
|
25
|
+
class_name += 'Controller' if class_name == 'Application'
|
26
|
+
process_class class_name.constantize
|
27
|
+
end
|
28
|
+
end # generate
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
# Load controller classes
|
33
|
+
def load_classes
|
34
|
+
begin
|
35
|
+
disable_stdout
|
36
|
+
# ApplicationController must be loaded first
|
37
|
+
require "app/controllers/application.rb"
|
38
|
+
files = Dir.glob("app/controllers/**/*_controller.rb") - @options.exclude
|
39
|
+
files.each {|c| require c }
|
40
|
+
enable_stdout
|
41
|
+
rescue LoadError
|
42
|
+
enable_stdout
|
43
|
+
print_error "controller classes"
|
44
|
+
raise
|
45
|
+
end
|
46
|
+
end # load_classes
|
47
|
+
|
48
|
+
# Proccess a controller class
|
49
|
+
def process_class(current_class)
|
50
|
+
|
51
|
+
STDERR.print "\tProcessing #{current_class}\n" if @options.verbose
|
52
|
+
|
53
|
+
if @options.brief
|
54
|
+
@graph.add_node ['controller-brief', current_class.name]
|
55
|
+
elsif current_class.is_a? Class
|
56
|
+
# Collect controller's methods
|
57
|
+
node_attribs = {:public => [],
|
58
|
+
:protected => [],
|
59
|
+
:private => []}
|
60
|
+
current_class.public_instance_methods(false).sort.each { |m|
|
61
|
+
node_attribs[:public] << m
|
62
|
+
} unless @options.hide_public
|
63
|
+
current_class.protected_instance_methods(false).sort.each { |m|
|
64
|
+
node_attribs[:protected] << m
|
65
|
+
} unless @options.hide_protected
|
66
|
+
current_class.private_instance_methods(false).sort.each { |m|
|
67
|
+
node_attribs[:private] << m
|
68
|
+
} unless @options.hide_private
|
69
|
+
@graph.add_node ['controller', current_class.name, node_attribs]
|
70
|
+
elsif @options.modules && current_class.is_a?(Module)
|
71
|
+
@graph.add_node ['module', current_class.name]
|
72
|
+
end
|
73
|
+
|
74
|
+
# Generate the inheritance edge (only for ApplicationControllers)
|
75
|
+
if @options.inheritance &&
|
76
|
+
(ApplicationController.subclasses.include? current_class.name)
|
77
|
+
@graph.add_edge ['is-a', current_class.superclass.name, current_class.name]
|
78
|
+
end
|
79
|
+
end # process_class
|
80
|
+
|
81
|
+
end # class ControllersDiagram
|
@@ -0,0 +1,133 @@
|
|
1
|
+
# RailRoad - RoR diagrams generator
|
2
|
+
# http://railroad.rubyforge.org
|
3
|
+
#
|
4
|
+
# Copyright 2007-2008 - Javier Smaldone (http://www.smaldone.com.ar)
|
5
|
+
# See COPYING for more details
|
6
|
+
|
7
|
+
|
8
|
+
# RailRoad diagram structure
|
9
|
+
class DiagramGraph
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@diagram_type = ''
|
13
|
+
@show_label = false
|
14
|
+
@nodes = []
|
15
|
+
@edges = []
|
16
|
+
end
|
17
|
+
|
18
|
+
def add_node(node)
|
19
|
+
@nodes << node
|
20
|
+
end
|
21
|
+
|
22
|
+
def add_edge(edge)
|
23
|
+
@edges << edge
|
24
|
+
end
|
25
|
+
|
26
|
+
def diagram_type= (type)
|
27
|
+
@diagram_type = type
|
28
|
+
end
|
29
|
+
|
30
|
+
def show_label= (value)
|
31
|
+
@show_label = value
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
# Generate DOT graph
|
36
|
+
def to_dot
|
37
|
+
return dot_header +
|
38
|
+
@nodes.map{|n| dot_node n[0], n[1], n[2]}.join +
|
39
|
+
@edges.map{|e| dot_edge e[0], e[1], e[2], e[3]}.join +
|
40
|
+
dot_footer
|
41
|
+
end
|
42
|
+
|
43
|
+
# Generate XMI diagram (not yet implemented)
|
44
|
+
def to_xmi
|
45
|
+
STDERR.print "Sorry. XMI output not yet implemented.\n\n"
|
46
|
+
return ""
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
# Build DOT diagram header
|
52
|
+
def dot_header
|
53
|
+
result = "digraph #{@diagram_type.downcase}_diagram {\n" +
|
54
|
+
"\tgraph[overlap=false, splines=true]\n"
|
55
|
+
result += dot_label if @show_label
|
56
|
+
return result
|
57
|
+
end
|
58
|
+
|
59
|
+
# Build DOT diagram footer
|
60
|
+
def dot_footer
|
61
|
+
return "}\n"
|
62
|
+
end
|
63
|
+
|
64
|
+
# Build diagram label
|
65
|
+
def dot_label
|
66
|
+
return "\t_diagram_info [shape=\"plaintext\", " +
|
67
|
+
"label=\"#{@diagram_type} diagram\\l" +
|
68
|
+
"Date: #{Time.now.strftime "%b %d %Y - %H:%M"}\\l" +
|
69
|
+
"Migration version: " +
|
70
|
+
"#{ActiveRecord::Migrator.current_version}\\l" +
|
71
|
+
"Generated by #{APP_HUMAN_NAME} #{APP_VERSION.join('.')}"+
|
72
|
+
"\\l\", fontsize=14]\n"
|
73
|
+
end
|
74
|
+
|
75
|
+
# Build a DOT graph node
|
76
|
+
def dot_node(type, name, attributes=nil)
|
77
|
+
case type
|
78
|
+
when 'model'
|
79
|
+
options = 'shape=Mrecord, label="{' + name + '|'
|
80
|
+
options += attributes.join('\l')
|
81
|
+
options += '\l}"'
|
82
|
+
when 'model-brief'
|
83
|
+
options = ''
|
84
|
+
when 'class'
|
85
|
+
options = 'shape=record, label="{' + name + '|}"'
|
86
|
+
when 'class-brief'
|
87
|
+
options = 'shape=box'
|
88
|
+
when 'controller'
|
89
|
+
options = 'shape=Mrecord, label="{' + name + '|'
|
90
|
+
public_methods = attributes[:public].join('\l')
|
91
|
+
protected_methods = attributes[:protected].join('\l')
|
92
|
+
private_methods = attributes[:private].join('\l')
|
93
|
+
options += public_methods + '\l|' + protected_methods + '\l|' +
|
94
|
+
private_methods + '\l'
|
95
|
+
options += '}"'
|
96
|
+
when 'controller-brief'
|
97
|
+
options = ''
|
98
|
+
when 'module'
|
99
|
+
options = 'shape=box, style=dotted, label="' + name + '"'
|
100
|
+
when 'aasm'
|
101
|
+
# Return subgraph format
|
102
|
+
return "subgraph cluster_#{name.downcase} {\n\tlabel = #{quote(name)}\n\t#{attributes.join("\n ")}}"
|
103
|
+
end # case
|
104
|
+
return "\t#{quote(name)} [#{options}]\n"
|
105
|
+
end # dot_node
|
106
|
+
|
107
|
+
# Build a DOT graph edge
|
108
|
+
def dot_edge(type, from, to, name = '')
|
109
|
+
options = name != '' ? "label=\"#{name}\", " : ''
|
110
|
+
case type
|
111
|
+
when 'one-one'
|
112
|
+
#options += 'taillabel="1"'
|
113
|
+
options += 'arrowtail=odot, arrowhead=dot, dir=both'
|
114
|
+
when 'one-many'
|
115
|
+
#options += 'taillabel="n"'
|
116
|
+
options += 'arrowtail=crow, arrowhead=dot, dir=both'
|
117
|
+
when 'many-many'
|
118
|
+
#options += 'taillabel="n", headlabel="n", arrowtail="normal"'
|
119
|
+
options += 'arrowtail=crow, arrowhead=crow, dir=both'
|
120
|
+
when 'is-a'
|
121
|
+
options += 'arrowhead="none", arrowtail="onormal"'
|
122
|
+
when 'event'
|
123
|
+
options += "fontsize=10"
|
124
|
+
end
|
125
|
+
return "\t#{quote(from)} -> #{quote(to)} [#{options}]\n"
|
126
|
+
end # dot_edge
|
127
|
+
|
128
|
+
# Quotes a class name
|
129
|
+
def quote(name)
|
130
|
+
'"' + name.to_s + '"'
|
131
|
+
end
|
132
|
+
|
133
|
+
end # class DiagramGraph
|
@@ -0,0 +1,190 @@
|
|
1
|
+
# RailRoad - RoR diagrams generator
|
2
|
+
# http://railroad.rubyforge.org
|
3
|
+
#
|
4
|
+
# Copyright 2007-2008 - Javier Smaldone (http://www.smaldone.com.ar)
|
5
|
+
# See COPYING for more details
|
6
|
+
|
7
|
+
# RailRoad models diagram
|
8
|
+
class ModelsDiagram < AppDiagram
|
9
|
+
|
10
|
+
def initialize(options)
|
11
|
+
#options.exclude.map! {|e| "app/models/" + e}
|
12
|
+
super options
|
13
|
+
@graph.diagram_type = 'Models'
|
14
|
+
# Processed habtm associations
|
15
|
+
@habtm = []
|
16
|
+
end
|
17
|
+
|
18
|
+
# Process model files
|
19
|
+
def generate
|
20
|
+
STDERR.print "Generating models diagram\n" if @options.verbose
|
21
|
+
base = "(app/models/|lib/)"
|
22
|
+
files = Dir.glob("app/models/**/*.rb")
|
23
|
+
files += Dir.glob("vendor/plugins/**/app/models/*.rb") if @options.plugins_models
|
24
|
+
files += Dir.glob("lib/**/*.rb") if @options.libraries
|
25
|
+
|
26
|
+
files -= @options.exclude
|
27
|
+
(files + ['filter_condition_type']).each do |file|
|
28
|
+
model_name =
|
29
|
+
@options.classes_by_files[file] ||
|
30
|
+
(model_path = file.gsub(/^#{base}([\w_\/\\]+)\.rb/, '\2')).camelize
|
31
|
+
STDERR.print "Processing #{file} ...\n" if @options.verbose
|
32
|
+
# Hack to skip all xxx_related.rb files
|
33
|
+
next if /_related/i =~ model_name
|
34
|
+
|
35
|
+
klass = begin
|
36
|
+
model_name.constantize
|
37
|
+
rescue LoadError
|
38
|
+
STDERR.print "\t#{model_name} raised LoadError.\n" if @options.verbose
|
39
|
+
oldlen = model_path.length
|
40
|
+
model_path.gsub!(/.*[\/\\]/, '')
|
41
|
+
model_name = model_path.camelize
|
42
|
+
if oldlen > model_path.length
|
43
|
+
retry
|
44
|
+
end
|
45
|
+
STDERR.print "\tDone trying to remove slashes, skipping this model.\n" if @options.verbose
|
46
|
+
next
|
47
|
+
rescue NameError
|
48
|
+
STDERR.print "\t#{model_name} raised NameError, skipping this model.\n" if @options.verbose
|
49
|
+
next
|
50
|
+
end
|
51
|
+
|
52
|
+
process_class klass
|
53
|
+
STDERR.print "Done #{file}\n" if @options.verbose
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
# Load model classes
|
60
|
+
def load_classes
|
61
|
+
begin
|
62
|
+
disable_stdout
|
63
|
+
files = Dir.glob("app/models/**/*.rb")
|
64
|
+
files += Dir.glob("vendor/plugins/**/app/models/*.rb") if @options.plugins_models
|
65
|
+
files += ["lib/search.rb"]
|
66
|
+
files -= @options.exclude
|
67
|
+
files.each do |m|
|
68
|
+
require m
|
69
|
+
end
|
70
|
+
enable_stdout
|
71
|
+
rescue LoadError
|
72
|
+
enable_stdout
|
73
|
+
print_error "model classes"
|
74
|
+
raise
|
75
|
+
end
|
76
|
+
end # load_classes
|
77
|
+
|
78
|
+
# Process a model class
|
79
|
+
def process_class(current_class)
|
80
|
+
|
81
|
+
STDERR.print "\tProcessing #{current_class} ...\n" if @options.verbose
|
82
|
+
|
83
|
+
generated = false
|
84
|
+
|
85
|
+
# Is current_clas derived from ActiveRecord::Base?
|
86
|
+
if current_class.respond_to?'reflect_on_all_associations'
|
87
|
+
|
88
|
+
|
89
|
+
node_attribs = []
|
90
|
+
if @options.brief || current_class.abstract_class? || current_class.superclass != ActiveRecord::Base
|
91
|
+
node_type = 'model-brief'
|
92
|
+
else
|
93
|
+
node_type = 'model'
|
94
|
+
|
95
|
+
# Collect model's content columns
|
96
|
+
|
97
|
+
content_columns = current_class.content_columns
|
98
|
+
|
99
|
+
if @options.hide_magic
|
100
|
+
magic_fields = [
|
101
|
+
# Restful Authentication
|
102
|
+
"login", "crypted_password", "salt", "remember_token", "remember_token_expires_at", "activation_code", "activated_at",
|
103
|
+
# From patch #13351
|
104
|
+
# http://wiki.rubyonrails.org/rails/pages/MagicFieldNames
|
105
|
+
"created_at", "created_on", "updated_at", "updated_on",
|
106
|
+
"lock_version", "type", "id", "position", "parent_id", "lft",
|
107
|
+
"rgt", "quote", "template"
|
108
|
+
]
|
109
|
+
magic_fields << current_class.table_name + "_count" if current_class.respond_to? 'table_name'
|
110
|
+
content_columns = current_class.content_columns.select {|c| ! magic_fields.include? c.name}
|
111
|
+
else
|
112
|
+
content_columns = current_class.content_columns
|
113
|
+
end
|
114
|
+
|
115
|
+
content_columns.each do |a|
|
116
|
+
content_column = a.name
|
117
|
+
content_column += ' :' + a.type.to_s unless @options.hide_types
|
118
|
+
node_attribs << content_column
|
119
|
+
end
|
120
|
+
end
|
121
|
+
@graph.add_node [node_type, current_class.name, node_attribs]
|
122
|
+
generated = true
|
123
|
+
# Process class associations
|
124
|
+
associations = current_class.reflect_on_all_associations
|
125
|
+
if @options.inheritance && ! @options.transitive
|
126
|
+
superclass_associations = current_class.superclass.reflect_on_all_associations
|
127
|
+
|
128
|
+
associations = associations.select{|a| ! superclass_associations.include? a}
|
129
|
+
# This doesn't works!
|
130
|
+
# associations -= current_class.superclass.reflect_on_all_associations
|
131
|
+
end
|
132
|
+
associations.each do |a|
|
133
|
+
process_association current_class.name, a
|
134
|
+
end
|
135
|
+
elsif @options.all && (current_class.is_a? Class)
|
136
|
+
# Not ActiveRecord::Base model
|
137
|
+
node_type = @options.brief ? 'class-brief' : 'class'
|
138
|
+
@graph.add_node [node_type, current_class.name]
|
139
|
+
generated = true
|
140
|
+
elsif @options.modules && (current_class.is_a? Module)
|
141
|
+
@graph.add_node ['module', current_class.name]
|
142
|
+
end
|
143
|
+
|
144
|
+
# Only consider meaningful inheritance relations for generated classes
|
145
|
+
if @options.inheritance && generated &&
|
146
|
+
(current_class.superclass != ActiveRecord::Base) &&
|
147
|
+
(current_class.superclass != Object)
|
148
|
+
@graph.add_edge ['is-a', current_class.superclass.name, current_class.name]
|
149
|
+
end
|
150
|
+
|
151
|
+
STDERR.print "\tDone #{current_class}\n" if @options.verbose
|
152
|
+
end # process_class
|
153
|
+
|
154
|
+
# Process a model association
|
155
|
+
def process_association(class_name, assoc)
|
156
|
+
begin
|
157
|
+
STDERR.print "\t\tProcessing model association #{assoc.name.to_s} ..." if @options.verbose
|
158
|
+
|
159
|
+
# Skip "belongs_to" associations
|
160
|
+
return if assoc.macro.to_s == 'belongs_to'
|
161
|
+
|
162
|
+
# Only non standard association names needs a label
|
163
|
+
|
164
|
+
# from patch #12384
|
165
|
+
# if assoc.class_name == assoc.name.to_s.singularize.camelize
|
166
|
+
assoc_class_name = (assoc.class_name.respond_to? 'underscore') ? assoc.class_name.underscore.singularize.camelize : assoc.class_name
|
167
|
+
if assoc_class_name == assoc.name.to_s.singularize.camelize
|
168
|
+
assoc_name = ''
|
169
|
+
else
|
170
|
+
assoc_name = assoc.name.to_s
|
171
|
+
end
|
172
|
+
# STDERR.print "#{assoc_name}\n"
|
173
|
+
if assoc.macro.to_s == 'has_one'
|
174
|
+
assoc_type = 'one-one'
|
175
|
+
elsif assoc.macro.to_s == 'has_many' && (! assoc.options[:through])
|
176
|
+
assoc_type = 'one-many'
|
177
|
+
else # habtm or has_many, :through
|
178
|
+
return if @habtm.include? [assoc.class_name, class_name, assoc_name]
|
179
|
+
assoc_type = 'many-many'
|
180
|
+
@habtm << [class_name, assoc.class_name, assoc_name]
|
181
|
+
end
|
182
|
+
# from patch #12384
|
183
|
+
# @graph.add_edge [assoc_type, class_name, assoc.class_name, assoc_name]
|
184
|
+
@graph.add_edge [assoc_type, class_name, assoc_class_name, assoc_name]
|
185
|
+
ensure
|
186
|
+
STDERR.print " done.\n" if @options.verbose
|
187
|
+
end
|
188
|
+
end # process_association
|
189
|
+
|
190
|
+
end # class ModelsDiagram
|