prailroady 1.5.3
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.
- checksums.yaml +7 -0
- data/.gitignore +11 -0
- data/.travis.yml +3 -0
- data/AUTHORS.rdoc +23 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +33 -0
- data/Guardfile +49 -0
- data/LICENSE.rdoc +340 -0
- data/README.md +34 -0
- data/Rakefile +12 -0
- data/bin/prailroady +58 -0
- data/lib/prailroady.rb +5 -0
- data/lib/prailroady/aasm_diagram.rb +105 -0
- data/lib/prailroady/app_diagram.rb +103 -0
- data/lib/prailroady/controllers_diagram.rb +103 -0
- data/lib/prailroady/diagram_graph.rb +125 -0
- data/lib/prailroady/models_diagram.rb +357 -0
- data/lib/prailroady/options_struct.rb +204 -0
- data/lib/prailroady/railtie.rb +11 -0
- data/lib/prailroady/version.rb +3 -0
- data/prailroady.gemspec +25 -0
- data/tasks/prailroady.rake +139 -0
- data/test/file_fixture/app/controllers/application_controller.rb +2 -0
- data/test/file_fixture/app/controllers/dummy1_controller.rb +0 -0
- data/test/file_fixture/app/controllers/dummy2_controller.rb +0 -0
- data/test/file_fixture/app/controllers/sub-dir/sub_dummy_controller.rb +0 -0
- data/test/file_fixture/app/models/concerns/author_settings.rb +12 -0
- data/test/file_fixture/app/models/concerns/taggable.rb +0 -0
- data/test/file_fixture/app/models/dummy1.rb +0 -0
- data/test/file_fixture/app/models/dummy2.rb +0 -0
- data/test/file_fixture/app/models/sub-dir/sub_dummy.rb +0 -0
- data/test/file_fixture/lib/app/controllers/dummy/dummy_controller.rb +0 -0
- data/test/file_fixture/lib/app/models/dummy1.rb +0 -0
- data/test/lib/railroady/aasm_diagram_spec.rb +54 -0
- data/test/lib/railroady/app_diagram_spec.rb +14 -0
- data/test/lib/railroady/controllers_diagram_spec.rb +56 -0
- data/test/lib/railroady/core_ext_spec.rb +13 -0
- data/test/lib/railroady/diagram_graph_spec.rb +53 -0
- data/test/lib/railroady/models_diagram_spec.rb +162 -0
- data/test/spec_helper.rb +5 -0
- metadata +159 -0
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
# PrailRoady - 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
|
+
require 'prailroady/app_diagram'
|
|
8
|
+
|
|
9
|
+
# PrailRoady models diagram
|
|
10
|
+
class ModelsDiagram < AppDiagram
|
|
11
|
+
def initialize(options = OptionsStruct.new)
|
|
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.puts 'Generating models diagram' if @options.verbose
|
|
21
|
+
get_files.each do |f|
|
|
22
|
+
begin
|
|
23
|
+
process_class extract_class_name(f).constantize
|
|
24
|
+
rescue Exception
|
|
25
|
+
STDERR.puts "Warning: exception #{$ERROR_INFO} raised while trying to load model class #{f}"
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def get_files(prefix = '')
|
|
31
|
+
files = !@options.specify.empty? ? Dir.glob(@options.specify) : Dir.glob(prefix + 'app/models/**/*.rb')
|
|
32
|
+
files += Dir.glob('vendor/plugins/**/app/models/*.rb') if @options.plugins_models
|
|
33
|
+
files -= Dir.glob(prefix + 'app/models/concerns/**/*.rb') unless @options.include_concerns
|
|
34
|
+
files += engine_files if @options.engine_models
|
|
35
|
+
files -= Dir.glob(@options.exclude)
|
|
36
|
+
files
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def engine_files
|
|
40
|
+
engines.collect { |engine| Dir.glob("#{engine.root}/app/models/**/*.rb") }.flatten
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def extract_class_name(filename)
|
|
44
|
+
filename_was, class_name = filename, nil
|
|
45
|
+
|
|
46
|
+
filename = "app/models/#{filename.split('app/models')[1]}"
|
|
47
|
+
|
|
48
|
+
while filename.split('/').length > 2
|
|
49
|
+
begin
|
|
50
|
+
class_name = filename.match(/.*\/models\/(.*).rb$/)[1].camelize
|
|
51
|
+
class_name.constantize
|
|
52
|
+
|
|
53
|
+
break
|
|
54
|
+
rescue Exception
|
|
55
|
+
class_name = nil
|
|
56
|
+
filename_end = filename.split('/')[2..-1]
|
|
57
|
+
filename_end.shift
|
|
58
|
+
filename = "#{filename.split('/')[0, 2].join('/')}/#{filename_end.join('/')}"
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
if class_name.nil?
|
|
63
|
+
filename_was.match(/.*\/models\/(.*).rb$/)[1].camelize
|
|
64
|
+
else
|
|
65
|
+
class_name
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Process a model class
|
|
70
|
+
def process_class(current_class)
|
|
71
|
+
STDERR.puts "Processing #{current_class}" if @options.verbose
|
|
72
|
+
|
|
73
|
+
generated =
|
|
74
|
+
if defined?(CouchRest::Model::Base) && current_class.new.is_a?(CouchRest::Model::Base)
|
|
75
|
+
process_couchrest_model(current_class)
|
|
76
|
+
elsif defined?(Mongoid::Document) && current_class.new.is_a?(Mongoid::Document)
|
|
77
|
+
process_mongoid_model(current_class)
|
|
78
|
+
elsif defined?(DataMapper::Resource) && current_class.new.is_a?(DataMapper::Resource)
|
|
79
|
+
process_datamapper_model(current_class)
|
|
80
|
+
elsif current_class.respond_to? 'reflect_on_all_associations'
|
|
81
|
+
process_active_record_model(current_class)
|
|
82
|
+
elsif @options.all && (current_class.is_a? Class)
|
|
83
|
+
process_basic_class(current_class)
|
|
84
|
+
elsif @options.modules && (current_class.is_a? Module)
|
|
85
|
+
process_basic_module(current_class)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
if @options.inheritance && generated && include_inheritance?(current_class)
|
|
89
|
+
@graph.add_edge ['is-a', current_class.superclass.name, current_class.name]
|
|
90
|
+
end
|
|
91
|
+
end # process_class
|
|
92
|
+
|
|
93
|
+
def include_inheritance?(current_class)
|
|
94
|
+
STDERR.puts current_class.superclass if @options.verbose
|
|
95
|
+
(defined?(ActiveRecord::Base) ? current_class.superclass != ActiveRecord::Base : true) &&
|
|
96
|
+
(defined?(CouchRest::Model::Base) ? current_class.superclass != CouchRest::Model::Base : true) &&
|
|
97
|
+
(current_class.superclass != Object)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def process_basic_class(current_class)
|
|
101
|
+
node_type = @options.brief ? 'class-brief' : 'class'
|
|
102
|
+
@graph.add_node [node_type, current_class.name]
|
|
103
|
+
true
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def process_basic_module(current_class)
|
|
107
|
+
@graph.add_node ['module', current_class.name]
|
|
108
|
+
false
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def process_active_record_model(current_class)
|
|
112
|
+
node_attribs = []
|
|
113
|
+
if @options.brief || current_class.abstract_class?
|
|
114
|
+
node_type = 'model-brief'
|
|
115
|
+
else
|
|
116
|
+
node_type = 'model'
|
|
117
|
+
|
|
118
|
+
# Collect model's content columns
|
|
119
|
+
# content_columns = current_class.content_columns
|
|
120
|
+
|
|
121
|
+
if @options.hide_magic
|
|
122
|
+
# From patch #13351
|
|
123
|
+
# http://wiki.rubyonrails.org/rails/pages/MagicFieldNames
|
|
124
|
+
magic_fields = %w(created_at created_on updated_at updated_on lock_version type id position parent_id lft rgt quote template)
|
|
125
|
+
magic_fields << current_class.table_name + '_count' if current_class.respond_to? 'table_name'
|
|
126
|
+
content_columns = current_class.content_columns.select { |c| !magic_fields.include? c.name }
|
|
127
|
+
else
|
|
128
|
+
content_columns = current_class.columns
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
content_columns.each do |a|
|
|
132
|
+
content_column = a.name
|
|
133
|
+
content_column += ' :' + a.sql_type.to_s unless @options.hide_types
|
|
134
|
+
node_attribs << content_column
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
@graph.add_node [node_type, current_class.name, node_attribs]
|
|
138
|
+
|
|
139
|
+
# Process class associations
|
|
140
|
+
associations = current_class.reflect_on_all_associations
|
|
141
|
+
if @options.inheritance && ! @options.transitive
|
|
142
|
+
superclass_associations = current_class.superclass.reflect_on_all_associations
|
|
143
|
+
|
|
144
|
+
associations = associations.select { |a| !superclass_associations.include? a }
|
|
145
|
+
# This doesn't works!
|
|
146
|
+
# associations -= current_class.superclass.reflect_on_all_associations
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
associations.each do |a|
|
|
150
|
+
process_association current_class.name, a
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
true
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def process_datamapper_model(current_class)
|
|
157
|
+
node_attribs = []
|
|
158
|
+
if @options.brief # || current_class.abstract_class?
|
|
159
|
+
node_type = 'model-brief'
|
|
160
|
+
else
|
|
161
|
+
node_type = 'model'
|
|
162
|
+
|
|
163
|
+
# Collect model's properties
|
|
164
|
+
props = current_class.properties.sort_by(&:name)
|
|
165
|
+
|
|
166
|
+
if @options.hide_magic
|
|
167
|
+
# From patch #13351
|
|
168
|
+
# http://wiki.rubyonrails.org/rails/pages/MagicFieldNames
|
|
169
|
+
magic_fields =
|
|
170
|
+
%w(created_at created_on updated_at updated_on lock_version _type _id position parent_id lft rgt quote template)
|
|
171
|
+
props = props.select { |c| !magic_fields.include?(c.name.to_s) }
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
props.each do |a|
|
|
175
|
+
prop = a.name.to_s
|
|
176
|
+
prop += ' :' + a.class.name.split('::').last unless @options.hide_types
|
|
177
|
+
node_attribs << prop
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
@graph.add_node [node_type, current_class.name, node_attribs]
|
|
181
|
+
|
|
182
|
+
# Process relationships
|
|
183
|
+
relationships = current_class.relationships
|
|
184
|
+
|
|
185
|
+
# TODO: Manage inheritance
|
|
186
|
+
|
|
187
|
+
relationships.each do |a|
|
|
188
|
+
process_datamapper_relationship current_class.name, a
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
true
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def process_mongoid_model(current_class)
|
|
195
|
+
node_attribs = []
|
|
196
|
+
|
|
197
|
+
if @options.brief
|
|
198
|
+
node_type = 'model-brief'
|
|
199
|
+
else
|
|
200
|
+
node_type = 'model'
|
|
201
|
+
|
|
202
|
+
# Collect model's content columns
|
|
203
|
+
content_columns = current_class.fields.values.sort_by(&:name)
|
|
204
|
+
|
|
205
|
+
if @options.hide_magic
|
|
206
|
+
# From patch #13351
|
|
207
|
+
# http://wiki.rubyonrails.org/rails/pages/MagicFieldNames
|
|
208
|
+
magic_fields = %w(created_at created_on updated_at updated_on lock_version _type _id position parent_id lft rgt quote template)
|
|
209
|
+
content_columns = content_columns.select { |c| !magic_fields.include?(c.name) }
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
content_columns.each do |a|
|
|
213
|
+
content_column = a.name
|
|
214
|
+
content_column += " :#{a.type}" unless @options.hide_types
|
|
215
|
+
node_attribs << content_column
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
@graph.add_node [node_type, current_class.name, node_attribs]
|
|
220
|
+
|
|
221
|
+
# Process class associations
|
|
222
|
+
associations = current_class.relations.values
|
|
223
|
+
|
|
224
|
+
if @options.inheritance && !@options.transitive &&
|
|
225
|
+
current_class.superclass.respond_to?(:relations)
|
|
226
|
+
associations -= current_class.superclass.relations.values
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
associations.each do |a|
|
|
230
|
+
process_association current_class.name, a
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
true
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
##
|
|
237
|
+
# Some very basic CouchRest::Model support
|
|
238
|
+
#
|
|
239
|
+
# Field types note: the field's type is rendered only if it's explicitly
|
|
240
|
+
# specified in a model.
|
|
241
|
+
#
|
|
242
|
+
def process_couchrest_model(current_class)
|
|
243
|
+
node_attribs = []
|
|
244
|
+
|
|
245
|
+
if @options.brief
|
|
246
|
+
node_type = 'model-brief'
|
|
247
|
+
else
|
|
248
|
+
node_type = 'model'
|
|
249
|
+
|
|
250
|
+
# Collect model's content columns
|
|
251
|
+
content_columns = current_class.properties
|
|
252
|
+
|
|
253
|
+
if @options.hide_magic
|
|
254
|
+
magic_fields = %w(created_at updated_at type _id _rev)
|
|
255
|
+
content_columns = content_columns.select { |c| !magic_fields.include?(c.name) }
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
content_columns.each do |a|
|
|
259
|
+
content_column = a.name
|
|
260
|
+
content_column += " :#{a.type}" unless @options.hide_types || a.type.nil?
|
|
261
|
+
node_attribs << content_column
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
@graph.add_node [node_type, current_class.name, node_attribs]
|
|
266
|
+
|
|
267
|
+
true
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
# Process a model association
|
|
271
|
+
def process_association(class_name, assoc)
|
|
272
|
+
STDERR.puts "- Processing model association #{assoc.name}" if @options.verbose
|
|
273
|
+
|
|
274
|
+
# Skip "belongs_to" associations
|
|
275
|
+
macro = assoc.macro.to_s
|
|
276
|
+
return if %w(belongs_to referenced_in).include?(macro) && !@options.show_belongs_to
|
|
277
|
+
|
|
278
|
+
# Skip "through" associations
|
|
279
|
+
through = assoc.options.include?(:through)
|
|
280
|
+
return if through && @options.hide_through
|
|
281
|
+
|
|
282
|
+
# TODO:
|
|
283
|
+
# FAIL: assoc.methods.include?(:class_name)
|
|
284
|
+
# FAIL: assoc.responds_to?(:class_name)
|
|
285
|
+
assoc_class_name = assoc.class_name rescue nil
|
|
286
|
+
assoc_class_name ||= assoc.name.to_s.underscore.singularize.camelize
|
|
287
|
+
|
|
288
|
+
# Only non standard association names needs a label
|
|
289
|
+
|
|
290
|
+
# from patch #12384
|
|
291
|
+
# if assoc.class_name == assoc.name.to_s.singularize.camelize
|
|
292
|
+
if assoc_class_name == assoc.name.to_s.singularize.camelize
|
|
293
|
+
assoc_name = ''
|
|
294
|
+
else
|
|
295
|
+
assoc_name = assoc.name.to_s
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
# Patch from "alpack" to support classes in a non-root module namespace. See: http://disq.us/yxl1v
|
|
299
|
+
if class_name.include?('::') && !assoc_class_name.include?('::')
|
|
300
|
+
assoc_class_name = class_name.split('::')[0..-2].push(assoc_class_name).join('::')
|
|
301
|
+
end
|
|
302
|
+
assoc_class_name.gsub!(/^::/, '')
|
|
303
|
+
|
|
304
|
+
if %w(has_one references_one embeds_one).include?(macro)
|
|
305
|
+
assoc_type = 'one-one'
|
|
306
|
+
elsif macro == 'has_many' && (!assoc.options[:through]) ||
|
|
307
|
+
%w(references_many embeds_many).include?(macro)
|
|
308
|
+
assoc_type = 'one-many'
|
|
309
|
+
elsif macro == 'belongs_to'
|
|
310
|
+
assoc_type = 'belongs-to'
|
|
311
|
+
else # habtm or has_many, :through
|
|
312
|
+
# Add FAKE associations too in order to understand mistakes
|
|
313
|
+
return if @habtm.include? [assoc_class_name, class_name, assoc_name]
|
|
314
|
+
assoc_type = 'many-many'
|
|
315
|
+
@habtm << [class_name, assoc_class_name, assoc_name]
|
|
316
|
+
end
|
|
317
|
+
# from patch #12384
|
|
318
|
+
# @graph.add_edge [assoc_type, class_name, assoc.class_name, assoc_name]
|
|
319
|
+
@graph.add_edge [assoc_type, class_name, assoc_class_name, assoc_name]
|
|
320
|
+
end # process_association
|
|
321
|
+
|
|
322
|
+
# Process a DataMapper relationship
|
|
323
|
+
def process_datamapper_relationship(class_name, relation)
|
|
324
|
+
STDERR.puts "- Processing DataMapper model relationship #{relation.name}" if @options.verbose
|
|
325
|
+
|
|
326
|
+
# Skip "belongs_to" relationships
|
|
327
|
+
dm_type = relation.class.to_s.split('::')[-2]
|
|
328
|
+
return if dm_type == 'ManyToOne' && !@options.show_belongs_to
|
|
329
|
+
|
|
330
|
+
dm_parent_model = relation.parent_model.to_s
|
|
331
|
+
dm_child_model = relation.child_model.to_s
|
|
332
|
+
|
|
333
|
+
assoc_class_name = ''
|
|
334
|
+
# Get the assoc_class_name
|
|
335
|
+
if dm_parent_model.eql?(class_name)
|
|
336
|
+
assoc_class_name = dm_child_model
|
|
337
|
+
else
|
|
338
|
+
assoc_class_name = dm_parent_model
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
# Only non standard association names needs a label
|
|
342
|
+
assoc_name = ''
|
|
343
|
+
unless relation.name.to_s.singularize.camelize.eql?(assoc_class_name.split('::').last)
|
|
344
|
+
assoc_name = relation.name.to_s
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
# TO BE IMPROVED
|
|
348
|
+
rel_type = 'many-many' # default value for ManyToOne and ManyToMany
|
|
349
|
+
if dm_type == 'OneToOne'
|
|
350
|
+
rel_type = 'one-one'
|
|
351
|
+
elsif dm_type == 'OneToMany'
|
|
352
|
+
rel_type = 'one-many'
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
@graph.add_edge [rel_type, class_name, assoc_class_name, assoc_name]
|
|
356
|
+
end
|
|
357
|
+
end # class ModelsDiagram
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
# PrailRoady - 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
|
+
require 'ostruct'
|
|
8
|
+
|
|
9
|
+
# PrailRoady command line options parser
|
|
10
|
+
class OptionsStruct < OpenStruct
|
|
11
|
+
require 'optparse'
|
|
12
|
+
|
|
13
|
+
def initialize(args = {})
|
|
14
|
+
init_options = { all: false,
|
|
15
|
+
brief: false,
|
|
16
|
+
specify: [],
|
|
17
|
+
exclude: [],
|
|
18
|
+
inheritance: false,
|
|
19
|
+
join: false,
|
|
20
|
+
label: false,
|
|
21
|
+
modules: false,
|
|
22
|
+
all_columns: false,
|
|
23
|
+
hide_magic: false,
|
|
24
|
+
hide_types: false,
|
|
25
|
+
hide_public: false,
|
|
26
|
+
hide_protected: false,
|
|
27
|
+
hide_private: false,
|
|
28
|
+
plugins_models: false,
|
|
29
|
+
engine_models: false,
|
|
30
|
+
engine_controllers: false,
|
|
31
|
+
include_concerns: false,
|
|
32
|
+
root: '',
|
|
33
|
+
show_belongs_to: false,
|
|
34
|
+
hide_through: false,
|
|
35
|
+
transitive: false,
|
|
36
|
+
verbose: false,
|
|
37
|
+
alphabetize: false,
|
|
38
|
+
xmi: false,
|
|
39
|
+
command: '',
|
|
40
|
+
config_file: 'config/environment',
|
|
41
|
+
app_name: 'orailroady', app_human_name: 'PrailRoady', app_version: '', copyright: '' }
|
|
42
|
+
super(init_options.merge(args))
|
|
43
|
+
end # initialize
|
|
44
|
+
|
|
45
|
+
def parse(args)
|
|
46
|
+
@opt_parser = OptionParser.new do |opts|
|
|
47
|
+
opts.banner = "Usage: #{app_name} [options] command"
|
|
48
|
+
opts.separator ''
|
|
49
|
+
opts.separator 'Common options:'
|
|
50
|
+
opts.on('-b', '--brief', 'Generate compact diagram',
|
|
51
|
+
' (no attributes nor methods)') do |b|
|
|
52
|
+
self.brief = b
|
|
53
|
+
end
|
|
54
|
+
opts.on('-s', '--specify file1[,fileN]', Array, 'Specify only given files') do |list|
|
|
55
|
+
self.specify = list
|
|
56
|
+
end
|
|
57
|
+
opts.on('-e', '--exclude file1[,fileN]', Array, 'Exclude given files') do |list|
|
|
58
|
+
self.exclude = list
|
|
59
|
+
end
|
|
60
|
+
opts.on('-i', '--inheritance', 'Include inheritance relations') do |i|
|
|
61
|
+
self.inheritance = i
|
|
62
|
+
end
|
|
63
|
+
opts.on('-l', '--label', 'Add a label with diagram information',
|
|
64
|
+
' (type, date, migration, version)') do |l|
|
|
65
|
+
self.label = l
|
|
66
|
+
end
|
|
67
|
+
opts.on('-o', '--output FILE', 'Write diagram to file FILE') do |f|
|
|
68
|
+
self.output = f
|
|
69
|
+
end
|
|
70
|
+
opts.on('-r', '--root PATH', 'Set PATH as the application root') do |r|
|
|
71
|
+
self.root = r
|
|
72
|
+
end
|
|
73
|
+
opts.on('-v', '--verbose', 'Enable verbose output',
|
|
74
|
+
' (produce messages to STDOUT)') do |v|
|
|
75
|
+
self.verbose = v
|
|
76
|
+
end
|
|
77
|
+
opts.on('-x', '--xmi', 'Produce XMI instead of DOT',
|
|
78
|
+
' (for UML tools)') do |x|
|
|
79
|
+
self.xmi = x
|
|
80
|
+
end
|
|
81
|
+
opts.on('--alphabetize', 'Sort methods alphabetically') do |a|
|
|
82
|
+
self.alphabetize = a
|
|
83
|
+
end
|
|
84
|
+
opts.separator ''
|
|
85
|
+
opts.separator 'Models diagram options:'
|
|
86
|
+
opts.on('-a', '--all', 'Include all models',
|
|
87
|
+
' (not only ActiveRecord::Base derived)') do |a|
|
|
88
|
+
self.all = a
|
|
89
|
+
end
|
|
90
|
+
opts.on('--show-belongs_to', 'Show belongs_to associations') do |s|
|
|
91
|
+
self.show_belongs_to = s
|
|
92
|
+
end
|
|
93
|
+
opts.on('--hide-through', 'Hide through associations') do |h|
|
|
94
|
+
self.hide_through = h
|
|
95
|
+
end
|
|
96
|
+
opts.on('--all-columns', 'Show all columns (not just content columns)') do |h|
|
|
97
|
+
self.all_columns = h
|
|
98
|
+
end
|
|
99
|
+
opts.on('--hide-magic', 'Hide magic field names') do |h|
|
|
100
|
+
self.hide_magic = h
|
|
101
|
+
end
|
|
102
|
+
opts.on('--hide-types', 'Hide attributes type') do |h|
|
|
103
|
+
self.hide_types = h
|
|
104
|
+
end
|
|
105
|
+
opts.on('-j', '--join', 'Concentrate edges') do |j|
|
|
106
|
+
self.join = j
|
|
107
|
+
end
|
|
108
|
+
opts.on('-m', '--modules', 'Include modules') do |m|
|
|
109
|
+
self.modules = m
|
|
110
|
+
end
|
|
111
|
+
opts.on('-p', '--plugins-models', 'Include plugins models') do |p|
|
|
112
|
+
self.plugins_models = p
|
|
113
|
+
end
|
|
114
|
+
opts.on('-z', '--engine-models', 'Include engine models') do |em|
|
|
115
|
+
self.engine_models = em
|
|
116
|
+
end
|
|
117
|
+
opts.on('--include-concerns', 'Include models in concerns subdirectory') do |c|
|
|
118
|
+
self.include_concerns = c
|
|
119
|
+
end
|
|
120
|
+
opts.on('-t', '--transitive', 'Include transitive associations',
|
|
121
|
+
'(through inheritance)') do |t|
|
|
122
|
+
self.transitive = t
|
|
123
|
+
end
|
|
124
|
+
opts.separator ''
|
|
125
|
+
opts.separator 'Controllers diagram options:'
|
|
126
|
+
opts.on('--hide-public', 'Hide public methods') do |h|
|
|
127
|
+
self.hide_public = h
|
|
128
|
+
end
|
|
129
|
+
opts.on('--hide-protected', 'Hide protected methods') do |h|
|
|
130
|
+
self.hide_protected = h
|
|
131
|
+
end
|
|
132
|
+
opts.on('--hide-private', 'Hide private methods') do |h|
|
|
133
|
+
self.hide_private = h
|
|
134
|
+
end
|
|
135
|
+
opts.on('--engine-controllers', 'Include engine controllers') do |ec|
|
|
136
|
+
self.engine_controllers = ec
|
|
137
|
+
end
|
|
138
|
+
opts.separator ''
|
|
139
|
+
opts.separator 'Other options:'
|
|
140
|
+
opts.on('-h', '--help', 'Show this message') do
|
|
141
|
+
STDOUT.print "#{opts}\n"
|
|
142
|
+
exit
|
|
143
|
+
end
|
|
144
|
+
opts.on('--version', 'Show version and copyright') do
|
|
145
|
+
STDOUT.print "#{app_human_name} version #{app_version}\n\n" \
|
|
146
|
+
"#{copyright}\nThis is free software; see the source " \
|
|
147
|
+
"for copying conditions.\n\n"
|
|
148
|
+
exit
|
|
149
|
+
end
|
|
150
|
+
opts.separator ''
|
|
151
|
+
opts.on('-c', '--config FILE', 'File to load environment (defaults to config/environment)') do |c|
|
|
152
|
+
self.config_file = c if c && c != ''
|
|
153
|
+
end
|
|
154
|
+
opts.separator 'Commands (you must supply one of these):'
|
|
155
|
+
opts.on('-M', '--models', 'Generate models diagram') do |_c|
|
|
156
|
+
if command != ''
|
|
157
|
+
STDERR.print "Error: Can only generate one diagram type\n\n"
|
|
158
|
+
exit 1
|
|
159
|
+
else
|
|
160
|
+
self.command = 'models'
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
opts.on('-C', '--controllers', 'Generate controllers diagram') do |_c|
|
|
164
|
+
if command != ''
|
|
165
|
+
STDERR.print "Error: Can only generate one diagram type\n\n"
|
|
166
|
+
exit 1
|
|
167
|
+
else
|
|
168
|
+
self.command = 'controllers'
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
# From Ana Nelson's patch
|
|
172
|
+
opts.on('-A', '--aasm', "Generate \"acts as state machine\" diagram") do |_c|
|
|
173
|
+
if command == 'controllers'
|
|
174
|
+
STDERR.print "Error: Can only generate one diagram type\n\n"
|
|
175
|
+
exit 1
|
|
176
|
+
else
|
|
177
|
+
self.command = 'aasm'
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
opts.separator ''
|
|
181
|
+
opts.separator 'For bug reporting and additional information, please see:'
|
|
182
|
+
opts.separator 'http://railroad.rubyforge.org/'
|
|
183
|
+
end # do
|
|
184
|
+
|
|
185
|
+
begin
|
|
186
|
+
@opt_parser.parse!(args)
|
|
187
|
+
rescue OptionParser::AmbiguousOption
|
|
188
|
+
option_error 'Ambiguous option'
|
|
189
|
+
rescue OptionParser::InvalidOption
|
|
190
|
+
option_error 'Invalid option'
|
|
191
|
+
rescue OptionParser::InvalidArgument
|
|
192
|
+
option_error 'Invalid argument'
|
|
193
|
+
rescue OptionParser::MissingArgument
|
|
194
|
+
option_error 'Missing argument'
|
|
195
|
+
end
|
|
196
|
+
end # parse
|
|
197
|
+
|
|
198
|
+
private
|
|
199
|
+
|
|
200
|
+
def option_error(msg)
|
|
201
|
+
STDERR.print "Error: #{msg}\n\n #{@opt_parser}\n"
|
|
202
|
+
exit 1
|
|
203
|
+
end
|
|
204
|
+
end # class OptionsStruct
|