lib/railroady/models_diagram.rb in railroady-1.0.6 vs lib/railroady/models_diagram.rb in railroady-1.0.7

- old
+ new

@@ -24,11 +24,11 @@ rescue Exception STDERR.puts "Warning: exception #{$!} raised while trying to load model class #{f}" end end - end + end def get_files(prefix ='') files = !@options.specify.empty? ? Dir.glob(@options.specify) : Dir.glob(prefix << "app/models/**/*.rb") files += Dir.glob("vendor/plugins/**/app/models/*.rb") if @options.plugins_models files -= Dir.glob(@options.exclude) @@ -36,38 +36,41 @@ 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 + generated = + if defined?(Mongoid::Document) && current_class.new.is_a?(Mongoid::Document) + process_mongoid_model(current_class) + elsif defined?(DataMapper::Resource) && current_class.new.is_a?(DataMapper::Resource) + process_datamapper_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 + if @options.inheritance && generated && include_inheritance?(current_class) @graph.add_edge ['is-a', current_class.superclass.name, current_class.name] - end - + 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 @@ -84,47 +87,89 @@ 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", + "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 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} + + 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_datamapper_model(current_class) + node_attribs = [] + if @options.brief #|| current_class.abstract_class? + node_type = 'model-brief' + else + node_type = 'model' + + # Collect model's properties + props = current_class.properties.sort_by(&:name) + + 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"] + props = props.select {|c| !magic_fields.include?(c.name.to_s) } + end + + props.each do |a| + prop = a.name.to_s + prop += ' :' + a.class.name.split('::').last unless @options.hide_types + node_attribs << prop + end + end + @graph.add_node [node_type, current_class.name, node_attribs] + + # Process relationships + relationships = current_class.relationships + + # TODO: Manage inheritance + + relationships.each do |a| + process_datamapper_relationship 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' @@ -139,59 +184,59 @@ "lock_version", "_type", "_id", "position", "parent_id", "lft", "rgt", "quote", "template" ] content_columns = content_columns.select {|c| !magic_fields.include?(c.name) } end - + content_columns.each do |a| content_column = a.name content_column += " :#{a.type}" 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.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 - + # Process a model association def process_association(class_name, assoc) STDERR.puts "- Processing model association #{assoc.name.to_s}" if @options.verbose # Skip "belongs_to" associations macro = assoc.macro.to_s return if %w[belongs_to referenced_in].include?(macro) && !@options.show_belongs_to - #TODO: + #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 if assoc_class_name == assoc.name.to_s.singularize.camelize assoc_name = '' else assoc_name = assoc.name.to_s - end + end # Patch from "alpack" to support classes in a non-root module namespace. See: http://disq.us/yxl1v if class_name.include?("::") && !assoc_class_name.include?("::") assoc_class_name = class_name.split("::")[0..-2].push(assoc_class_name).join("::") end @@ -205,11 +250,47 @@ else # habtm or has_many, :through # 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] - 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] end # process_association end # class ModelsDiagram + + # Process a DataMapper relationship + def process_datamapper_relationship(class_name, relation) + STDERR.puts "- Processing DataMapper model relationship #{relation.name.to_s}" if @options.verbose + + # Skip "belongs_to" relationships + dm_type = relation.class.to_s.split('::')[-2] + return if dm_type == 'ManyToOne' && !@options.show_belongs_to + + dm_parent_model = relation.parent_model.to_s + dm_child_model = relation.child_model.to_s + + assoc_class_name = '' + # Get the assoc_class_name + if dm_parent_model.eql?(class_name) + assoc_class_name = dm_child_model + else + assoc_class_name = dm_parent_model + end + + # Only non standard association names needs a label + assoc_name = '' + if !(relation.name.to_s.singularize.camelize.eql?(assoc_class_name.split('::').last)) + assoc_name = relation.name.to_s + end + + # TO BE IMPROVED + rel_type = 'many-many' # default value for ManyToOne and ManyToMany + if dm_type == 'OneToOne' + rel_type = 'one-one' + elsif dm_type == 'OneToMany' + rel_type = 'one-many' + end + + @graph.add_edge [rel_type, class_name, assoc_class_name, assoc_name ] + end