DZone Snippets is a public source code repository. Easily build up your personal collection of code snippets, categorize them with tags / keywords, and share them with the world

Snippets has posted 5883 posts at DZone. View Full User Profile

Simple Ruby Script To Create A .dot File From Rails Models

10.19.2005
| 6617 views |
  • submit to reddit
        Save the script below railmodel, then try:

railmodel myrailapp | dot -Tpng > myrailmodel.png

where myrailapp, is the complete directory to one of your rails applications. You will of course have to have graphviz installed (http://www.research.att.com/sw/tools/graphviz/).


#!/bin/ruby -w
require 'active_support'

Relation = Struct.new("Relation", :parent, :child, :relation, :options)
#     

def gendot(dir)
  nodes = []
  relations = []
  cur_model = ''
  
  base = File.join(dir, 'app', 'models')
  Dir.new(base).each do |fname|
    next if fname =~ /^\./
    File.open(File.join(base,fname)) do |handle|
      handle.each do |line|
        if line =~ /class\s+(\S+)\s+<\s+(\S*)/
          if $2 != "ActiveRecord::Base"
            cur_model = "$1 [shape=box,bottomlabel=\"#{$2}\"]"
          else
            cur_model = $1
          end
          nodes << cur_model
        elsif line =~ /^\s*([A-Za-z_0-9]+)\s*(.*)/
          #puts "  Found >>#{$1}<< >>#{$2}<< >>#{line.strip}<<"
          relation = $1
          opts = $2.split(/\s*[,:]+\s*/).delete_if {|elem| elem.empty? }
          
          #puts "  Found #{relation} - #{opts.inspect} -- >>#{$2}<< >>#{line.strip}<<"
          case relation
            when 'has_one'	
              relations << Relation.new(cur_model, opts[0].classify, relation)
              
            when 'has_many'	
              relations << Relation.new(cur_model, opts[0].classify, relation, 'arrowhead' => 'inv')
              
            when 'has_and_belongs_to_many'	
              relations << Relation.new(cur_model, opts[0].classify, 'fix', 'dir' => 'both')
              
            when 'belongs_to'
              relations << Relation.new(cur_model, opts[0].classify, relation, 'label' => "belongs_to", "style" => "dashed")
              
            when 'acts_as_tree'
              relations << Relation.new(cur_model, cur_model, relation)
              
            else	
              #puts "  *** NOT FOUND #{$1} ***"
          end
        end
      end
    end
  end
  
  # look for duplicate has_and_belongs_to_many relations
  relations.each do |a|
    if a.relation == "fix"
      a.relation = "has_and_belongs_to_many" 
      STDERR.puts "Checking #{a.inspect}"
      relations.delete_if { |b| b.relation == "fix" && a.parent == b.child && a.child = b.parent }
    end
  end
  
  puts "digraph simple {"
  puts nodes.join("\n")
  relations.each do |rel|
    #rel.label = rel.relation unless rel.label
    print "#{rel.parent}->#{rel.child}"
    rel.options = {} unless rel.options
    if rel.options 
      rel.options['label'] = rel.relation unless rel.options['label']
      print " ["
      opts = rel.options.map do |k,v|
        k + "=" +
        if k == "label"
          %Q["#{v}"]
        else
          v
        end
      end
      print opts.join(",")
      print "]"
    end
    puts 
  end
  puts "}"
end

if $0 == __FILE__
   dir = ARGV[0]
   gendot(dir)
end