code-explorer 0.1.0 → 0.2.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fb4f5f3aec4c52c667605028d891dccf6bab2b6d
4
- data.tar.gz: c3590e42ee2bf91facc0553fee487c19873b884c
3
+ metadata.gz: 37cc2f42a16925fac9f2dde64d9e2a174d915187
4
+ data.tar.gz: e016d6a05e33163a83a970f8a70b693ccff90fdd
5
5
  SHA512:
6
- metadata.gz: 7757f66ac7088828f58beb5ddcfb84f84dd415e5680c5493a1f3bcc23e22c6e602b3f59a2aaf38eafe1eb641f289d1a10ae8dc8170d8078a55049119bc745436
7
- data.tar.gz: d0e8a1630fdf7a037ffbced5fa1bf7daad69990d33377cc4daa8c41965e9b0632804784924e559a475a3154b670a0b031b0824701e312a4fc89594272762aa92
6
+ metadata.gz: c47e95b326b405eecbe74654071c93db23f4ffa32ddb7c99b4da13fc247d56989c20d9892b5abebd32140ec9d23a9bddc9c8190aa69af26bb67938b4aed15cbe
7
+ data.tar.gz: 8514c9663ddeebce427ba42c8f0d315d5213319df57855501e0778b169359cc4d4f22d3c1ecae7384bc4d3bf8ab5f801212005f97249fd11bcc66e00d5ac3e5c
data/README.md CHANGED
@@ -1,16 +1,29 @@
1
- # Call Graph
1
+ # Code Explorer
2
+
3
+ ## Tools
4
+
5
+ ### code-explorer
6
+
7
+ Starts a local web server which lets you apply the other tools to all `*.rb`
8
+ files in a directory subtree.
9
+
10
+ ### call-graph
2
11
 
3
12
  This makes a call graph among methods of a single Ruby file.
4
13
 
5
14
  I made it to help me orient myself in unfamiliar legacy code and to help
6
15
  identify cohesive parts that could be split out.
7
16
 
8
- Yes, it is quick and dirty.
17
+ ### class-dependencies
18
+
19
+ Identifies fully qualified class names and makes an inheritance graph
9
20
 
10
21
  ## Requirements
11
22
 
