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