railroad 0.3.3 → 0.3.4
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/ChangeLog +11 -0
- data/README +3 -1
- data/bin/railroad +41 -0
- data/lib/railroad/app_diagram.rb +83 -0
- data/lib/railroad/controllers_diagram.rb +81 -0
- data/lib/railroad/diagram_graph.rb +119 -0
- data/lib/railroad/models_diagram.rb +115 -0
- data/lib/railroad/options_struct.rb +138 -0
- data/rake.gemspec +5 -4
- metadata +9 -5
- data/lib/railroad +0 -459
data/ChangeLog
CHANGED
@@ -1,6 +1,17 @@
|
|
1
|
+
Version 0.3.4 (apr 12 2007)
|
2
|
+
- Add support for model abstract classes.
|
3
|
+
(don't try to get content columns, bug #10033)
|
4
|
+
- Add verbose mode
|
5
|
+
- More code cleanup.
|
6
|
+
- Using an internal representation and then
|
7
|
+
generating the DOT output. This will allow to
|
8
|
+
add more output formats in the future.
|
9
|
+
|
10
|
+
|
1
11
|
Version 0.3.3 (apr 10 2007)
|
2
12
|
- Code cleanup
|
3
13
|
|
14
|
+
|
4
15
|
Version 0.3.2 (apr 9 2007)
|
5
16
|
- Disable STDOUT when loading applications classes, avoiding
|
6
17
|
messing up the DOT output.
|
data/README
CHANGED
@@ -22,6 +22,8 @@ Common options:
|
|
22
22
|
-l, --label Add a label with diagram information
|
23
23
|
(type, date, migration, version)
|
24
24
|
-o, --output FILE Write diagram to file FILE
|
25
|
+
-v, --verbose Enable verbose output
|
26
|
+
(produce messages to STDOUT)
|
25
27
|
|
26
28
|
Models diagram options:
|
27
29
|
-a, --all Include all models
|
@@ -98,7 +100,7 @@ from Graphviz.
|
|
98
100
|
|
99
101
|
= Website and Project Home
|
100
102
|
|
101
|
-
http://railroad.rubyforge.org
|
103
|
+
http://railroad.rubyforge.org
|
102
104
|
|
103
105
|
= License
|
104
106
|
|
data/bin/railroad
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# RailRoad - RoR diagrams generator
|
4
|
+
# http://railroad.rubyforge.org
|
5
|
+
#
|
6
|
+
# RailRoad generates models and controllers diagrams in DOT language
|
7
|
+
# for a Rails application.
|
8
|
+
#
|
9
|
+
# Copyright 2007 - Javier Smaldone (http://www.smaldone.com.ar)
|
10
|
+
#
|
11
|
+
# This program is free software; you can redistribute it and/or modify
|
12
|
+
# it under the terms of the GNU General Public License as published by
|
13
|
+
# the Free Software Foundation; either version 2 of the License, or
|
14
|
+
# (at your option) any later version.
|
15
|
+
#
|
16
|
+
|
17
|
+
APP_NAME = "railroad"
|
18
|
+
APP_HUMAN_NAME = "RailRoad"
|
19
|
+
APP_VERSION = [0,3,4]
|
20
|
+
COPYRIGHT = "Copyright (C) 2007 Javier Smaldone"
|
21
|
+
|
22
|
+
require 'railroad/options_struct'
|
23
|
+
require 'railroad/models_diagram'
|
24
|
+
require 'railroad/controllers_diagram'
|
25
|
+
|
26
|
+
options = OptionsStruct.new
|
27
|
+
|
28
|
+
options.parse ARGV
|
29
|
+
|
30
|
+
if options.command == 'models'
|
31
|
+
diagram = ModelsDiagram.new options
|
32
|
+
elsif options.command == 'controllers'
|
33
|
+
diagram = ControllersDiagram.new options
|
34
|
+
else
|
35
|
+
STDERR.print "Error: You must supply a command\n" +
|
36
|
+
" (try #{APP_NAME} -h)\n\n"
|
37
|
+
exit 1
|
38
|
+
end
|
39
|
+
|
40
|
+
diagram.generate
|
41
|
+
diagram.print
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# RailRoad - RoR diagrams generator
|
2
|
+
# http://railroad.rubyforge.org
|
3
|
+
#
|
4
|
+
# Copyright 2007 - Javier Smaldone (http://www.smaldone.com.ar)
|
5
|
+
# See COPYING for more details
|
6
|
+
|
7
|
+
require 'railroad/diagram_graph'
|
8
|
+
|
9
|
+
# Root class for RailRoad diagrams
|
10
|
+
class AppDiagram
|
11
|
+
|
12
|
+
def initialize(options)
|
13
|
+
@options = options
|
14
|
+
@graph = DiagramGraph.new
|
15
|
+
@graph.show_label = @options.label
|
16
|
+
|
17
|
+
STDERR.print "Loading application environment\n" if @options.verbose
|
18
|
+
load_environment
|
19
|
+
|
20
|
+
STDERR.print "Loading application classes\n" if @options.verbose
|
21
|
+
load_classes
|
22
|
+
end
|
23
|
+
|
24
|
+
# Print diagram
|
25
|
+
def print
|
26
|
+
if @options.output
|
27
|
+
old_stdout = STDOUT.dup
|
28
|
+
begin
|
29
|
+
STDOUT.reopen(@options.output)
|
30
|
+
rescue
|
31
|
+
STDERR.print "Error: Cannot write diagram to #{@options.output}\n\n"
|
32
|
+
exit 2
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
STDERR.print "Generating DOT graph\n" if @options.verbose
|
37
|
+
|
38
|
+
STDOUT.print @graph.to_dot
|
39
|
+
|
40
|
+
if @options.output
|
41
|
+
STDOUT.reopen(old_stdout)
|
42
|
+
end
|
43
|
+
end # print
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
# Prevents Rails application from writing to STDOUT
|
48
|
+
def disable_stdout
|
49
|
+
@old_stdout = STDOUT.dup
|
50
|
+
STDOUT.reopen(PLATFORM =~ /mswin/ ? "NUL" : "/dev/null")
|
51
|
+
end
|
52
|
+
|
53
|
+
# Restore STDOUT
|
54
|
+
def enable_stdout
|
55
|
+
STDOUT.reopen(@old_stdout)
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
# Print error when loading Rails application
|
60
|
+
def print_error(type)
|
61
|
+
STDERR.print "Error loading #{type}.\n (Are you running " +
|
62
|
+
"#{APP_NAME} on the aplication's root directory?)\n\n"
|
63
|
+
end
|
64
|
+
|
65
|
+
# Load Rails application's environment
|
66
|
+
def load_environment
|
67
|
+
begin
|
68
|
+
disable_stdout
|
69
|
+
require "config/environment"
|
70
|
+
enable_stdout
|
71
|
+
rescue LoadError
|
72
|
+
enable_stdout
|
73
|
+
print_error "application environment"
|
74
|
+
raise
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# Extract class name from filename
|
79
|
+
def extract_class_name(filename)
|
80
|
+
filename.split('/')[2..-1].join('/').split('.').first.camelize
|
81
|
+
end
|
82
|
+
|
83
|
+
end # class AppDiagram
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# RailRoad - RoR diagrams generator
|
2
|
+
# http://railroad.rubyforge.org
|
3
|
+
#
|
4
|
+
# Copyright 2007 - Javier Smaldone (http://www.smaldone.com.ar)
|
5
|
+
# See COPYING for more details
|
6
|
+
|
7
|
+
require 'railroad/app_diagram'
|
8
|
+
|
9
|
+
# RailRoad controllers diagram
|
10
|
+
class ControllersDiagram < AppDiagram
|
11
|
+
|
12
|
+
def initialize(options)
|
13
|
+
super options
|
14
|
+
@graph.diagram_type = 'Controllers'
|
15
|
+
end
|
16
|
+
|
17
|
+
# Process controller files
|
18
|
+
def generate
|
19
|
+
STDERR.print "Generating controllers diagram\n" if @options.verbose
|
20
|
+
|
21
|
+
files = Dir.glob("app/controllers/**/*_controller.rb")
|
22
|
+
files << 'app/controllers/application.rb'
|
23
|
+
files.each do |f|
|
24
|
+
class_name = extract_class_name(f)
|
25
|
+
# ApplicationController's file is 'application.rb'
|
26
|
+
class_name += 'Controller' if class_name == 'Application'
|
27
|
+
process_class class_name.constantize
|
28
|
+
end
|
29
|
+
end # generate
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
# Load controller classes
|
34
|
+
def load_classes
|
35
|
+
begin
|
36
|
+
disable_stdout
|
37
|
+
# ApplicationController must be loaded first
|
38
|
+
require "app/controllers/application.rb"
|
39
|
+
Dir.glob("app/controllers/**/*_controller.rb") {|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.name, current_class.superclass.name]
|
78
|
+
end
|
79
|
+
end # process_class
|
80
|
+
|
81
|
+
end # class ControllersDiagram
|
@@ -0,0 +1,119 @@
|
|
1
|
+
# RailRoad - RoR diagrams generator
|
2
|
+
# http://railroad.rubyforge.org
|
3
|
+
#
|
4
|
+
# Copyright 2007 - 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
|
+
private
|
44
|
+
|
45
|
+
# Build DOT diagram header
|
46
|
+
def dot_header
|
47
|
+
result = "digraph #{@diagram_type.downcase}_diagram {\n" +
|
48
|
+
"\tgraph[overlap=false, splines=true]\n"
|
49
|
+
result += dot_label if @show_label
|
50
|
+
return result
|
51
|
+
end
|
52
|
+
|
53
|
+
# Build DOT diagram footer
|
54
|
+
def dot_footer
|
55
|
+
return "}\n"
|
56
|
+
end
|
57
|
+
|
58
|
+
# Build diagram label
|
59
|
+
def dot_label
|
60
|
+
return "\t_diagram_info [shape=\"plaintext\", " +
|
61
|
+
"label=\"Diagram: #{@diagram_type}\\l" +
|
62
|
+
"Date: #{Time.now.strftime "%b %d %Y - %H:%M"}\\l" +
|
63
|
+
"Migration version: " +
|
64
|
+
"#{ActiveRecord::Migrator.current_version}\\l" +
|
65
|
+
"Generated by #{APP_HUMAN_NAME} #{APP_VERSION.join('.')}"+
|
66
|
+
"\\l\", fontsize=14]\n"
|
67
|
+
end
|
68
|
+
|
69
|
+
# Build a DOT graph node
|
70
|
+
def dot_node(type, name, attributes=nil)
|
71
|
+
case type
|
72
|
+
when 'model'
|
73
|
+
options = 'shape=Mrecord, label="{' + name + '|'
|
74
|
+
options += attributes.join('\l')
|
75
|
+
options += '\l}"'
|
76
|
+
when 'model-brief'
|
77
|
+
options = ''
|
78
|
+
when 'class'
|
79
|
+
options = 'shape=record, label="{' + name + '|}"'
|
80
|
+
when 'class-brief'
|
81
|
+
options = 'shape=box'
|
82
|
+
when 'controller'
|
83
|
+
options = 'shape=Mrecord, label="{' + name + '|'
|
84
|
+
public_methods = attributes[:public].join('\l')
|
85
|
+
protected_methods = attributes[:protected].join('\l')
|
86
|
+
private_methods = attributes[:private].join('\l')
|
87
|
+
options += public_methods + '\l|' + protected_methods + '\l|' +
|
88
|
+
private_methods + '\l'
|
89
|
+
options += '}"'
|
90
|
+
when 'controller-brief'
|
91
|
+
options = ''
|
92
|
+
when 'module'
|
93
|
+
options = 'shape=box, style=dotted, label="' + name + '"'
|
94
|
+
end # case
|
95
|
+
return "\t#{quote(name)} [#{options}]\n"
|
96
|
+
end # dot_node
|
97
|
+
|
98
|
+
# Build a DOT graph edge
|
99
|
+
def dot_edge(type, from, to, name = '')
|
100
|
+
options = name != '' ? "label=\"#{name}\", " : ''
|
101
|
+
case type
|
102
|
+
when 'one-one'
|
103
|
+
options += 'taillabel="1"'
|
104
|
+
when 'one-many'
|
105
|
+
options += 'taillabel="n"'
|
106
|
+
when 'many-many'
|
107
|
+
options += 'taillabel="n", headlabel="n", arrowtail="normal"'
|
108
|
+
when 'is-a'
|
109
|
+
options += 'arrowhead="onormal"'
|
110
|
+
end
|
111
|
+
return "\t#{quote(from)} -> #{quote(to)} [#{options}]\n"
|
112
|
+
end # dot_edge
|
113
|
+
|
114
|
+
# Quotes a class name
|
115
|
+
def quote(name)
|
116
|
+
'"' + name + '"'
|
117
|
+
end
|
118
|
+
|
119
|
+
end # class DiagramGraph
|
@@ -0,0 +1,115 @@
|
|
1
|
+
# RailRoad - RoR diagrams generator
|
2
|
+
# http://railroad.rubyforge.org
|
3
|
+
#
|
4
|
+
# Copyright 2007 - Javier Smaldone (http://www.smaldone.com.ar)
|
5
|
+
# See COPYING for more details
|
6
|
+
|
7
|
+
require 'railroad/app_diagram'
|
8
|
+
|
9
|
+
# RailRoad models diagram
|
10
|
+
class ModelsDiagram < AppDiagram
|
11
|
+
|
12
|
+
def initialize(options)
|
13
|
+
super options
|
14
|
+
@graph.diagram_type = 'Models'
|
15
|
+
# Processed habtm associations
|
16
|
+
@habtm = []
|
17
|
+
end
|
18
|
+
|
19
|
+
# Process model files
|
20
|
+
def generate
|
21
|
+
STDERR.print "Generating models diagram\n" if @options.verbose
|
22
|
+
files = Dir.glob("app/models/**/*.rb")
|
23
|
+
files.each do |f|
|
24
|
+
process_class extract_class_name(f).constantize
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
# Load model classes
|
31
|
+
def load_classes
|
32
|
+
begin
|
33
|
+
disable_stdout
|
34
|
+
Dir.glob("app/models/**/*.rb") {|m| require m }
|
35
|
+
enable_stdout
|
36
|
+
rescue LoadError
|
37
|
+
enable_stdout
|
38
|
+
print_error "model classes"
|
39
|
+
raise
|
40
|
+
end
|
41
|
+
end # load_classes
|
42
|
+
|
43
|
+
# Process a model class
|
44
|
+
def process_class(current_class)
|
45
|
+
|
46
|
+
STDERR.print "\tProcessing #{current_class}\n" if @options.verbose
|
47
|
+
|
48
|
+
generated = false
|
49
|
+
|
50
|
+
# Is current_clas derived from ActiveRecord::Base?
|
51
|
+
if current_class.respond_to?'reflect_on_all_associations'
|
52
|
+
node_attribs = []
|
53
|
+
if @options.brief || current_class.abstract_class?
|
54
|
+
node_type = 'model-brief'
|
55
|
+
else
|
56
|
+
node_type = 'model'
|
57
|
+
# Collect model's content columns
|
58
|
+
current_class.content_columns.each do |a|
|
59
|
+
content_column = a.name
|
60
|
+
content_column += ' :' + a.type.to_s unless @options.hide_types
|
61
|
+
node_attribs << content_column
|
62
|
+
end
|
63
|
+
end
|
64
|
+
@graph.add_node [node_type, current_class.name, node_attribs]
|
65
|
+
generated = true
|
66
|
+
# Process class associations
|
67
|
+
current_class.reflect_on_all_associations.each do |a|
|
68
|
+
process_association current_class.name, a
|
69
|
+
end
|
70
|
+
elsif @options.all && (current_class.is_a? Class)
|
71
|
+
# Not ActiveRecord::Base model
|
72
|
+
node_type = @options.brief ? 'class-brief' : 'class'
|
73
|
+
@graph.add_node [node_type, current_class.name]
|
74
|
+
generated = true
|
75
|
+
elsif @options.modules && (current_class.is_a? Module)
|
76
|
+
@graph.add_node ['module', current_class.name]
|
77
|
+
end
|
78
|
+
|
79
|
+
# Only consider meaningful inheritance relations for generated classes
|
80
|
+
if @options.inheritance && generated &&
|
81
|
+
(current_class.superclass != ActiveRecord::Base) &&
|
82
|
+
(current_class.superclass != Object)
|
83
|
+
@graph.add_edge ['is-a', current_class.name, current_class.superclass.name]
|
84
|
+
end
|
85
|
+
|
86
|
+
end # process_class
|
87
|
+
|
88
|
+
# Process a model association
|
89
|
+
def process_association(class_name, assoc)
|
90
|
+
|
91
|
+
STDERR.print "\t\tProcessing model association #{assoc.name.to_s}\n" if @options.verbose
|
92
|
+
|
93
|
+
# Skip "belongs_to" associations
|
94
|
+
return if assoc.macro.to_s == 'belongs_to'
|
95
|
+
|
96
|
+
# Only non standard association names needs a label
|
97
|
+
if assoc.class_name == assoc.name.to_s.singularize.camelize
|
98
|
+
assoc_name = ''
|
99
|
+
else
|
100
|
+
assoc_name = assoc.name.to_s
|
101
|
+
end
|
102
|
+
|
103
|
+
if assoc.macro.to_s == 'has_one'
|
104
|
+
assoc_type = 'one-one'
|
105
|
+
elsif assoc.macro.to_s == 'has_many' && (! assoc.options[:through])
|
106
|
+
assoc_type = 'one-many'
|
107
|
+
else # habtm or has_many, :through
|
108
|
+
return if @habtm.include? [assoc.class_name, class_name, assoc_name]
|
109
|
+
assoc_type = 'many-many'
|
110
|
+
@habtm << [class_name, assoc.class_name, assoc_name]
|
111
|
+
end
|
112
|
+
@graph.add_edge [assoc_type, class_name, assoc.class_name, assoc_name]
|
113
|
+
end # process_association
|
114
|
+
|
115
|
+
end # class ModelsDiagram
|
@@ -0,0 +1,138 @@
|
|
1
|
+
# RailRoad - RoR diagrams generator
|
2
|
+
# http://railroad.rubyforge.org
|
3
|
+
#
|
4
|
+
# Copyright 2007 - Javier Smaldone (http://www.smaldone.com.ar)
|
5
|
+
# See COPYING for more details
|
6
|
+
|
7
|
+
require 'ostruct'
|
8
|
+
|
9
|
+
# RailRoad command line options parser
|
10
|
+
class OptionsStruct < OpenStruct
|
11
|
+
|
12
|
+
require 'optparse'
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
init_options = { :all => false,
|
16
|
+
:brief => false,
|
17
|
+
:inheritance => false,
|
18
|
+
:join => false,
|
19
|
+
:label => false,
|
20
|
+
:modules => false,
|
21
|
+
:hide_types => false,
|
22
|
+
:hide_public => false,
|
23
|
+
:hide_protected => false,
|
24
|
+
:hide_private => false,
|
25
|
+
:verbose => false,
|
26
|
+
:command => '' }
|
27
|
+
super(init_options)
|
28
|
+
end # initialize
|
29
|
+
|
30
|
+
def parse(args)
|
31
|
+
@opt_parser = OptionParser.new do |opts|
|
32
|
+
opts.banner = "Usage: #{APP_NAME} [options] command"
|
33
|
+
opts.separator ""
|
34
|
+
opts.separator "Common options:"
|
35
|
+
opts.on("-b", "--brief", "Generate compact diagram",
|
36
|
+
" (no attributes nor methods)") do |b|
|
37
|
+
self.brief = b
|
38
|
+
end
|
39
|
+
opts.on("-i", "--inheritance", "Include inheritance relations") do |i|
|
40
|
+
self.inheritance = i
|
41
|
+
end
|
42
|
+
opts.on("-l", "--label", "Add a label with diagram information",
|
43
|
+
" (type, date, migration, version)") do |l|
|
44
|
+
self.label = l
|
45
|
+
end
|
46
|
+
opts.on("-o", "--output FILE", "Write diagram to file FILE") do |f|
|
47
|
+
self.output = f
|
48
|
+
end
|
49
|
+
opts.on("-v", "--verbose", "Enable verbose output",
|
50
|
+
" (produce messages to STDOUT)") do |v|
|
51
|
+
self.verbose = v
|
52
|
+
end
|
53
|
+
opts.separator ""
|
54
|
+
opts.separator "Models diagram options:"
|
55
|
+
opts.on("-a", "--all", "Include all models",
|
56
|
+
" (not only ActiveRecord::Base derived)") do |a|
|
57
|
+
self.all = a
|
58
|
+
end
|
59
|
+
opts.on("--hide-types", "Hide attributes type") do |h|
|
60
|
+
self.hide_types = h
|
61
|
+
end
|
62
|
+
opts.on("-j", "--join", "Concentrate edges") do |j|
|
63
|
+
self.join = j
|
64
|
+
end
|
65
|
+
opts.on("-m", "--modules", "Include modules") do |m|
|
66
|
+
self.modules = m
|
67
|
+
end
|
68
|
+
opts.separator ""
|
69
|
+
opts.separator "Controllers diagram options:"
|
70
|
+
opts.on("--hide-public", "Hide public methods") do |h|
|
71
|
+
self.hide_public = h
|
72
|
+
end
|
73
|
+
opts.on("--hide-protected", "Hide protected methods") do |h|
|
74
|
+
self.hide_protected = h
|
75
|
+
end
|
76
|
+
opts.on("--hide-private", "Hide private methods") do |h|
|
77
|
+
self.hide_private = h
|
78
|
+
end
|
79
|
+
opts.separator ""
|
80
|
+
opts.separator "Other options:"
|
81
|
+
opts.on("-h", "--help", "Show this message") do
|
82
|
+
STDOUT.print "#{opts}\n"
|
83
|
+
exit
|
84
|
+
end
|
85
|
+
opts.on("--version", "Show version and copyright") do
|
86
|
+
STDOUT.print "#{APP_HUMAN_NAME} version #{APP_VERSION.join('.')}\n\n" +
|
87
|
+
"#{COPYRIGHT}\nThis is free software; see the source " +
|
88
|
+
"for copying conditions.\n\n"
|
89
|
+
exit
|
90
|
+
end
|
91
|
+
opts.separator ""
|
92
|
+
opts.separator "Commands (you must supply one of these):"
|
93
|
+
opts.on("-M", "--models", "Generate models diagram") do |c|
|
94
|
+
if self.command == 'controllers'
|
95
|
+
STDERR.print "Error: Can't generate models AND " +
|
96
|
+
"controllers diagram\n\n"
|
97
|
+
exit 1
|
98
|
+
else
|
99
|
+
self.command = 'models'
|
100
|
+
end
|
101
|
+
end
|
102
|
+
opts.on("-C", "--controllers", "Generate controllers diagram") do |c|
|
103
|
+
if self.command == 'models'
|
104
|
+
STDERR.print "Error: Can't generate models AND " +
|
105
|
+
"controllers diagram\n\n"
|
106
|
+
exit 1
|
107
|
+
else
|
108
|
+
self.command = 'controllers'
|
109
|
+
end
|
110
|
+
end
|
111
|
+
opts.separator ""
|
112
|
+
opts.separator "For bug reporting and additional information, please see:"
|
113
|
+
opts.separator "http://railroad.rubyforge.org"
|
114
|
+
end # do
|
115
|
+
|
116
|
+
begin
|
117
|
+
@opt_parser.parse!(args)
|
118
|
+
rescue OptionParser::AmbiguousOption
|
119
|
+
option_error "Ambiguous option"
|
120
|
+
rescue OptionParser::InvalidOption
|
121
|
+
option_error "Invalid option"
|
122
|
+
rescue OptionParser::InvalidArgument
|
123
|
+
option_error "Invalid argument"
|
124
|
+
rescue OptionParser::MissingArgument
|
125
|
+
option_error "Missing argument"
|
126
|
+
rescue
|
127
|
+
option_error "Unknown error"
|
128
|
+
end
|
129
|
+
end # parse
|
130
|
+
|
131
|
+
private
|
132
|
+
|
133
|
+
def option_error(msg)
|
134
|
+
STDERR.print "Error: #{msg}\n\n #{@opt_parser}\n"
|
135
|
+
exit 1
|
136
|
+
end
|
137
|
+
|
138
|
+
end # class OptionsStruct
|
data/rake.gemspec
CHANGED
@@ -1,16 +1,17 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
SPEC = Gem::Specification.new do |s|
|
3
3
|
s.name = "railroad"
|
4
|
-
s.version = "0.3.
|
4
|
+
s.version = "0.3.4"
|
5
5
|
s.author = "Javier Smaldone"
|
6
6
|
s.email = "javier@smaldone.com.ar"
|
7
7
|
s.homepage = "http://railroad.rubyforge.org"
|
8
8
|
s.platform = Gem::Platform::RUBY
|
9
9
|
s.summary = "A DOT diagram generator for Ruby on Rail applications"
|
10
|
-
s.files =
|
11
|
-
|
10
|
+
s.files = Dir.glob("lib/railroad/*.rb") +
|
11
|
+
["ChangeLog", "COPYING", "rake.gemspec"]
|
12
|
+
s.bindir = "bin"
|
12
13
|
s.executables = ["railroad"]
|
13
14
|
s.default_executable = "railroad"
|
14
15
|
s.has_rdoc = true
|
15
|
-
s.extra_rdoc_files = ["README", "
|
16
|
+
s.extra_rdoc_files = ["README", "COPYING"]
|
16
17
|
end
|
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.2
|
|
3
3
|
specification_version: 1
|
4
4
|
name: railroad
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.3.
|
7
|
-
date: 2007-04-
|
6
|
+
version: 0.3.4
|
7
|
+
date: 2007-04-12 00:00:00 -03:00
|
8
8
|
summary: A DOT diagram generator for Ruby on Rail applications
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -14,7 +14,7 @@ rubyforge_project:
|
|
14
14
|
description:
|
15
15
|
autorequire:
|
16
16
|
default_executable: railroad
|
17
|
-
bindir:
|
17
|
+
bindir: bin
|
18
18
|
has_rdoc: true
|
19
19
|
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
20
|
requirements:
|
@@ -29,7 +29,11 @@ post_install_message:
|
|
29
29
|
authors:
|
30
30
|
- Javier Smaldone
|
31
31
|
files:
|
32
|
-
- lib/railroad
|
32
|
+
- lib/railroad/models_diagram.rb
|
33
|
+
- lib/railroad/app_diagram.rb
|
34
|
+
- lib/railroad/controllers_diagram.rb
|
35
|
+
- lib/railroad/diagram_graph.rb
|
36
|
+
- lib/railroad/options_struct.rb
|
33
37
|
- ChangeLog
|
34
38
|
- COPYING
|
35
39
|
- rake.gemspec
|
@@ -40,7 +44,7 @@ rdoc_options: []
|
|
40
44
|
|
41
45
|
extra_rdoc_files:
|
42
46
|
- README
|
43
|
-
-
|
47
|
+
- COPYING
|
44
48
|
executables:
|
45
49
|
- railroad
|
46
50
|
extensions: []
|
data/lib/railroad
DELETED
@@ -1,459 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
# RailRoad - RoR diagrams generator
|
4
|
-
# http://railroad.rubyforge.org
|
5
|
-
#
|
6
|
-
# RailRoad generates models and controllers diagrams in DOT language
|
7
|
-
# for a Rails application.
|
8
|
-
#
|
9
|
-
# Copyright 2007 - Javier Smaldone (http://www.smaldone.com.ar)
|
10
|
-
#
|
11
|
-
# This program is free software; you can redistribute it and/or modify
|
12
|
-
# it under the terms of the GNU General Public License as published by
|
13
|
-
# the Free Software Foundation; either version 2 of the License, or
|
14
|
-
# (at your option) any later version.
|
15
|
-
#
|
16
|
-
|
17
|
-
APP_NAME = "railroad"
|
18
|
-
APP_HUMAN_NAME = "RailRoad"
|
19
|
-
APP_VERSION = [0,3,3]
|
20
|
-
COPYRIGHT = "Copyright (C) 2007 Javier Smaldone"
|
21
|
-
|
22
|
-
require 'ostruct'
|
23
|
-
|
24
|
-
# Command line options structure
|
25
|
-
class OptionsStruct < OpenStruct
|
26
|
-
|
27
|
-
require 'optparse'
|
28
|
-
|
29
|
-
def initialize
|
30
|
-
init_options = { :all => false,
|
31
|
-
:brief => false,
|
32
|
-
:inheritance => false,
|
33
|
-
:join => false,
|
34
|
-
:label => false,
|
35
|
-
:modules => false,
|
36
|
-
:hide_types => false,
|
37
|
-
:hide_public => false,
|
38
|
-
:hide_protected => false,
|
39
|
-
:hide_private => false,
|
40
|
-
:command => '' }
|
41
|
-
super(init_options)
|
42
|
-
end # initialize
|
43
|
-
|
44
|
-
def parse(args)
|
45
|
-
@opt_parser = OptionParser.new do |opts|
|
46
|
-
opts.banner = "Usage: #{APP_NAME} [options] command"
|
47
|
-
opts.separator ""
|
48
|
-
opts.separator "Common options:"
|
49
|
-
opts.on("-b", "--brief", "Generate compact diagram",
|
50
|
-
" (no attributes nor methods)") do |b|
|
51
|
-
self.brief = b
|
52
|
-
end
|
53
|
-
opts.on("-i", "--inheritance", "Include inheritance relations") do |i|
|
54
|
-
self.inheritance = i
|
55
|
-
end
|
56
|
-
opts.on("-l", "--label", "Add a label with diagram information",
|
57
|
-
" (type, date, migration, version)") do |l|
|
58
|
-
self.label = l
|
59
|
-
end
|
60
|
-
opts.on("-o", "--output FILE", "Write diagram to file FILE") do |f|
|
61
|
-
self.output = f
|
62
|
-
end
|
63
|
-
opts.separator ""
|
64
|
-
opts.separator "Models diagram options:"
|
65
|
-
opts.on("-a", "--all", "Include all models",
|
66
|
-
" (not only ActiveRecord::Base derived)") do |a|
|
67
|
-
self.all = a
|
68
|
-
end
|
69
|
-
opts.on("--hide-types", "Hide attributes type") do |h|
|
70
|
-
self.hide_types = h
|
71
|
-
end
|
72
|
-
opts.on("-j", "--join", "Concentrate edges") do |j|
|
73
|
-
self.join = j
|
74
|
-
end
|
75
|
-
opts.on("-m", "--modules", "Include modules") do |m|
|
76
|
-
self.modules = m
|
77
|
-
end
|
78
|
-
opts.separator ""
|
79
|
-
opts.separator "Controllers diagram options:"
|
80
|
-
opts.on("--hide-public", "Hide public methods") do |h|
|
81
|
-
self.hide_public = h
|
82
|
-
end
|
83
|
-
opts.on("--hide-protected", "Hide protected methods") do |h|
|
84
|
-
@options.hide_protected = h
|
85
|
-
end
|
86
|
-
opts.on("--hide-private", "Hide private methods") do |h|
|
87
|
-
@options.hide_private = h
|
88
|
-
end
|
89
|
-
opts.separator ""
|
90
|
-
opts.separator "Other options:"
|
91
|
-
opts.on("-h", "--help", "Show this message") do
|
92
|
-
print "#{opts}\n"
|
93
|
-
exit
|
94
|
-
end
|
95
|
-
opts.on("--version", "Show version and copyright") do
|
96
|
-
print "#{APP_HUMAN_NAME} version #{APP_VERSION.join('.')}\n\n" +
|
97
|
-
"#{COPYRIGHT}\n" +
|
98
|
-
"This is free software; see the source for copying conditions.\n\n"
|
99
|
-
exit
|
100
|
-
end
|
101
|
-
opts.separator ""
|
102
|
-
opts.separator "Commands (you must supply one of these):"
|
103
|
-
opts.on("-M", "--models", "Generate models diagram") do |c|
|
104
|
-
if self.command == 'controllers'
|
105
|
-
STDERR.print "Error: Can't generate models AND " +
|
106
|
-
"controllers diagram\n\n"
|
107
|
-
exit 1
|
108
|
-
else
|
109
|
-
self.command = 'models'
|
110
|
-
end
|
111
|
-
end
|
112
|
-
opts.on("-C", "--controllers", "Generate controllers diagram") do |c|
|
113
|
-
if self.command == 'models'
|
114
|
-
STDERR.print "Error: Can't generate models AND " +
|
115
|
-
"controllers diagram\n\n"
|
116
|
-
exit 1
|
117
|
-
else
|
118
|
-
self.command = 'controllers'
|
119
|
-
end
|
120
|
-
end
|
121
|
-
opts.separator ""
|
122
|
-
opts.separator "For bug reporting and additional information, please see:"
|
123
|
-
opts.separator "http://railroad.rubyforge.org"
|
124
|
-
end
|
125
|
-
|
126
|
-
begin
|
127
|
-
@opt_parser.parse!(args)
|
128
|
-
rescue OptionParser::AmbiguousOption
|
129
|
-
option_error "Ambiguous option"
|
130
|
-
rescue OptionParser::InvalidOption
|
131
|
-
option_error "Invalid option"
|
132
|
-
rescue OptionParser::InvalidArgument
|
133
|
-
option_error "Invalid argument"
|
134
|
-
rescue OptionParser::MissingArgument
|
135
|
-
option_error "Missing argument"
|
136
|
-
rescue
|
137
|
-
option_error "Unknown error"
|
138
|
-
end
|
139
|
-
end # initialize
|
140
|
-
|
141
|
-
private
|
142
|
-
|
143
|
-
def option_error(msg)
|
144
|
-
STDERR.print "Error: #{msg}\n\n #{@opt_parser}\n"
|
145
|
-
exit 1
|
146
|
-
end
|
147
|
-
|
148
|
-
end # class OptionsStruct
|
149
|
-
|
150
|
-
|
151
|
-
# Root class for RailRoad diagrams
|
152
|
-
class AppDiagram
|
153
|
-
|
154
|
-
def initialize(options)
|
155
|
-
@options = options
|
156
|
-
load_environment
|
157
|
-
load_classes
|
158
|
-
end
|
159
|
-
|
160
|
-
private
|
161
|
-
|
162
|
-
# Quotes a class name
|
163
|
-
def node(name)
|
164
|
-
'"' + name + '"'
|
165
|
-
end
|
166
|
-
|
167
|
-
# Prevents Rails application from writing to STDOUT
|
168
|
-
def disable_stdout
|
169
|
-
@old_stdout = STDOUT.dup
|
170
|
-
STDOUT.reopen( PLATFORM =~ /mswin/ ? "NUL" : "/dev/null" )
|
171
|
-
end
|
172
|
-
|
173
|
-
# Restore STDOUT
|
174
|
-
def enable_stdout
|
175
|
-
STDOUT.reopen(@old_stdout)
|
176
|
-
end
|
177
|
-
|
178
|
-
# Print diagram header
|
179
|
-
def print_header(type)
|
180
|
-
print "digraph #{type.downcase}_diagram {\n" +
|
181
|
-
"\tgraph[overlap=false, splines=true]\n"
|
182
|
-
print_info(type) if @options.label
|
183
|
-
end
|
184
|
-
|
185
|
-
|
186
|
-
# Print diagram label
|
187
|
-
def print_info(type)
|
188
|
-
print "\t_diagram_info [shape=\"plaintext\", label=\"Diagram: #{type}\\l" +
|
189
|
-
"Date: #{Time.now.strftime "%b %d %Y - %H:%M"}\\l" +
|
190
|
-
"Migration version: #{ActiveRecord::Migrator.current_version}\\l" +
|
191
|
-
"Generated by #{APP_HUMAN_NAME} #{APP_VERSION.join('.')}\\l\""+
|
192
|
-
", fontsize=14]\n"
|
193
|
-
end
|
194
|
-
|
195
|
-
# Print an edge
|
196
|
-
def print_edge(from, to, attributes)
|
197
|
-
print "\t#{node(from)} -> #{node(to)} [#{attributes}]\n"
|
198
|
-
end
|
199
|
-
|
200
|
-
# Print a node
|
201
|
-
def print_node(name, attributes=nil)
|
202
|
-
print "\t#{node(name)}"
|
203
|
-
print " [#{attributes}]" if attributes && attributes != ''
|
204
|
-
print "\n"
|
205
|
-
end
|
206
|
-
|
207
|
-
# Print error when loading Rails application
|
208
|
-
def print_error(type)
|
209
|
-
STDERR.print "Error loading #{type}.\n (Are you running " +
|
210
|
-
"#{APP_NAME} on the aplication's root directory?)\n\n"
|
211
|
-
end
|
212
|
-
|
213
|
-
# Load Rails application's environment
|
214
|
-
def load_environment
|
215
|
-
begin
|
216
|
-
disable_stdout
|
217
|
-
require "config/environment"
|
218
|
-
enable_stdout
|
219
|
-
rescue LoadError
|
220
|
-
enable_stdout
|
221
|
-
print_error "application environment"
|
222
|
-
raise
|
223
|
-
end
|
224
|
-
end
|
225
|
-
|
226
|
-
end # class AppDiagram
|
227
|
-
|
228
|
-
|
229
|
-
# RailRoad models diagram
|
230
|
-
class ModelsDiagram < AppDiagram
|
231
|
-
|
232
|
-
def initialize(options)
|
233
|
-
super options
|
234
|
-
# Processed habtm associations
|
235
|
-
@habtm = []
|
236
|
-
end
|
237
|
-
|
238
|
-
# Generate models diagram
|
239
|
-
def generate
|
240
|
-
print_header 'Models'
|
241
|
-
models_files = Dir.glob("app/models/**/*.rb")
|
242
|
-
models_files.each do |m|
|
243
|
-
# Extract the class name from the file name
|
244
|
-
class_name = m.split('/')[2..-1].join('/').split('.').first.camelize
|
245
|
-
process_class class_name.constantize
|
246
|
-
end
|
247
|
-
print "}\n"
|
248
|
-
end # generate
|
249
|
-
|
250
|
-
private
|
251
|
-
|
252
|
-
# Load model classes
|
253
|
-
def load_classes
|
254
|
-
begin
|
255
|
-
disable_stdout
|
256
|
-
Dir.glob("app/models/**/*.rb") {|m| require m }
|
257
|
-
enable_stdout
|
258
|
-
rescue LoadError
|
259
|
-
enable_stdout
|
260
|
-
print_error "model classes"
|
261
|
-
raise
|
262
|
-
end
|
263
|
-
end # load_classes
|
264
|
-
|
265
|
-
# Process model class
|
266
|
-
def process_class(current_class)
|
267
|
-
|
268
|
-
class_printed = false
|
269
|
-
|
270
|
-
# Is current_clas derived from ActiveRecord::Base?
|
271
|
-
if current_class.respond_to?'reflect_on_all_associations'
|
272
|
-
|
273
|
-
# Print the node
|
274
|
-
if @options.brief
|
275
|
-
node_attrib = ''
|
276
|
-
else
|
277
|
-
node_attrib = 'shape=Mrecord, label="{' + current_class.name + '|'
|
278
|
-
current_class.content_columns.each do |a|
|
279
|
-
node_attrib += a.name
|
280
|
-
node_attrib += ' :' + a.type.to_s unless @options.hide_types
|
281
|
-
node_attrib += '\l'
|
282
|
-
end
|
283
|
-
node_attrib += '}"'
|
284
|
-
end
|
285
|
-
print_node current_class.name, node_attrib
|
286
|
-
class_printed = true
|
287
|
-
# Iterate over the class associations
|
288
|
-
current_class.reflect_on_all_associations.each do |a|
|
289
|
-
process_association(current_class, a)
|
290
|
-
end
|
291
|
-
elsif @options.all && (current_class.is_a? Class)
|
292
|
-
# Not ActiveRecord::Base model
|
293
|
-
if @options.brief
|
294
|
-
node_attrib = 'shape=box'
|
295
|
-
else
|
296
|
-
node_attrib = 'shape=record, label="{' + current_class.name + '|}"'
|
297
|
-
end
|
298
|
-
print_node current_class.name, node_attrib
|
299
|
-
class_printed = true
|
300
|
-
elsif @options.modules && (current_class.is_a? Module)
|
301
|
-
print_node current_class.name,
|
302
|
-
'shape=box, style=dotted, label="' + current_class.name + '"'
|
303
|
-
end
|
304
|
-
|
305
|
-
# Only consider meaningful inheritance relations for printed classes
|
306
|
-
if @options.inheritance && class_printed &&
|
307
|
-
(current_class.superclass != ActiveRecord::Base) &&
|
308
|
-
(current_class.superclass != Object)
|
309
|
-
print_edge(current_class.name, current_class.superclass.name,
|
310
|
-
'arrowhead="onormal"')
|
311
|
-
end
|
312
|
-
|
313
|
-
end # process_class
|
314
|
-
|
315
|
-
# Process model association
|
316
|
-
def process_association(current_class, association)
|
317
|
-
|
318
|
-
# Skip "belongs_to" associations
|
319
|
-
return if association.macro.to_s == 'belongs_to'
|
320
|
-
|
321
|
-
assoc_attrib = ""
|
322
|
-
assoc_name = ""
|
323
|
-
# Only non standard association names needs a label
|
324
|
-
if association.class_name != association.name.to_s.singularize.camelize
|
325
|
-
association_name = association.name.to_s
|
326
|
-
assoc_attrib += 'label="' + association_name + '", '
|
327
|
-
end
|
328
|
-
|
329
|
-
# Tail label with the association arity
|
330
|
-
assoc_attrib += association.macro.to_s == 'has_one' ? 'taillabel="1"' : 'taillabel="n"'
|
331
|
-
|
332
|
-
# Use double-headed arrows for habtm and has_many, :through
|
333
|
-
if association.macro.to_s == 'has_and_belongs_to_many' ||
|
334
|
-
(association.macro.to_s == 'has_many' && association.options[:through])
|
335
|
-
|
336
|
-
# Skip habtm associations already considered
|
337
|
-
return if @habtm.include? [association.class_name, current_class.name,
|
338
|
-
association_name]
|
339
|
-
@habtm << [current_class.name, association.class_name,association_name]
|
340
|
-
assoc_attrib += ', headlabel="n", arrowtail="normal"'
|
341
|
-
end
|
342
|
-
print_edge(current_class.name, association.class_name, assoc_attrib)
|
343
|
-
end # process_association
|
344
|
-
|
345
|
-
end # class ModelsDiagram
|
346
|
-
|
347
|
-
|
348
|
-
# RailRoad controllers diagram
|
349
|
-
class ControllersDiagram < AppDiagram
|
350
|
-
|
351
|
-
def initialize(options)
|
352
|
-
super options
|
353
|
-
@app_controller = ApplicationController
|
354
|
-
end
|
355
|
-
|
356
|
-
# Generate controllers diagram
|
357
|
-
def generate
|
358
|
-
|
359
|
-
print_header 'Controllers'
|
360
|
-
|
361
|
-
controllers_files = Dir.glob("app/controllers/**/*_controller.rb")
|
362
|
-
controllers_files << 'app/controllers/application.rb'
|
363
|
-
controllers_files.each do |c|
|
364
|
-
# Extract the class name from the file name
|
365
|
-
class_name = c.split('/')[2..-1].join('/').split('.').first.camelize
|
366
|
-
# ApplicationController's file is 'application.rb'
|
367
|
-
class_name += 'Controller' if class_name == 'Application'
|
368
|
-
process_class class_name.constantize
|
369
|
-
end
|
370
|
-
print "}\n"
|
371
|
-
end # generate
|
372
|
-
|
373
|
-
private
|
374
|
-
|
375
|
-
# Load controller classes
|
376
|
-
def load_classes
|
377
|
-
begin
|
378
|
-
disable_stdout
|
379
|
-
# ApplicationController must be loaded first
|
380
|
-
require "app/controllers/application.rb"
|
381
|
-
Dir.glob("app/controllers/**/*_controller.rb") {|c| require c }
|
382
|
-
enable_stdout
|
383
|
-
rescue LoadError
|
384
|
-
enable_stdout
|
385
|
-
print_error "controller classes"
|
386
|
-
raise
|
387
|
-
end
|
388
|
-
end # load_classes
|
389
|
-
|
390
|
-
# Proccess controller class
|
391
|
-
def process_class(current_class)
|
392
|
-
|
393
|
-
if @options.brief
|
394
|
-
print_node current_class.name
|
395
|
-
|
396
|
-
elsif current_class.is_a? Class
|
397
|
-
node_attrib = 'shape=Mrecord, label="{' + current_class.name + '|'
|
398
|
-
current_class.public_instance_methods(false).sort.each { |m|
|
399
|
-
node_attrib += m + '\l'
|
400
|
-
} unless @options.hide_public
|
401
|
-
node_attrib += '|'
|
402
|
-
current_class.protected_instance_methods(false).sort.each { |m|
|
403
|
-
node_attrib += m + '\l'
|
404
|
-
} unless @options.hide_protected
|
405
|
-
node_attrib += '|'
|
406
|
-
current_class.private_instance_methods(false).sort.each { |m|
|
407
|
-
node_attrib += m + '\l'
|
408
|
-
} unless @options.hide_private
|
409
|
-
node_attrib += '}"'
|
410
|
-
print_node current_class.name, node_attrib
|
411
|
-
|
412
|
-
elsif @options.modules && current_class.is_a?(Module)
|
413
|
-
print_node current_class.name,
|
414
|
-
'shape=box, style=dotted, label="' + current_class.name + '"'
|
415
|
-
end
|
416
|
-
|
417
|
-
# Print the inheritance edge (only for ApplicationControllers)
|
418
|
-
if @options.inheritance &&
|
419
|
-
(@app_controller.subclasses.include? current_class.name)
|
420
|
-
print_edge(current_class.name, current_class.superclass.name,
|
421
|
-
'arrowhead="onormal"')
|
422
|
-
end
|
423
|
-
|
424
|
-
end # process_class
|
425
|
-
|
426
|
-
end # class ControllersDiagram
|
427
|
-
|
428
|
-
|
429
|
-
# Main program
|
430
|
-
|
431
|
-
options = OptionsStruct.new
|
432
|
-
|
433
|
-
options.parse ARGV
|
434
|
-
|
435
|
-
if options.command == 'models'
|
436
|
-
diagram = ModelsDiagram.new options
|
437
|
-
elsif options.command == 'controllers'
|
438
|
-
diagram = ControllersDiagram.new options
|
439
|
-
else
|
440
|
-
STDERR.print "Error: You must supply a command\n" +
|
441
|
-
" (try #{APP_NAME} -h)\n\n"
|
442
|
-
exit 1
|
443
|
-
end
|
444
|
-
|
445
|
-
if options.output
|
446
|
-
old_stdout = STDOUT.dup
|
447
|
-
begin
|
448
|
-
STDOUT.reopen(options.output)
|
449
|
-
rescue
|
450
|
-
STDERR.print "Error: Cannot write diagram to #{options.output}\n\n"
|
451
|
-
exit 2
|
452
|
-
end
|
453
|
-
end
|
454
|
-
|
455
|
-
diagram.generate
|
456
|
-
|
457
|
-
if options.output
|
458
|
-
STDOUT.reopen(old_stdout)
|
459
|
-
end
|