code_node 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +17 -0
- data/cog/templates/code_node/generator.rb.erb +8 -0
- data/cog/templates/code_node/graph.dot.erb +48 -0
- data/lib/code_node/cog_tool.rb +15 -0
- data/lib/code_node/dsl/graph_definer.rb +13 -0
- data/lib/code_node/dsl.rb +7 -0
- data/lib/code_node/ir/class_node.rb +22 -0
- data/lib/code_node/ir/graph.rb +66 -0
- data/lib/code_node/ir/module_node.rb +26 -0
- data/lib/code_node/ir/node.rb +51 -0
- data/lib/code_node/ir.rb +9 -0
- data/lib/code_node/sexp_walker.rb +40 -0
- data/lib/code_node/version.rb +5 -0
- data/lib/code_node.rb +55 -0
- metadata +136 -0
data/LICENSE
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
Copyright (c) 2012 Kevin Tonon
|
2
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
3
|
+
this software and associated documentation files (the "Software"), to deal in
|
4
|
+
the Software without restriction, including without limitation the rights to
|
5
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
6
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
7
|
+
subject to the following conditions:
|
8
|
+
|
9
|
+
The above copyright notice and this permission notice shall be included in all
|
10
|
+
copies or substantial portions of the Software.
|
11
|
+
|
12
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
13
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
14
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
15
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
16
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
17
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@@ -0,0 +1,48 @@
|
|
1
|
+
digraph
|
2
|
+
{
|
3
|
+
graph [rankdir="LR"]
|
4
|
+
node [style="filled"]
|
5
|
+
|
6
|
+
<% @graph.nodes.each do |node| %>
|
7
|
+
<% if node.should_render? %>
|
8
|
+
<%= node.key %> [label="<%= node.label %>" shape="<%= node.shape %>" fillcolor="<%= node.fillcolor %>" fontcolor="<%= node.fontcolor %>"];
|
9
|
+
<% end %>
|
10
|
+
<% end %>
|
11
|
+
|
12
|
+
/* Contains */
|
13
|
+
edge [color="#e8e8e8"]
|
14
|
+
<% @graph.nodes.each do |node| %>
|
15
|
+
<% if node.parent && node.should_render? && node.parent.should_render? %>
|
16
|
+
<%= node.parent.key %> -> <%= node.key %>;
|
17
|
+
<% end %>
|
18
|
+
<% end %>
|
19
|
+
|
20
|
+
/* Extended by */
|
21
|
+
edge [color="#000000"]
|
22
|
+
<% @graph.nodes.each do |node| %>
|
23
|
+
<% node.extended_by.values.sort.each do |other| %>
|
24
|
+
<% if node.should_render? && other.should_render? %>
|
25
|
+
<%= node.key %> -> <%= other.key %>;
|
26
|
+
<% end %>
|
27
|
+
<% end %>
|
28
|
+
<% end %>
|
29
|
+
|
30
|
+
/* Includes */
|
31
|
+
edge [color="#336699"]
|
32
|
+
<% @graph.nodes.each do |node| %>
|
33
|
+
<% node.includes.values.sort.each do |other| %>
|
34
|
+
<% if node.should_render? && other.should_render? %>
|
35
|
+
<%= node.key %> -> <%= other.key %>;
|
36
|
+
<% end %>
|
37
|
+
<% end %>
|
38
|
+
<% end %>
|
39
|
+
|
40
|
+
/* Inherits from */
|
41
|
+
edge [color="#996633"]
|
42
|
+
<% @graph.nodes.each do |node| %>
|
43
|
+
<% if node.should_render? && node.inherits_from && node.inherits_from.should_render? %>
|
44
|
+
<%= node.key %> -> <%= node.inherits_from.key %>;
|
45
|
+
<% end %>
|
46
|
+
<% end %>
|
47
|
+
|
48
|
+
}
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'cog'
|
2
|
+
|
3
|
+
# Register code_node as a tool with cog
|
4
|
+
Cog::Config.instance.register_tool __FILE__ do |tool|
|
5
|
+
|
6
|
+
# Define how new code_node generators are created
|
7
|
+
#
|
8
|
+
# Add context as required by your generator template.
|
9
|
+
#
|
10
|
+
# When the block is executed, +self+ will be an instance of Cog::Config::Tool::GeneratorStamper
|
11
|
+
tool.stamp_generator do
|
12
|
+
stamp 'code_node/generator.rb', generator_dest, :absolute_destination => true
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'code_node/ir/node'
|
2
|
+
|
3
|
+
module CodeNode
|
4
|
+
module IR
|
5
|
+
|
6
|
+
class ClassNode < Node
|
7
|
+
def inherits_from=(super_node)
|
8
|
+
@inherits_from = super_node
|
9
|
+
end
|
10
|
+
def shape
|
11
|
+
:ellipse
|
12
|
+
end
|
13
|
+
def fillcolor
|
14
|
+
'#cccccc'
|
15
|
+
end
|
16
|
+
def fontcolor
|
17
|
+
'#000000'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'code_node/ir/class_node'
|
2
|
+
require 'code_node/ir/module_node'
|
3
|
+
|
4
|
+
module CodeNode
|
5
|
+
module IR
|
6
|
+
|
7
|
+
class Graph
|
8
|
+
|
9
|
+
attr_reader :scope
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@nodes = {}
|
13
|
+
@scope = []
|
14
|
+
end
|
15
|
+
|
16
|
+
def node_for(node_type, s, opt={}, &block)
|
17
|
+
name = if s.is_a? Symbol
|
18
|
+
s
|
19
|
+
elsif s[0] == :const
|
20
|
+
s[1]
|
21
|
+
elsif s[0] == :colon2
|
22
|
+
x = []
|
23
|
+
while s[0] == :colon2
|
24
|
+
x << s[2] ; s = s[1]
|
25
|
+
end
|
26
|
+
x << s[1]
|
27
|
+
x.reverse
|
28
|
+
elsif s[0] == :self
|
29
|
+
@scope.last.mark_as_singleton
|
30
|
+
nil
|
31
|
+
end
|
32
|
+
return if name.nil?
|
33
|
+
|
34
|
+
node = if opt[:not_sure_if_nested]
|
35
|
+
if @scope.length > 1 && @scope[-2].find(name)
|
36
|
+
@scope[-2].find name
|
37
|
+
else
|
38
|
+
(node_type == :module ? ModuleNode : ClassNode).new(name)
|
39
|
+
end
|
40
|
+
else
|
41
|
+
(node_type == :module ? ModuleNode : ClassNode).new(name, @scope.last)
|
42
|
+
end
|
43
|
+
|
44
|
+
node = self << node
|
45
|
+
unless block.nil? || node.nil?
|
46
|
+
@scope << node
|
47
|
+
block.call node
|
48
|
+
@scope.pop
|
49
|
+
end
|
50
|
+
node
|
51
|
+
end
|
52
|
+
|
53
|
+
def <<(node)
|
54
|
+
@nodes[node.key] ||= node
|
55
|
+
@nodes[node.key]
|
56
|
+
end
|
57
|
+
def [](key)
|
58
|
+
@nodes[key]
|
59
|
+
end
|
60
|
+
def nodes
|
61
|
+
@nodes.values.sort
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'code_node/ir/node'
|
2
|
+
|
3
|
+
module CodeNode
|
4
|
+
module IR
|
5
|
+
|
6
|
+
class ModuleNode < Node
|
7
|
+
def shape
|
8
|
+
:box
|
9
|
+
end
|
10
|
+
def fillcolor
|
11
|
+
if children["#{key}_ClassMethods"] && extended_by['ActiveSupport_Concern']
|
12
|
+
'#000000'
|
13
|
+
else
|
14
|
+
'#666666'
|
15
|
+
end
|
16
|
+
end
|
17
|
+
def fontcolor
|
18
|
+
'#ffffff'
|
19
|
+
end
|
20
|
+
def mark_as_singleton
|
21
|
+
@singleton = true
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module CodeNode
|
2
|
+
module IR
|
3
|
+
|
4
|
+
class Node
|
5
|
+
attr_reader :name, :parent, :children, :includes, :extended_by, :inherits_from
|
6
|
+
def initialize(name, parent = nil)
|
7
|
+
parent_path = parent ? parent.instance_eval {@path} : []
|
8
|
+
@path = if name.is_a? Array
|
9
|
+
parent_path + name
|
10
|
+
else
|
11
|
+
parent_path + [name]
|
12
|
+
end
|
13
|
+
@name = @path.last
|
14
|
+
@parent = parent
|
15
|
+
parent.children[key] = self unless parent.nil?
|
16
|
+
@children = {}
|
17
|
+
@includes = {}
|
18
|
+
@extended_by = {}
|
19
|
+
end
|
20
|
+
def find(name)
|
21
|
+
key = (@path + [name].flatten).join '_'
|
22
|
+
@children[key] || (orphan? ? nil : @parent.find(name))
|
23
|
+
end
|
24
|
+
def key
|
25
|
+
@path.join '_'
|
26
|
+
end
|
27
|
+
def path
|
28
|
+
@path.join '::'
|
29
|
+
end
|
30
|
+
def label
|
31
|
+
orphan? ? path : name
|
32
|
+
end
|
33
|
+
def to_s
|
34
|
+
path
|
35
|
+
end
|
36
|
+
def <=>(other)
|
37
|
+
path <=> other.path
|
38
|
+
end
|
39
|
+
def orphan?
|
40
|
+
@parent.nil?
|
41
|
+
end
|
42
|
+
def derives_from?(k)
|
43
|
+
key == k || @inherits_from && (@inherits_from.key == k || @inherits_from.derives_from?(k))
|
44
|
+
end
|
45
|
+
def should_render?
|
46
|
+
!(orphan? && @children.empty? && @inherits_from.nil? && @extended_by.empty? && @includes.empty?) && key != 'ActiveSupport_Concern' && !derives_from?('ActiveRecord_ActiveRecordError') && name != :ClassMethods
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
data/lib/code_node/ir.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
module CodeNode
|
2
|
+
|
3
|
+
class SexpWalker
|
4
|
+
def initialize(graph, sexp)
|
5
|
+
@graph = graph
|
6
|
+
@root = sexp
|
7
|
+
end
|
8
|
+
def walk(purpose, s = nil)
|
9
|
+
s ||= @root
|
10
|
+
if [:module, :class].member?(s[0])
|
11
|
+
@graph.node_for(s[0], s[1]) do |node|
|
12
|
+
if purpose == :find_relations && s[0] == :class && !s[2].nil?
|
13
|
+
super_node = @graph.node_for :class, s[2], :not_sure_if_nested => true
|
14
|
+
unless super_node.nil?
|
15
|
+
node.inherits_from = super_node
|
16
|
+
end
|
17
|
+
end
|
18
|
+
rest = s[0] == :module ? s.slice(2..-1) : s.slice(3..-1)
|
19
|
+
rest.each do |c|
|
20
|
+
walk(purpose, c) if c.class == Sexp
|
21
|
+
end
|
22
|
+
end
|
23
|
+
elsif purpose == :find_relations && s[0] == :call && s.length >= 4 && [:extend, :include].member?(s[2]) && !@graph.scope.empty?
|
24
|
+
node = @graph.node_for :module, s[3], :not_sure_if_nested => true
|
25
|
+
unless node.nil?
|
26
|
+
if s[2] == :extend
|
27
|
+
@graph.scope.last.extended_by[node.key] = node
|
28
|
+
else
|
29
|
+
@graph.scope.last.includes[node.key] = node
|
30
|
+
end
|
31
|
+
end
|
32
|
+
else
|
33
|
+
s.slice(1..-1).each do |c|
|
34
|
+
walk(purpose, c) if c.class == Sexp
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
data/lib/code_node.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
$LOAD_PATH << File.join(File.dirname(__FILE__))
|
2
|
+
require 'code_node/version'
|
3
|
+
require 'code_node/dsl'
|
4
|
+
require 'code_node/ir'
|
5
|
+
require 'code_node/sexp_walker'
|
6
|
+
require 'cog'
|
7
|
+
require 'ruby_parser'
|
8
|
+
|
9
|
+
# Create Class and Module graphs for Ruby prjects
|
10
|
+
module CodeNode
|
11
|
+
|
12
|
+
extend Cog::Generator
|
13
|
+
|
14
|
+
# @param graph_name [String] name of the dot file to generate (without +.dot+ extension)
|
15
|
+
# @option opt [Symbol] :ruby_version (:ruby19) either <tt>:ruby18</tt> or <tt>:ruby19</tt>, indicating which parser to use
|
16
|
+
# @yield [GraphDefinition] define rules for creating the graph
|
17
|
+
def self.graph(graph_name, opt={}, &block)
|
18
|
+
root = Cog::Config.instance.project_source_path
|
19
|
+
@graph = IR::Graph.new
|
20
|
+
graph_definer = DSL::GraphDefiner.new @graph
|
21
|
+
block.call graph_definer
|
22
|
+
|
23
|
+
rp = case opt[:ruby_version]
|
24
|
+
when :ruby18
|
25
|
+
Ruby18Parser.new
|
26
|
+
else
|
27
|
+
Ruby19Parser.new
|
28
|
+
end
|
29
|
+
|
30
|
+
sexp = []
|
31
|
+
[:find_nodes, :find_relations].each_with_index do |purpose, pass|
|
32
|
+
puts "#{(pass+1).ordinalize} pass: #{purpose.to_s.gsub('_', ' ')}".color(:cyan)
|
33
|
+
Dir.glob("#{root}/**/*.rb").each_with_index do |filename, i|
|
34
|
+
sexp[i] ||= begin
|
35
|
+
rp.parse(File.read filename)
|
36
|
+
rescue Racc::ParseError
|
37
|
+
STDERR.write "{filename.relative_to_project_root}, skipped...\n".color(:red)
|
38
|
+
nil
|
39
|
+
end
|
40
|
+
if sexp[i]
|
41
|
+
walker = SexpWalker.new @graph, sexp[i]
|
42
|
+
walker.walk purpose
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Activate code_node while rendering templates
|
48
|
+
# so that cog will be able to find code_node templates
|
49
|
+
Cog::Config.instance.activate_tool 'code_node' do
|
50
|
+
stamp 'code_node/graph.dot', "#{graph_name}.dot"
|
51
|
+
end
|
52
|
+
|
53
|
+
nil
|
54
|
+
end
|
55
|
+
end
|
metadata
ADDED
@@ -0,0 +1,136 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: code_node
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Kevin Tonon
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2012-11-18 00:00:00 Z
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: cog
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
hash: 3
|
29
|
+
segments:
|
30
|
+
- 0
|
31
|
+
version: "0"
|
32
|
+
type: :runtime
|
33
|
+
version_requirements: *id001
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
name: ruby_parser
|
36
|
+
prerelease: false
|
37
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
38
|
+
none: false
|
39
|
+
requirements:
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
hash: 3
|
43
|
+
segments:
|
44
|
+
- 0
|
45
|
+
version: "0"
|
46
|
+
type: :runtime
|
47
|
+
version_requirements: *id002
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
name: rake
|
50
|
+
prerelease: false
|
51
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
52
|
+
none: false
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
hash: 3
|
57
|
+
segments:
|
58
|
+
- 0
|
59
|
+
version: "0"
|
60
|
+
type: :development
|
61
|
+
version_requirements: *id003
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: yard
|
64
|
+
prerelease: false
|
65
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
66
|
+
none: false
|
67
|
+
requirements:
|
68
|
+
- - ">="
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
hash: 3
|
71
|
+
segments:
|
72
|
+
- 0
|
73
|
+
version: "0"
|
74
|
+
type: :development
|
75
|
+
version_requirements: *id004
|
76
|
+
description:
|
77
|
+
email: kevin@betweenconcepts.com
|
78
|
+
executables: []
|
79
|
+
|
80
|
+
extensions: []
|
81
|
+
|
82
|
+
extra_rdoc_files: []
|
83
|
+
|
84
|
+
files:
|
85
|
+
- LICENSE
|
86
|
+
- cog/templates/code_node/generator.rb.erb
|
87
|
+
- cog/templates/code_node/graph.dot.erb
|
88
|
+
- lib/code_node/cog_tool.rb
|
89
|
+
- lib/code_node/dsl/graph_definer.rb
|
90
|
+
- lib/code_node/dsl.rb
|
91
|
+
- lib/code_node/ir/class_node.rb
|
92
|
+
- lib/code_node/ir/graph.rb
|
93
|
+
- lib/code_node/ir/module_node.rb
|
94
|
+
- lib/code_node/ir/node.rb
|
95
|
+
- lib/code_node/ir.rb
|
96
|
+
- lib/code_node/sexp_walker.rb
|
97
|
+
- lib/code_node/version.rb
|
98
|
+
- lib/code_node.rb
|
99
|
+
homepage: http://betweenconcepts.com
|
100
|
+
licenses: []
|
101
|
+
|
102
|
+
post_install_message:
|
103
|
+
rdoc_options:
|
104
|
+
- --title
|
105
|
+
- code_node
|
106
|
+
- -ri
|
107
|
+
require_paths:
|
108
|
+
- lib
|
109
|
+
- lib
|
110
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
111
|
+
none: false
|
112
|
+
requirements:
|
113
|
+
- - ">="
|
114
|
+
- !ruby/object:Gem::Version
|
115
|
+
hash: 3
|
116
|
+
segments:
|
117
|
+
- 0
|
118
|
+
version: "0"
|
119
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
120
|
+
none: false
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
hash: 3
|
125
|
+
segments:
|
126
|
+
- 0
|
127
|
+
version: "0"
|
128
|
+
requirements: []
|
129
|
+
|
130
|
+
rubyforge_project:
|
131
|
+
rubygems_version: 1.8.24
|
132
|
+
signing_key:
|
133
|
+
specification_version: 3
|
134
|
+
summary: Create Class and Module graphs for Ruby projects
|
135
|
+
test_files: []
|
136
|
+
|