这个是什么
标题翻译过来就是一个简单的数据库Schema生成器,源代码在Github上面传送门。
你需要安装好Ruby,Graphviz,然后把代码clone下来就可以运行了。
这个的原理
Graphviz是一个根据特定语言的可视化软件,它可以根据dot文件里面描述的内容生成一张图片。
假设我有下面一段数据库的Schema
Club
==================================================
id int(11)
name varchar(255)
address varchar(255)
expired_at datetime
created_at datetime
updated_at datetime
ecard int(11)
image_keys varchar(255)
portable tinyint(1)
--------------------------------------------------
School
==================================================
id int(11)
name varchar(255)
address varchar(255)
ecard int(11)
expired_at datetime
created_at datetime
updated_at datetime
portable tinyint(1)
--------------------------------------------------
Game
==================================================
id int(11)
start_at datetime
end_at datetime
lat double
lng double
address varchar(255)
created_at datetime
updated_at datetime
name varchar(255)
image_key varchar(255)
image_keys varchar(255)
--------------------------------------------------
ClubGame
==================================================
game_id int(11)
club_id int(11)
--------------------------------------------------
SchoolGame
==================================================
game_id int(11)
school_id int(11)
--------------------------------------------------
ClubProfile
==================================================
id int(11)
user_id int(11)
club_id int(11)
avatar_key varchar(255)
course_id int(11)
gender int(11)
birthday datetime
point int(11)
ce int(11)
created_at datetime
updated_at datetime
realname varchar(255)
--------------------------------------------------
SchoolProfile
==================================================
id int(11)
user_id int(11)
avatar_key varchar(255)
classroom_id int(11)
gender int(11)
birthday datetime
point int(11)
ce int(11)
created_at datetime
updated_at datetime
realname varchar(255)
school_id int(11)
--------------------------------------------------
好吧,其实上面的schema也是我生成出来的,因为之前Rails里面是有schema.rb
的,所以我觉得我的Cubanana里面也要有一个schema.txt
。
所以当你有这样一个schema.txt
的时候,你可以看到单独的表中所包含的字段,但是你也想看到表与表之间的联系,这个时候,其实最简单的就是foreign_key,Rails里面的convention over configuration思想其实让你在设计数据表的时候就考虑数据表之间的关联,虽然它没有真正的foreign_key这个东西,但你知道当另一个表(User)里面出现club_id的时候,你就知道这两个其实是有关联的。
所以你需要的是把schema.txt
解析生成为schema.dot
文件,一个简单的dot文件例如:
digraph G {
rankdir=LR
node [shape=box, color=blue]
node1 [style=filled]
node2 [style=filled, fillcolor=red]
node0 -> node1 -> node2
}
对应了下图
简单来说一个图,包含了node(节点),edge(边),我这边为了偷懒,就用了无向图,也就是没有箭头的。
上面Schema中每一个Model其实就是一个节点,那么对应了下面的dot语言
ClubGame [label="ClubGame|{game_id|club_id|club_game_id}"];
边的描述更加简单
Game -> SchoolGame [dir="none"]
详细的你可以看Graphviz的文档,RTFD,:)
了解了原理,那么接下来就是解析文件,一通操作:
def split_into_nodes
original = File.open(@source, 'r') { |file| file.readlines }
blankless = original.reject{ |line| line.match(/^$/) }
@nodes = blankless
.join()
.split("--------------------------------------------------").map do |b|
sources = b.lines.reject{|line| line == "\n"}
name = sources.first.gsub("\n",'')
attrs = sources[2..-1].map { |e| e.split(' ')
.join(':') }
.reject{ |s| s.include? "created_at" }
.reject{ |s| s.include? "updated_at"}
.reject{ |s| s == "id:int(11)"}
.concat ["#{name.underscore}_id:int(11)"]
.uniq
sn = SchemaNode.new(name, attrs)
end
end
这么做是为了让你把上面Schema分解成单独的SchemaNode,那么现在节点有了,用to_structs
方法就可以生成节点了,那么边呢?
所以其实就是,两点生成一条直线,唯一要做的就是你需要判断它们俩能不能连在一起,而连不连在一起就是说我一个表里面有没有另一个表的id,如下:
class Link
def initialize(node_a, node_b)
@node_a = node_a
@node_b = node_b
end
def link?
case_a = @node_b.attrs.include?("#{@node_a.name.underscore}_id:int(11)")
case_b = @node_a.attrs.include?("#{@node_b.name.underscore}_id:int(11)")
case_a || case_b
end
end
哈哈哈,一开始遇到了一个问题,让我生成的图少了好几条线,百思不得其解,后来发现是因为比如ClubGame小写之后拼接的id是clubgame_id
,而其实应该是club_game_id
,Camel case 的锅,所以会有一个monkey patch:
class String
def underscore
self.gsub(/::/, '/').
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
gsub(/([a-z\d])([A-Z])/,'\1_\2').
tr("-", "_").
downcase
end
end
这个代替了downcase。
好的,非常棒,那么两个节点的操作完了,你生成的那么多节点呢???
那不就是两两组合吗,然后把连接着一起的数出来吗?鄙视写for循环的程序员,而且还是两层的。在数学上,这个是排列组合的问题,你们的数学都还给老师了吗???
所以Ruby的Array有一个方法叫做 combination 啊!其他语言也有,(尽管可能它也是for循环),重要的是,你写代码的时候,要知道它的数学原理。能简化你很多不必要的低级操作,避免你成为一个低级的程序员。
这个的演示
最终的结果如图,我偷懒,不画箭头,但是箭头无非就是改变一下edge的判断条件。
May the source be with U, gays!