root/lang/ruby/ymldot/lib/ymldot.rb @ 19118

Revision 19118, 3.6 kB (checked in by authorNari, 5 years ago)

add test. bug fix.

Line 
1require 'yaml'
2require 'pp'
3
4class 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
25end
26
27
28class 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"
46digraph #{@file_name} {
47#{add_2_tab(config_to_dot)}
48#{add_2_tab(entities_to_dot)}
49#{add_2_tab(relations_to_dot)}
50}
51EOS
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
137end
Note: See TracBrowser for help on using the browser.