lib/railroady/models_diagram.rb in railroady-1.0.4 vs lib/railroady/models_diagram.rb in railroady-1.0.5

- old
+ new

@@ -6,27 +6,25 @@ require 'railroady/app_diagram' # RailRoady models diagram class ModelsDiagram < AppDiagram - def initialize(options = OptionsStruct.new) - #options.exclude.map! {|e| "app/models/" + e} - super options + super options @graph.diagram_type = 'Models' # Processed habtm associations @habtm = [] end # Process model files def generate - STDERR.print "Generating models diagram\n" if @options.verbose + STDERR.puts "Generating models diagram" if @options.verbose get_files.each do |f| begin process_class extract_class_name(f).constantize rescue Exception - STDERR.print "Warning: exception #{$!} raised while trying to load model class #{f}\n" + STDERR.puts "Warning: exception #{$!} raised while trying to load model class #{f}" end end end @@ -37,96 +35,158 @@ files end # Process a model class def process_class(current_class) + STDERR.puts "Processing #{current_class}" if @options.verbose + + generated = if defined?(Mongoid::Document) && current_class.new.is_a?(Mongoid::Document) + process_mongoid_model(current_class) + elsif current_class.respond_to?'reflect_on_all_associations' + process_active_record_model(current_class) + elsif @options.all && (current_class.is_a? Class) + process_basic_class(current_class) + elsif @options.modules && (current_class.is_a? Module) + process_basic_module(current_class) + end - STDERR.print "\tProcessing #{current_class}\n" if @options.verbose + if @options.inheritance && generated && include_inheritance?(current_class) + @graph.add_edge ['is-a', current_class.superclass.name, current_class.name] + end + + end # process_class + + def include_inheritance?(current_class) + (defined?(ActiveRecord::Base) && current_class.superclass != ActiveRecord::Base) && + (current_class.superclass != Object) + end + + def process_basic_class(current_class) + node_type = @options.brief ? 'class-brief' : 'class' + @graph.add_node [node_type, current_class.name] + true + end + + def process_basic_module(current_class) + @graph.add_node ['module', current_class.name] + false + end - generated = false - - # Is current_clas derived from ActiveRecord::Base? - if current_class.respond_to?'reflect_on_all_associations' + def process_active_record_model(current_class) + node_attribs = [] + if @options.brief || current_class.abstract_class? + node_type = 'model-brief' + else + node_type = 'model' + # Collect model's content columns + content_columns = current_class.content_columns - node_attribs = [] - if @options.brief || current_class.abstract_class? - node_type = 'model-brief' - else - node_type = 'model' + if @options.hide_magic + # From patch #13351 + # http://wiki.rubyonrails.org/rails/pages/MagicFieldNames + magic_fields = [ + "created_at", "created_on", "updated_at", "updated_on", + "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' + 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 + node_attribs << content_column + end + end + @graph.add_node [node_type, current_class.name, node_attribs] + + # Process class associations + 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} + # This doesn't works! + # associations -= current_class.superclass.reflect_on_all_associations + end + + associations.each do |a| + process_association current_class.name, a + end + + true + end + + def process_mongoid_model(current_class) + node_attribs = [] + + if @options.brief + node_type = 'model-brief' + else + node_type = 'model' - # Collect model's content columns or all columns if all_columns flag is passed - if @options.all_columns - columns = current_class.columns - else - columns = current_class.content_columns - end - - if @options.hide_magic - # From patch #13351 - # http://wiki.rubyonrails.org/rails/pages/MagicFieldNames - magic_fields = [ - "created_at", "created_on", "updated_at", "updated_on", - "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' - columns = current_class.content_columns.select {|c| ! magic_fields.include? c.name} - end + # Collect model's content columns + content_columns = current_class.fields.values.sort_by(&:name) - columns.each do |a| - column = a.name - column += ' :' + a.type.to_s unless @options.hide_types - node_attribs << column - end + if @options.hide_magic + # From patch #13351 + # http://wiki.rubyonrails.org/rails/pages/MagicFieldNames + magic_fields = [ + "created_at", "created_on", "updated_at", "updated_on", + "lock_version", "_type", "_id", "position", "parent_id", "lft", + "rgt", "quote", "template" + ] + content_columns = content_columns.select {|c| !magic_fields.include?(c.name) } end - - @graph.add_node [node_type, current_class.name, node_attribs] - generated = true - # Process class associations - 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} - # This doesn't works! - # associations -= current_class.superclass.reflect_on_all_associations + + content_columns.each do |a| + content_column = a.name + content_column += " :#{a.type}" unless @options.hide_types + node_attribs << content_column end - associations.each do |a| - process_association current_class.name, a - end - elsif @options.all && (current_class.is_a? Class) - # Not ActiveRecord::Base model - node_type = @options.brief ? 'class-brief' : 'class' - @graph.add_node [node_type, current_class.name] - generated = true - elsif @options.modules && (current_class.is_a? Module) - @graph.add_node ['module', current_class.name] end + + @graph.add_node [node_type, current_class.name, node_attribs] + + # Process class associations + associations = current_class.relations.values + + if @options.inheritance && !@options.transitive && + current_class.superclass.respond_to?(:relations) + associations -= current_class.superclass.relations.values + end + + associations.each do |a| + process_association current_class.name, a + end + + true + end + - # Only consider meaningful inheritance relations for generated classes - 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 # process_class - # Process a model association def process_association(class_name, assoc) + STDERR.puts "- Processing model association #{assoc.name.to_s}" if @options.verbose - STDERR.print "\t\tProcessing model association #{assoc.name.to_s}\n" if @options.verbose - # Skip "belongs_to" associations - return if assoc.macro.to_s == 'belongs_to' && !@options.show_belongs_to + macro = assoc.macro.to_s + return if %w[belongs_to referenced_in].include?(macro) && !@options.show_belongs_to + #TODO: + # FAIL: assoc.methods.include?(:class_name) + # FAIL: assoc.responds_to?(:class_name) + assoc_class_name = assoc.class_name rescue nil + assoc_class_name ||= assoc.name.to_s.underscore.singularize.camelize + # 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.camelize : assoc.class_name if assoc_class_name == assoc.name.to_s.singularize.camelize assoc_name = '' else assoc_name = assoc.name.to_s end @@ -135,20 +195,21 @@ if class_name.include?("::") && !assoc_class_name.include?("::") assoc_class_name = class_name.split("::")[0..-2].push(assoc_class_name).join("::") end assoc_class_name.gsub!(%r{^::}, '') - if ['has_one', 'belongs_to'].include? assoc.macro.to_s + if %w[has_one references_one embeds_one].include?(macro) assoc_type = 'one-one' - elsif assoc.macro.to_s == 'has_many' && (! assoc.options[:through]) + elsif macro == 'has_many' && (!assoc.options[:through]) || + %w[references_many embeds_many].include?(macro) assoc_type = 'one-many' else # habtm or has_many, :through - return if @habtm.include? [assoc.class_name, class_name, assoc_name] + # Add FAKE associations too in order to understand mistakes + return if @habtm.include? [assoc_class_name, class_name, assoc_name] assoc_type = 'many-many' - @habtm << [class_name, assoc.class_name, assoc_name] + @habtm << [class_name, assoc_class_name, assoc_name] 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] end # process_association - end # class ModelsDiagram