railroady 1.0.6 → 1.0.7
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/README.rdoc +1 -1
- data/VERSION.yml +1 -1
- data/lib/railroady/models_diagram.rb +121 -40
- data/tasks/railroady.rake +20 -15
- metadata +4 -4
data/README.rdoc
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
= RailRoady
|
2
2
|
|
3
|
-
RailRoady generates Rails 3 model (AcitveRecord, Mongoid) and controller UML diagrams as cross-platform .svg files, as well as in the DOT language.
|
3
|
+
RailRoady generates Rails 3 model (AcitveRecord, Mongoid, Datamapper) and controller UML diagrams as cross-platform .svg files, as well as in the DOT language.
|
4
4
|
|
5
5
|
Code is based on the original "railroad" gem, patched and maintained over the years. Lineage can be traced via GitHub.
|
6
6
|
|
data/VERSION.yml
CHANGED
@@ -26,7 +26,7 @@ class ModelsDiagram < AppDiagram
|
|
26
26
|
end
|
27
27
|
|
28
28
|
end
|
29
|
-
end
|
29
|
+
end
|
30
30
|
|
31
31
|
def get_files(prefix ='')
|
32
32
|
files = !@options.specify.empty? ? Dir.glob(@options.specify) : Dir.glob(prefix << "app/models/**/*.rb")
|
@@ -38,34 +38,37 @@ class ModelsDiagram < AppDiagram
|
|
38
38
|
# Process a model class
|
39
39
|
def process_class(current_class)
|
40
40
|
STDERR.puts "Processing #{current_class}" if @options.verbose
|
41
|
-
|
42
|
-
generated =
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
41
|
+
|
42
|
+
generated =
|
43
|
+
if defined?(Mongoid::Document) && current_class.new.is_a?(Mongoid::Document)
|
44
|
+
process_mongoid_model(current_class)
|
45
|
+
elsif defined?(DataMapper::Resource) && current_class.new.is_a?(DataMapper::Resource)
|
46
|
+
process_datamapper_model(current_class)
|
47
|
+
elsif current_class.respond_to?'reflect_on_all_associations'
|
48
|
+
process_active_record_model(current_class)
|
49
|
+
elsif @options.all && (current_class.is_a? Class)
|
50
|
+
process_basic_class(current_class)
|
51
|
+
elsif @options.modules && (current_class.is_a? Module)
|
52
|
+
process_basic_module(current_class)
|
53
|
+
end
|
51
54
|
|
52
55
|
if @options.inheritance && generated && include_inheritance?(current_class)
|
53
56
|
@graph.add_edge ['is-a', current_class.superclass.name, current_class.name]
|
54
|
-
end
|
55
|
-
|
57
|
+
end
|
58
|
+
|
56
59
|
end # process_class
|
57
|
-
|
60
|
+
|
58
61
|
def include_inheritance?(current_class)
|
59
62
|
(defined?(ActiveRecord::Base) && current_class.superclass != ActiveRecord::Base) &&
|
60
63
|
(current_class.superclass != Object)
|
61
64
|
end
|
62
|
-
|
65
|
+
|
63
66
|
def process_basic_class(current_class)
|
64
67
|
node_type = @options.brief ? 'class-brief' : 'class'
|
65
68
|
@graph.add_node [node_type, current_class.name]
|
66
69
|
true
|
67
70
|
end
|
68
|
-
|
71
|
+
|
69
72
|
def process_basic_module(current_class)
|
70
73
|
@graph.add_node ['module', current_class.name]
|
71
74
|
false
|
@@ -86,15 +89,15 @@ class ModelsDiagram < AppDiagram
|
|
86
89
|
# http://wiki.rubyonrails.org/rails/pages/MagicFieldNames
|
87
90
|
magic_fields = [
|
88
91
|
"created_at", "created_on", "updated_at", "updated_on",
|
89
|
-
"lock_version", "type", "id", "position", "parent_id", "lft",
|
92
|
+
"lock_version", "type", "id", "position", "parent_id", "lft",
|
90
93
|
"rgt", "quote", "template"
|
91
94
|
]
|
92
|
-
magic_fields << current_class.table_name + "_count" if current_class.respond_to? 'table_name'
|
95
|
+
magic_fields << current_class.table_name + "_count" if current_class.respond_to? 'table_name'
|
93
96
|
content_columns = current_class.content_columns.select {|c| ! magic_fields.include? c.name}
|
94
97
|
else
|
95
98
|
content_columns = current_class.content_columns
|
96
99
|
end
|
97
|
-
|
100
|
+
|
98
101
|
content_columns.each do |a|
|
99
102
|
content_column = a.name
|
100
103
|
content_column += ' :' + a.type.to_s unless @options.hide_types
|
@@ -102,27 +105,69 @@ class ModelsDiagram < AppDiagram
|
|
102
105
|
end
|
103
106
|
end
|
104
107
|
@graph.add_node [node_type, current_class.name, node_attribs]
|
105
|
-
|
108
|
+
|
106
109
|
# Process class associations
|
107
110
|
associations = current_class.reflect_on_all_associations
|
108
111
|
if @options.inheritance && ! @options.transitive
|
109
112
|
superclass_associations = current_class.superclass.reflect_on_all_associations
|
110
|
-
|
111
|
-
associations = associations.select{|a| ! superclass_associations.include? a}
|
113
|
+
|
114
|
+
associations = associations.select{|a| ! superclass_associations.include? a}
|
112
115
|
# This doesn't works!
|
113
116
|
# associations -= current_class.superclass.reflect_on_all_associations
|
114
117
|
end
|
115
|
-
|
118
|
+
|
116
119
|
associations.each do |a|
|
117
120
|
process_association current_class.name, a
|
118
121
|
end
|
119
|
-
|
122
|
+
|
120
123
|
true
|
121
124
|
end
|
122
|
-
|
125
|
+
|
126
|
+
def process_datamapper_model(current_class)
|
127
|
+
node_attribs = []
|
128
|
+
if @options.brief #|| current_class.abstract_class?
|
129
|
+
node_type = 'model-brief'
|
130
|
+
else
|
131
|
+
node_type = 'model'
|
132
|
+
|
133
|
+
# Collect model's properties
|
134
|
+
props = current_class.properties.sort_by(&:name)
|
135
|
+
|
136
|
+
if @options.hide_magic
|
137
|
+
# From patch #13351
|
138
|
+
# http://wiki.rubyonrails.org/rails/pages/MagicFieldNames
|
139
|
+
magic_fields =
|
140
|
+
["created_at", "created_on", "updated_at", "updated_on", "lock_version", "_type", "_id",
|
141
|
+
"position", "parent_id", "lft", "rgt", "quote", "template"]
|
142
|
+
props = props.select {|c| !magic_fields.include?(c.name.to_s) }
|
143
|
+
end
|
144
|
+
|
145
|
+
props.each do |a|
|
146
|
+
prop = a.name.to_s
|
147
|
+
prop += ' :' + a.class.name.split('::').last unless @options.hide_types
|
148
|
+
node_attribs << prop
|
149
|
+
end
|
150
|
+
end
|
151
|
+
@graph.add_node [node_type, current_class.name, node_attribs]
|
152
|
+
|
153
|
+
# Process relationships
|
154
|
+
relationships = current_class.relationships
|
155
|
+
|
156
|
+
# TODO: Manage inheritance
|
157
|
+
|
158
|
+
relationships.each do |a|
|
159
|
+
process_datamapper_relationship current_class.name, a
|
160
|
+
end
|
161
|
+
|
162
|
+
true
|
163
|
+
end
|
164
|
+
|
165
|
+
|
166
|
+
|
167
|
+
|
123
168
|
def process_mongoid_model(current_class)
|
124
169
|
node_attribs = []
|
125
|
-
|
170
|
+
|
126
171
|
if @options.brief
|
127
172
|
node_type = 'model-brief'
|
128
173
|
else
|
@@ -141,31 +186,31 @@ class ModelsDiagram < AppDiagram
|
|
141
186
|
]
|
142
187
|
content_columns = content_columns.select {|c| !magic_fields.include?(c.name) }
|
143
188
|
end
|
144
|
-
|
189
|
+
|
145
190
|
content_columns.each do |a|
|
146
191
|
content_column = a.name
|
147
192
|
content_column += " :#{a.type}" unless @options.hide_types
|
148
193
|
node_attribs << content_column
|
149
194
|
end
|
150
195
|
end
|
151
|
-
|
196
|
+
|
152
197
|
@graph.add_node [node_type, current_class.name, node_attribs]
|
153
|
-
|
198
|
+
|
154
199
|
# Process class associations
|
155
200
|
associations = current_class.relations.values
|
156
|
-
|
201
|
+
|
157
202
|
if @options.inheritance && !@options.transitive &&
|
158
203
|
current_class.superclass.respond_to?(:relations)
|
159
204
|
associations -= current_class.superclass.relations.values
|
160
205
|
end
|
161
|
-
|
206
|
+
|
162
207
|
associations.each do |a|
|
163
208
|
process_association current_class.name, a
|
164
209
|
end
|
165
|
-
|
210
|
+
|
166
211
|
true
|
167
212
|
end
|
168
|
-
|
213
|
+
|
169
214
|
|
170
215
|
# Process a model association
|
171
216
|
def process_association(class_name, assoc)
|
@@ -175,21 +220,21 @@ class ModelsDiagram < AppDiagram
|
|
175
220
|
macro = assoc.macro.to_s
|
176
221
|
return if %w[belongs_to referenced_in].include?(macro) && !@options.show_belongs_to
|
177
222
|
|
178
|
-
#TODO:
|
223
|
+
#TODO:
|
179
224
|
# FAIL: assoc.methods.include?(:class_name)
|
180
225
|
# FAIL: assoc.responds_to?(:class_name)
|
181
226
|
assoc_class_name = assoc.class_name rescue nil
|
182
227
|
assoc_class_name ||= assoc.name.to_s.underscore.singularize.camelize
|
183
|
-
|
228
|
+
|
184
229
|
# Only non standard association names needs a label
|
185
|
-
|
230
|
+
|
186
231
|
# from patch #12384
|
187
232
|
# if assoc.class_name == assoc.name.to_s.singularize.camelize
|
188
233
|
if assoc_class_name == assoc.name.to_s.singularize.camelize
|
189
234
|
assoc_name = ''
|
190
235
|
else
|
191
236
|
assoc_name = assoc.name.to_s
|
192
|
-
end
|
237
|
+
end
|
193
238
|
|
194
239
|
# Patch from "alpack" to support classes in a non-root module namespace. See: http://disq.us/yxl1v
|
195
240
|
if class_name.include?("::") && !assoc_class_name.include?("::")
|
@@ -207,9 +252,45 @@ class ModelsDiagram < AppDiagram
|
|
207
252
|
return if @habtm.include? [assoc_class_name, class_name, assoc_name]
|
208
253
|
assoc_type = 'many-many'
|
209
254
|
@habtm << [class_name, assoc_class_name, assoc_name]
|
210
|
-
end
|
211
|
-
# from patch #12384
|
255
|
+
end
|
256
|
+
# from patch #12384
|
212
257
|
# @graph.add_edge [assoc_type, class_name, assoc.class_name, assoc_name]
|
213
|
-
@graph.add_edge [assoc_type, class_name, assoc_class_name, assoc_name]
|
258
|
+
@graph.add_edge [assoc_type, class_name, assoc_class_name, assoc_name]
|
214
259
|
end # process_association
|
215
260
|
end # class ModelsDiagram
|
261
|
+
|
262
|
+
# Process a DataMapper relationship
|
263
|
+
def process_datamapper_relationship(class_name, relation)
|
264
|
+
STDERR.puts "- Processing DataMapper model relationship #{relation.name.to_s}" if @options.verbose
|
265
|
+
|
266
|
+
# Skip "belongs_to" relationships
|
267
|
+
dm_type = relation.class.to_s.split('::')[-2]
|
268
|
+
return if dm_type == 'ManyToOne' && !@options.show_belongs_to
|
269
|
+
|
270
|
+
dm_parent_model = relation.parent_model.to_s
|
271
|
+
dm_child_model = relation.child_model.to_s
|
272
|
+
|
273
|
+
assoc_class_name = ''
|
274
|
+
# Get the assoc_class_name
|
275
|
+
if dm_parent_model.eql?(class_name)
|
276
|
+
assoc_class_name = dm_child_model
|
277
|
+
else
|
278
|
+
assoc_class_name = dm_parent_model
|
279
|
+
end
|
280
|
+
|
281
|
+
# Only non standard association names needs a label
|
282
|
+
assoc_name = ''
|
283
|
+
if !(relation.name.to_s.singularize.camelize.eql?(assoc_class_name.split('::').last))
|
284
|
+
assoc_name = relation.name.to_s
|
285
|
+
end
|
286
|
+
|
287
|
+
# TO BE IMPROVED
|
288
|
+
rel_type = 'many-many' # default value for ManyToOne and ManyToMany
|
289
|
+
if dm_type == 'OneToOne'
|
290
|
+
rel_type = 'one-one'
|
291
|
+
elsif dm_type == 'OneToMany'
|
292
|
+
rel_type = 'one-many'
|
293
|
+
end
|
294
|
+
|
295
|
+
@graph.add_edge [rel_type, class_name, assoc_class_name, assoc_name ]
|
296
|
+
end
|
data/tasks/railroady.rake
CHANGED
@@ -7,22 +7,27 @@
|
|
7
7
|
#
|
8
8
|
# Author: Preston Lee, http://railroady.prestonlee.com
|
9
9
|
|
10
|
-
#
|
11
|
-
|
12
|
-
|
13
|
-
|
10
|
+
# wrap helper methods so they don't conflict w/ methods on Object
|
11
|
+
module RailRoady
|
12
|
+
class RakeHelpers
|
13
|
+
def self.format
|
14
|
+
@@DIAGRAM_FORMAT ||= 'svg'
|
15
|
+
end
|
14
16
|
|
15
|
-
|
16
|
-
|
17
|
-
|
17
|
+
# Returns an absolute path for the following file.
|
18
|
+
def self.full_path(name = 'test.txt')
|
19
|
+
f = File.join(Rails.root.to_s.gsub(' ', '\ '), 'doc', name)
|
20
|
+
f.to_s
|
21
|
+
end
|
22
|
+
end
|
18
23
|
end
|
19
24
|
|
20
25
|
namespace :diagram do
|
21
26
|
|
22
|
-
@MODELS_ALL = full_path("models_complete.#{format}").freeze
|
23
|
-
@MODELS_BRIEF = full_path("models_brief.#{format}").freeze
|
24
|
-
@CONTROLLERS_ALL = full_path("controllers_complete.#{format}").freeze
|
25
|
-
@CONTROLLERS_BRIEF = full_path("controllers_brief.#{format}").freeze
|
27
|
+
@MODELS_ALL = RailRoady::RakeHelpers.full_path("models_complete.#{RailRoady::RakeHelpers.format}").freeze
|
28
|
+
@MODELS_BRIEF = RailRoady::RakeHelpers.full_path("models_brief.#{RailRoady::RakeHelpers.format}").freeze
|
29
|
+
@CONTROLLERS_ALL = RailRoady::RakeHelpers.full_path("controllers_complete.#{RailRoady::RakeHelpers.format}").freeze
|
30
|
+
@CONTROLLERS_BRIEF = RailRoady::RakeHelpers.full_path("controllers_brief.#{RailRoady::RakeHelpers.format}").freeze
|
26
31
|
|
27
32
|
namespace :models do
|
28
33
|
|
@@ -30,14 +35,14 @@ namespace :diagram do
|
|
30
35
|
task :complete do
|
31
36
|
f = @MODELS_ALL
|
32
37
|
puts "Generating #{f}"
|
33
|
-
sh "railroady -ilamM | dot -T#{format} > #{f}"
|
38
|
+
sh "railroady -ilamM | dot -T#{RailRoady::RakeHelpers.format} > #{f}"
|
34
39
|
end
|
35
40
|
|
36
41
|
desc 'Generates an abbreviated class diagram for all models.'
|
37
42
|
task :brief do
|
38
43
|
f = @MODELS_BRIEF
|
39
44
|
puts "Generating #{f}"
|
40
|
-
sh "railroady -bilamM | dot -T#{format} > #{f}"
|
45
|
+
sh "railroady -bilamM | dot -T#{RailRoady::RakeHelpers.format} > #{f}"
|
41
46
|
end
|
42
47
|
|
43
48
|
end
|
@@ -48,14 +53,14 @@ namespace :diagram do
|
|
48
53
|
task :complete do
|
49
54
|
f = @CONTROLLERS_ALL
|
50
55
|
puts "Generating #{f}"
|
51
|
-
sh "railroady -ilC | neato -T#{format} > #{f}"
|
56
|
+
sh "railroady -ilC | neato -T#{RailRoady::RakeHelpers.format} > #{f}"
|
52
57
|
end
|
53
58
|
|
54
59
|
desc 'Generates an abbreviated class diagram for all controllers.'
|
55
60
|
task :brief do
|
56
61
|
f = @CONTROLLERS_BRIEF
|
57
62
|
puts "Generating #{f}"
|
58
|
-
sh "railroady -bilC | neato -T#{format} > #{f}"
|
63
|
+
sh "railroady -bilC | neato -T#{RailRoady::RakeHelpers.format} > #{f}"
|
59
64
|
end
|
60
65
|
|
61
66
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: railroady
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 25
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 1
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 1.0.
|
9
|
+
- 7
|
10
|
+
version: 1.0.7
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Preston Lee
|
@@ -18,7 +18,7 @@ autorequire:
|
|
18
18
|
bindir: bin
|
19
19
|
cert_chain: []
|
20
20
|
|
21
|
-
date: 2012-02-
|
21
|
+
date: 2012-02-28 00:00:00 +05:30
|
22
22
|
default_executable: railroady
|
23
23
|
dependencies: []
|
24
24
|
|