| 1 | require 'yaml' |
|---|
| 2 | require 'pp' |
|---|
| 3 | |
|---|
| 4 | class Entity |
|---|
| 5 | def initialize(name, dependent, columns = []) |
|---|
| 6 | @name = name |
|---|
| 7 | @dependent = dependent |
|---|
| 8 | @columns = columns |
|---|
| 9 | @foreignkeys = [] |
|---|
| 10 | end |
|---|
| 11 | |
|---|
| 12 | def dependent? |
|---|
| 13 | @dependent |
|---|
| 14 | end |
|---|
| 15 | |
|---|
| 16 | def to_dot |
|---|
| 17 | res = "" |
|---|
| 18 | res << %Q!"#{@name}" [#{@dependent? 'shape=Mrecord, ' : 'shape=record, '}label=\"{#{@name}|! |
|---|
| 19 | @foreignkeys.each{|f| res << "#{f}\\l"} |
|---|
| 20 | @columns.each{|c| res << "#{c}\\l"} |
|---|
| 21 | res << '}"]' |
|---|
| 22 | end |
|---|
| 23 | |
|---|
| 24 | attr_accessor :name, :dependent, :columns, :foreignkeys |
|---|
| 25 | end |
|---|
| 26 | |
|---|
| 27 | |
|---|
| 28 | class Ymldot |
|---|
| 29 | attr_accessor :file_name |
|---|
| 30 | |
|---|
| 31 | def initialize(filepath) |
|---|
| 32 | open(filepath) do |f| |
|---|
| 33 | str = f.read |
|---|
| 34 | @node = YAML.load(str) |
|---|
| 35 | end |
|---|
| 36 | @file_name = $1 if filepath[/(\w+).yml\z/] |
|---|
| 37 | @entities = {} |
|---|
| 38 | @one_relations = [] |
|---|
| 39 | @many_relations = [] |
|---|
| 40 | eval |
|---|
| 41 | end |
|---|
| 42 | |
|---|
| 43 | def dot_generate |
|---|
| 44 | code = "" |
|---|
| 45 | code += <<"EOS" |
|---|
| 46 | digraph #{@file_name} { |
|---|
| 47 | #{add_2_tab(config_to_dot)} |
|---|
| 48 | #{add_2_tab(entities_to_dot)} |
|---|
| 49 | #{add_2_tab(relations_to_dot)} |
|---|
| 50 | } |
|---|
| 51 | EOS |
|---|
| 52 | end |
|---|
| 53 | |
|---|
| 54 | private |
|---|
| 55 | def add_2_tab(str) |
|---|
| 56 | res = "" |
|---|
| 57 | str.each_line{|s| res << " " << s} |
|---|
| 58 | res |
|---|
| 59 | end |
|---|
| 60 | |
|---|
| 61 | def config_to_dot |
|---|
| 62 | res = "" |
|---|
| 63 | res << "graph[overlap=false, splines=true]\n" |
|---|
| 64 | res << %Q!node [fontname="#{@config["font"]}"]! if @config["font"] |
|---|
| 65 | res |
|---|
| 66 | end |
|---|
| 67 | |
|---|
| 68 | def entities_to_dot |
|---|
| 69 | res = "" |
|---|
| 70 | s = [] |
|---|
| 71 | @entities.each_value{|e| s << e } |
|---|
| 72 | s.sort{|a, b| a.name <=> b.name}.each{|e| res << e.to_dot << "\n"} |
|---|
| 73 | res |
|---|
| 74 | end |
|---|
| 75 | |
|---|
| 76 | def relations_to_dot |
|---|
| 77 | res = "" |
|---|
| 78 | @one_relations.sort{|a, b| a[:self].name <=> b[:self].name}.each do |r| |
|---|
| 79 | res << %Q!"#{r[:self].name}" -> "#{r[:have].name}" [arrowtail=none arrowhead=dot headlabel="1" taillabel="1"]! << "\n" |
|---|
| 80 | end |
|---|
| 81 | @many_relations.sort{|a, b| a[:self].name <=> b[:self].name}.each do |r| |
|---|
| 82 | res << %Q!"#{r[:self].name}" -> "#{r[:have].name}" [arrowtail=none arrowhead=dot headlabel="n" taillabel="1"]! << "\n" |
|---|
| 83 | end |
|---|
| 84 | res |
|---|
| 85 | end |
|---|
| 86 | |
|---|
| 87 | def eval |
|---|
| 88 | @config = @node["config"]? @node["config"] : {} |
|---|
| 89 | @node["tables"].each {|t| eval_entity(t) } |
|---|
| 90 | @node["tables"].each {|t| eval_relation(t) } |
|---|
| 91 | end |
|---|
| 92 | |
|---|
| 93 | def eval_entity(table) |
|---|
| 94 | columns = table["columns"]; columns ||= [] |
|---|
| 95 | @entities[table["name"]] ||= Entity.new(table["name"], table["dependent"], columns) |
|---|
| 96 | end |
|---|
| 97 | |
|---|
| 98 | def eval_relation(table) |
|---|
| 99 | foreignkeys = table["foreignkeys"] |
|---|
| 100 | tname = table["name"] |
|---|
| 101 | eval_relation_belongs_to(foreignkeys["belongs_to"], tname) if foreignkeys["belongs_to"] |
|---|
| 102 | eval_relation_has_many(foreignkeys["has_many"], tname) if foreignkeys["has_many"] |
|---|
| 103 | eval_relation_has_one(foreignkeys["has_one"], tname) if foreignkeys["has_one"] |
|---|
| 104 | eval_relation_hmabt(foreignkeys["has_many_and_belongs_to"], tname) if foreignkeys["has_many_and_belongs_to"] |
|---|
| 105 | end |
|---|
| 106 | |
|---|
| 107 | def eval_relation_belongs_to(keys, tname) |
|---|
| 108 | keys.each do |rel| |
|---|
| 109 | @entities[tname].foreignkeys << "#{rel}ID(FK)" |
|---|
| 110 | end |
|---|
| 111 | end |
|---|
| 112 | |
|---|
| 113 | def eval_relation_has_many(keys, tname) |
|---|
| 114 | keys.each do |rel| |
|---|
| 115 | @many_relations << {:self => @entities[tname], :have => @entities[rel]} |
|---|
| 116 | end |
|---|
| 117 | end |
|---|
| 118 | |
|---|
| 119 | def eval_relation_has_one(keys, tname) |
|---|
| 120 | keys.each do |rel| |
|---|
| 121 | @one_relations << {:self => @entities[tname], :have => @entities[rel]} |
|---|
| 122 | end |
|---|
| 123 | end |
|---|
| 124 | |
|---|
| 125 | def eval_relation_hmabt(keys, tname) |
|---|
| 126 | keys.each do |rel| |
|---|
| 127 | join_tname = "#{rel}_#{tname}" |
|---|
| 128 | return if @entities.has_key? join_tname |
|---|
| 129 | join_tname = "#{tname}_#{rel}" |
|---|
| 130 | @entities[join_tname] ||= Entity.new(join_tname, true) |
|---|
| 131 | @entities[join_tname].foreignkeys << "#{tname}ID(FK)" |
|---|
| 132 | @entities[join_tname].foreignkeys << "#{rel}ID(FK)" |
|---|
| 133 | @many_relations << {:self => @entities[tname], :have => @entities[join_tname]} |
|---|
| 134 | @many_relations << {:self => @entities[rel], :have => @entities[join_tname]} |
|---|
| 135 | end |
|---|
| 136 | end |
|---|
| 137 | end |
|---|