Skip to content

Commit

Permalink
Fix infinite load retry loop. Even more verbose --verbose. Option to …
Browse files Browse the repository at this point in the history
…explicitly map file names to model names. Option to include lib/. Fix model name inference (#classify -> #camelize).
  • Loading branch information
Tero Tilus committed Feb 13, 2009
1 parent 71b1fed commit f3b020a
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 50 deletions.
91 changes: 55 additions & 36 deletions lib/railroad/models_diagram.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class ModelsDiagram < AppDiagram

def initialize(options)
#options.exclude.map! {|e| "app/models/" + e}
super options
super options
@graph.diagram_type = 'Models'
# Processed habtm associations
@habtm = []
Expand All @@ -18,27 +18,41 @@ def initialize(options)
# Process model files
def generate
STDERR.print "Generating models diagram\n" if @options.verbose
base = "app/models/"
base = "(app/models/|lib/)"
files = Dir.glob("app/models/**/*.rb")
files += Dir.glob("vendor/plugins/**/app/models/*.rb") if @options.plugins_models
files += Dir.glob("vendor/plugins/**/app/models/*.rb") if @options.plugins_models
files += Dir.glob("lib/**/*.rb") if @options.libraries

files -= @options.exclude
files.each do |file|
model_name = file.gsub(/^#{base}([\w_\/\\]+)\.rb/, '\1')
(files + ['filter_condition_type']).each do |file|
model_name =
@options.classes_by_files[file] ||
(model_path = file.gsub(/^#{base}([\w_\/\\]+)\.rb/, '\2')).camelize
STDERR.print "Processing #{file} ...\n" if @options.verbose
# Hack to skip all xxx_related.rb files
next if /_related/i =~ model_name

klass = begin
model_name.classify.constantize
rescue LoadError
model_name.gsub!(/.*[\/\\]/, '')
retry
rescue NameError
next
end
model_name.constantize
rescue LoadError
STDERR.print "\t#{model_name} raised LoadError.\n" if @options.verbose
oldlen = model_path.length
model_path.gsub!(/.*[\/\\]/, '')
model_name = model_path.camelize
if oldlen > model_path.length
retry
end
STDERR.print "\tDone trying to remove slashes, skipping this model.\n" if @options.verbose
next
rescue NameError
STDERR.print "\t#{model_name} raised NameError, skipping this model.\n" if @options.verbose
next
end

process_class klass
STDERR.print "Done #{file}\n" if @options.verbose
end
end
end

private

Expand All @@ -48,8 +62,9 @@ def load_classes
disable_stdout
files = Dir.glob("app/models/**/*.rb")
files += Dir.glob("vendor/plugins/**/app/models/*.rb") if @options.plugins_models
files += ["lib/search.rb"]
files -= @options.exclude
files.each do |m|
files.each do |m|
require m
end
enable_stdout
Expand All @@ -63,40 +78,40 @@ def load_classes
# Process a model class
def process_class(current_class)

STDERR.print "\tProcessing #{current_class}\n" if @options.verbose
STDERR.print "\tProcessing #{current_class} ...\n" if @options.verbose

generated = false

# Is current_clas derived from ActiveRecord::Base?
if current_class.respond_to?'reflect_on_all_associations'


node_attribs = []
if @options.brief || current_class.abstract_class? || current_class.superclass != ActiveRecord::Base
node_type = 'model-brief'
else
else
node_type = 'model'

# Collect model's content columns

content_columns = current_class.content_columns
if @options.hide_magic
content_columns = current_class.content_columns

if @options.hide_magic
magic_fields = [
# Restful Authentication
"login", "crypted_password", "salt", "remember_token", "remember_token_expires_at", "activation_code", "activated_at",
# From patch #13351
# http://wiki.rubyonrails.org/rails/pages/MagicFieldNames
"created_at", "created_on", "updated_at", "updated_on",
"lock_version", "type", "id", "position", "parent_id", "lft",
"lock_version", "type", "id", "position", "parent_id", "lft",
"rgt", "quote", "template"
]
magic_fields << current_class.table_name + "_count" if current_class.respond_to? 'table_name'
magic_fields << current_class.table_name + "_count" if current_class.respond_to? 'table_name'
content_columns = current_class.content_columns.select {|c| ! magic_fields.include? c.name}
else
content_columns = current_class.content_columns
end

content_columns.each do |a|
content_column = a.name
content_column += ' :' + a.type.to_s unless @options.hide_types
Expand All @@ -109,8 +124,8 @@ def process_class(current_class)
associations = current_class.reflect_on_all_associations
if @options.inheritance && ! @options.transitive
superclass_associations = current_class.superclass.reflect_on_all_associations
associations = associations.select{|a| ! superclass_associations.include? a}

associations = associations.select{|a| ! superclass_associations.include? a}
# This doesn't works!
# associations -= current_class.superclass.reflect_on_all_associations
end
Expand All @@ -127,45 +142,49 @@ def process_class(current_class)
end

# Only consider meaningful inheritance relations for generated classes
if @options.inheritance && generated &&
if @options.inheritance && generated &&
(current_class.superclass != ActiveRecord::Base) &&
(current_class.superclass != Object)
@graph.add_edge ['is-a', current_class.superclass.name, current_class.name]
end
end

STDERR.print "\tDone #{current_class}\n" if @options.verbose
end # process_class

# Process a model association
def process_association(class_name, assoc)

STDERR.print "\t\tProcessing model association #{assoc.name.to_s}\n" if @options.verbose
begin
STDERR.print "\t\tProcessing model association #{assoc.name.to_s} ..." if @options.verbose

# Skip "belongs_to" associations
return if assoc.macro.to_s == 'belongs_to'

# Only non standard association names needs a label

# from patch #12384
# if assoc.class_name == assoc.name.to_s.singularize.camelize
assoc_class_name = (assoc.class_name.respond_to? 'underscore') ? assoc.class_name.underscore.singularize.camelize : assoc.class_name
assoc_class_name = (assoc.class_name.respond_to? 'underscore') ? assoc.class_name.underscore.singularize.camelize : assoc.class_name
if assoc_class_name == assoc.name.to_s.singularize.camelize
assoc_name = ''
else
assoc_name = assoc.name.to_s
end
end
# STDERR.print "#{assoc_name}\n"
if assoc.macro.to_s == 'has_one'
if assoc.macro.to_s == 'has_one'
assoc_type = 'one-one'
elsif assoc.macro.to_s == 'has_many' && (! assoc.options[:through])
assoc_type = 'one-many'
else # habtm or has_many, :through
return if @habtm.include? [assoc.class_name, class_name, assoc_name]
assoc_type = 'many-many'
@habtm << [class_name, assoc.class_name, assoc_name]
end
# from patch #12384
end
# from patch #12384
# @graph.add_edge [assoc_type, class_name, assoc.class_name, assoc_name]
@graph.add_edge [assoc_type, class_name, assoc_class_name, assoc_name]
@graph.add_edge [assoc_type, class_name, assoc_class_name, assoc_name]
ensure
STDERR.print " done.\n" if @options.verbose
end
end # process_association

end # class ModelsDiagram
36 changes: 22 additions & 14 deletions lib/railroad/options_struct.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ def initialize
init_options = { :all => false,
:brief => false,
:exclude => [],
:classes_by_files => {},
:inheritance => false,
:join => false,
:label => false,
Expand All @@ -25,6 +26,7 @@ def initialize
:hide_protected => false,
:hide_private => false,
:plugins_models => false,
:libraries => false,
:root => '',
:transitive => false,
:verbose => false,
Expand All @@ -38,17 +40,20 @@ def parse(args)
opts.banner = "Usage: #{APP_NAME} [options] command"
opts.separator ""
opts.separator "Common options:"
opts.on("-b", "--brief", "Generate compact diagram",
opts.on("-b", "--brief", "Generate compact diagram",
" (no attributes nor methods)") do |b|
self.brief = b
end
opts.on("-e", "--exclude file1[,fileN]", Array, "Exclude given files") do |list|
opts.on("-e", "--exclude=file1[,fileN]", Array, "Exclude given files") do |list|
self.exclude = list
end
opts.on("-c", "--class-map=file1,MyClass1[,fileN,MyClassN]", Array, "Map files to classes they contain") do |list|
self.classes_by_files = Hash[*list]
end
opts.on("-i", "--inheritance", "Include inheritance relations") do |i|
self.inheritance = i
end
opts.on("-l", "--label", "Add a label with diagram information",
opts.on("-l", "--label", "Add a label with diagram information",
" (type, date, migration, version)") do |l|
self.label = l
end
Expand All @@ -58,17 +63,17 @@ def parse(args)
opts.on("-r", "--root PATH", "Set PATH as the application root") do |r|
self.root = r
end
opts.on("-v", "--verbose", "Enable verbose output",
opts.on("-v", "--verbose", "Enable verbose output",
" (produce messages to STDOUT)") do |v|
self.verbose = v
end
opts.on("-x", "--xmi", "Produce XMI instead of DOT",
opts.on("-x", "--xmi", "Produce XMI instead of DOT",
" (for UML tools)") do |x|
self.xmi = x
end
opts.separator ""
opts.separator "Models diagram options:"
opts.on("-a", "--all", "Include all models",
opts.on("-a", "--all", "Include all models",
" (not only ActiveRecord::Base derived)") do |a|
self.all = a
end
Expand All @@ -87,6 +92,9 @@ def parse(args)
opts.on("-p", "--plugins-models", "Include plugins models") do |p|
self.plugins_models = p
end
opts.on("-y", "--libraries", "Include application library") do |y|
self.libraries = y
end
opts.on("-t", "--transitive", "Include transitive associations",
"(through inheritance)") do |t|
self.transitive = t
Expand Down Expand Up @@ -120,27 +128,27 @@ def parse(args)
if self.command != ''
STDERR.print "Error: Can only generate one diagram type\n\n"
exit 1
else
self.command = 'models'
else
self.command = 'models'
end
end
end
opts.on("-C", "--controllers", "Generate controllers diagram") do |c|
if self.command != ''
STDERR.print "Error: Can only generate one diagram type\n\n"
exit 1
else
self.command = 'controllers'
else
self.command = 'controllers'
end
end
# From Ana Nelson's patch
opts.on("-A", "--aasm", "Generate \"acts as state machine\" diagram") do |c|
if self.command == 'controllers'
STDERR.print "Error: Can only generate one diagram type\n\n"
exit 1
else
else
self.command = 'aasm'
end
end
end
opts.separator ""
opts.separator "For bug reporting and additional information, please see:"
opts.separator "http://railroad.rubyforge.org/"
Expand All @@ -159,7 +167,7 @@ def parse(args)
end
end # parse

private
private

def option_error(msg)
STDERR.print "Error: #{msg}\n\n #{@opt_parser}\n"
Expand Down

0 comments on commit f3b020a

Please sign in to comment.