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.
@@ -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