code_node 0.1.2 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- data/cog/templates/code_node/generator.rb.erb +2 -2
- data/lib/code_node.rb +10 -46
- data/lib/code_node/graph_builder.rb +102 -0
- data/lib/code_node/ir/graph.rb +2 -182
- data/lib/code_node/ir/graph/builder_methods.rb +113 -0
- data/lib/code_node/ir/graph/template_methods.rb +97 -0
- data/lib/code_node/ir/node.rb +18 -212
- data/lib/code_node/ir/node/builder_methods.rb +83 -0
- data/lib/code_node/ir/node/query_methods.rb +55 -0
- data/lib/code_node/ir/node/template_methods.rb +73 -0
- data/lib/code_node/sexp_walker.rb +35 -26
- data/lib/code_node/spec_helpers/dot_file.rb +10 -12
- data/lib/code_node/version.rb +1 -1
- metadata +10 -4
@@ -1,5 +1,5 @@
|
|
1
|
-
<% if
|
2
|
-
<% else %>require '<%=
|
1
|
+
<% if Cog.active_tool.explicit_require %>require '<%= Cog.active_tool.path %>'
|
2
|
+
<% else %>require '<%= Cog.active_tool.name %>'
|
3
3
|
<% end %>
|
4
4
|
|
5
5
|
# Makes a graph of the project_source_path.
|
data/lib/code_node.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
$LOAD_PATH << File.join(File.dirname(__FILE__))
|
2
2
|
require 'code_node/version'
|
3
3
|
require 'code_node/dsl'
|
4
|
+
require 'code_node/graph_builder'
|
4
5
|
require 'code_node/ir'
|
5
6
|
require 'code_node/sexp_walker'
|
6
7
|
require 'cog'
|
@@ -13,57 +14,20 @@ module CodeNode
|
|
13
14
|
|
14
15
|
# @param graph_name [String] name of the dot file to generate (without +.dot+ extension)
|
15
16
|
# @option opt [Symbol] :ruby_version (:ruby19) either <tt>:ruby18</tt> or <tt>:ruby19</tt>, indicating which parser to use
|
16
|
-
# @
|
17
|
+
# @yieldparam graph [DSL::GraphDefiner] define rules for creating the graph
|
17
18
|
def self.graph(graph_name, opt={}, &block)
|
18
|
-
|
19
|
-
root = Cog.project_source_path
|
20
|
-
@graph = IR::Graph.new
|
21
|
-
graph_definer = DSL::GraphDefiner.new @graph
|
22
|
-
block.call graph_definer
|
23
|
-
|
24
|
-
rp = case opt[:ruby_version]
|
25
|
-
when :ruby18
|
19
|
+
parser = if opt[:ruby_version] == :ruby18
|
26
20
|
Ruby18Parser.new
|
27
21
|
else
|
28
22
|
Ruby19Parser.new
|
29
23
|
end
|
30
24
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
rescue Racc::ParseError
|
38
|
-
STDERR.write "#{filename.relative_to_project_root}, skipped...\n".color(:yellow)
|
39
|
-
nil
|
40
|
-
end
|
41
|
-
if sexp[i]
|
42
|
-
walker = SexpWalker.new @graph, sexp[i], :mode => mode
|
43
|
-
walker.walk
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
# Apply styles before pruning because some relations may be destroyed while pruning
|
49
|
-
puts "Applying styles".color(feedback_color)
|
50
|
-
@graph.apply_styles
|
51
|
-
|
52
|
-
# Prune the graph according to ignore rules.
|
53
|
-
# We keep pruning until there are no more changes because some rules don't apply the first time (for example: &:island?)
|
54
|
-
puts "Pruning nodes".color(feedback_color)
|
55
|
-
i = 1
|
56
|
-
while (x = @graph.prune) > 0
|
57
|
-
puts " #{x} nodes pruned on #{i.ordinalize} pass".color(feedback_color)
|
58
|
-
i += 1
|
59
|
-
end
|
60
|
-
|
61
|
-
# Activate code_node while rendering templates
|
62
|
-
# so that cog will be able to find code_node templates
|
63
|
-
Cog.activate_tool 'code_node' do
|
64
|
-
stamp 'code_node/graph.dot', "#{graph_name}.dot"
|
65
|
-
end
|
66
|
-
|
67
|
-
nil
|
25
|
+
GraphBuilder.new(graph_name, parser).
|
26
|
+
define(&block).
|
27
|
+
find_nodes.
|
28
|
+
find_relations.
|
29
|
+
finalize.
|
30
|
+
render
|
68
31
|
end
|
32
|
+
|
69
33
|
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'cog'
|
2
|
+
require 'code_node/ir/graph'
|
3
|
+
|
4
|
+
module CodeNode
|
5
|
+
|
6
|
+
# Helps to build an {IR::Graph}
|
7
|
+
class GraphBuilder
|
8
|
+
|
9
|
+
# @return [IR::Graph] the graph being built
|
10
|
+
attr_reader :graph
|
11
|
+
|
12
|
+
# @param name [String] the name of the graph
|
13
|
+
# @param parser [Ruby18Parser, Ruby19Parser] a ruby parser instance
|
14
|
+
def initialize(name, parser)
|
15
|
+
@name = name
|
16
|
+
@graph = IR::Graph.new
|
17
|
+
@parser = parser
|
18
|
+
@sexp = [] # array of file sexp, one per file
|
19
|
+
end
|
20
|
+
|
21
|
+
# @return [Array<String>] paths to ruby source files
|
22
|
+
def sources
|
23
|
+
Dir.glob("#{Cog.project_source_path}/**/*.rb")
|
24
|
+
end
|
25
|
+
|
26
|
+
# Define the custom graph generation rules
|
27
|
+
# @return [self]
|
28
|
+
def define(&block)
|
29
|
+
block.call DSL::GraphDefiner.new(@graph)
|
30
|
+
self
|
31
|
+
end
|
32
|
+
|
33
|
+
# Search the sources for nodes
|
34
|
+
# @return [self]
|
35
|
+
def find_nodes
|
36
|
+
puts '1st pass: find nodes'
|
37
|
+
find :nodes
|
38
|
+
self
|
39
|
+
end
|
40
|
+
|
41
|
+
# Search the sources for relations
|
42
|
+
# @return [nil]
|
43
|
+
def find_relations
|
44
|
+
puts '2nd pass: find relations'
|
45
|
+
find :relations
|
46
|
+
self
|
47
|
+
end
|
48
|
+
|
49
|
+
# Apply styles and prune the graph
|
50
|
+
# @return [nil]
|
51
|
+
def finalize
|
52
|
+
# Apply styles before pruning because some relations may be destroyed while pruning
|
53
|
+
puts "Applying styles"
|
54
|
+
@graph.apply_styles
|
55
|
+
|
56
|
+
# Prune the graph according to ignore rules.
|
57
|
+
# We keep pruning until there are no more changes because some rules don't apply the first time (for example: &:island?)
|
58
|
+
puts "Pruning nodes"
|
59
|
+
i = 1
|
60
|
+
while (x = @graph.prune) > 0
|
61
|
+
puts " #{x} nodes pruned on #{i.ordinalize} pass"
|
62
|
+
i += 1
|
63
|
+
end
|
64
|
+
self
|
65
|
+
end
|
66
|
+
|
67
|
+
# Render the graph
|
68
|
+
# @return [nil]
|
69
|
+
def render
|
70
|
+
# Activate code_node while rendering templates
|
71
|
+
# so that cog will be able to find code_node templates
|
72
|
+
Cog.activate_tool 'code_node' do
|
73
|
+
stamp 'code_node/graph.dot', "#{@name}.dot"
|
74
|
+
end
|
75
|
+
nil
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
# @param type [Symbol] one of <tt>:nodes</tt> or <tt>:relations</tt>
|
81
|
+
# @return [nil]
|
82
|
+
def find(type)
|
83
|
+
sources.each_with_index do |filename, i|
|
84
|
+
@sexp[i] ||= parse filename
|
85
|
+
if @sexp[i]
|
86
|
+
walker = SexpWalker.new @graph, @sexp[i], :mode => "find_#{type}".to_sym
|
87
|
+
walker.walk
|
88
|
+
end
|
89
|
+
end
|
90
|
+
nil
|
91
|
+
end
|
92
|
+
|
93
|
+
# @param filename [String] path to the file to parse
|
94
|
+
# @return [Sexp]
|
95
|
+
def parse(filename)
|
96
|
+
@parser.parse(File.read filename)
|
97
|
+
rescue Racc::ParseError
|
98
|
+
STDERR.write "#{filename.relative_to_project_root}, skipped...\n".color(:yellow)
|
99
|
+
nil
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
data/lib/code_node/ir/graph.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
require 'code_node/ir/node'
|
2
|
+
require 'code_node/ir/graph/builder_methods'
|
3
|
+
require 'code_node/ir/graph/template_methods'
|
2
4
|
|
3
5
|
module CodeNode
|
4
6
|
module IR
|
@@ -6,188 +8,6 @@ module CodeNode
|
|
6
8
|
# A collection of {Node}s
|
7
9
|
class Graph
|
8
10
|
|
9
|
-
# {Graph} methods which are useful in templates
|
10
|
-
module TemplateMethods
|
11
|
-
|
12
|
-
# Iterate through each {Node} with {Node::QueryMethods#class?} in the graph
|
13
|
-
# @yieldparam node [Node::TemplateMethods] a class node. Does not yield ignored nodes.
|
14
|
-
# @return [nil]
|
15
|
-
def each_class(&block)
|
16
|
-
@nodes.values.select do |node|
|
17
|
-
node.class?
|
18
|
-
end.sort.each &block
|
19
|
-
end
|
20
|
-
|
21
|
-
# Iterate through each {Node} with {Node::QueryMethods#module?} in the graph
|
22
|
-
# @yieldparam node [Node::TemplateMethods] a module node. Does not yield ignored nodes.
|
23
|
-
# @return [nil]
|
24
|
-
def each_module(&block)
|
25
|
-
@nodes.values.select do |node|
|
26
|
-
node.module?
|
27
|
-
end.sort.each &block
|
28
|
-
end
|
29
|
-
|
30
|
-
# Iterate through each containment relation in the graph
|
31
|
-
# @example
|
32
|
-
# # a -> b (A contains B)
|
33
|
-
# module A
|
34
|
-
# module B
|
35
|
-
# end
|
36
|
-
# end
|
37
|
-
# @yieldparam a [Node::TemplateMethods] the container node
|
38
|
-
# @yieldparam b [Node::TemplateMethods] the contained node
|
39
|
-
# @return [nil]
|
40
|
-
def each_containment(&block)
|
41
|
-
@nodes.values.sort.each do |node|
|
42
|
-
if node.parent
|
43
|
-
block.call node.parent, node
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
# Iterate through each inheritance relation in the graph
|
49
|
-
# @example
|
50
|
-
# # a -> b (A inherits from B)
|
51
|
-
# class A < B
|
52
|
-
# end
|
53
|
-
# @yieldparam a [Node::TemplateMethods] the derived class node
|
54
|
-
# @yieldparam b [Node::TemplateMethods] the super class node
|
55
|
-
# @return [nil]
|
56
|
-
def each_inheritance(&block)
|
57
|
-
@nodes.values.sort.each do |node|
|
58
|
-
if node.super_class_node
|
59
|
-
block.call node, node.super_class_node
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
# Iterate through each inclusion relation in the graph
|
65
|
-
# @example
|
66
|
-
# # a -> b (A includes B)
|
67
|
-
# module A
|
68
|
-
# include B
|
69
|
-
# end
|
70
|
-
# @yieldparam a [Node::TemplateMethods] the which includes
|
71
|
-
# @yieldparam b [Node::TemplateMethods] the included node
|
72
|
-
# @return [nil]
|
73
|
-
def each_inclusion(&block)
|
74
|
-
@nodes.values.sort.each do |node|
|
75
|
-
node.inclusions.each do |other|
|
76
|
-
block.call node, other
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
# Iterate through each extension relation in the graph
|
82
|
-
# @example
|
83
|
-
# # a -> b (A extends B)
|
84
|
-
# module A
|
85
|
-
# extend B
|
86
|
-
# end
|
87
|
-
# @yieldparam a [Node::TemplateMethods] the which extends
|
88
|
-
# @yieldparam b [Node::TemplateMethods] the extended node
|
89
|
-
# @return [nil]
|
90
|
-
def each_extension(&block)
|
91
|
-
@nodes.values.sort.each do |node|
|
92
|
-
node.extensions.each do |other|
|
93
|
-
block.call node, other
|
94
|
-
end
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
|
-
end
|
99
|
-
|
100
|
-
# {Graph} methods used during the graph building phase
|
101
|
-
# @api developer
|
102
|
-
module BuilderMethods
|
103
|
-
|
104
|
-
attr_reader :scope
|
105
|
-
|
106
|
-
def apply_styles
|
107
|
-
@nodes.each_value do |node|
|
108
|
-
@style_matchers.each do |pair|
|
109
|
-
if pair[0].matches? node
|
110
|
-
node.style.update pair[1]
|
111
|
-
end
|
112
|
-
end
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
|
-
# @return [FixNum] were any more nodes pruned?
|
117
|
-
def prune
|
118
|
-
prunees = []
|
119
|
-
@nodes.each_value do |node|
|
120
|
-
if @exclude_matchers.any? {|m| m.matches? node}
|
121
|
-
prunees << node
|
122
|
-
end
|
123
|
-
end
|
124
|
-
prunees.each do |node|
|
125
|
-
puts " #{node.path}"
|
126
|
-
node.prune
|
127
|
-
@nodes.delete node.path
|
128
|
-
end
|
129
|
-
prunees.length
|
130
|
-
end
|
131
|
-
|
132
|
-
# Find a node or create it and add it to the graph
|
133
|
-
# @api developer
|
134
|
-
# @param node_type [Symbol] either <tt>:module</tt> or <tt>:class</tt>
|
135
|
-
# @param s [Symbol, Sexp] either flat name, or a Sexp representing a color (<tt>:</tt>) separated path.
|
136
|
-
# @yieldparam node [Node]
|
137
|
-
# @return [Node]
|
138
|
-
def node_for(node_type, s, opt={}, &block)
|
139
|
-
name = if s.is_a? Symbol
|
140
|
-
s
|
141
|
-
elsif s[0] == :const
|
142
|
-
s[1]
|
143
|
-
elsif s[0] == :colon2
|
144
|
-
x = []
|
145
|
-
while s[0] == :colon2
|
146
|
-
x << s[2] ; s = s[1]
|
147
|
-
end
|
148
|
-
x << s[1]
|
149
|
-
x.reverse
|
150
|
-
elsif s[0] == :self
|
151
|
-
@scope.last.mark_as_singleton
|
152
|
-
nil
|
153
|
-
end
|
154
|
-
return if name.nil?
|
155
|
-
|
156
|
-
node = if opt[:not_sure_if_nested]
|
157
|
-
candidate = if name.is_a?(Array)
|
158
|
-
parts = name.dup
|
159
|
-
n = !@scope.empty? && @scope.last.find(parts.shift)
|
160
|
-
while n && !parts.empty?
|
161
|
-
n = n.find parts.shift
|
162
|
-
end
|
163
|
-
n
|
164
|
-
else
|
165
|
-
!@scope.empty? && @scope.last.find(name)
|
166
|
-
end
|
167
|
-
candidate || Node.new(name, :node_type => node_type)
|
168
|
-
else
|
169
|
-
Node.new name, :parent => @scope.last, :node_type => node_type
|
170
|
-
end
|
171
|
-
|
172
|
-
node = self << node
|
173
|
-
unless block.nil? || node.nil?
|
174
|
-
@scope << node
|
175
|
-
block.call node
|
176
|
-
@scope.pop
|
177
|
-
end
|
178
|
-
node
|
179
|
-
end
|
180
|
-
|
181
|
-
# Add the given node to the graph and return it. If a node with the same path is already in the graph, do not add it again, and return the original node.
|
182
|
-
# @param node [Node] a node to add to the graph
|
183
|
-
# @return [Node] the newly added node, or another node with the same path which was already in the graph
|
184
|
-
def <<(node)
|
185
|
-
@nodes[node.path] ||= node
|
186
|
-
@nodes[node.path]
|
187
|
-
end
|
188
|
-
|
189
|
-
end
|
190
|
-
|
191
11
|
include BuilderMethods
|
192
12
|
include TemplateMethods
|
193
13
|
|
@@ -0,0 +1,113 @@
|
|
1
|
+
module CodeNode
|
2
|
+
module IR
|
3
|
+
class Graph
|
4
|
+
|
5
|
+
# {Graph} methods used during the graph building phase
|
6
|
+
# @api developer
|
7
|
+
module BuilderMethods
|
8
|
+
|
9
|
+
attr_reader :scope
|
10
|
+
|
11
|
+
def apply_styles
|
12
|
+
@nodes.each_value do |node|
|
13
|
+
@style_matchers.each do |pair|
|
14
|
+
if pair[0].matches? node
|
15
|
+
node.style.update pair[1]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# @return [FixNum] were any more nodes pruned?
|
22
|
+
def prune
|
23
|
+
prunees = []
|
24
|
+
@nodes.each_value do |node|
|
25
|
+
if @exclude_matchers.any? {|m| m.matches? node}
|
26
|
+
prunees << node
|
27
|
+
end
|
28
|
+
end
|
29
|
+
prunees.each do |node|
|
30
|
+
puts " #{node.path}"
|
31
|
+
node.prune
|
32
|
+
@nodes.delete node.path
|
33
|
+
end
|
34
|
+
prunees.length
|
35
|
+
end
|
36
|
+
|
37
|
+
# Find a node or create it and add it to the graph
|
38
|
+
# @api developer
|
39
|
+
# @param node_type [Symbol] either <tt>:module</tt> or <tt>:class</tt>
|
40
|
+
# @param s [Symbol, Sexp] either flat name, or a Sexp representing a color (<tt>:</tt>) separated path.
|
41
|
+
# @option opt [Boolean] :not_sure_if_nested (false)
|
42
|
+
# @yieldparam node [Node]
|
43
|
+
# @return [Node]
|
44
|
+
def node_for(node_type, s, opt={}, &block)
|
45
|
+
name = determine_name s
|
46
|
+
return if name.nil?
|
47
|
+
|
48
|
+
node = if opt[:not_sure_if_nested]
|
49
|
+
search_for_node_in_parent(@scope.last, name) ||
|
50
|
+
Node.new(name, :node_type => node_type)
|
51
|
+
else
|
52
|
+
Node.new name, :parent => @scope.last, :node_type => node_type
|
53
|
+
end
|
54
|
+
|
55
|
+
node = add_or_find_duplicate node
|
56
|
+
unless block.nil?
|
57
|
+
@scope << node
|
58
|
+
block.call node
|
59
|
+
@scope.pop
|
60
|
+
end
|
61
|
+
node
|
62
|
+
end
|
63
|
+
|
64
|
+
# Add the given node to the graph and return it. If a node with the same path is already in the graph, do not add it again, and return the original node.
|
65
|
+
# @param node [Node] a node to add to the graph
|
66
|
+
# @return [Node] the newly added node, or another node with the same path which was already in the graph
|
67
|
+
def add_or_find_duplicate(node)
|
68
|
+
@nodes[node.path] ||= node
|
69
|
+
@nodes[node.path]
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
# @param s [Sexp]
|
75
|
+
# @return [Symbol, Array<Symbol>, nil]
|
76
|
+
def determine_name(s)
|
77
|
+
name = if s.is_a? Symbol
|
78
|
+
s
|
79
|
+
elsif s[0] == :const
|
80
|
+
s[1]
|
81
|
+
elsif s[0] == :colon2
|
82
|
+
x = []
|
83
|
+
while s[0] == :colon2
|
84
|
+
x << s[2] ; s = s[1]
|
85
|
+
end
|
86
|
+
x << s[1]
|
87
|
+
x.reverse
|
88
|
+
elsif s[0] == :self
|
89
|
+
@scope.last.mark_as_singleton
|
90
|
+
nil
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# @param parent [Node, nil] the parent to search in
|
95
|
+
# @param name [Array<Symbol>, Symbol] name of the node to search for
|
96
|
+
# @return [Node, nil] the node, if found in the parent
|
97
|
+
def search_for_node_in_parent(parent, name)
|
98
|
+
if name.is_a?(Array)
|
99
|
+
parts = name.dup
|
100
|
+
n = parent && parent.find(parts.shift)
|
101
|
+
while n && !parts.empty?
|
102
|
+
n = n.find parts.shift
|
103
|
+
end
|
104
|
+
n
|
105
|
+
else
|
106
|
+
parent && parent.find(name)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
module CodeNode
|
2
|
+
module IR
|
3
|
+
class Graph
|
4
|
+
|
5
|
+
# {Graph} methods which are useful in templates
|
6
|
+
module TemplateMethods
|
7
|
+
|
8
|
+
# Iterate through each {Node} with {Node::QueryMethods#class?} in the graph
|
9
|
+
# @yieldparam node [Node::TemplateMethods] a class node. Does not yield ignored nodes.
|
10
|
+
# @return [nil]
|
11
|
+
def each_class(&block)
|
12
|
+
@nodes.values.select do |node|
|
13
|
+
node.class?
|
14
|
+
end.sort.each &block
|
15
|
+
end
|
16
|
+
|
17
|
+
# Iterate through each {Node} with {Node::QueryMethods#module?} in the graph
|
18
|
+
# @yieldparam node [Node::TemplateMethods] a module node. Does not yield ignored nodes.
|
19
|
+
# @return [nil]
|
20
|
+
def each_module(&block)
|
21
|
+
@nodes.values.select do |node|
|
22
|
+
node.module?
|
23
|
+
end.sort.each &block
|
24
|
+
end
|
25
|
+
|
26
|
+
# Iterate through each containment relation in the graph
|
27
|
+
# @example
|
28
|
+
# # a -> b (A contains B)
|
29
|
+
# module A
|
30
|
+
# module B
|
31
|
+
# end
|
32
|
+
# end
|
33
|
+
# @yieldparam a [Node::TemplateMethods] the container node
|
34
|
+
# @yieldparam b [Node::TemplateMethods] the contained node
|
35
|
+
# @return [nil]
|
36
|
+
def each_containment(&block)
|
37
|
+
@nodes.values.sort.each do |node|
|
38
|
+
if node.parent
|
39
|
+
block.call node.parent, node
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Iterate through each inheritance relation in the graph
|
45
|
+
# @example
|
46
|
+
# # a -> b (A inherits from B)
|
47
|
+
# class A < B
|
48
|
+
# end
|
49
|
+
# @yieldparam a [Node::TemplateMethods] the derived class node
|
50
|
+
# @yieldparam b [Node::TemplateMethods] the super class node
|
51
|
+
# @return [nil]
|
52
|
+
def each_inheritance(&block)
|
53
|
+
@nodes.values.sort.each do |node|
|
54
|
+
if node.super_class_node
|
55
|
+
block.call node, node.super_class_node
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Iterate through each inclusion relation in the graph
|
61
|
+
# @example
|
62
|
+
# # a -> b (A includes B)
|
63
|
+
# module A
|
64
|
+
# include B
|
65
|
+
# end
|
66
|
+
# @yieldparam a [Node::TemplateMethods] the which includes
|
67
|
+
# @yieldparam b [Node::TemplateMethods] the included node
|
68
|
+
# @return [nil]
|
69
|
+
def each_inclusion(&block)
|
70
|
+
@nodes.values.sort.each do |node|
|
71
|
+
node.inclusions.each do |other|
|
72
|
+
block.call node, other
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Iterate through each extension relation in the graph
|
78
|
+
# @example
|
79
|
+
# # a -> b (A extends B)
|
80
|
+
# module A
|
81
|
+
# extend B
|
82
|
+
# end
|
83
|
+
# @yieldparam a [Node::TemplateMethods] the which extends
|
84
|
+
# @yieldparam b [Node::TemplateMethods] the extended node
|
85
|
+
# @return [nil]
|
86
|
+
def each_extension(&block)
|
87
|
+
@nodes.values.sort.each do |node|
|
88
|
+
node.extensions.each do |other|
|
89
|
+
block.call node, other
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
data/lib/code_node/ir/node.rb
CHANGED
@@ -1,4 +1,7 @@
|
|
1
1
|
require 'cog'
|
2
|
+
require 'code_node/ir/node/builder_methods'
|
3
|
+
require 'code_node/ir/node/template_methods'
|
4
|
+
require 'code_node/ir/node/query_methods'
|
2
5
|
|
3
6
|
module CodeNode
|
4
7
|
module IR
|
@@ -8,210 +11,6 @@ module CodeNode
|
|
8
11
|
class Node
|
9
12
|
|
10
13
|
include Cog::Generator
|
11
|
-
|
12
|
-
# Node methods which are useful in templates
|
13
|
-
module TemplateMethods
|
14
|
-
|
15
|
-
# @return [String] the name of the node. Not necessarilly unique.
|
16
|
-
# @see {#path}
|
17
|
-
attr_reader :name
|
18
|
-
|
19
|
-
# @return [Node] node which contains this node
|
20
|
-
# @example
|
21
|
-
# module Foo # Foo is the parent
|
22
|
-
# module Bar # to Bar
|
23
|
-
# end
|
24
|
-
# end
|
25
|
-
attr_reader :parent
|
26
|
-
|
27
|
-
# The child nodes of this node
|
28
|
-
# @return [Hash<String,Node>] a mapping from node {#path} names to nodes
|
29
|
-
# @example
|
30
|
-
# module Foo # Foo has children
|
31
|
-
# module Bar # Bar
|
32
|
-
# end # and
|
33
|
-
# class Car # Car
|
34
|
-
# end
|
35
|
-
# end
|
36
|
-
attr_reader :children
|
37
|
-
|
38
|
-
# @return [Node,nil] the super class of this class. Will be +nil+ for modules.
|
39
|
-
def super_class_node
|
40
|
-
@inherits_from
|
41
|
-
end
|
42
|
-
|
43
|
-
# @return [Array<Node>] module nodes for which this node has an +include+ statement
|
44
|
-
def inclusions
|
45
|
-
@includes.values.sort
|
46
|
-
end
|
47
|
-
|
48
|
-
# @return [Array<Node>] module nodes for which this node has an +extend+ statement
|
49
|
-
def extensions
|
50
|
-
@extends.values.sort
|
51
|
-
end
|
52
|
-
|
53
|
-
# @return [String] fully qualified identifier for the node in the form <tt>Foo_Bar_Car</tt>. Ideal for graphviz identifiers.
|
54
|
-
def key
|
55
|
-
@path.join '_'
|
56
|
-
end
|
57
|
-
|
58
|
-
# @return [String] how the node will be labelled in the graph. Nodes without parents display their full {#path}, while nodes with parents only display their {#name}.
|
59
|
-
def label
|
60
|
-
@parent.nil? ? path : name
|
61
|
-
end
|
62
|
-
|
63
|
-
# Stamp the accumulated GraphViz styles in a format suitable for inclusion in a <tt>.dot</tt> file
|
64
|
-
# @return [String] style in the form <tt>key1="value1" key2="value2"</tt>...
|
65
|
-
def stamp_styles
|
66
|
-
x = []
|
67
|
-
style.each_pair do |key, value|
|
68
|
-
x << "#{key}=\"#{value}\""
|
69
|
-
end
|
70
|
-
x.join ' '
|
71
|
-
end
|
72
|
-
|
73
|
-
end
|
74
|
-
|
75
|
-
# {Node} methods which are useful for querying in matchers
|
76
|
-
module QueryMethods
|
77
|
-
|
78
|
-
# @return [Boolean] does this node represent a module?
|
79
|
-
def module?
|
80
|
-
@node_type == :module
|
81
|
-
end
|
82
|
-
|
83
|
-
# @return [Boolean] does this node represent a class?
|
84
|
-
def class?
|
85
|
-
@node_type == :class
|
86
|
-
end
|
87
|
-
|
88
|
-
# @return [String] fully qualified name of the node in the form <tt>'Foo::Bar::Car'</tt>. Not good as a graphviz identifier because of the colon (<tt>:</tt>) characters. Use {TemplateMethods#key} for graphviz identifiers instead.
|
89
|
-
def path
|
90
|
-
@path.join '::'
|
91
|
-
end
|
92
|
-
|
93
|
-
# @param path [String] a class or module path in the form <tt>Foo::Bar::Car</tt>
|
94
|
-
# @return [Boolean] does this node include a {#module?} node with the given {#path}?
|
95
|
-
def includes?(path)
|
96
|
-
@includes.member? path
|
97
|
-
end
|
98
|
-
|
99
|
-
# @param path [String] a class or module path in the form <tt>Foo::Bar::Car</tt>
|
100
|
-
# @return [Boolean] does this node extend a {#module?} node with the given {#path}?
|
101
|
-
def extends?(path)
|
102
|
-
@extends.member? path
|
103
|
-
end
|
104
|
-
|
105
|
-
# @param path [String] a class or module path in the form <tt>Foo::Bar::Car</tt>
|
106
|
-
# @return [Boolean] does this node inherit from (directly or indirectly) a {#class?} node with the given {#path}? Note that a node inherits from itself according to this method. Recursively checks the ancestry of the node.
|
107
|
-
def inherits_from?(path)
|
108
|
-
# TODO: need process all nodes first, marking for deletion on the first pass, because the superclass gets deleting and then the inherits from relation breaks down
|
109
|
-
self.path == path || @inherits_from && @inherits_from.inherits_from?(path)
|
110
|
-
end
|
111
|
-
|
112
|
-
# @return [Boolean] whether or not this node represents a singleton module. A singleton module is one which contains an <tt>extend self</tt> statement.
|
113
|
-
def singleton?
|
114
|
-
@singleton
|
115
|
-
end
|
116
|
-
|
117
|
-
# @return [Boolean] whether or not this node is an island. An island is a node with no connections to other nodes.
|
118
|
-
def island?
|
119
|
-
([@parent, @inherits_from].all?(&:nil?) &&
|
120
|
-
[@children, @inherited_by, @extends, @includes, @extended_by, @included_by].all?(&:empty?))
|
121
|
-
end
|
122
|
-
|
123
|
-
end
|
124
|
-
|
125
|
-
# {Node} methods used during the graph building phase
|
126
|
-
# @api developer
|
127
|
-
module BuilderMethods
|
128
|
-
|
129
|
-
attr_reader :style
|
130
|
-
|
131
|
-
# Find a node contained in this node, or contained in this nodes {#parent}, recursively.
|
132
|
-
# @return [Node, nil]
|
133
|
-
def find(name)
|
134
|
-
path = (@path + [name].flatten).join '::'
|
135
|
-
@children[path] || (@parent && @parent.find(name))
|
136
|
-
end
|
137
|
-
|
138
|
-
# Add other as a child of this node
|
139
|
-
# @param other [Node] another node
|
140
|
-
# @return [nil]
|
141
|
-
def contains(other)
|
142
|
-
this = self
|
143
|
-
@children[other.path] = other
|
144
|
-
other.instance_eval {@parent = this}
|
145
|
-
nil
|
146
|
-
end
|
147
|
-
|
148
|
-
# Add other as the super class of this node
|
149
|
-
# @param other [Node] another node
|
150
|
-
# @return [nil]
|
151
|
-
def inherits_from(other)
|
152
|
-
this = self
|
153
|
-
@inherits_from = other
|
154
|
-
other.instance_eval {@inherited_by[this.path] = this}
|
155
|
-
nil
|
156
|
-
end
|
157
|
-
|
158
|
-
# Add other to this nodes includes set
|
159
|
-
# @param other [Node] another node
|
160
|
-
# @return [nil]
|
161
|
-
def includes(other)
|
162
|
-
this = self
|
163
|
-
@includes[other.path] = other
|
164
|
-
other.instance_eval {@included_by[this.path] = this}
|
165
|
-
nil
|
166
|
-
end
|
167
|
-
|
168
|
-
# Add other to this nodes extends set
|
169
|
-
# @param other [Node] another node
|
170
|
-
# @return [nil]
|
171
|
-
def extends(other)
|
172
|
-
this = self
|
173
|
-
@extends[other.path] = other
|
174
|
-
other.instance_eval {@extended_by[this.path] = this}
|
175
|
-
nil
|
176
|
-
end
|
177
|
-
|
178
|
-
# Mark this module node as a singleton
|
179
|
-
# @return [nil]
|
180
|
-
def mark_as_singleton
|
181
|
-
throw :NodeNotAModule unless module?
|
182
|
-
@singleton = true
|
183
|
-
end
|
184
|
-
|
185
|
-
# Remove any relations involving this node
|
186
|
-
def prune
|
187
|
-
this = self
|
188
|
-
if @inherits_from
|
189
|
-
@inherits_from.instance_eval {@inherited_by.delete this.path}
|
190
|
-
end
|
191
|
-
@inherited_by.each_value do |other|
|
192
|
-
other.instance_eval {@inherits_from = nil}
|
193
|
-
end
|
194
|
-
if @parent
|
195
|
-
@parent.instance_eval {@children.delete this.path}
|
196
|
-
end
|
197
|
-
@children.each_value do |other|
|
198
|
-
other.instance_eval {@parent = nil}
|
199
|
-
end
|
200
|
-
@includes.each_value do |other|
|
201
|
-
other.instance_eval {@included_by.delete this.path}
|
202
|
-
end
|
203
|
-
@included_by.each_value do |other|
|
204
|
-
other.instance_eval {@includes.delete this.path}
|
205
|
-
end
|
206
|
-
@extends.each_value do |other|
|
207
|
-
other.instance_eval {@extended_by.delete this.path}
|
208
|
-
end
|
209
|
-
@extended_by.each_value do |other|
|
210
|
-
other.instance_eval {@extends.delete this.path}
|
211
|
-
end
|
212
|
-
end
|
213
|
-
end
|
214
|
-
|
215
14
|
include BuilderMethods
|
216
15
|
include TemplateMethods
|
217
16
|
include QueryMethods
|
@@ -231,14 +30,21 @@ module CodeNode
|
|
231
30
|
parent_path + [name]
|
232
31
|
end
|
233
32
|
@name = @path.last
|
234
|
-
@
|
235
|
-
@
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
33
|
+
@inverse_relation = {} # :rel => :inv_rel
|
34
|
+
@edge = {} # :rel => { 'node::path' => }
|
35
|
+
define_relation :parent, :children
|
36
|
+
define_relation :inherits_from, :inherited_by
|
37
|
+
define_relation :includes, :included_by
|
38
|
+
define_relation :extends, :extended_by
|
39
|
+
end
|
40
|
+
|
41
|
+
# @param rel [Symbol] the name of a relation
|
42
|
+
# @param inv [Symbol] the name of the inverse relation
|
43
|
+
def define_relation(rel, inv)
|
44
|
+
@inverse_relation[rel] = inv
|
45
|
+
@inverse_relation[inv] = rel
|
46
|
+
@edge[rel] = {}
|
47
|
+
@edge[inv] = {}
|
242
48
|
end
|
243
49
|
|
244
50
|
# @return [FixNum] order nodes by {#path}
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module CodeNode
|
2
|
+
module IR
|
3
|
+
class Node
|
4
|
+
|
5
|
+
# {Node} methods used during the graph building phase
|
6
|
+
# @api developer
|
7
|
+
module BuilderMethods
|
8
|
+
|
9
|
+
attr_reader :style
|
10
|
+
|
11
|
+
# Find a node contained in this node, or contained in this nodes {#parent}, recursively.
|
12
|
+
# @return [Node, nil]
|
13
|
+
def find(name)
|
14
|
+
path = (@path + [name].flatten).join '::'
|
15
|
+
children[path] || (parent && parent.find(name))
|
16
|
+
end
|
17
|
+
|
18
|
+
# Add other as a child of this node
|
19
|
+
# @param other [Node] another node
|
20
|
+
# @return [nil]
|
21
|
+
def contains(other)
|
22
|
+
add_edge :children, other
|
23
|
+
end
|
24
|
+
|
25
|
+
# Add other as the super class of this node
|
26
|
+
# @param other [Node] another node
|
27
|
+
# @return [nil]
|
28
|
+
def inherits_from(other)
|
29
|
+
add_edge :inherits_from, other
|
30
|
+
end
|
31
|
+
|
32
|
+
# Add other to this nodes includes set
|
33
|
+
# @param other [Node] another node
|
34
|
+
# @return [nil]
|
35
|
+
def includes(other)
|
36
|
+
add_edge :includes, other
|
37
|
+
end
|
38
|
+
|
39
|
+
# Add other to this nodes extends set
|
40
|
+
# @param other [Node] another node
|
41
|
+
# @return [nil]
|
42
|
+
def extends(other)
|
43
|
+
add_edge :extends, other
|
44
|
+
end
|
45
|
+
|
46
|
+
# Add an edge between this node and another with the given relation type
|
47
|
+
# @param rel [Symbol] the type of relation
|
48
|
+
# @param other [Node] another node
|
49
|
+
# @return [nil]
|
50
|
+
def add_edge(rel, other)
|
51
|
+
this = self
|
52
|
+
inv = @inverse_relation[rel]
|
53
|
+
@edge[rel][other.path] = other
|
54
|
+
other.instance_eval do
|
55
|
+
@edge[inv][this.path] = this
|
56
|
+
end
|
57
|
+
nil
|
58
|
+
end
|
59
|
+
|
60
|
+
# Mark this module node as a singleton
|
61
|
+
# @return [nil]
|
62
|
+
def mark_as_singleton
|
63
|
+
throw :NodeNotAModule unless module?
|
64
|
+
@singleton = true
|
65
|
+
end
|
66
|
+
|
67
|
+
# Remove any relations involving this node
|
68
|
+
def prune
|
69
|
+
this = self
|
70
|
+
@edge.each_pair do |rel, edges|
|
71
|
+
inv = @inverse_relation[rel]
|
72
|
+
edges.each_value do |other|
|
73
|
+
other.instance_eval do
|
74
|
+
@edge[inv].delete this.path
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module CodeNode
|
2
|
+
module IR
|
3
|
+
class Node
|
4
|
+
|
5
|
+
# {Node} methods which are useful for querying in matchers
|
6
|
+
module QueryMethods
|
7
|
+
|
8
|
+
# @return [Boolean] does this node represent a module?
|
9
|
+
def module?
|
10
|
+
@node_type == :module
|
11
|
+
end
|
12
|
+
|
13
|
+
# @return [Boolean] does this node represent a class?
|
14
|
+
def class?
|
15
|
+
@node_type == :class
|
16
|
+
end
|
17
|
+
|
18
|
+
# @return [String] fully qualified name of the node in the form <tt>'Foo::Bar::Car'</tt>. Not good as a graphviz identifier because of the colon (<tt>:</tt>) characters. Use {TemplateMethods#key} for graphviz identifiers instead.
|
19
|
+
def path
|
20
|
+
@path.join '::'
|
21
|
+
end
|
22
|
+
|
23
|
+
# @param path [String] a class or module path in the form <tt>Foo::Bar::Car</tt>
|
24
|
+
# @return [Boolean] does this node include a {#module?} node with the given {#path}?
|
25
|
+
def includes?(path)
|
26
|
+
includes.member? path
|
27
|
+
end
|
28
|
+
|
29
|
+
# @param path [String] a class or module path in the form <tt>Foo::Bar::Car</tt>
|
30
|
+
# @return [Boolean] does this node extend a {#module?} node with the given {#path}?
|
31
|
+
def extends?(path)
|
32
|
+
extends.member? path
|
33
|
+
end
|
34
|
+
|
35
|
+
# @param path [String] a class or module path in the form <tt>Foo::Bar::Car</tt>
|
36
|
+
# @return [Boolean] does this node inherit from (directly or indirectly) a {#class?} node with the given {#path}? Note that a node inherits from itself according to this method. Recursively checks the ancestry of the node.
|
37
|
+
def inherits_from?(path)
|
38
|
+
# TODO: need process all nodes first, marking for deletion on the first pass, because the superclass gets deleting and then the inherits from relation breaks down
|
39
|
+
self.path == path || !@edge[:inherits_from].empty? && @edge[:inherits_from].values.first.inherits_from?(path)
|
40
|
+
end
|
41
|
+
|
42
|
+
# @return [Boolean] whether or not this node represents a singleton module. A singleton module is one which contains an <tt>extend self</tt> statement.
|
43
|
+
def singleton?
|
44
|
+
@singleton
|
45
|
+
end
|
46
|
+
|
47
|
+
# @return [Boolean] whether or not this node is an island. An island is a node with no connections to other nodes.
|
48
|
+
def island?
|
49
|
+
@edge.values.all? &:empty?
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module CodeNode
|
2
|
+
module IR
|
3
|
+
class Node
|
4
|
+
|
5
|
+
# Node methods which are useful in templates
|
6
|
+
module TemplateMethods
|
7
|
+
|
8
|
+
# @return [String] the name of the node. Not necessarilly unique.
|
9
|
+
# @see {#path}
|
10
|
+
attr_reader :name
|
11
|
+
|
12
|
+
# @return [Node] node which contains this node
|
13
|
+
# @example
|
14
|
+
# module Foo # Foo is the parent
|
15
|
+
# module Bar # to Bar
|
16
|
+
# end
|
17
|
+
# end
|
18
|
+
def parent
|
19
|
+
@edge[:parent].values.first
|
20
|
+
end
|
21
|
+
|
22
|
+
# The child nodes of this node
|
23
|
+
# @return [Hash<String,Node>] a mapping from node {#path} names to nodes
|
24
|
+
# @example
|
25
|
+
# module Foo # Foo has children
|
26
|
+
# module Bar # Bar
|
27
|
+
# end # and
|
28
|
+
# class Car # Car
|
29
|
+
# end
|
30
|
+
# end
|
31
|
+
def children
|
32
|
+
@edge[:children]
|
33
|
+
end
|
34
|
+
|
35
|
+
# @return [Node,nil] the super class of this class. Will be +nil+ for modules.
|
36
|
+
def super_class_node
|
37
|
+
@edge[:inherits_from].values.first
|
38
|
+
end
|
39
|
+
|
40
|
+
# @return [Array<Node>] module nodes for which this node has an +include+ statement
|
41
|
+
def inclusions
|
42
|
+
@edge[:includes].values.sort
|
43
|
+
end
|
44
|
+
|
45
|
+
# @return [Array<Node>] module nodes for which this node has an +extend+ statement
|
46
|
+
def extensions
|
47
|
+
@edge[:extends].values.sort
|
48
|
+
end
|
49
|
+
|
50
|
+
# @return [String] fully qualified identifier for the node in the form <tt>Foo_Bar_Car</tt>. Ideal for graphviz identifiers.
|
51
|
+
def key
|
52
|
+
@path.join '_'
|
53
|
+
end
|
54
|
+
|
55
|
+
# @return [String] how the node will be labelled in the graph. Nodes without parents display their full {#path}, while nodes with parents only display their {#name}.
|
56
|
+
def label
|
57
|
+
parent.nil? ? path : name
|
58
|
+
end
|
59
|
+
|
60
|
+
# Stamp the accumulated GraphViz styles in a format suitable for inclusion in a <tt>.dot</tt> file
|
61
|
+
# @return [String] style in the form <tt>key1="value1" key2="value2"</tt>...
|
62
|
+
def stamp_styles
|
63
|
+
x = []
|
64
|
+
style.each_pair do |key, value|
|
65
|
+
x << "#{key}=\"#{value}\""
|
66
|
+
end
|
67
|
+
x.join ' '
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -23,39 +23,48 @@ module CodeNode
|
|
23
23
|
def walk(s = nil)
|
24
24
|
s ||= @root
|
25
25
|
if [:module, :class].member?(s[0])
|
26
|
-
|
27
|
-
if find_relations? && s[0] == :class && !s[2].nil?
|
28
|
-
super_node = @graph.node_for :class, s[2], :not_sure_if_nested => true
|
29
|
-
node.inherits_from super_node unless super_node.nil?
|
30
|
-
end
|
31
|
-
rest = s[0] == :module ? s.slice(2..-1) : s.slice(3..-1)
|
32
|
-
rest.each do |c|
|
33
|
-
walk(c) if c.class == Sexp
|
34
|
-
end
|
35
|
-
end
|
36
|
-
unless @graph.scope.empty?
|
37
|
-
@graph.scope.last.contains node
|
38
|
-
end
|
26
|
+
add_node s
|
39
27
|
elsif find_relations? && s[0] == :call && s.length >= 4 && [:extend, :include].member?(s[2]) && !@graph.scope.empty?
|
40
|
-
s
|
41
|
-
node = @graph.node_for :module, mod_sexp, :not_sure_if_nested => true
|
42
|
-
unless node.nil?
|
43
|
-
if s[2] == :extend
|
44
|
-
@graph.scope.last.extends node
|
45
|
-
else
|
46
|
-
@graph.scope.last.includes node
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
28
|
+
add_relation s
|
50
29
|
else
|
51
|
-
s.slice(1..-1)
|
52
|
-
walk(c) if c.class == Sexp
|
53
|
-
end
|
30
|
+
walk_siblings s.slice(1..-1)
|
54
31
|
end
|
55
32
|
end
|
56
33
|
|
57
34
|
private
|
58
35
|
|
36
|
+
def walk_siblings(s)
|
37
|
+
s.each do |c|
|
38
|
+
walk(c) if c.class == Sexp
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def add_node(s)
|
43
|
+
node = @graph.node_for(s[0], s[1]) do |node|
|
44
|
+
if find_relations? && s[0] == :class && !s[2].nil?
|
45
|
+
super_node = @graph.node_for :class, s[2], :not_sure_if_nested => true
|
46
|
+
node.inherits_from super_node unless super_node.nil?
|
47
|
+
end
|
48
|
+
walk_siblings s.slice((s[0] == :module ? 2 : 3)..-1)
|
49
|
+
end
|
50
|
+
unless @graph.scope.empty?
|
51
|
+
@graph.scope.last.contains node
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def add_relation(s)
|
56
|
+
s.slice(3..-1).each do |mod_sexp|
|
57
|
+
node = @graph.node_for :module, mod_sexp, :not_sure_if_nested => true
|
58
|
+
unless node.nil?
|
59
|
+
if s[2] == :extend
|
60
|
+
@graph.scope.last.extends node
|
61
|
+
else
|
62
|
+
@graph.scope.last.includes node
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
59
68
|
# @return [Boolean] whether or not the walker should look for relations
|
60
69
|
def find_relations?
|
61
70
|
@mode == :find_relations
|
@@ -59,27 +59,19 @@ module CodeNode
|
|
59
59
|
end
|
60
60
|
|
61
61
|
def has_containment?(path1, path2)
|
62
|
-
|
63
|
-
key2 = path2.to_s.split('::').join '_'
|
64
|
-
@edges[:containment].member?([key1, key2])
|
62
|
+
has_relation? :containment, path1, path2
|
65
63
|
end
|
66
64
|
|
67
65
|
def has_inheritance?(path1, path2)
|
68
|
-
|
69
|
-
key2 = path2.to_s.split('::').join '_'
|
70
|
-
@edges[:inheritance].member?([key1, key2])
|
66
|
+
has_relation? :inheritance, path1, path2
|
71
67
|
end
|
72
68
|
|
73
69
|
def has_inclusion?(path1, path2)
|
74
|
-
|
75
|
-
key2 = path2.to_s.split('::').join '_'
|
76
|
-
@edges[:inclusion].member?([key1, key2])
|
70
|
+
has_relation? :inclusion, path1, path2
|
77
71
|
end
|
78
72
|
|
79
73
|
def has_extension?(path1, path2)
|
80
|
-
|
81
|
-
key2 = path2.to_s.split('::').join '_'
|
82
|
-
@edges[:extension].member?([key1, key2])
|
74
|
+
has_relation? :extension, path1, path2
|
83
75
|
end
|
84
76
|
|
85
77
|
private
|
@@ -108,6 +100,12 @@ module CodeNode
|
|
108
100
|
end
|
109
101
|
@i += 1
|
110
102
|
end
|
103
|
+
|
104
|
+
def has_relation?(relation, path1, path2)
|
105
|
+
key1 = path1.to_s.split('::').join '_'
|
106
|
+
key2 = path2.to_s.split('::').join '_'
|
107
|
+
@edges[relation].member?([key1, key2])
|
108
|
+
end
|
111
109
|
end
|
112
110
|
|
113
111
|
end
|
data/lib/code_node/version.rb
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: code_node
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 29
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 1
|
9
|
-
-
|
10
|
-
version: 0.1.
|
9
|
+
- 3
|
10
|
+
version: 0.1.3
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Kevin Tonon
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2012-12-
|
18
|
+
date: 2012-12-17 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: cog
|
@@ -117,7 +117,13 @@ files:
|
|
117
117
|
- lib/code_node/cog_tool.rb
|
118
118
|
- lib/code_node/dsl/graph_definer.rb
|
119
119
|
- lib/code_node/dsl.rb
|
120
|
+
- lib/code_node/graph_builder.rb
|
121
|
+
- lib/code_node/ir/graph/builder_methods.rb
|
122
|
+
- lib/code_node/ir/graph/template_methods.rb
|
120
123
|
- lib/code_node/ir/graph.rb
|
124
|
+
- lib/code_node/ir/node/builder_methods.rb
|
125
|
+
- lib/code_node/ir/node/query_methods.rb
|
126
|
+
- lib/code_node/ir/node/template_methods.rb
|
121
127
|
- lib/code_node/ir/node.rb
|
122
128
|
- lib/code_node/ir/node_matcher.rb
|
123
129
|
- lib/code_node/ir.rb
|