visualize_ruby 0.1.0 → 0.2.1
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.
- checksums.yaml +4 -4
- data/.gitignore +4 -0
- data/README.md +18 -3
- data/lib/visualize_ruby.rb +2 -1
- data/lib/visualize_ruby/builder.rb +64 -0
- data/lib/visualize_ruby/edge.rb +4 -2
- data/lib/visualize_ruby/graph.rb +18 -0
- data/lib/visualize_ruby/graphviz.rb +18 -10
- data/lib/visualize_ruby/node.rb +2 -2
- data/lib/visualize_ruby/parser.rb +22 -127
- data/lib/visualize_ruby/parser/and.rb +22 -0
- data/lib/visualize_ruby/parser/ast_helper.rb +27 -0
- data/lib/visualize_ruby/parser/begin.rb +22 -0
- data/lib/visualize_ruby/parser/false.rb +6 -0
- data/lib/visualize_ruby/parser/if.rb +57 -0
- data/lib/visualize_ruby/parser/or.rb +22 -0
- data/lib/visualize_ruby/parser/send.rb +14 -0
- data/lib/visualize_ruby/parser/str.rb +14 -0
- data/lib/visualize_ruby/parser/true.rb +6 -0
- data/lib/visualize_ruby/parser/type.rb +14 -0
- data/lib/visualize_ruby/version.rb +1 -1
- data/visualize_ruby.gemspec +1 -1
- metadata +14 -11
- data/.idea/.rakeTasks +0 -7
- data/.idea/codeStyles/codeStyleConfig.xml +0 -5
- data/.idea/inspectionProfiles/Project_Default.xml +0 -6
- data/.idea/misc.xml +0 -7
- data/.idea/modules.xml +0 -8
- data/.idea/vcs.xml +0 -6
- data/.idea/visualize_ruby.iml +0 -32
- data/.idea/workspace.xml +0 -656
- data/Gemfile.lock +0 -66
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 284bdd9d3e46c28221780826ae5376b9630aa1d528614a2aa0fc4d577cfabbaa
|
4
|
+
data.tar.gz: a56f98ea0a7b26cdb206594bd9e1b08407b4cc3035452a56bd022ab1b6046ce4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3637961a42bca6ea8f9c017d23c42b3627fbf24575faa5da775d98fb9f0b09006d0f93fce0bae4cf3ca7bad7cdfd5283e4b1e6dc3e6a82e3bc5df3d1afdcc23e
|
7
|
+
data.tar.gz: 0b1660513b29d821a74f402f93aa5383182f50e885777550b577d2e8ca87d8d81a51b6b35ec18c4fc82be01875e7289d3cf1aa2c676f4b31ebf11ca8063729f8
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
# VisualizeRuby
|
2
2
|
|
3
|
-
|
3
|
+
Write a Ruby class and see method interactions. Works with procedural code and bare methods.</span>
|
4
|
+
This is experimental project and does not support all types of code.
|
5
|
+
If you'd like it to support more types of code please pull request.
|
4
6
|
|
5
|
-
|
7
|
+
[Demo](https://visualize-ruby.herokuapp.com/)
|
6
8
|
|
7
9
|
## Installation
|
8
10
|
|
@@ -22,7 +24,20 @@ Or install it yourself as:
|
|
22
24
|
|
23
25
|
## Usage
|
24
26
|
|
25
|
-
|
27
|
+
```ruby
|
28
|
+
require "visualize_ruby"
|
29
|
+
|
30
|
+
ruby_code = <<-RUBY
|
31
|
+
if hungry?
|
32
|
+
eat
|
33
|
+
else
|
34
|
+
work
|
35
|
+
end
|
36
|
+
RUBY
|
37
|
+
|
38
|
+
results = VisualizeRuby::Builder.new(ruby_code: ruby_code).build
|
39
|
+
VisualizeRuby::Graphviz.new(*results).to_graph(png: "example.png")
|
40
|
+
```
|
26
41
|
|
27
42
|
## Development
|
28
43
|
|
data/lib/visualize_ruby.rb
CHANGED
@@ -2,8 +2,9 @@ require "visualize_ruby/version"
|
|
2
2
|
require "visualize_ruby/parser"
|
3
3
|
require "visualize_ruby/node"
|
4
4
|
require "visualize_ruby/edge"
|
5
|
+
require "visualize_ruby/graph"
|
6
|
+
require "visualize_ruby/builder"
|
5
7
|
require "visualize_ruby/graphviz"
|
6
8
|
|
7
9
|
module VisualizeRuby
|
8
|
-
# Your code goes here...
|
9
10
|
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require "dissociated_introspection"
|
2
|
+
|
3
|
+
module VisualizeRuby
|
4
|
+
class Builder
|
5
|
+
|
6
|
+
def initialize(ruby_code:)
|
7
|
+
@ruby_code = ruby_code
|
8
|
+
end
|
9
|
+
|
10
|
+
def build
|
11
|
+
ruby_code = DissociatedIntrospection::RubyCode.build_from_source(@ruby_code)
|
12
|
+
ruby_class = DissociatedIntrospection::RubyClass.new(ruby_code)
|
13
|
+
|
14
|
+
if ruby_class.class?
|
15
|
+
[build_from_class(ruby_class), { label: ruby_class.class_name }]
|
16
|
+
elsif bare_methods?(ruby_code)
|
17
|
+
wrap_bare_methods(ruby_code)
|
18
|
+
else
|
19
|
+
Graph.new(ruby_code: @ruby_code)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def build_from_class(ruby_class)
|
26
|
+
graphs = build_graphs_by_method(ruby_class)
|
27
|
+
|
28
|
+
graphs.each do |graph|
|
29
|
+
graphs.each do |sub_graph|
|
30
|
+
sub_graph.nodes.each do |node|
|
31
|
+
sub_graph.edges << Edge.new(
|
32
|
+
nodes: [node, graph.nodes.first],
|
33
|
+
dir: :none
|
34
|
+
) if node.name == graph.name
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
graphs
|
40
|
+
end
|
41
|
+
|
42
|
+
def build_graphs_by_method(ruby_class)
|
43
|
+
ruby_class.defs.map do |meth|
|
44
|
+
Graph.new(ruby_code: meth.body, name: meth.name)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def bare_methods?(ruby_code)
|
49
|
+
ruby_code.ast.type == :def ||
|
50
|
+
ruby_code.ast.type == :begin && ruby_code.ast.children.map(&:type).uniq == [:def]
|
51
|
+
end
|
52
|
+
|
53
|
+
def wrap_bare_methods(ruby_code)
|
54
|
+
wrapped_ruby_code = <<~Ruby
|
55
|
+
class BareMethodsClass
|
56
|
+
#{ruby_code.source}
|
57
|
+
end
|
58
|
+
Ruby
|
59
|
+
di_ruby_code = DissociatedIntrospection::RubyCode.build_from_source(wrapped_ruby_code)
|
60
|
+
ruby_class = DissociatedIntrospection::RubyClass.new(di_ruby_code)
|
61
|
+
build_from_class(ruby_class)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
data/lib/visualize_ruby/edge.rb
CHANGED
@@ -5,7 +5,7 @@ module VisualizeRuby
|
|
5
5
|
:node_b,
|
6
6
|
:dir
|
7
7
|
def initialize(name: nil, nodes:, dir: :forward)
|
8
|
-
@name = name
|
8
|
+
@name = name.to_s if name
|
9
9
|
@node_a = nodes[0]
|
10
10
|
@node_b = nodes[1]
|
11
11
|
@dir = dir
|
@@ -24,6 +24,8 @@ module VisualizeRuby
|
|
24
24
|
case dir
|
25
25
|
when :forward
|
26
26
|
"->"
|
27
|
+
when :none
|
28
|
+
"-"
|
27
29
|
end
|
28
30
|
end
|
29
31
|
|
@@ -33,4 +35,4 @@ module VisualizeRuby
|
|
33
35
|
|
34
36
|
alias_method :to_s, :inspect
|
35
37
|
end
|
36
|
-
end
|
38
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module VisualizeRuby
|
2
|
+
class Graph
|
3
|
+
attr_reader :name, :nodes, :edges
|
4
|
+
|
5
|
+
def initialize(ruby_code:, name: nil)
|
6
|
+
@name = name.to_s
|
7
|
+
@nodes, @edges = Parser.new(ruby_code).parse
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_hash
|
11
|
+
{
|
12
|
+
name: name,
|
13
|
+
edges: edges.map(&:to_a),
|
14
|
+
nodes: nodes.map(&:to_a),
|
15
|
+
}
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -2,22 +2,30 @@ require "ruby-graphviz"
|
|
2
2
|
|
3
3
|
module VisualizeRuby
|
4
4
|
class Graphviz
|
5
|
-
attr_reader :
|
5
|
+
attr_reader :graphs, :label
|
6
6
|
|
7
|
-
def initialize(
|
8
|
-
@
|
9
|
-
@
|
7
|
+
def initialize(graphs, label: nil)
|
8
|
+
@graphs = [*graphs]
|
9
|
+
@label = label
|
10
10
|
end
|
11
11
|
|
12
12
|
def to_graph(type: :digraph, **output)
|
13
|
-
g
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
13
|
+
g = GraphViz.new(:G, type: type, label: label)
|
14
|
+
nodes = {}
|
15
|
+
sub_graphs = graphs.reverse.map.with_index do |graph, index|
|
16
|
+
sub_graph = g.add_graph("cluster#{index}", **{ label: graph.name }.reject { |_, v| v.nil? })
|
17
|
+
graph.nodes.each do |node|
|
18
|
+
nodes[node.name] = sub_graph.add_node(node.name, shape: node.shape)
|
19
|
+
end
|
20
|
+
[graph, sub_graph]
|
18
21
|
end
|
19
22
|
|
23
|
+
sub_graphs.each do |r_graph, g_graph|
|
24
|
+
r_graph.edges.each do |edge|
|
25
|
+
g_graph.add_edges(nodes[edge.node_a.name], nodes[edge.node_b.name], **{ label: edge.name, dir: edge.dir }.reject { |_, v| v.nil? })
|
26
|
+
end
|
27
|
+
end
|
20
28
|
g.output(output)
|
21
29
|
end
|
22
30
|
end
|
23
|
-
end
|
31
|
+
end
|
data/lib/visualize_ruby/node.rb
CHANGED
@@ -1,4 +1,14 @@
|
|
1
1
|
require "parser/current"
|
2
|
+
require_relative "parser/or"
|
3
|
+
require_relative "parser/and"
|
4
|
+
require_relative "parser/ast_helper"
|
5
|
+
require_relative "parser/begin"
|
6
|
+
require_relative "parser/send"
|
7
|
+
require_relative "parser/str"
|
8
|
+
require_relative "parser/if"
|
9
|
+
require_relative "parser/type"
|
10
|
+
require_relative "parser/true"
|
11
|
+
require_relative "parser/false"
|
2
12
|
|
3
13
|
module VisualizeRuby
|
4
14
|
class Parser
|
@@ -8,136 +18,13 @@ module VisualizeRuby
|
|
8
18
|
@ast = ast
|
9
19
|
end
|
10
20
|
|
21
|
+
# @return [Array<VisualizeRuby::Node>, Array<VisualizeRuby::Edge>]
|
11
22
|
def parse
|
12
|
-
merge *
|
23
|
+
merge *parse_by_type
|
13
24
|
|
14
25
|
return nodes, edges
|
15
26
|
end
|
16
|
-
|
17
|
-
class If
|
18
|
-
def initialize(ast)
|
19
|
-
@ast = ast
|
20
|
-
end
|
21
|
-
|
22
|
-
def parse
|
23
|
-
condition, on_true, on_false = @ast.children.to_a
|
24
|
-
nodes = []
|
25
|
-
edges = []
|
26
|
-
on_false_nodes, on_false_edges = Parser.new(ast: on_false).parse
|
27
|
-
on_false_node = on_false_nodes.first
|
28
|
-
condition_nodes, condition_edges = Parser.new(ast: condition).parse
|
29
|
-
condition_nodes.first.type = :decision
|
30
|
-
nodes.concat(condition_nodes)
|
31
|
-
edges.concat(condition_edges)
|
32
|
-
on_true_node = Node.new(name: AstHelper.new(on_true).description)
|
33
|
-
nodes << on_true_node
|
34
|
-
nodes.concat(on_false_nodes)
|
35
|
-
|
36
|
-
condition_nodes.each do |condition_node|
|
37
|
-
edges << Edge.new(name: "true", nodes: [condition_node, on_true_node])
|
38
|
-
edges << Edge.new(name: "false", nodes: [condition_node, on_false_node])
|
39
|
-
end
|
40
|
-
edges.concat(on_false_edges)
|
41
|
-
return [nodes, edges]
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
class Or
|
46
|
-
def initialize(ast)
|
47
|
-
@ast = ast
|
48
|
-
end
|
49
|
-
|
50
|
-
def parse
|
51
|
-
last_node = nil
|
52
|
-
edges = []
|
53
|
-
nodes = @ast.children.reverse.map do |c|
|
54
|
-
node = Node.new(name: AstHelper.new(c).description, type: :decision)
|
55
|
-
edges << Edge.new(name: "OR", nodes: [node, last_node]) if last_node
|
56
|
-
last_node = node
|
57
|
-
node
|
58
|
-
end.reverse
|
59
|
-
return nodes, edges
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
class And
|
64
|
-
def initialize(ast)
|
65
|
-
@ast = ast
|
66
|
-
end
|
67
|
-
|
68
|
-
def parse
|
69
|
-
last_node = nil
|
70
|
-
edges = []
|
71
|
-
nodes = @ast.children.reverse.map do |c|
|
72
|
-
node = Node.new(name: c.children.last, type: :decision)
|
73
|
-
edges << Edge.new(name: "AND", nodes: [node, last_node]) if last_node
|
74
|
-
last_node = node
|
75
|
-
node
|
76
|
-
end.reverse
|
77
|
-
return nodes, edges
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
class AstHelper
|
82
|
-
def initialize(ast)
|
83
|
-
@ast = ast
|
84
|
-
end
|
85
|
-
|
86
|
-
def description(ast: @ast)
|
87
|
-
case ast
|
88
|
-
when Symbol, String
|
89
|
-
ast
|
90
|
-
when NilClass
|
91
|
-
nil
|
92
|
-
else
|
93
|
-
ast.children.flat_map do |c|
|
94
|
-
description(ast: c)
|
95
|
-
end.reject do |c|
|
96
|
-
c.nil? || c == :""
|
97
|
-
end.join(" ")
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
end
|
102
|
-
|
103
|
-
class Begin
|
104
|
-
def initialize(ast)
|
105
|
-
@ast = ast
|
106
|
-
end
|
107
|
-
|
108
|
-
def parse
|
109
|
-
edges = []
|
110
|
-
last_node = nil
|
111
|
-
nodes = @ast.children.to_a.compact.reverse.map do |a|
|
112
|
-
node = Node.new(name: AstHelper.new(a).description, type: :action)
|
113
|
-
edges << Edge.new(nodes: [node, last_node]) if last_node
|
114
|
-
last_node = node
|
115
|
-
end
|
116
|
-
|
117
|
-
return nodes.reverse, edges.reverse
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
class Send
|
122
|
-
def initialize(ast)
|
123
|
-
@ast = ast
|
124
|
-
end
|
125
|
-
|
126
|
-
def parse
|
127
|
-
return [Node.new(name: AstHelper.new(@ast).description, type: :action)], []
|
128
|
-
end
|
129
|
-
end
|
130
|
-
|
131
|
-
class Str
|
132
|
-
def initialize(ast)
|
133
|
-
@ast = ast
|
134
|
-
end
|
135
|
-
|
136
|
-
def parse
|
137
|
-
return [Node.new(name: AstHelper.new(@ast).description, type: :action)], []
|
138
|
-
end
|
139
|
-
end
|
140
|
-
|
27
|
+
|
141
28
|
def nodes
|
142
29
|
@nodes ||= []
|
143
30
|
end
|
@@ -146,9 +33,17 @@ module VisualizeRuby
|
|
146
33
|
@edges ||= []
|
147
34
|
end
|
148
35
|
|
36
|
+
private
|
37
|
+
|
38
|
+
def parse_by_type
|
39
|
+
Parser.const_get(ast.type.to_s.capitalize).new(ast).parse
|
40
|
+
rescue NameError
|
41
|
+
Str.new(ast).parse
|
42
|
+
end
|
43
|
+
|
149
44
|
def merge(nodes, edges)
|
150
45
|
self.nodes.concat(nodes)
|
151
46
|
self.edges.concat(edges)
|
152
47
|
end
|
153
48
|
end
|
154
|
-
end
|
49
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module VisualizeRuby
|
2
|
+
class Parser
|
3
|
+
class And
|
4
|
+
def initialize(ast)
|
5
|
+
@ast = ast
|
6
|
+
end
|
7
|
+
|
8
|
+
# @return [Array<VisualizeRuby::Node>, Array<VisualizeRuby::Edge>]
|
9
|
+
def parse
|
10
|
+
last_node = nil
|
11
|
+
edges = []
|
12
|
+
nodes = @ast.children.reverse.map do |c|
|
13
|
+
node = Node.new(name: c.children.last, type: :decision)
|
14
|
+
edges << Edge.new(name: "AND", nodes: [node, last_node]) if last_node
|
15
|
+
last_node = node
|
16
|
+
node
|
17
|
+
end.reverse
|
18
|
+
return nodes, edges
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module VisualizeRuby
|
2
|
+
class Parser
|
3
|
+
class AstHelper
|
4
|
+
def initialize(ast)
|
5
|
+
@ast = ast
|
6
|
+
end
|
7
|
+
|
8
|
+
def description(ast: @ast)
|
9
|
+
case ast
|
10
|
+
when Symbol, String
|
11
|
+
ast
|
12
|
+
when NilClass
|
13
|
+
nil
|
14
|
+
|
15
|
+
else
|
16
|
+
ast.children.flat_map do |c|
|
17
|
+
description(ast: c)
|
18
|
+
end.reject do |c|
|
19
|
+
c.nil? || c == :""
|
20
|
+
end.join(" ")
|
21
|
+
end
|
22
|
+
rescue NoMethodError
|
23
|
+
ast
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|