verd 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: f99eae22401a270ac40a8e4dc0a8323fbaf0898e6edc781356cd67512a0af962
4
+ data.tar.gz: 8b77dd9b0f3cf6a11d6e414892661af9d5d690e5e9bf32ad2aa01f067b97cb0c
5
+ SHA512:
6
+ metadata.gz: 2a429432b60c3d3db723d2073ea17da439a74863faeb0bc93e4c0b3f1bb7993bb3916105069dadd35af43385df105ffc0b67fcd7f27cbd461759a0922c0afdea
7
+ data.tar.gz: 14391c980d8ba121c1ea99d2084d9aba02b55e7e8a4abe47d8542906f9c3d0ab40c6e9d0be3de3d1b53937d0eff87876dd9eabeb716274e68a9ef27e9c0e7d80
data/.gitignore ADDED
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+ /Gemfile.lock
10
+ .tool-versions
11
+ *.gem
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ ---
2
+ language: ruby
3
+ cache: bundler
4
+ rvm:
5
+ - 2.4.2
6
+ before_install: gem install bundler -v 2.1.4
@@ -0,0 +1,74 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, gender identity and expression, level of experience,
9
+ nationality, personal appearance, race, religion, or sexual identity and
10
+ orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or
41
+ reject comments, commits, code, wiki edits, issues, and other contributions
42
+ that are not aligned to this Code of Conduct, or to ban temporarily or
43
+ permanently any contributor for other behaviors that they deem inappropriate,
44
+ threatening, offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team at jim.yuan@rccchina.com. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an incident.
62
+ Further details of specific enforcement policies may be posted separately.
63
+
64
+ Project maintainers who do not follow or enforce the Code of Conduct in good
65
+ faith may face temporary or permanent repercussions as determined by other
66
+ members of the project's leadership.
67
+
68
+ ## Attribution
69
+
70
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
+ available at [https://contributor-covenant.org/version/1/4][version]
72
+
73
+ [homepage]: https://contributor-covenant.org
74
+ [version]: https://contributor-covenant.org/version/1/4/
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in verd.gemspec
4
+ gemspec
5
+
6
+ gem "rake", "~> 12.0"
7
+ gem "minitest", "~> 5.0"
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2020 jim.yuan
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,44 @@
1
+ # Verd
2
+
3
+ Generate entity-relationship diagram for rails project. Based on [Rails ERD](https://github.com/voormedia/rails-erd).
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'verd'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle install
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install verd
20
+
21
+ ## Usage
22
+
23
+ In your rails root directory, execute:
24
+
25
+ $ rake verd
26
+
27
+ ## Development
28
+
29
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
30
+
31
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
32
+
33
+ ## Contributing
34
+
35
+ Bug reports and pull requests are welcome on GitHub at https://github.com/turnon/verd. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/turnon/verd/blob/master/CODE_OF_CONDUCT.md).
36
+
37
+
38
+ ## License
39
+
40
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
41
+
42
+ ## Code of Conduct
43
+
44
+ Everyone interacting in the Verd project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/verd/blob/master/CODE_OF_CONDUCT.md).
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList["test/**/*_test.rb"]
8
+ end
9
+
10
+ task :default => :test
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "verd"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,25 @@
1
+ class ActiveRecord::Base
2
+ class << self
3
+ attr_accessor :source_location, :model_validity
4
+
5
+ def source_dir
6
+ return @source_dir if @source_dir
7
+ path = name.split(/::/)
8
+ path.pop
9
+ @source_dir = "/#{path.map(&:underscore).join('/')}"
10
+ end
11
+
12
+ def human_attribute_names
13
+ attribute_names.each_with_object({}){ |attr, h| h[attr] = human_attribute_name(attr) }
14
+ end
15
+
16
+ def verd_columns
17
+ columns.map{|c| "#{c.name} : #{c.sql_type}"}
18
+ end
19
+
20
+ def verd_indexes
21
+ connection.indexes(table_name).sort_by(&:columns).
22
+ map{|idx| {cols: idx.columns, uniq: idx.unique} }
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,25 @@
1
+ module ActiveSupport::Dependencies
2
+ alias_method :o_require_or_load, :require_or_load
3
+
4
+ def require_or_load file_name, *args
5
+ if file_name.starts_with?(models_dir)
6
+ loaded_model_paths[File.basename(file_name, '.rb')] = model_path(file_name)
7
+ end
8
+ o_require_or_load file_name, *args
9
+ end
10
+
11
+ def loaded_model_paths
12
+ @loaded_model_paths ||= {}
13
+ end
14
+
15
+ private
16
+
17
+ def model_path file_name
18
+ @_start ||= (models_dir.length..-1)
19
+ file_name.slice(@_start)
20
+ end
21
+
22
+ def models_dir
23
+ @_models_dir ||= File.join Rails.root.to_s, 'app', 'models'
24
+ end
25
+ end
@@ -0,0 +1,29 @@
1
+ module RailsERD
2
+ class Domain
3
+ class Relationship
4
+
5
+ NoneSym = 'none'
6
+ HasManySym ='rect'
7
+ HasOneSym = 'triangle'
8
+
9
+ def verd_link
10
+ src, tar, macro = nil, nil, nil
11
+
12
+ associations.each do |asso|
13
+ if asso.macro == :has_many
14
+ src, tar, macro = asso.active_record.to_s, asso.klass.to_s, :has_many
15
+ break
16
+ elsif asso.macro == :has_one
17
+ src, tar, macro = asso.active_record.to_s, asso.klass.to_s, :has_one
18
+ else
19
+ break if macro
20
+ src, tar, macro = asso.klass.to_s, asso.active_record.to_s, :has_many
21
+ end
22
+ end
23
+
24
+ sym = macro == :has_many ? HasManySym : HasOneSym
25
+ {source: src, target: tar, macro: macro, symbol: [NoneSym, sym]}
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,7 @@
1
+ module Verd
2
+ class Railtie < Rails::Railtie
3
+ rake_tasks do
4
+ load "verd/tasks.rake"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,23 @@
1
+ require 'rails-erd'
2
+ spec = Gem::Specification.find_by_name 'rails-erd'
3
+ load "#{spec.gem_dir}/lib/rails_erd/tasks.rake"
4
+
5
+ namespace :verd do
6
+ task :assign_source_location do
7
+ ActiveRecord::Base.descendants.each do |klass|
8
+ klass.source_location = ActiveSupport::Dependencies.loaded_model_paths[klass.name.underscore]
9
+ end
10
+ end
11
+
12
+ task :generate => ['erd:options', 'erd:load_models', 'assign_source_location'] do
13
+ g = Verd::Graph.new
14
+ #pp g.links
15
+ #pp g.nodes
16
+ #pp g.categories
17
+ puts g.to_json
18
+ g.write_html
19
+ #pp ActiveSupport::Dependencies.loaded_model_paths
20
+ end
21
+ end
22
+
23
+ task :verd => "verd:generate"
@@ -0,0 +1,416 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+
4
+ <head>
5
+ <meta charset="utf-8">
6
+ <title>erd</title>
7
+ <link href="https://cdn.bootcdn.net/ajax/libs/iview/3.5.4/styles/iview.css" rel="stylesheet">
8
+ <style>
9
+ html,
10
+ body,
11
+ #app {
12
+ height: 100%;
13
+ overflow: hidden;
14
+ }
15
+
16
+ #switch {
17
+ position: fixed;
18
+ bottom: 4%;
19
+ right: 2%;
20
+ }
21
+
22
+ button.filter-switch {
23
+ position: fixed;
24
+ top: 3%;
25
+ left: 2%;
26
+ }
27
+
28
+ a.ivu-drawer-close {
29
+ margin-top: 7px;
30
+ right: 20px;
31
+ }
32
+
33
+ .ivu-drawer-body {
34
+ overflow-y: scroll;
35
+ }
36
+
37
+ .highlight {
38
+ background-color: yellow;
39
+ }
40
+ </style>
41
+ <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.14/vue.min.js"></script>
42
+ <script src="https://cdn.bootcdn.net/ajax/libs/iview/3.5.4/iview.min.js"></script>
43
+
44
+ <script src="https://cdn.bootcdn.net/ajax/libs/echarts/4.1.0-release/echarts.min.js"></script>
45
+ <script src="https://cdn.bootcdn.net/ajax/libs/vue-echarts/4.0.2/vue-echarts.min.js"></script>
46
+ </head>
47
+
48
+ <body>
49
+ <div id="app">
50
+ <Drawer placement="left" v-model="filter" :mask-closable="false" :mask="false" :draggable="true" :transfer="false">
51
+ <i-input v-model="keyword" style="width: calc(100% - 27px); margin-bottom: 10px;"></i-input>
52
+ <Tree :data="relations" show-checkbox @on-check-change="selectRelation($event)" ref="filter-panel" />
53
+ </Drawer>
54
+
55
+ <v-chart :options="opts" theme="light" autoresize @click="handleClick($event)"></v-chart>
56
+
57
+ <i-button @click="filter = !filter" type="info" class="filter-switch">
58
+ filter
59
+ <Icon type="ios-arrow-forward" />
60
+ </i-button>
61
+
62
+ <div id="switch">
63
+ label&nbsp;<Checkbox v-model="opts.series[0].label.show"></Checkbox>
64
+ </div>
65
+ </div>
66
+
67
+ <script>
68
+ // (function () {
69
+
70
+ let graph =
71
+ //start-sub
72
+ {
73
+ "nodes": [{
74
+ "name": "n1",
75
+ "category": 0
76
+ }, {
77
+ "name": "n2",
78
+ "category": 1
79
+ }, {
80
+ "name": "n3",
81
+ "category": 2
82
+ }, {
83
+ "name": "n4",
84
+ "category": 2
85
+ }],
86
+ "links": [{
87
+ "source": "n1",
88
+ "target": "n2"
89
+ }, {
90
+ "source": "n2",
91
+ "target": "n3"
92
+ }, {
93
+ "source": "n3",
94
+ "target": "n1"
95
+ }, {
96
+ "source": "n3",
97
+ "target": "n4"
98
+ }],
99
+ "categories": [{
100
+ "name": "/A"
101
+ }, {
102
+ "name": "/B"
103
+ }, {
104
+ "name": "/C"
105
+ }]
106
+ }
107
+ //end-sub
108
+
109
+ let source_checkboxes = {},
110
+ target_checkboxes = [],
111
+ child_renderer = (h, { data }) => {
112
+ return h('span', `${data.macro} ${data.title}`);
113
+ },
114
+ add_to_checkboxs_tree = (link, i) => {
115
+ if (!source_checkboxes[link.source]) {
116
+ source_checkboxes[link.source] = {
117
+ title: link.source,
118
+ expand: true,
119
+ checked: false,
120
+ indeterminate: false,
121
+ children: []
122
+ }
123
+ }
124
+
125
+ let child_checkbox = {
126
+ link_idx: i,
127
+ title: link.target,
128
+ macro: link.macro,
129
+ render: child_renderer,
130
+ checked: false
131
+ }
132
+ target_checkboxes[i] = child_checkbox
133
+ source_checkboxes[link.source].children.push(child_checkbox)
134
+ }
135
+
136
+ graph.links.forEach((link, i) => {
137
+ link.idx = i
138
+ link.lineStyle = {
139
+ opacity: 0.5,
140
+ width: 1
141
+ }
142
+ add_to_checkboxs_tree(link, i)
143
+ })
144
+
145
+ let node_tooltip = {
146
+ formatter({ data }) {
147
+ return data.schema.join("<br>")
148
+ // let schema = data.schema.join("<br>")
149
+ // let idx = data.indexes.map((idx) => {
150
+ // let cols = `[${idx.cols.join(', ')}]`
151
+ // return idx.uniq ? `${cols}:uniq` : cols
152
+ // }).join("<br>")
153
+ // return `${schema}<br>${idx}`
154
+ }
155
+ }
156
+ graph.nodes.forEach((node, i) => {
157
+ node.tooltip = node_tooltip
158
+ node.itemStyle = {
159
+ opacity: 1
160
+ }
161
+ })
162
+
163
+ let relations = Object.values(source_checkboxes)
164
+ // console.log(relations)
165
+
166
+
167
+ let opts = {
168
+ // title: {
169
+ // text: 'Les Miserables',
170
+ // subtext: 'Default layout',
171
+ // top: 'bottom',
172
+ // left: 'right'
173
+ // },
174
+ tooltip: {},
175
+ legend: [{
176
+ // selectedMode: 'single',
177
+ top: "3%",
178
+ right: "2%",
179
+ orient: "vertical",
180
+ align: "right",
181
+ data: graph.categories.map(function (a) {
182
+ return a.name;
183
+ })
184
+ }],
185
+ animationDuration: 1500,
186
+ animationEasingUpdate: 'quinticInOut',
187
+ series: [{
188
+ name: 'Les Miserables',
189
+ type: 'graph',
190
+ layout: 'force',
191
+ data: graph.nodes,
192
+ links: graph.links,
193
+ categories: graph.categories,
194
+ roam: true,
195
+ // focusNodeAdjacency: true,
196
+ itemStyle: {
197
+ borderColor: '#fff',
198
+ borderWidth: 1,
199
+ shadowBlur: 10,
200
+ shadowColor: 'rgba(0, 0, 0, 0.3)'
201
+ },
202
+ label: {
203
+ show: false,
204
+ position: 'right',
205
+ formatter: '{b}'
206
+ },
207
+ lineStyle: {
208
+ color: 'source',
209
+ // curveness: 0.3
210
+ },
211
+ edgeSymbolSize: 15,
212
+ emphasis: {
213
+ lineStyle: {
214
+ width: 10
215
+ }
216
+ }
217
+ }]
218
+ }
219
+
220
+ Vue.component('v-chart', VueECharts)
221
+ new Vue({
222
+ el: '#app',
223
+ data: {
224
+ keyword: "",
225
+ filter: false,
226
+ opts: opts,
227
+ relations: relations
228
+ },
229
+ watch: {
230
+ keyword() {
231
+ tree.search(this.keyword)
232
+ }
233
+ },
234
+ methods: {
235
+ selectRelation(checkeds) {
236
+ let checked_links = new Set(),
237
+ checked_nodes = new Set()
238
+
239
+ checkeds.forEach((c) => {
240
+ if (c.link_idx !== undefined) {
241
+ checked_links.add(c.link_idx)
242
+ checked_nodes.add(graph.links[c.link_idx].source)
243
+ }
244
+ checked_nodes.add(c.title)
245
+ })
246
+
247
+ if (checked_nodes.size === 0) {
248
+ graph.links.forEach((link) => {
249
+ link.lineStyle.width = 1
250
+ link.lineStyle.opacity = 1
251
+ })
252
+ graph.nodes.forEach((node) => {
253
+ node.itemStyle.opacity = 1
254
+ })
255
+ return
256
+ }
257
+
258
+ graph.links.forEach((link, i) => {
259
+ if (checked_links.has(i)) {
260
+ link.lineStyle.width = 10
261
+ link.lineStyle.opacity = 1
262
+ } else {
263
+ link.lineStyle.width = 1
264
+ link.lineStyle.opacity = 0.1
265
+ }
266
+ })
267
+
268
+ graph.nodes.forEach((node, i) => {
269
+ if (checked_nodes.has(node.name)) {
270
+ node.itemStyle.opacity = 1
271
+ } else {
272
+ node.itemStyle.opacity = 0.1
273
+ }
274
+ })
275
+ },
276
+ handleClick(e) {
277
+ let link = e.data,
278
+ target_box = target_checkboxes[link.idx],
279
+ source_box = source_checkboxes[link.source]
280
+
281
+ target_box.checked = !target_box.checked
282
+
283
+ let checked_targets_count = source_box.children.reduce((count, c) => {
284
+ count = c.checked ? count + 1 : count
285
+ return count
286
+ }, 0)
287
+
288
+ if (checked_targets_count === 0) {
289
+ source_box.checked = false
290
+ source_box.indeterminate = false
291
+ } else if (checked_targets_count === source_box.children.length) {
292
+ source_box.checked = true
293
+ source_box.indeterminate = false
294
+ } else {
295
+ source_box.checked = false
296
+ source_box.indeterminate = true
297
+ }
298
+
299
+ this.$nextTick(() => {
300
+ let checkeds = this.$refs['filter-panel'].getCheckedAndIndeterminateNodes()
301
+ this.selectRelation(checkeds)
302
+ })
303
+ }
304
+ }
305
+ });
306
+
307
+ const Tree = (function () {
308
+ const node_class_name = "ivu-tree-children",
309
+ node_class = "." + node_class_name,
310
+ text_class = "label + span",
311
+ highlight_html = '<span class="highlight">$1</span>'
312
+
313
+ class Tree {
314
+ constructor(selector) {
315
+ let n = document.querySelector(selector + " " + node_class)
316
+ this.roots = n ? (new Node(n).this_and_siblings()) : []
317
+ }
318
+
319
+ search(keyword) {
320
+ let kw = keyword === "" ? /./ : new RegExp(keyword)
321
+ this.show_only_when_match((txt) => { return txt.match(kw) })
322
+
323
+ if (keyword !== "") {
324
+ let repl = new RegExp('(' + keyword + ')')
325
+ this.highlight((txt) => { return txt.replace(repl, highlight_html) })
326
+ } else {
327
+ document.querySelectorAll('.highlight').forEach((n) => { n.parentNode.innerText = n.parentNode.innerText })
328
+ }
329
+ }
330
+
331
+ show_only_when_match(fn) {
332
+ this.roots.forEach((n) => { n.show_only_when_match(fn) })
333
+ }
334
+
335
+ highlight(fn) {
336
+ this.roots.forEach((n) => { n.highlight(fn) })
337
+ }
338
+ }
339
+
340
+ class Node {
341
+ constructor(node) {
342
+ this.raw_node = node
343
+ this.visible = true
344
+
345
+ let child = this.raw_node.querySelector(node_class)
346
+ this.children = child ? (new Node(child).this_and_siblings()) : []
347
+ }
348
+
349
+ this_and_siblings() {
350
+ let ss = [this],
351
+ s = this.raw_node.nextSibling
352
+
353
+ while (s && s.getAttribute && (s.getAttribute('class') === node_class_name)) {
354
+ ss.push(new Node(s))
355
+ s = s.nextSibling
356
+ }
357
+ return ss
358
+ }
359
+
360
+ highlight(fn) {
361
+ if (!this.visible) {
362
+ return
363
+ }
364
+ this.title_node().innerHTML = fn(this.title_node().innerText)
365
+ this.children.forEach((c) => { c.highlight(fn) })
366
+ }
367
+
368
+ title_node() {
369
+ return this.raw_node.querySelector(text_class)
370
+ }
371
+
372
+ show_only_when_match(fn) {
373
+ let text = this.title_node().innerText
374
+ if (fn(text)) {
375
+ this.set_sub_tree_visible()
376
+ return
377
+ }
378
+ this.children.forEach((c) => {
379
+ c.show_only_when_match(fn)
380
+ })
381
+ if (this.children.some((c) => { return c.visible })) {
382
+ this.set_display(true)
383
+ return
384
+ }
385
+ this.set_display(false)
386
+ }
387
+
388
+ set_display(vi) {
389
+ this.visible = vi
390
+ this.raw_node.style.display = (vi ? "block" : "none")
391
+
392
+ }
393
+
394
+ set_sub_tree_visible() {
395
+ this.set_display(true)
396
+ this.children.forEach((c) => {
397
+ c.set_sub_tree_visible()
398
+ })
399
+ }
400
+ }
401
+
402
+ return Tree
403
+ })()
404
+
405
+ let tree = new Tree('#app')
406
+
407
+ setTimeout(() => {
408
+ let chart = document.querySelector(".echarts")
409
+ chart.style.width = "100%"
410
+ chart.style.height = "100%"
411
+ })
412
+ // })()
413
+ </script>
414
+ </body>
415
+
416
+ </html>
@@ -0,0 +1,3 @@
1
+ module Verd
2
+ VERSION = "0.1.1"
3
+ end
data/lib/verd.rb ADDED
@@ -0,0 +1,78 @@
1
+ require "verd/version"
2
+ require "verd/railtie" if defined? Rails
3
+ require "verd/active_record_base"
4
+ require "verd/dependencies"
5
+ require "rails_erd/domain"
6
+ require "verd/rails_erd_domain_relationship"
7
+
8
+ module Verd
9
+
10
+ class Graph
11
+ attr_reader :domain
12
+
13
+ def initialize
14
+ @domain = RailsERD::Domain.generate
15
+ end
16
+
17
+ def relations
18
+ @relations ||= domain.relationships.each_with_object({}) do |rel, rs|
19
+ next if rel.destination.model.nil? || rel.source.model.nil?
20
+ rs[[rel.source.model, rel.destination.model]] = rel
21
+ end.values.sort_by do |rel|
22
+ [rel.source.model.name, rel.destination.model.name]
23
+ end
24
+ end
25
+
26
+ def models
27
+ @models ||= relations.each_with_object(Set.new) do |rel, s|
28
+ s << rel.destination.model << rel.source.model
29
+ end.sort_by(&:name)
30
+ end
31
+
32
+ def nodes
33
+ models.map do |model|
34
+ category = plain_categories.index(model.source_dir)
35
+ {
36
+ name: model.name,
37
+ category: category,
38
+ schema: model.verd_columns,
39
+ indexes: model.verd_indexes
40
+ }
41
+ end
42
+ end
43
+
44
+ def links
45
+ relations.map(&:verd_link)
46
+ end
47
+
48
+ def categories
49
+ plain_categories.map do |d|
50
+ {name: d}
51
+ end
52
+ end
53
+
54
+ def plain_categories
55
+ @categories ||= models.each_with_object(Set.new) do |m, s|
56
+ s << m.source_dir
57
+ end.sort
58
+ end
59
+
60
+ def to_h
61
+ {nodes: nodes, links: links, categories: categories}
62
+ end
63
+
64
+ def to_json
65
+ JSON.pretty_generate(to_h)
66
+ end
67
+
68
+ def to_html
69
+ tmpl = File.join(__dir__, 'verd', 'template.html')
70
+ File.read(tmpl).sub(/\/\/start-sub.*\/\/end-sub/m, to_json)
71
+ end
72
+
73
+ def write_html
74
+ path = File.join Rails.root.to_s, 'verd.html'
75
+ File.open(path, 'w'){ |f| f.puts to_html }
76
+ end
77
+ end
78
+ end
data/verd.gemspec ADDED
@@ -0,0 +1,24 @@
1
+ require_relative 'lib/verd/version'
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = "verd"
5
+ spec.version = Verd::VERSION
6
+ spec.authors = ["ken"]
7
+ spec.email = ["block24block@gmail.com"]
8
+
9
+ spec.summary = %q{rails-erd + echarts + iview}
10
+ spec.homepage = "https://github.com/turnon/trace_tree"
11
+ spec.license = "MIT"
12
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
13
+
14
+ # Specify which files should be added to the gem when it is released.
15
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
16
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
17
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ end
19
+ spec.bindir = "exe"
20
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
+ spec.require_paths = ["lib"]
22
+
23
+ spec.add_dependency "rails-erd", "~> 1.6.1"
24
+ end
metadata ADDED
@@ -0,0 +1,75 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: verd
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - ken
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2022-05-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails-erd
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 1.6.1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 1.6.1
27
+ description:
28
+ email:
29
+ - block24block@gmail.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - ".gitignore"
35
+ - ".travis.yml"
36
+ - CODE_OF_CONDUCT.md
37
+ - Gemfile
38
+ - LICENSE.txt
39
+ - README.md
40
+ - Rakefile
41
+ - bin/console
42
+ - bin/setup
43
+ - lib/verd.rb
44
+ - lib/verd/active_record_base.rb
45
+ - lib/verd/dependencies.rb
46
+ - lib/verd/rails_erd_domain_relationship.rb
47
+ - lib/verd/railtie.rb
48
+ - lib/verd/tasks.rake
49
+ - lib/verd/template.html
50
+ - lib/verd/version.rb
51
+ - verd.gemspec
52
+ homepage: https://github.com/turnon/trace_tree
53
+ licenses:
54
+ - MIT
55
+ metadata: {}
56
+ post_install_message:
57
+ rdoc_options: []
58
+ require_paths:
59
+ - lib
60
+ required_ruby_version: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: 2.3.0
65
+ required_rubygems_version: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ requirements: []
71
+ rubygems_version: 3.1.6
72
+ signing_key:
73
+ specification_version: 4
74
+ summary: rails-erd + echarts + iview
75
+ test_files: []