mongoid-erd 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gem 'rviz'
4
+ gem 'term-ansicolor'
5
+
@@ -0,0 +1,60 @@
1
+ = Mongoid-ERD
2
+
3
+ Create model-diagram (ERD graph) via Graphviz using the mongoid's model source code.
4
+
5
+ Export a executable <tt>merd</tt>:
6
+
7
+ $ merd | dot -Tpng > docs/erd.png
8
+
9
+ or:
10
+
11
+ $ merd --model_dir=other/models | dot -Tpng > docs/erd.png
12
+
13
+ You can also create only a subset of models by:
14
+
15
+ $ merd --include=Class1,Class2
16
+
17
+ or
18
+
19
+ $ merd --exclude=Class4,Class5
20
+
21
+ You need to add <tt>dot</tt> executable in your PATH.
22
+
23
+ = Class, Inherits and Fields
24
+
25
+ - <tt>class xxx:yyy</tt>: a record xxx inherit from yyy. The first class that contains <tt>include Mongoid::Document</tt> on the file will be tracked. yyy will be created as a <tt>box</tt> node if no corresponding file found.
26
+
27
+ - <tt>field :xxx, type:yyy</tt>: a field xxx with type yyy. <tt>type</tt> and <tt>default</tt> will be tracked.
28
+
29
+ - <tt>embeds_many/embeds_one/has_many/has_one :xxx, :as => :yyy</tt>: field and a belong_to/embeds_many/embeds_one/has_one link to xxx
30
+
31
+ - <tt>belongs_to/embedded_in :xxx</tt>: a field without link.
32
+
33
+ Methods before <tt>:private</tt> keyword will also be tracked.
34
+
35
+ = Special Markers
36
+
37
+ <tt>erd_tag user.core</tt> tag name of the current class (see configuration files below)
38
+
39
+ <tt>erd{}</tt>
40
+
41
+ <tt>class xxx:yyy # erd{fillcolor:xxx} yyy</tt>: yyy will become the label of the current class.
42
+
43
+ <tt>field ... # erd{...} xxx</tt>: xxx will become the label of that field.
44
+
45
+ <tt>embeds_many ... # erd{...} yyy</tt>: yyy will become the label of that edge.
46
+
47
+ <tt>erd -> node_name{color:yyy} label</tt>: arbitrage edge with attributes.
48
+
49
+ = Configuration Files
50
+
51
+ <tt>config/mongoid_erd.yml</tt>:
52
+
53
+ user: {shape: Mrecord}
54
+ user.core: {fillcolor: blue}
55
+
56
+ <tt>user.core</tt> attributes will be merged to <tt>user</tt>, then should be merged into <tt>class ... # erd{attrs}</tt>. Those attributes will passed to dot language as the attributes associate to the class node.
57
+
58
+ An other will to use tag is restrict output contains only classes with specific tag:
59
+
60
+ $ merd --tag=user
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/env ruby
2
+ # vim :set syn=ruby
3
+
4
+ $: << 'lib'
5
+
6
+ require "mongoid_erd"
7
+ require "optparse"
8
+ require "active_support/core_ext/string/inflections"
9
+
10
+ options = {}
11
+
12
+ optparse = OptionParser.new do |opts|
13
+ opts.banner = "Usage: merd [options]"
14
+
15
+ # configuration file
16
+ options[:conf_file] = "config/mongoid_erd.yml"
17
+ opts.on('-c', '--conf_file FILE', 'use configuration file other than config/mongoid_erd.yml' ) do |file|
18
+ options[:conf_file] = file
19
+ end
20
+
21
+ # directory contains model files
22
+ options[:model_dir] = "app/models"
23
+ opts.on('-d', '--model_dir DIR', 'parse model directory other than app/models') do |dir|
24
+ options[:model_dir] = dir
25
+ end
26
+
27
+ # include/exclude model list
28
+ options[:include] = []
29
+ opts.on('-i', '--include CLASS,LIST', 'include only specified models') do |list|
30
+ options[:include] = list.split(/\s*,\s*/).map{|s| s.underscore}
31
+ end
32
+
33
+ options[:exclude] = []
34
+ opts.on('-e', '--exclude CLASS,LIST', 'exclude those specified models') do |list|
35
+ options[:exclude] = list.split(/\s*,\s*/).map{|s| s.underscore}
36
+ end
37
+
38
+ # restrict output for models with those tag only
39
+ options[:tag] = []
40
+ opts.on('-t', '--tag TAG,LIST', 'restrict output for models with those tag only') do |list|
41
+ options[:tag] = list.split(/\s*,\s*/)
42
+ end
43
+
44
+ # output file for dot source
45
+ options[:output] = ''
46
+ opts.on('-o', '--output FILE', 'save dot language source to a file other than STDOUT') do |output|
47
+ options[:output] = output
48
+ end
49
+
50
+ opts.on_tail( '-h', '--help', 'Display this screen' ) do
51
+ puts opts
52
+ exit
53
+ end
54
+ end
55
+
56
+ optparse.parse!(ARGV)
57
+ config = options.select {|k,v| %w[tag conf_file model_dir exclude include output].include? k.to_s}
58
+
59
+ MongoidErd.new(config).parse.output()
@@ -0,0 +1,10 @@
1
+
2
+ # erd_tag user.core
3
+ class Hello # erd{color: red} Hello Class
4
+ field :name, type:String # erd Name Field
5
+ field :link, type:String # -> World
6
+
7
+ def say # erd say hello world
8
+
9
+ end
10
+ end
@@ -0,0 +1,6 @@
1
+
2
+ # erd_tag: user
3
+ class World # erd: Hi!
4
+
5
+ belongs_to :hello
6
+ end
@@ -0,0 +1,6 @@
1
+ title: Model Diagrams
2
+ tags:
3
+ _default: {shape: record, fillcolor: yellow, style: filled, color: blue}
4
+ user: {fillcolor: gold, color: black}
5
+ user.core: {color: red}
6
+ pix: {style: pix}
@@ -0,0 +1,211 @@
1
+ require "rviz"
2
+ require "yaml"
3
+ require "active_support/core_ext/string/inflections"
4
+
5
+ class Fields
6
+ attr_accessor :name, :erd_label, :type, :edge
7
+ def as_row
8
+ str = type == "function" ? '+ ' + name : '- ' + name
9
+ str += ":" + type unless type == "function"
10
+ str += ", #{erd_label}" if erd_label and erd_label.size > 0
11
+ str
12
+ end
13
+ end
14
+
15
+ class Model
16
+ attr_accessor :name, :erd_label, :attrs, :fields, :tag, :parent
17
+ def initialize
18
+ @attrs = Hash.new
19
+ @fields = Array.new
20
+ end
21
+
22
+ def title
23
+ "- [#{name}:#{erd_label}] -"
24
+ end
25
+ end
26
+
27
+ class MongoidErd
28
+
29
+ def initialize config = {}
30
+ @config = config
31
+ # tag: [], tags: {}, include = [], exclude = [], model_dir = ""
32
+
33
+ # parse the option file
34
+ if @config[:conf_file] and File.exists? @config[:conf_file]
35
+ yml = YAML.load(File.open(@config[:conf_file], 'r:utf-8').read)
36
+ @config[:tags] = yml["tags"]
37
+ @config[:title] = yml["title"]
38
+ else
39
+ raise "#{@config[:conf_file]} does not exists" if @config[:conf_file]
40
+ end
41
+
42
+ # merge tag attributes recursively
43
+ @config[:tags].each do |t,v|
44
+ tv = @config[:tags]["_default"].clone || {}
45
+ p = nil
46
+ t.split('.').each do |pt|
47
+ p = p ? [p, pt].join('.') : pt # merge in order: tv < a < a.b < a.b.c, ...
48
+ # puts "merge from #{p} to #{t}"
49
+ tv.merge! @config[:tags][p] if @config[:tags][p]
50
+ end
51
+ @config[:tags][t] = tv
52
+ end
53
+
54
+ @models = Hash.new
55
+ end
56
+
57
+ def set key, value
58
+ @config[key] = value
59
+ end
60
+
61
+ def get key
62
+ @config[key]
63
+ end
64
+
65
+ def parse_erd o, line
66
+ if /erd\{(?<yml_>.+?)\}:?/ =~ line
67
+ o.attrs.merge! YAML.load(yml_) if yml_
68
+ end
69
+ if /erd(\{.+?\})?\s*:?\s+(?<label_>.+)/ =~ line
70
+ o.erd_label = label_ if label_
71
+ end
72
+ end
73
+
74
+ # parse the fold contains mongoid source
75
+ def parse
76
+ raise "directory #{@config[:model_dir]} not exists" unless File.directory? @config[:model_dir]
77
+ Dir["#{@config[:model_dir]}/*.rb"].each do |file|
78
+ crt_model = Model.new
79
+ model_attrs_ = Hash.new
80
+ in_public = true
81
+ File.open(file, 'r:utf-8').each do |line|
82
+ line.chomp!
83
+
84
+ # erd_tag and attr
85
+ if /^[\#\s]*erd_tag\:?\s*(?<tag_>[\w\.]+)/ =~ line
86
+ crt_model.tag = tag_
87
+ crt_model.attrs = @config[:tags][tag_]
88
+ end
89
+
90
+ # catch class definition
91
+ if /^\s*class\s+(?<name_>\w+)/ =~ line
92
+ crt_model.name = name_.underscore
93
+ self.parse_erd crt_model, line
94
+ if /^\s*class\s+\w+\s+\<\s+(?<parent_>\w+)/ =~ line
95
+ crt_model.parent = parent_.underscore if parent_
96
+ end
97
+ end
98
+
99
+ # catch functions
100
+ in_public = true if /public\:/ =~ line
101
+ in_public = false if /private\:/ =~ line
102
+
103
+ if /^\s*def\s+(?<func_>[^#]+)\s*/ =~ line
104
+ field_ = Fields.new
105
+ field_.name, field_.type = func_, 'function'
106
+ self.parse_erd field_, line # parse erd attr and label
107
+ # arbitrage link
108
+ if /\-\>\s*(?<name_>\w+)(\{(?<attrs_>.+)\})?/ =~ line
109
+ attrs = {}
110
+ attrs = YAML.load(attrs_) if attrs_
111
+ field_.edge = [name_, '', attrs]
112
+ end
113
+ crt_model.fields << field_
114
+ end
115
+
116
+ # catch field
117
+ if /^\s*field\s+\:(?<name_>\w+)\s*\,.*\:?type\:?\s*(?<type_>[A-Za-z_0-9\:]+)/ =~ line
118
+ field_ = Fields.new
119
+ field_.name, field_.type = name_, type_
120
+ self.parse_erd field_, line # parse erd attr and label
121
+ # arbitrage link
122
+ if /\-\>\s*(?<name_>\w+)(\{(?<attrs_>.+)\})?/ =~ line
123
+ attrs = {}
124
+ attrs = YAML.load(attrs_) if attrs_
125
+ field_.edge = [name_, '', attrs]
126
+ end
127
+ crt_model.fields << field_
128
+ end
129
+
130
+ # catch relations
131
+ if /^\s*(?<rel_>embeds_many|embeds_one|has_many|has_one|belongs_to|embedded_in)\s+\:(?<name_>\w+)\s*(\,.*\:?as\:?\s*(?<as_>\w+))?/ =~ line
132
+ field_ = Fields.new
133
+ field_.name, field_.type = rel_, name_
134
+ field_.name = "#{rel_} (as #{as_})" if as_
135
+ self.parse_erd field_, line # parse erd attr and label
136
+ crt_model.fields << field_
137
+ unless %w[belongs_to embedded_in].include? rel_
138
+ field_.edge = [name_, '', {label: rel_}]
139
+ end
140
+ end
141
+
142
+ # common extension field
143
+ if /^\s*symbolize\s+\:(?<name>\w+)\s*\,.*\:?in\:?.*(?<in_>\[.+\])/ =~ line
144
+ field_ = Fields.new
145
+ field_.name, field_.type = name_, "symbolized in #{in_}"
146
+ self.parse_erd field_, line # parse erd attr and label
147
+ crt_model.fields << field_
148
+ end
149
+
150
+ if /^\s*state_machine\s+\:(?<state_>\w+)/ =~ line
151
+ field_ = Fields.new
152
+ field_.name = state_ == "initial" ? "state" : state_
153
+ field_.type = "state_machine"
154
+ self.parse_erd field_, line # parse erd attr and label
155
+ crt_model.fields << field_
156
+ end
157
+ end # open and parse one file
158
+
159
+ # assign attributes at the last moment
160
+ crt_model.attrs.merge! model_attrs_
161
+
162
+ # if config.include/tag, default to exclude_it = true
163
+ if @config[:include].size > 0 or @config[:tag].size > 0
164
+ include_it = false
165
+ else
166
+ include_it = true
167
+ end
168
+
169
+ # if in the include list, include it
170
+ include_it = true if @config[:include] and @config[:include].include? crt_model.name
171
+ @config[:tag].each do |t|
172
+ include_it = true if t == crt_model.tag or /^#{t}(\..+)?/.match(crt_model.tag)
173
+ end
174
+
175
+ include_it = false if @config[:exclude].include? crt_model.name
176
+ @models[crt_model.name] = crt_model if include_it
177
+ end # open directory
178
+ self
179
+ end
180
+
181
+ def output
182
+ g = Rviz::Graph.new @config[:title], {rankdir: 'LR', dpi: 300}
183
+
184
+ @models.each do |mname, model|
185
+ g.add_record(model.name, model.attrs)
186
+ g.node(model.name).add_row(model.title, true)
187
+ model.fields.each do |field|
188
+ g.node(model.name).add_row(field.as_row, true, 'l')
189
+ if field.edge
190
+ to_node, to_anchor, attrs = field.edge[0].underscore, field.edge[1], field.edge[2]
191
+ unless @models[to_node]
192
+ g.add(to_node, 'oval', {style:'filled', fillcolor:'grey', color:'grey'})
193
+ to_anchor = ''
194
+ end
195
+ g.link(model.name, field.as_row, to_node, to_anchor, attrs)
196
+ end
197
+ end
198
+
199
+ # relations
200
+ if model.parent
201
+ unless @models[model.parent]
202
+ g.add(model.parent, 'oval', {style:'filled', fillcolor:'grey', color:'grey'})
203
+ end
204
+ g.link(model.name, model.title, model.parent, '', {arrowhead: 'onormal'})
205
+ end
206
+ end
207
+
208
+ g.output
209
+ end
210
+
211
+ end
@@ -0,0 +1,18 @@
1
+ $LOAD_PATH.unshift File.expand_path('../lib', __FILE__)
2
+
3
+ Gem::Specification.new 'mongoid-erd', '0.0.3' do |s|
4
+ s.executables << "merd"
5
+ s.description = "Create model diagram graph (ERD graph) in graphviz's dot language"
6
+ s.summary = "Mongoid ERD diagram creator"
7
+ s.authors = ["Huang Wei"]
8
+ s.email = "huangw@pe-po.com"
9
+ s.homepage = "https://github.com/huangw/mongoid-erd-gem"
10
+ s.files = `git ls-files`.split("\n") - %w[.gitignore]
11
+ s.test_files = Dir.glob("{spec,test}/**/*.rb")
12
+ s.rdoc_options = %w[--line-numbers --inline-source --title Rviz --main README.rdoc --encoding=UTF-8]
13
+
14
+ s.add_dependency 'rviz'
15
+ s.add_dependency 'active_support'
16
+ # s.add_development_dependency 'rspec', '~> 2.5'
17
+ end
18
+
metadata ADDED
@@ -0,0 +1,92 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mongoid-erd
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.3
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Huang Wei
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-03-10 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rviz
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: active_support
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ description: Create model diagram graph (ERD graph) in graphviz's dot language
47
+ email: huangw@pe-po.com
48
+ executables:
49
+ - merd
50
+ extensions: []
51
+ extra_rdoc_files: []
52
+ files:
53
+ - Gemfile
54
+ - README.rdoc
55
+ - bin/merd
56
+ - examples/models/hello.rb
57
+ - examples/models/world.rb
58
+ - examples/mongoid_erd.yml
59
+ - lib/mongoid_erd.rb
60
+ - mongoid-erd.gemspec
61
+ homepage: https://github.com/huangw/mongoid-erd-gem
62
+ licenses: []
63
+ post_install_message:
64
+ rdoc_options:
65
+ - --line-numbers
66
+ - --inline-source
67
+ - --title
68
+ - Rviz
69
+ - --main
70
+ - README.rdoc
71
+ - --encoding=UTF-8
72
+ require_paths:
73
+ - lib
74
+ required_ruby_version: !ruby/object:Gem::Requirement
75
+ none: false
76
+ requirements:
77
+ - - ! '>='
78
+ - !ruby/object:Gem::Version
79
+ version: '0'
80
+ required_rubygems_version: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ requirements: []
87
+ rubyforge_project:
88
+ rubygems_version: 1.8.24
89
+ signing_key:
90
+ specification_version: 3
91
+ summary: Mongoid ERD diagram creator
92
+ test_files: []