ymldot 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/README ADDED
@@ -0,0 +1,102 @@
1
+ = ymldot
2
+
3
+ == Description
4
+
5
+ Can describe ERD in a form of ".yml".
6
+ ERD method use IDEF1X.
7
+ ".yml" file to ".dot" file. ".dot" is a language used in "graphviz".
8
+
9
+ == Installation
10
+ (1) if not install graphviz, type following.
11
+ * $ apt-get install graphviz
12
+ (2) get ymldot.
13
+ * $ gem install ymldot
14
+
15
+ == Option
16
+ please see ymldot --help
17
+
18
+ == Yml format
19
+
20
+ * config
21
+ * font
22
+ * Please set the font name that You want to use.
23
+ * tables
24
+ * name
25
+ * table name
26
+ * columns
27
+ * enumerate column names
28
+ * foreginkeys
29
+ * has_many
30
+ * has_one
31
+ * has_many_and_belongs_to
32
+ * same meaning the relation of ActiveRecord.
33
+ * enumerate ref table name.
34
+ * polymorphic
35
+ * name
36
+ * type
37
+ * tables
38
+ * target table name.
39
+
40
+ * category
41
+ * label
42
+ * category name set. and set frame.
43
+ * table
44
+ * nested table.
45
+
46
+ == Sample
47
+ * in file. "sample/sample.yml"
48
+
49
+ tables:
50
+ - name: customer
51
+ columns:
52
+ - name
53
+ - phone_number
54
+ foreignkeys:
55
+ has_many:
56
+ - order
57
+ - name: order
58
+ columns:
59
+ - order_num
60
+ - name: product
61
+ columns:
62
+ - name
63
+ - amount
64
+ - tax
65
+ - product_div
66
+ foreignkeys:
67
+ has_many:
68
+ - order
69
+ - name: category
70
+ columns:
71
+ - name
72
+ foreignkeys:
73
+ has_many:
74
+ - product
75
+
76
+ * output dot file. "sample/sample.dot"
77
+
78
+ digraph sample {
79
+ graph[overlap=false, splines=true]
80
+
81
+ "category" [shape=record, label="{category|name\l}"]
82
+ "customer" [shape=record, label="{customer|name\lphone_number\l}"]
83
+ "order" [shape=record, label="{order|customerID(FK)\lproductID(FK)\lorder_num\l}"]
84
+ "product" [shape=record, label="{product|categoryID(FK)\lname\lamount\ltax\lproduct_div\l}"]
85
+
86
+ "category" -> "product" [arrowtail=none arrowhead=dot headlabel="n" taillabel="1"]
87
+ "customer" -> "order" [arrowtail=none arrowhead=dot headlabel="n" taillabel="1"]
88
+ "product" -> "order" [arrowtail=none arrowhead=dot headlabel="n" taillabel="1"]
89
+
90
+ }
91
+
92
+ * please type
93
+ $ dot -Tpng sample.dot -o sample.png
94
+
95
+ == Copyright
96
+
97
+ * Author:: nari <authorNari@gmail.com>
98
+ * Copyright:: Copyright (c) 2008 nari
99
+ * License:: Ruby's
100
+
101
+ == Link
102
+ * ((<ymldot-project|URL:http://rubyforge.org/projects/ymldot>))
data/bin/ymldot ADDED
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/ruby -wKU
2
+
3
+ if File.symlink?(__FILE__)
4
+ $: << File.join(File.dirname(File.readlink(__FILE__)), '../lib')
5
+ else
6
+ $: << File.join(File.dirname(__FILE__), '../lib')
7
+ end
8
+
9
+ require 'ymldot'
10
+ require 'optparse'
11
+
12
+ Version = "0.0.1"
13
+ is_output = false
14
+ opt = OptionParser.new
15
+ opt.version = Version
16
+ opt.release = "0.0.1"
17
+ opt.on('-o', 'output file to current directory. name is "#{input_filename}.dot"') {|v| is_output = true }
18
+ opt.parse!(ARGV)
19
+
20
+ if is_output
21
+ y = Ymldot.new(ARGV[0])
22
+ open("./#{y.file_name}.dot", "w") do |f|
23
+ f.write(y.dot_generate)
24
+ end
25
+ else
26
+ puts Ymldot.new(ARGV[0]).dot_generate
27
+ end
data/lib/ymldot.rb ADDED
@@ -0,0 +1,187 @@
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
+ class Tables
28
+ def initialize(table_node, label=nil, category=false)
29
+ @label = label
30
+ @category = category
31
+ @table_node = table_node
32
+ @entity_dict = {}
33
+ table_node.each {|t| eval_entity(t) }
34
+ end
35
+ attr_accessor :entity_dict, :label, :table_node, :category
36
+
37
+ def entity_dict_to_dot
38
+ res = ""
39
+ s = []
40
+ @entity_dict.each_value{|e| s << e }
41
+ if @category
42
+ res << "label=#{@label}\n" if @label
43
+ res << "style=invis\n" unless @label
44
+ end
45
+ s.sort{|a, b| a.name <=> b.name}.each{|e| res << e.to_dot << "\n"}
46
+ res
47
+ end
48
+
49
+ private
50
+ def eval_entity(table)
51
+ columns = table["columns"]; columns ||= []
52
+ @entity_dict[table["name"]] ||= Entity.new(table["name"], table["dependent"], columns)
53
+ end
54
+
55
+ end
56
+
57
+
58
+ class Ymldot
59
+ attr_accessor :file_name
60
+
61
+ def initialize(filepath)
62
+ open(filepath) do |f|
63
+ str = f.read
64
+ @node = YAML.load(str)
65
+ end
66
+ @file_name = $1 if filepath[/(\w+).yml\z/]
67
+ @entity_dict = {}
68
+ @category = []
69
+ @one_relations = []
70
+ @many_relations = []
71
+ eval_yml
72
+ end
73
+
74
+ def dot_generate
75
+ code = ""
76
+ code += <<"EOS"
77
+ digraph #{@file_name} {
78
+ #{add_2_tab(config_to_dot)}
79
+ #{add_2_tab(entity_dict_to_dot)}
80
+ #{add_2_tab(relations_to_dot)}
81
+ }
82
+ EOS
83
+ end
84
+
85
+ private
86
+ def add_2_tab(str)
87
+ res = ""
88
+ str.each_line{|s| res << " " << s}
89
+ res
90
+ end
91
+
92
+ def config_to_dot
93
+ res = ""
94
+ res << "graph[overlap=false, splines=true]\n"
95
+ res << %Q!node [fontname="#{@config["font"]}"]! if @config["font"]
96
+ res
97
+ end
98
+
99
+ def entity_dict_to_dot
100
+ res = ""
101
+ unless @category.empty?
102
+ @category.each_with_index do |c, i|
103
+ unless c.category
104
+ res << c.entity_dict_to_dot
105
+ else
106
+ res << "subgraph cluster#{i} {\n"
107
+ res << add_2_tab(c.entity_dict_to_dot)
108
+ res << "}\n"
109
+ end
110
+ end
111
+ end
112
+ res
113
+ end
114
+
115
+ def relations_to_dot
116
+ res = ""
117
+ @one_relations.sort{|a, b| a[:self].name <=> b[:self].name}.each do |r|
118
+ res << %Q!"#{r[:self].name}" -> "#{r[:have].name}" [arrowtail=none arrowhead=dot headlabel="1" taillabel="1"]! << "\n"
119
+ end
120
+ @many_relations.sort{|a, b| a[:self].name <=> b[:self].name}.each do |r|
121
+ res << %Q!"#{r[:self].name}" -> "#{r[:have].name}" [arrowtail=none arrowhead=dot headlabel="n" taillabel="1"]! << "\n"
122
+ end
123
+ res
124
+ end
125
+
126
+ def eval_yml
127
+ @config = @node["config"]? @node["config"] : {}
128
+ @category << Tables.new(@node["tables"]) if @node["tables"] and !@node["tables"].empty?
129
+ @node["category"].each{|c| @category << Tables.new(c["tables"], c["label"], true) } if @node["category"]
130
+ @category.each{|c| c.entity_dict.each_pair{|k, v| @entity_dict[k] = v}}
131
+ @category.each{|c| c.table_node.each{|e| eval_relation(e) } }
132
+ end
133
+
134
+ def eval_relation(table)
135
+ foreignkeys = table["foreignkeys"]
136
+ tname = table["name"]
137
+ return unless foreignkeys
138
+ eval_relation_has_many(foreignkeys["has_many"], tname) if foreignkeys["has_many"]
139
+ eval_relation_has_one(foreignkeys["has_one"], tname) if foreignkeys["has_one"]
140
+ eval_relation_hmabt(foreignkeys["has_many_and_belongs_to"], tname) if foreignkeys["has_many_and_belongs_to"]
141
+ eval_relation_polymorphic(foreignkeys["polymorphic"], tname) if foreignkeys["polymorphic"]
142
+ end
143
+
144
+ def eval_relation_has_many(keys, tname)
145
+ keys.each do |rel|
146
+ @entity_dict[rel].foreignkeys << "#{tname}ID(FK)"
147
+ @many_relations << {:self => @entity_dict[tname], :have => @entity_dict[rel]}
148
+ end
149
+ end
150
+
151
+ def eval_relation_has_one(keys, tname)
152
+ keys.each do |rel|
153
+ @entity_dict[rel].foreignkeys << "#{tname}ID(FK)"
154
+ @one_relations << {:self => @entity_dict[tname], :have => @entity_dict[rel]}
155
+ end
156
+ end
157
+
158
+ def eval_relation_hmabt(keys, tname)
159
+ keys.each do |rel|
160
+ join_tname = "#{rel}_#{tname}"
161
+ return if @entity_dict.has_key? join_tname
162
+ join_tname = "#{tname}_#{rel}"
163
+ @entity_dict[join_tname] ||= Entity.new(join_tname, true)
164
+ @entity_dict[join_tname].foreignkeys << "#{tname}ID(FK)"
165
+ @entity_dict[join_tname].foreignkeys << "#{rel}ID(FK)"
166
+
167
+ # make new category
168
+ keys = []
169
+ keys << "#{tname}ID(FK)"
170
+ keys << "#{rel}ID(FK)"
171
+ @category << Tables.new([{"name" => join_tname, "columns" => keys}])
172
+
173
+ @many_relations << {:self => @entity_dict[tname], :have => @entity_dict[join_tname]}
174
+ @many_relations << {:self => @entity_dict[rel], :have => @entity_dict[join_tname]}
175
+ end
176
+ end
177
+
178
+ def eval_relation_polymorphic(keys, tname)
179
+ keys.each do |rel|
180
+ @entity_dict[tname].foreignkeys << "#{rel["name"]}ID(FK)" if key = rel["name"]
181
+ @entity_dict[tname].columns.unshift "#{rel["type"]}(type)" if key = rel["type"]
182
+ rel["tables"].each do |t|
183
+ @one_relations << {:self => @entity_dict[t], :have => @entity_dict[tname] }
184
+ end
185
+ end
186
+ end
187
+ end
data/sample/sample.dot ADDED
@@ -0,0 +1,13 @@
1
+ digraph sample {
2
+ graph[overlap=false, splines=true]
3
+
4
+ "category" [shape=record, label="{category|name\l}"]
5
+ "customer" [shape=record, label="{customer|name\lphone_number\l}"]
6
+ "order" [shape=record, label="{order|customerID(FK)\lproductID(FK)\lorder_num\l}"]
7
+ "product" [shape=record, label="{product|categoryID(FK)\lname\lamount\ltax\lproduct_div\l}"]
8
+
9
+ "category" -> "product" [arrowtail=none arrowhead=dot headlabel="n" taillabel="1"]
10
+ "customer" -> "order" [arrowtail=none arrowhead=dot headlabel="n" taillabel="1"]
11
+ "product" -> "order" [arrowtail=none arrowhead=dot headlabel="n" taillabel="1"]
12
+
13
+ }
data/sample/sample.png ADDED
Binary file
data/sample/sample.yml ADDED
@@ -0,0 +1,30 @@
1
+ # reference http://codezine.jp/article/detail/154?p=1
2
+ tables:
3
+ - name: customer
4
+ columns:
5
+ - name
6
+ - phone_number
7
+ foreignkeys:
8
+ has_many:
9
+ - order
10
+
11
+ - name: order
12
+ columns:
13
+ - order_num
14
+
15
+ - name: product
16
+ columns:
17
+ - name
18
+ - amount
19
+ - tax
20
+ - product_div
21
+ foreignkeys:
22
+ has_many:
23
+ - order
24
+
25
+ - name: category
26
+ columns:
27
+ - name
28
+ foreignkeys:
29
+ has_many:
30
+ - product
@@ -0,0 +1,13 @@
1
+ digraph sample_jp {
2
+ graph[overlap=false, splines=true]
3
+ node [fontname="MSUIGOTHIC.ttf"]
4
+ "商品" [shape=record, label="{商品|商品カテゴリID(FK)\l名前\l金額\l税\l商品区分\l}"]
5
+ "商品カテゴリ" [shape=record, label="{商品カテゴリ|商品カテゴリ名\l}"]
6
+ "注文" [shape=record, label="{注文|顧客ID(FK)\l商品ID(FK)\l注文数\l}"]
7
+ "顧客" [shape=record, label="{顧客|名前\l電話番号\l}"]
8
+
9
+ "商品" -> "注文" [arrowtail=none arrowhead=dot headlabel="n" taillabel="1"]
10
+ "商品カテゴリ" -> "商品" [arrowtail=none arrowhead=dot headlabel="n" taillabel="1"]
11
+ "顧客" -> "注文" [arrowtail=none arrowhead=dot headlabel="n" taillabel="1"]
12
+
13
+ }
Binary file
@@ -0,0 +1,34 @@
1
+ # reference http://codezine.jp/article/detail/154?p=1
2
+ config:
3
+ font: MSUIGOTHIC.ttf
4
+
5
+ tables:
6
+ - name: 顧客
7
+ columns:
8
+ - 名前
9
+ - 電話番号
10
+ foreignkeys:
11
+ has_many:
12
+ - 注文
13
+
14
+ - name: 注文
15
+ columns:
16
+ - 注文数
17
+
18
+ - name: 商品
19
+ columns:
20
+ - 名前
21
+ - 金額
22
+ - 税
23
+ - 商品区分
24
+ foreignkeys:
25
+ has_many:
26
+ - 注文
27
+
28
+ - name: 商品カテゴリ
29
+ columns:
30
+ - 商品カテゴリ名
31
+ foreignkeys:
32
+ has_many:
33
+ - 商品
34
+
metadata ADDED
@@ -0,0 +1,61 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ymldot
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - nari
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-09-10 00:00:00 +09:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: ""
17
+ email: authornari@gmail.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files: []
23
+
24
+ files:
25
+ - README
26
+ - bin/ymldot
27
+ - lib/ymldot.rb
28
+ - sample/sample.dot
29
+ - sample/sample.png
30
+ - sample/sample.yml
31
+ - sample/sample_jp.dot
32
+ - sample/sample_jp.png
33
+ - sample/sample_jp.yml
34
+ has_rdoc: false
35
+ homepage: http://coderepos.org/share/browser/lang/ruby/ymldot
36
+ post_install_message:
37
+ rdoc_options: []
38
+
39
+ require_paths:
40
+ - lib
41
+ required_ruby_version: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ version: "0"
46
+ version:
47
+ required_rubygems_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: "0"
52
+ version:
53
+ requirements: []
54
+
55
+ rubyforge_project: ymldot
56
+ rubygems_version: 1.1.1
57
+ signing_key:
58
+ specification_version: 2
59
+ summary: Can describe ERD in a from of ".yml"
60
+ test_files: []
61
+