12
23
  - [parser gem](https://github.com/whitequark/parser)
13
24
  - [Graphviz](http://www.graphviz.org/)
25
+ - Sinatra
26
+ - Cheetah
14
27
 
15
28
  ## License
16
29
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.0
1
+ 0.2.0
@@ -5,11 +5,12 @@
5
5
  require "parser/current"
6
6
  require "pp"
7
7
 
8
+ require "code_explorer/consts"
8
9
  require "code_explorer/dot"
9
10
 
10
11
  def main
11
12
  asts = ARGV.map {|fn| ast_for_filename(fn)}
12
- cs = Consts.new
13
+ cs = CodeExplorer::Consts.new
13
14
  cs.report_modules(asts)
14
15
  graph = cs.superclasses
15
16
  puts dot_from_hash(graph)
@@ -20,158 +21,4 @@ def ast_for_filename(fn)
20
21
  Parser::CurrentRuby.parse(ruby)
21
22
  end
22
23
 
23
- # ruby [String] a ruby program
24
- # @return a dot graph string
25
- def dep_graph(ast)
26
- Consts.new.report_modules(ast)
27
- dot_from_hash({})
28
- end
29
-
30
- # tracks what constants are resolvable
31
- class ConstBinding
32
- def initialize(fqname, parent = nil)
33
- @fqname = fqname
34
- @parent = parent
35
- @known = {}
36
- end
37
-
38
- # @return [ConstBinding] the new scope
39
- def open_namespace(fqname)
40
- ns = @known[fqname]
41
- if ns.is_a? ConstBinding
42
- # puts "(reopening #{fqname})"
43
- else
44
- ns = self.class.new(fqname, self)
45
- @known[fqname] = ns
46
- end
47
- ns
48
- end
49
-
50
- # @return [ConstBinding] the parent scope
51
- def close_namespace
52
- @parent
53
- end
54
-
55
- def declare_const(fqname)
56
- if @known[fqname]
57
- # puts "warning: #{fqname} already declared"
58
- end
59
- @known[fqname] = :const
60
- end
61
-
62
- def resolve_declared_const(name)
63
- if @fqname.empty?
64
- name
65
- else
66
- "#{@fqname}::#{name}"
67
- end
68
- end
69
-
70
- def resolve_used_const(name)
71
- # puts "resolving #{name} in #{@fqname}, known #{@known.inspect}"
72
- candidate = resolve_declared_const(name)
73
- if @known.include?(candidate)
74
- candidate
75
- elsif @parent
76
- @parent.resolve_used_const(name)
77
- else
78
- name
79
- end
80
- end
81
- end
82
-
83
- class Consts < Parser::AST::Processor
84
- include AST::Sexp
85
-
86
- # @return [ConstBinding]
87
- attr_reader :cb
88
- # @return [Hash{String => Array<String>}]
89
- attr_reader :superclasses
90
-
91
- def initialize
92
- @cb = ConstBinding.new("")
93
- @superclasses = {}
94
- end
95
-
96
- def report_modules(asts)
97
- Array(asts).each do |ast|
98
- process(ast)
99
- end
100
- end
101
-
102
- def const_name_from_sexp(node)
103
- case node.type
104
- when :self
105
- "self"
106
- when :cbase
107
- ""
108
- when :const, :casgn
109
- parent, name, _maybe_value = *node
110
- if parent
111
- const_name_from_sexp(parent) + "::#{name}"
112
- else
113
- name.to_s
114
- end
115
- else
116
- raise "Unexpected #{node.type}"
117
- end
118
- end
119
-
120
- def new_scope(name, &block)
121
- @cb = cb.open_namespace(name)
122
- block.call
123
- @cb = cb.close_namespace
124
- end
125
-
126
- def on_module(node)
127
- name, _body = *node
128
- name = cb.resolve_declared_const(const_name_from_sexp(name))
129
- # puts "module #{name}"
130
-
131
- new_scope(name) do
132
- super
133
- end
134
- end
135
-
136
- def on_class(node)
137
- name, parent, _body = *node
138
- parent ||= s(:const, s(:cbase), :Object)
139
-
140
- name = cb.resolve_declared_const(const_name_from_sexp(name))
141
- parent = cb.resolve_used_const(const_name_from_sexp(parent))
142
- # puts "class #{name} < #{parent}"
143
-
144
- @superclasses[name] = [parent]
145
-
146
- new_scope(name) do
147
- super
148
- end
149
- end
150
-
151
- def on_sclass(node)
152
- parent, _body = *node
153
-
154
- parent = const_name_from_sexp(parent)
155
- name = "<< #{parent}" # cheating
156
- # puts "class #{name}"
157
-
158
- new_scope(name) do
159
- super
160
- end
161
- end
162
-
163
- def on_casgn(node)
164
- name = cb.resolve_declared_const(const_name_from_sexp(node))
165
- cb.declare_const(name)
166
- # puts "casgn #{name}"
167
- end
168
-
169
- def on_const(node)
170
- name = const_name_from_sexp(node)
171
- fqname = cb.resolve_used_const(name)
172
- # puts "CONST #{fqname}"
173
- end
174
-
175
- end
176
-
177
24
  main
@@ -4,6 +4,7 @@ require "sinatra"
4
4
  require "cheetah"
5
5
 
6
6
  require "code_explorer/call_graph"
7
+ require "code_explorer/consts"
7
8
  require "code_explorer/numbered_lines"
8
9
 
9
10
  def path_link(prefix, path, text)
@@ -16,11 +17,40 @@ configure do
16
17
  end
17
18
 
18
19
  get "/" do
20
+ page = ""
21
+
22
+ page << path_link("", "class-inheritance", "Class inheritance in /lib/")
23
+ page << "<hr>\n"
24
+
19
25
  files = Dir.glob("**/*.rb").sort
20
- files.map do |f|
26
+ page << files.map do |f|
21
27
  path_link("/files", f, f) + " " +
22
28
  path_link("/call-graph", f, "call graph")
23
29
  end.join("<br>\n")
30
+ page
31
+ end
32
+
33
+ def ast_for_filename(fn)
34
+ ruby = File.read(fn)
35
+ Parser::CurrentRuby.parse(ruby)
36
+ end
37
+
38
+ def serve_dot(dot)
39
+ type = :svg
40
+ graph = Cheetah.run(["dot", "-T#{type}"], stdin: dot, stdout: :capture)
41
+
42
+ content_type type
43
+ graph
44
+ end
45
+
46
+ get "/class-inheritance" do
47
+ files = Dir.glob("lib/**/*.rb").sort
48
+ asts = files.map {|fn| ast_for_filename(fn)}
49
+ cs = CodeExplorer::Consts.new
50
+ cs.report_modules(asts)
51
+ graph = cs.superclasses
52
+
53
+ serve_dot(dot_from_hash(graph))
24
54
  end
25
55
 
26
56
  get "/files/*" do |path|
@@ -31,9 +61,5 @@ end
31
61
  get "/call-graph/*" do |path|
32
62
  dot = call_graph(path)
33
63
 
34
- type = :svg
35
- graph = Cheetah.run(["dot", "-T#{type}"], stdin: dot, stdout: :capture)
36
-
37
- content_type type
38
- graph
64
+ serve_dot(dot)
39
65
  end
@@ -0,0 +1,54 @@
1
+ module CodeExplorer
2
+ # tracks what constants are resolvable
3
+ class ConstBinding
4
+ def initialize(fqname, parent = nil)
5
+ @fqname = fqname
6
+ @parent = parent
7
+ @known = {}
8
+ end
9
+
10
+ # @return [ConstBinding] the new scope
11
+ def open_namespace(fqname)
12
+ ns = @known[fqname]
13
+ if ns.is_a? ConstBinding
14
+ # puts "(reopening #{fqname})"
15
+ else
16
+ ns = self.class.new(fqname, self)
17
+ @known[fqname] = ns
18
+ end
19
+ ns
20
+ end
21
+
22
+ # @return [ConstBinding] the parent scope
23
+ def close_namespace
24
+ @parent
25
+ end
26
+
27
+ def declare_const(fqname)
28
+ if @known[fqname]
29
+ # puts "warning: #{fqname} already declared"
30
+ end
31
+ @known[fqname] = :const
32
+ end
33
+
34
+ def resolve_declared_const(name)
35
+ if @fqname.empty?
36
+ name
37
+ else
38
+ "#{@fqname}::#{name}"
39
+ end
40
+ end
41
+
42
+ def resolve_used_const(name)
43
+ # puts "resolving #{name} in #{@fqname}, known #{@known.inspect}"
44
+ candidate = resolve_declared_const(name)
45
+ if @known.include?(candidate)
46
+ candidate
47
+ elsif @parent
48
+ @parent.resolve_used_const(name)
49
+ else
50
+ name
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,96 @@
1
+ require_relative "const_binding"
2
+
3
+ module CodeExplorer
4
+ class Consts < Parser::AST::Processor
5
+ include AST::Sexp
6
+
7
+ # @return [ConstBinding]
8
+ attr_reader :cb
9
+ # @return [Hash{String => Array<String>}]
10
+ attr_reader :superclasses
11
+
12
+ def initialize
13
+ @cb = ConstBinding.new("")
14
+ @superclasses = {}
15
+ end
16
+
17
+ def report_modules(asts)
18
+ Array(asts).each do |ast|
19
+ process(ast)
20
+ end
21
+ end
22
+
23
+ def const_name_from_sexp(node)
24
+ case node.type
25
+ when :self
26
+ "self"
27
+ when :cbase
28
+ ""
29
+ when :const, :casgn
30
+ parent, name, _maybe_value = *node
31
+ if parent
32
+ const_name_from_sexp(parent) + "::#{name}"
33
+ else
34
+ name.to_s
35
+ end
36
+ else
37
+ raise "Unexpected #{node.type}"
38
+ end
39
+ end
40
+
41
+ def new_scope(name, &block)
42
+ @cb = cb.open_namespace(name)
43
+ block.call
44
+ @cb = cb.close_namespace
45
+ end
46
+
47
+ def on_module(node)
48
+ name, _body = *node
49
+ name = cb.resolve_declared_const(const_name_from_sexp(name))
50
+ # puts "module #{name}"
51
+
52
+ new_scope(name) do
53
+ super
54
+ end
55
+ end
56
+
57
+ def on_class(node)
58
+ name, parent, _body = *node
59
+ parent ||= s(:const, s(:cbase), :Object)
60
+
61
+ name = cb.resolve_declared_const(const_name_from_sexp(name))
62
+ parent = cb.resolve_used_const(const_name_from_sexp(parent))
63
+ # puts "class #{name} < #{parent}"
64
+
65
+ @superclasses[name] = [parent]
66
+
67
+ new_scope(name) do
68
+ super
69
+ end
70
+ end
71
+
72
+ def on_sclass(node)
73
+ parent, _body = *node
74
+
75
+ parent = const_name_from_sexp(parent)
76
+ name = "<< #{parent}" # cheating
77
+ # puts "class #{name}"
78
+
79
+ new_scope(name) do
80
+ super
81
+ end
82
+ end
83
+
84
+ def on_casgn(node)
85
+ name = cb.resolve_declared_const(const_name_from_sexp(node))
86
+ cb.declare_const(name)
87
+ # puts "casgn #{name}"
88
+ end
89
+
90
+ def on_const(node)
91
+ name = const_name_from_sexp(node)
92
+ fqname = cb.resolve_used_const(name)
93
+ # puts "CONST #{fqname}"
94
+ end
95
+ end
96
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: code-explorer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Martin Vidner
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-06-20 00:00:00.000000000 Z
11
+ date: 2016-06-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: parser
@@ -69,6 +69,8 @@ files:
69
69
  - bin/code-explorer
70
70
  - bin/required-files
71
71
  - lib/code_explorer/call_graph.rb
72
+ - lib/code_explorer/const_binding.rb
73
+ - lib/code_explorer/consts.rb
72
74
  - lib/code_explorer/dot.rb
73
75
  - lib/code_explorer/numbered_lines.rb
74
76
  - lib/code_explorer/version.rb