mongoid-erd 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []