rails_graph 0.1.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +45 -45
- data/README.md +13 -0
- data/lib/rails_graph/commands/build_graph.rb +22 -68
- data/lib/rails_graph/commands/builders/associations.rb +63 -0
- data/lib/rails_graph/commands/builders/databases.rb +65 -0
- data/lib/rails_graph/commands/builders/models.rb +118 -0
- data/lib/rails_graph/commands/builders/packs.rb +102 -0
- data/lib/rails_graph/configuration.rb +11 -1
- data/lib/rails_graph/exporters/base.rb +1 -1
- data/lib/rails_graph/exporters/neo4j.rb +7 -3
- data/lib/rails_graph/graph/nodes/database.rb +38 -0
- data/lib/rails_graph/graph/nodes/pack.rb +29 -0
- data/lib/rails_graph/graph/nodes/table.rb +31 -0
- data/lib/rails_graph/graph/relationships/pack_dependency.rb +20 -0
- data/lib/rails_graph/graph/relationships/pack_model.rb +20 -0
- data/lib/rails_graph/helpers/models.rb +2 -0
- data/lib/rails_graph/inspector.rb +31 -0
- data/lib/rails_graph/version.rb +1 -1
- data/lib/rails_graph.rb +1 -0
- metadata +12 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2aa9e5821416a34e7eca9ba18199bea6007190821c50cabb84147db6232b06e2
|
4
|
+
data.tar.gz: f54cb43940dba30d7ddc5915cb8669601d0e05b9b5eb4582c18a504859101ed0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 714e4e3d097f8fda7958665d9f4f8ee0ca78978ecab77be2a59ad84fa1921c3204ae93d111072882068ce33ee3966786a3d7473d787e275077d61785f105815a
|
7
|
+
data.tar.gz: f3889f7437868743a38971f1808e2b3e9d6ac8445a8567f3e96ebb7722d35a24795830f220bdf7eeb1940bfefe472c1cc33f2539a4bd380358af006ad4c62035
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
rails_graph (0.
|
4
|
+
rails_graph (0.3.0)
|
5
5
|
activerecord (>= 6.0, < 7.1)
|
6
6
|
neo4j-ruby-driver (>= 4.4.0.alpha.8)
|
7
7
|
railties (>= 6.0, < 7.1)
|
@@ -9,83 +9,83 @@ PATH
|
|
9
9
|
GEM
|
10
10
|
remote: https://rubygems.org/
|
11
11
|
specs:
|
12
|
-
actionpack (7.0.
|
13
|
-
actionview (= 7.0.
|
14
|
-
activesupport (= 7.0.
|
15
|
-
rack (~> 2.0, >= 2.2.
|
12
|
+
actionpack (7.0.5)
|
13
|
+
actionview (= 7.0.5)
|
14
|
+
activesupport (= 7.0.5)
|
15
|
+
rack (~> 2.0, >= 2.2.4)
|
16
16
|
rack-test (>= 0.6.3)
|
17
17
|
rails-dom-testing (~> 2.0)
|
18
18
|
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
19
|
-
actionview (7.0.
|
20
|
-
activesupport (= 7.0.
|
19
|
+
actionview (7.0.5)
|
20
|
+
activesupport (= 7.0.5)
|
21
21
|
builder (~> 3.1)
|
22
22
|
erubi (~> 1.4)
|
23
23
|
rails-dom-testing (~> 2.0)
|
24
24
|
rails-html-sanitizer (~> 1.1, >= 1.2.0)
|
25
|
-
activemodel (7.0.
|
26
|
-
activesupport (= 7.0.
|
27
|
-
activerecord (7.0.
|
28
|
-
activemodel (= 7.0.
|
29
|
-
activesupport (= 7.0.
|
30
|
-
activesupport (7.0.
|
25
|
+
activemodel (7.0.5)
|
26
|
+
activesupport (= 7.0.5)
|
27
|
+
activerecord (7.0.5)
|
28
|
+
activemodel (= 7.0.5)
|
29
|
+
activesupport (= 7.0.5)
|
30
|
+
activesupport (7.0.5)
|
31
31
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
32
32
|
i18n (>= 1.6, < 2)
|
33
33
|
minitest (>= 5.1)
|
34
34
|
tzinfo (~> 2.0)
|
35
35
|
ast (2.4.2)
|
36
|
-
async (2.1
|
36
|
+
async (2.5.1)
|
37
37
|
console (~> 1.10)
|
38
|
-
io-event (~> 1.
|
38
|
+
io-event (~> 1.1)
|
39
39
|
timers (~> 4.1)
|
40
|
-
async-io (1.34.
|
40
|
+
async-io (1.34.3)
|
41
41
|
async
|
42
|
-
async-pool (0.3.12)
|
43
|
-
async (>= 1.25)
|
44
42
|
builder (3.2.4)
|
45
|
-
concurrent-ruby (1.
|
46
|
-
|
43
|
+
concurrent-ruby (1.2.2)
|
44
|
+
connection_pool (2.4.1)
|
45
|
+
console (1.16.2)
|
47
46
|
fiber-local
|
48
47
|
crass (1.0.6)
|
49
48
|
diff-lcs (1.5.0)
|
50
|
-
erubi (1.
|
49
|
+
erubi (1.12.0)
|
51
50
|
fiber-local (1.0.0)
|
52
|
-
i18n (1.
|
51
|
+
i18n (1.13.0)
|
53
52
|
concurrent-ruby (~> 1.0)
|
54
|
-
io-event (1.
|
53
|
+
io-event (1.2.2)
|
55
54
|
json (2.6.2)
|
56
|
-
loofah (2.
|
55
|
+
loofah (2.21.3)
|
57
56
|
crass (~> 1.0.2)
|
58
|
-
nokogiri (>= 1.
|
57
|
+
nokogiri (>= 1.12.0)
|
59
58
|
method_source (1.0.0)
|
60
|
-
mini_portile2 (2.8.
|
61
|
-
minitest (5.
|
62
|
-
neo4j-ruby-driver (4.4.
|
59
|
+
mini_portile2 (2.8.2)
|
60
|
+
minitest (5.18.0)
|
61
|
+
neo4j-ruby-driver (4.4.4)
|
63
62
|
activesupport
|
64
63
|
async-io
|
65
|
-
|
64
|
+
connection_pool
|
66
65
|
zeitwerk (>= 2.1.10)
|
67
|
-
nokogiri (1.
|
68
|
-
mini_portile2 (~> 2.8.
|
66
|
+
nokogiri (1.15.2)
|
67
|
+
mini_portile2 (~> 2.8.2)
|
69
68
|
racc (~> 1.4)
|
70
|
-
nokogiri (1.
|
69
|
+
nokogiri (1.15.2-arm64-darwin)
|
71
70
|
racc (~> 1.4)
|
72
|
-
nokogiri (1.
|
71
|
+
nokogiri (1.15.2-x86_64-linux)
|
73
72
|
racc (~> 1.4)
|
74
73
|
parallel (1.22.1)
|
75
74
|
parser (3.1.2.1)
|
76
75
|
ast (~> 2.4.1)
|
77
|
-
racc (1.6.
|
78
|
-
rack (2.2.
|
79
|
-
rack-test (2.0
|
76
|
+
racc (1.6.2)
|
77
|
+
rack (2.2.7)
|
78
|
+
rack-test (2.1.0)
|
80
79
|
rack (>= 1.3)
|
81
80
|
rails-dom-testing (2.0.3)
|
82
81
|
activesupport (>= 4.2.0)
|
83
82
|
nokogiri (>= 1.6)
|
84
|
-
rails-html-sanitizer (1.
|
85
|
-
loofah (~> 2.
|
86
|
-
|
87
|
-
|
88
|
-
|
83
|
+
rails-html-sanitizer (1.6.0)
|
84
|
+
loofah (~> 2.21)
|
85
|
+
nokogiri (~> 1.14)
|
86
|
+
railties (7.0.5)
|
87
|
+
actionpack (= 7.0.5)
|
88
|
+
activesupport (= 7.0.5)
|
89
89
|
method_source
|
90
90
|
rake (>= 12.2)
|
91
91
|
thor (~> 1.0)
|
@@ -120,12 +120,12 @@ GEM
|
|
120
120
|
rubocop-ast (1.21.0)
|
121
121
|
parser (>= 3.1.1.0)
|
122
122
|
ruby-progressbar (1.11.0)
|
123
|
-
thor (1.2.
|
124
|
-
timers (4.3.
|
125
|
-
tzinfo (2.0.
|
123
|
+
thor (1.2.2)
|
124
|
+
timers (4.3.5)
|
125
|
+
tzinfo (2.0.6)
|
126
126
|
concurrent-ruby (~> 1.0)
|
127
127
|
unicode-display_width (2.3.0)
|
128
|
-
zeitwerk (2.6.
|
128
|
+
zeitwerk (2.6.8)
|
129
129
|
|
130
130
|
PLATFORMS
|
131
131
|
arm64-darwin-21
|
data/README.md
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# RailsGraph
|
2
2
|
|
3
|
+
[![Gem Version](https://badge.fury.io/rb/rails_graph.svg)](https://badge.fury.io/rb/rails_graph)
|
4
|
+
|
3
5
|
Graph visualization for Rails applications using [Neo4j Browser](https://neo4j.com/docs/browser-manual/current/).
|
4
6
|
|
5
7
|
Gain visibility on your Rails application data model and identify bottlenecks. It is an ERD, but interactive and queryable using [Cypher](https://neo4j.com/developer/cypher/).
|
@@ -8,6 +10,7 @@ Gain visibility on your Rails application data model and identify bottlenecks. I
|
|
8
10
|
|
9
11
|
## Features
|
10
12
|
|
13
|
+
- Application Databases and Tables
|
11
14
|
- ActiveRecord Models
|
12
15
|
- Metadata, e.g size, indexes
|
13
16
|
- Columns (disabled by default)
|
@@ -20,6 +23,7 @@ Gain visibility on your Rails application data model and identify bottlenecks. I
|
|
20
23
|
- Support for Polymorphic Associations
|
21
24
|
- ActiveRecord Abstract models
|
22
25
|
- Class Hierarchy
|
26
|
+
- [Packwerk](https://github.com/Shopify/packwerk) packages (disabled by default)
|
23
27
|
|
24
28
|
## Installation
|
25
29
|
|
@@ -59,10 +63,19 @@ RailsGraph.configure do |config|
|
|
59
63
|
config.include_classes = [ActsAsTaggableOn::Tag, ActsAsTaggableOn::Tagging]
|
60
64
|
|
61
65
|
# Configure Columns parsing
|
66
|
+
# default false
|
62
67
|
config.columns = true
|
63
68
|
|
64
69
|
# Configure Class Hierarchy parsing
|
65
70
|
config.inheritance = true
|
71
|
+
|
72
|
+
# Configure Database entities
|
73
|
+
# default true
|
74
|
+
config.database = false
|
75
|
+
|
76
|
+
# Configure inclusion of Packwerk packages
|
77
|
+
# default false
|
78
|
+
config.include_packwerk = true
|
66
79
|
end
|
67
80
|
```
|
68
81
|
|
@@ -6,16 +6,26 @@ require_relative "../graph/relationship"
|
|
6
6
|
|
7
7
|
require_relative "../graph/nodes/abstract_model"
|
8
8
|
require_relative "../graph/nodes/column"
|
9
|
+
require_relative "../graph/nodes/database"
|
9
10
|
require_relative "../graph/nodes/model"
|
11
|
+
require_relative "../graph/nodes/pack"
|
12
|
+
require_relative "../graph/nodes/table"
|
10
13
|
require_relative "../graph/nodes/virtual_model"
|
11
14
|
|
12
15
|
require_relative "../graph/relationships/association"
|
13
16
|
require_relative "../graph/relationships/attribute"
|
14
17
|
require_relative "../graph/relationships/inheritance"
|
18
|
+
require_relative "../graph/relationships/pack_dependency"
|
19
|
+
require_relative "../graph/relationships/pack_model"
|
15
20
|
|
16
21
|
require_relative "../helpers/associations"
|
17
22
|
require_relative "../helpers/models"
|
18
23
|
|
24
|
+
require_relative "./builders/associations"
|
25
|
+
require_relative "./builders/models"
|
26
|
+
require_relative "./builders/packs"
|
27
|
+
require_relative "./builders/databases"
|
28
|
+
|
19
29
|
module RailsGraph
|
20
30
|
module Commands
|
21
31
|
class BuildGraph
|
@@ -24,89 +34,33 @@ module RailsGraph
|
|
24
34
|
end
|
25
35
|
|
26
36
|
def call
|
27
|
-
|
28
|
-
graph.add_node(polymorphic_node)
|
37
|
+
setup_generic_nodes
|
29
38
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
build_associations_relationships
|
35
|
-
build_column_nodes if configuration.columns?
|
36
|
-
build_inheritance_relationships if configuration.inheritance?
|
39
|
+
RailsGraph::Commands::Builders::Databases.enrich(graph: graph) if configuration.databases?
|
40
|
+
RailsGraph::Commands::Builders::Models.enrich(inspector: inspector, graph: graph, classes: classes,
|
41
|
+
configuration: configuration)
|
42
|
+
RailsGraph::Commands::Builders::Packs.enrich(graph: graph) if configuration.include_packwerk?
|
37
43
|
|
38
44
|
graph
|
39
45
|
end
|
40
46
|
|
41
47
|
private
|
42
48
|
|
43
|
-
attr_reader :configuration, :classes, :graph
|
49
|
+
attr_reader :configuration, :classes, :graph, :inspector
|
44
50
|
|
45
51
|
def initialize(configuration:)
|
46
52
|
@configuration = configuration
|
47
53
|
@classes = ActiveRecord::Base.descendants + configuration.include_classes
|
48
54
|
@graph = RailsGraph::Graph::Graph.new
|
55
|
+
@inspector = RailsGraph::Inspector.new
|
49
56
|
end
|
50
57
|
|
51
|
-
def
|
52
|
-
|
53
|
-
|
54
|
-
node = RailsGraph::Graph::Nodes::AbstractModel.new(model)
|
55
|
-
graph.add_node(node)
|
56
|
-
next
|
57
|
-
end
|
58
|
-
|
59
|
-
node = RailsGraph::Graph::Nodes::Model.new(model)
|
60
|
-
graph.add_node(node)
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
def build_column_nodes
|
65
|
-
processed = Hash.new(false)
|
66
|
-
|
67
|
-
classes.each do |model|
|
68
|
-
next if model.attribute_names.empty?
|
69
|
-
|
70
|
-
identifier = RailsGraph::Helpers::Models.identifier(model)
|
71
|
-
node = graph.node(identifier)
|
72
|
-
|
73
|
-
next if processed[node.id]
|
74
|
-
|
75
|
-
processed[node.id] = true
|
76
|
-
|
77
|
-
model.columns.each do |column|
|
78
|
-
column_node = RailsGraph::Graph::Nodes::Column.new(column)
|
79
|
-
graph.add_node(column_node)
|
80
|
-
|
81
|
-
relationship = RailsGraph::Graph::Relationships::Attribute.new(node, column_node)
|
82
|
-
graph.add_relationship(relationship)
|
83
|
-
end
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
def build_inheritance_relationships
|
88
|
-
classes.each do |model|
|
89
|
-
identifier = RailsGraph::Helpers::Models.identifier(model)
|
90
|
-
node = graph.node(identifier)
|
91
|
-
|
92
|
-
superclass_node_identifier = RailsGraph::Helpers::Models.identifier(model.superclass)
|
93
|
-
superclass_node = graph.node(superclass_node_identifier)
|
94
|
-
|
95
|
-
relationship = RailsGraph::Graph::Relationships::Inheritance.new(node, superclass_node)
|
96
|
-
graph.add_relationship(relationship)
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
def build_associations_relationships
|
101
|
-
classes.each do |model|
|
102
|
-
model.reflect_on_all_associations.each do |association|
|
103
|
-
source_node = RailsGraph::Helpers::Associations.source_node(graph, association)
|
104
|
-
target_node = RailsGraph::Helpers::Associations.target_node(graph, association)
|
58
|
+
def setup_generic_nodes
|
59
|
+
polymorphic_node = RailsGraph::Graph::Nodes::VirtualModel.new("PolymorphicModel")
|
60
|
+
graph.add_node(polymorphic_node)
|
105
61
|
|
106
|
-
|
107
|
-
|
108
|
-
end
|
109
|
-
end
|
62
|
+
active_record_base_node = RailsGraph::Graph::Nodes::AbstractModel.new(ActiveRecord::Base)
|
63
|
+
graph.add_node(active_record_base_node)
|
110
64
|
end
|
111
65
|
end
|
112
66
|
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsGraph
|
4
|
+
module Commands
|
5
|
+
module Builders
|
6
|
+
class Associations
|
7
|
+
attr_reader :graph, :inspector
|
8
|
+
|
9
|
+
def self.enrich(...)
|
10
|
+
new(...).enrich
|
11
|
+
end
|
12
|
+
|
13
|
+
def enrich
|
14
|
+
build_associations_relationships
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
attr_reader :classes
|
20
|
+
|
21
|
+
def initialize(inspector:, graph:, classes:)
|
22
|
+
@inspector = inspector
|
23
|
+
@graph = graph
|
24
|
+
@classes = classes
|
25
|
+
end
|
26
|
+
|
27
|
+
def build_associations_relationships
|
28
|
+
classes.each do |model|
|
29
|
+
model.reflect_on_all_associations.each do |association|
|
30
|
+
association.check_validity!
|
31
|
+
|
32
|
+
source_node, target_node = fetch_association_nodes(graph, association)
|
33
|
+
next report_invalid_class(model) if source_node.nil? || target_node.nil?
|
34
|
+
|
35
|
+
add_relationship(association, source_node, target_node)
|
36
|
+
rescue ActiveRecord::ActiveRecordError => _e
|
37
|
+
report_invalid_association(association)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def fetch_association_nodes(graph, association)
|
43
|
+
source_node = RailsGraph::Helpers::Associations.source_node(graph, association)
|
44
|
+
target_node = RailsGraph::Helpers::Associations.target_node(graph, association)
|
45
|
+
[source_node, target_node]
|
46
|
+
end
|
47
|
+
|
48
|
+
def add_relationship(association, source_node, target_node)
|
49
|
+
relationship = RailsGraph::Graph::Relationships::Association.new(association, source_node, target_node)
|
50
|
+
graph.add_relationship(relationship)
|
51
|
+
end
|
52
|
+
|
53
|
+
def report_invalid_association(association)
|
54
|
+
inspector.add_association(association)
|
55
|
+
end
|
56
|
+
|
57
|
+
def report_invalid_class(klass)
|
58
|
+
inspector.add_class(klass)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsGraph
|
4
|
+
module Commands
|
5
|
+
module Builders
|
6
|
+
class Databases
|
7
|
+
def self.enrich(graph:)
|
8
|
+
new(graph: graph).enrich
|
9
|
+
|
10
|
+
graph
|
11
|
+
end
|
12
|
+
|
13
|
+
def enrich
|
14
|
+
build_databases_nodes
|
15
|
+
build_tables_nodes
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
attr_reader :graph
|
21
|
+
|
22
|
+
def initialize(graph:)
|
23
|
+
@graph = graph
|
24
|
+
end
|
25
|
+
|
26
|
+
def build_databases_nodes
|
27
|
+
databases.each do |config|
|
28
|
+
node = RailsGraph::Graph::Nodes::Database.new(config)
|
29
|
+
graph.add_node(node)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def build_tables_nodes
|
34
|
+
ActiveRecord::Base.connection_handler.connection_pools.each do |connection_pool|
|
35
|
+
database_name = connection_pool.db_config.name
|
36
|
+
database_node = graph.node("database_#{database_name}")
|
37
|
+
|
38
|
+
connection_pool.connection.tables.each do |table|
|
39
|
+
table_node = RailsGraph::Graph::Nodes::Table.new(table, database_name)
|
40
|
+
graph.add_node(table_node)
|
41
|
+
|
42
|
+
add_persisted_in_relationship(table_node, database_node)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def add_persisted_in_relationship(table_node, database_node)
|
48
|
+
relationship = RailsGraph::Graph::Relationship.new(
|
49
|
+
source: table_node,
|
50
|
+
target: database_node,
|
51
|
+
label: "PersistedIn",
|
52
|
+
name: "persisted_in",
|
53
|
+
properties: {}
|
54
|
+
)
|
55
|
+
|
56
|
+
graph.add_relationship(relationship)
|
57
|
+
end
|
58
|
+
|
59
|
+
def databases
|
60
|
+
@databases ||= ActiveRecord::Base.configurations.configs_for(env_name: Rails.env)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsGraph
|
4
|
+
module Commands
|
5
|
+
module Builders
|
6
|
+
class Models
|
7
|
+
attr_reader :graph, :inspector
|
8
|
+
|
9
|
+
def self.enrich(...)
|
10
|
+
new(...).enrich
|
11
|
+
end
|
12
|
+
|
13
|
+
def enrich
|
14
|
+
build_model_nodes
|
15
|
+
build_associations
|
16
|
+
build_column_nodes if configuration.columns?
|
17
|
+
build_model_table_relationships if configuration.databases?
|
18
|
+
build_inheritance_relationships if configuration.inheritance?
|
19
|
+
|
20
|
+
inspector.log
|
21
|
+
graph
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
attr_reader :classes, :configuration
|
27
|
+
|
28
|
+
def initialize(inspector:, graph:, classes:, configuration:)
|
29
|
+
@inspector = inspector
|
30
|
+
@graph = graph
|
31
|
+
@configuration = configuration
|
32
|
+
@classes = classes
|
33
|
+
end
|
34
|
+
|
35
|
+
def build_model_nodes
|
36
|
+
classes.each do |model|
|
37
|
+
if model.abstract_class
|
38
|
+
node = RailsGraph::Graph::Nodes::AbstractModel.new(model)
|
39
|
+
graph.add_node(node)
|
40
|
+
next
|
41
|
+
end
|
42
|
+
|
43
|
+
node = RailsGraph::Graph::Nodes::Model.new(model)
|
44
|
+
graph.add_node(node)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def build_associations
|
49
|
+
Builders::Associations.enrich(inspector: inspector, graph: graph, classes: classes)
|
50
|
+
end
|
51
|
+
|
52
|
+
def build_column_nodes
|
53
|
+
processed = Hash.new(false)
|
54
|
+
|
55
|
+
classes.each do |model|
|
56
|
+
next if model.attribute_names.empty?
|
57
|
+
|
58
|
+
identifier = RailsGraph::Helpers::Models.identifier(model)
|
59
|
+
node = graph.node(identifier)
|
60
|
+
|
61
|
+
next if processed[node.id]
|
62
|
+
|
63
|
+
processed[node.id] = true
|
64
|
+
add_column_nodes(model: model, node: node)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def build_inheritance_relationships
|
69
|
+
classes.each do |model|
|
70
|
+
identifier = RailsGraph::Helpers::Models.identifier(model)
|
71
|
+
node = graph.node(identifier)
|
72
|
+
|
73
|
+
superclass_node_identifier = RailsGraph::Helpers::Models.identifier(model.superclass)
|
74
|
+
superclass_node = graph.node(superclass_node_identifier)
|
75
|
+
|
76
|
+
relationship = RailsGraph::Graph::Relationships::Inheritance.new(node, superclass_node)
|
77
|
+
graph.add_relationship(relationship)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def build_model_table_relationships
|
82
|
+
classes.each do |model|
|
83
|
+
database_name = model.connection_pool.db_config.name
|
84
|
+
table_node = graph.node("table_#{database_name}.#{model.table_name}")
|
85
|
+
|
86
|
+
next unless table_node
|
87
|
+
|
88
|
+
identifier = RailsGraph::Helpers::Models.identifier(model)
|
89
|
+
node = graph.node(identifier)
|
90
|
+
|
91
|
+
relationship = build_represents_table_relationship(node, table_node)
|
92
|
+
graph.add_relationship(relationship)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def build_represents_table_relationship(model, table)
|
97
|
+
RailsGraph::Graph::Relationship.new(
|
98
|
+
source: model,
|
99
|
+
target: table,
|
100
|
+
label: "RepresentsTable",
|
101
|
+
name: "represents_table",
|
102
|
+
properties: {}
|
103
|
+
)
|
104
|
+
end
|
105
|
+
|
106
|
+
def add_column_nodes(model:, node:)
|
107
|
+
model.columns.each do |column|
|
108
|
+
column_node = RailsGraph::Graph::Nodes::Column.new(column)
|
109
|
+
graph.add_node(column_node)
|
110
|
+
|
111
|
+
relationship = RailsGraph::Graph::Relationships::Attribute.new(node, column_node)
|
112
|
+
graph.add_relationship(relationship)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsGraph
|
4
|
+
module Commands
|
5
|
+
module Builders
|
6
|
+
class Packs
|
7
|
+
def self.enrich(graph:)
|
8
|
+
new(graph: graph).enrich
|
9
|
+
|
10
|
+
graph
|
11
|
+
end
|
12
|
+
|
13
|
+
def enrich
|
14
|
+
build_pack_nodes
|
15
|
+
build_pack_relationships
|
16
|
+
build_pack_model_relationships
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
attr_reader :graph
|
22
|
+
|
23
|
+
def initialize(graph:)
|
24
|
+
@graph = graph
|
25
|
+
end
|
26
|
+
|
27
|
+
def build_pack_nodes
|
28
|
+
packs.each do |pack|
|
29
|
+
name, owner = fetch_pack_attributes(pack)
|
30
|
+
node = RailsGraph::Graph::Nodes::Pack.new(name, owner)
|
31
|
+
graph.add_node(node)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def build_pack_relationships
|
36
|
+
packs.each do |pack|
|
37
|
+
source_identifier = pack.name
|
38
|
+
source_node = graph.node(source_identifier)
|
39
|
+
|
40
|
+
pack.dependencies.each do |dep|
|
41
|
+
target_identifier = dep.gsub("packs/", "")
|
42
|
+
target_node = graph.node(target_identifier)
|
43
|
+
|
44
|
+
relationship = RailsGraph::Graph::Relationships::PackDependency.new(source_node, target_node)
|
45
|
+
graph.add_relationship(relationship)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def build_pack_model_relationships
|
51
|
+
packs.each do |pack|
|
52
|
+
next if pack.name == "goals"
|
53
|
+
|
54
|
+
models = fetch_pack_models(pack: pack)
|
55
|
+
models.each do |model|
|
56
|
+
add_model_relationship(pack: pack, model: model)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def packs
|
62
|
+
@packs ||=
|
63
|
+
Packwerk::PackageSet.load_all_from("packs").reject do |pack|
|
64
|
+
pack.name == "."
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def fetch_pack_attributes(pack)
|
69
|
+
[
|
70
|
+
pack.name,
|
71
|
+
pack.config["metadata"]["owner"] || pack.config["metadata"]["stewards"]
|
72
|
+
]
|
73
|
+
end
|
74
|
+
|
75
|
+
def fetch_pack_models(pack:)
|
76
|
+
generic_path = "packs/#{pack.name}/app/models"
|
77
|
+
Dir["#{generic_path}/**/*.rb"].map do |path|
|
78
|
+
path
|
79
|
+
.gsub(generic_path, "")
|
80
|
+
.gsub(".rb", "")
|
81
|
+
.prepend(pack.name)
|
82
|
+
.camelize
|
83
|
+
.constantize
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def add_model_relationship(pack:, model:)
|
88
|
+
return unless model.respond_to?(:descends_from_active_record?)
|
89
|
+
|
90
|
+
source_identifier = RailsGraph::Helpers::Models.identifier(model)
|
91
|
+
target_identifier = pack.name
|
92
|
+
|
93
|
+
source_node = graph.node(source_identifier)
|
94
|
+
target_node = graph.node(target_identifier)
|
95
|
+
|
96
|
+
relationship = RailsGraph::Graph::Relationships::PackModel.new(source_node, target_node)
|
97
|
+
graph.add_relationship(relationship)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -3,10 +3,12 @@
|
|
3
3
|
module RailsGraph
|
4
4
|
class Configuration
|
5
5
|
attr_reader :include_classes
|
6
|
-
attr_writer :columns, :inheritance
|
6
|
+
attr_writer :columns, :inheritance, :include_packwerk, :databases
|
7
7
|
|
8
8
|
def initialize
|
9
9
|
@include_classes = []
|
10
|
+
@include_packwerk = false
|
11
|
+
@databases = true
|
10
12
|
@columns = false
|
11
13
|
@inheritance = true
|
12
14
|
end
|
@@ -22,5 +24,13 @@ module RailsGraph
|
|
22
24
|
def inheritance?
|
23
25
|
@inheritance
|
24
26
|
end
|
27
|
+
|
28
|
+
def include_packwerk?
|
29
|
+
@include_packwerk
|
30
|
+
end
|
31
|
+
|
32
|
+
def databases?
|
33
|
+
@databases
|
34
|
+
end
|
25
35
|
end
|
26
36
|
end
|
@@ -11,9 +11,7 @@ module RailsGraph
|
|
11
11
|
|
12
12
|
::Neo4j::Driver::GraphDatabase.driver(host, auth) do |driver|
|
13
13
|
driver.session do |session|
|
14
|
-
queries =
|
15
|
-
queries.shift
|
16
|
-
queries = queries.join("\n")
|
14
|
+
queries = build_query(graph)
|
17
15
|
|
18
16
|
session.write_transaction do |tx|
|
19
17
|
tx.run(queries, message: "Success!")
|
@@ -21,6 +19,12 @@ module RailsGraph
|
|
21
19
|
end
|
22
20
|
end
|
23
21
|
end
|
22
|
+
|
23
|
+
def self.build_query(graph)
|
24
|
+
queries = Cypher.build_queries(graph)
|
25
|
+
queries.shift
|
26
|
+
queries.join("\n")
|
27
|
+
end
|
24
28
|
end
|
25
29
|
end
|
26
30
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../node"
|
4
|
+
|
5
|
+
module RailsGraph
|
6
|
+
module Graph
|
7
|
+
module Nodes
|
8
|
+
class Database < Node
|
9
|
+
attr_reader :configs
|
10
|
+
|
11
|
+
def initialize(configs)
|
12
|
+
@configs = configs
|
13
|
+
|
14
|
+
super(labels: "Database", name: configs.name, properties: build_properties)
|
15
|
+
end
|
16
|
+
|
17
|
+
def identifier
|
18
|
+
"database_#{name}"
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def build_properties
|
24
|
+
{
|
25
|
+
pool_size: configs.pool,
|
26
|
+
adapter: configs.adapter,
|
27
|
+
schema_cache_path: configs.schema_cache_path,
|
28
|
+
replica: configs.replica? || false,
|
29
|
+
database_name: configs.database,
|
30
|
+
reaping_frequency: configs.reaping_frequency,
|
31
|
+
min_threads: configs.min_threads,
|
32
|
+
max_threads: configs.max_threads
|
33
|
+
}
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../node"
|
4
|
+
|
5
|
+
module RailsGraph
|
6
|
+
module Graph
|
7
|
+
module Nodes
|
8
|
+
class Pack < Node
|
9
|
+
attr_reader :pack_owner
|
10
|
+
|
11
|
+
def initialize(name, owner)
|
12
|
+
@pack_owner = owner
|
13
|
+
|
14
|
+
super(labels: "Pack", name: name, properties: build_properties)
|
15
|
+
end
|
16
|
+
|
17
|
+
def identifier
|
18
|
+
name
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def build_properties
|
24
|
+
{ owner: pack_owner }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../node"
|
4
|
+
|
5
|
+
module RailsGraph
|
6
|
+
module Graph
|
7
|
+
module Nodes
|
8
|
+
class Table < Node
|
9
|
+
attr_reader :name, :database_name
|
10
|
+
|
11
|
+
def initialize(name, database_name)
|
12
|
+
@database_name = database_name
|
13
|
+
|
14
|
+
super(labels: "Table", name: name, properties: build_properties)
|
15
|
+
end
|
16
|
+
|
17
|
+
def identifier
|
18
|
+
"table_#{database_name}.#{name}"
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def build_properties
|
24
|
+
{
|
25
|
+
database_name: database_name
|
26
|
+
}
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../relationship"
|
4
|
+
|
5
|
+
module RailsGraph
|
6
|
+
module Graph
|
7
|
+
module Relationships
|
8
|
+
class PackDependency < Relationship
|
9
|
+
def initialize(source, target)
|
10
|
+
super(
|
11
|
+
source: source,
|
12
|
+
target: target,
|
13
|
+
label: "DependsOnPack",
|
14
|
+
name: target.name
|
15
|
+
)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../relationship"
|
4
|
+
|
5
|
+
module RailsGraph
|
6
|
+
module Graph
|
7
|
+
module Relationships
|
8
|
+
class PackModel < Relationship
|
9
|
+
def initialize(source, target)
|
10
|
+
super(
|
11
|
+
source: source,
|
12
|
+
target: target,
|
13
|
+
label: "PartOfPack",
|
14
|
+
name: target.name
|
15
|
+
)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "set"
|
4
|
+
|
5
|
+
module RailsGraph
|
6
|
+
class Inspector
|
7
|
+
def initialize
|
8
|
+
@classes = Set.new
|
9
|
+
@associations = Set.new
|
10
|
+
end
|
11
|
+
|
12
|
+
def add_class(klass)
|
13
|
+
@classes.add(klass)
|
14
|
+
end
|
15
|
+
|
16
|
+
def add_association(association)
|
17
|
+
@associations.add(association)
|
18
|
+
end
|
19
|
+
|
20
|
+
def log
|
21
|
+
@classes.each do |klass|
|
22
|
+
puts "[WARN][RailsGraph]: Invalid class configuration: #{klass}"
|
23
|
+
end
|
24
|
+
|
25
|
+
@associations.each do |association|
|
26
|
+
puts "[WARN][RailsGraph]: #{association.name} association defined " \
|
27
|
+
"under #{association.active_record.name} class has invalid configurations!"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/rails_graph/version.rb
CHANGED
data/lib/rails_graph.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rails_graph
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ahmad Elassuty
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-05-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -85,6 +85,10 @@ files:
|
|
85
85
|
- graph.svg
|
86
86
|
- lib/rails_graph.rb
|
87
87
|
- lib/rails_graph/commands/build_graph.rb
|
88
|
+
- lib/rails_graph/commands/builders/associations.rb
|
89
|
+
- lib/rails_graph/commands/builders/databases.rb
|
90
|
+
- lib/rails_graph/commands/builders/models.rb
|
91
|
+
- lib/rails_graph/commands/builders/packs.rb
|
88
92
|
- lib/rails_graph/commands/export_graph.rb
|
89
93
|
- lib/rails_graph/configuration.rb
|
90
94
|
- lib/rails_graph/error.rb
|
@@ -97,15 +101,21 @@ files:
|
|
97
101
|
- lib/rails_graph/graph/node.rb
|
98
102
|
- lib/rails_graph/graph/nodes/abstract_model.rb
|
99
103
|
- lib/rails_graph/graph/nodes/column.rb
|
104
|
+
- lib/rails_graph/graph/nodes/database.rb
|
100
105
|
- lib/rails_graph/graph/nodes/model.rb
|
106
|
+
- lib/rails_graph/graph/nodes/pack.rb
|
107
|
+
- lib/rails_graph/graph/nodes/table.rb
|
101
108
|
- lib/rails_graph/graph/nodes/virtual_model.rb
|
102
109
|
- lib/rails_graph/graph/relationship.rb
|
103
110
|
- lib/rails_graph/graph/relationships/association.rb
|
104
111
|
- lib/rails_graph/graph/relationships/attribute.rb
|
105
112
|
- lib/rails_graph/graph/relationships/inheritance.rb
|
113
|
+
- lib/rails_graph/graph/relationships/pack_dependency.rb
|
114
|
+
- lib/rails_graph/graph/relationships/pack_model.rb
|
106
115
|
- lib/rails_graph/helpers/associations.rb
|
107
116
|
- lib/rails_graph/helpers/models.rb
|
108
117
|
- lib/rails_graph/helpers/options_parser.rb
|
118
|
+
- lib/rails_graph/inspector.rb
|
109
119
|
- lib/rails_graph/railtie.rb
|
110
120
|
- lib/rails_graph/tasks/rails_graph.rake
|
111
121
|
- lib/rails_graph/version.rb
|