graph 1.0.0 → 1.1.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.
- data.tar.gz.sig +0 -0
- data/.autotest +1 -0
- data/History.txt +8 -0
- data/Manifest.txt +3 -0
- data/bin/graph +0 -140
- data/lib/dep_analyzer.rb +140 -0
- data/lib/freebsd_analyzer.rb +2 -0
- data/lib/graph.rb +12 -8
- data/lib/macports_analyzer.rb +2 -0
- data/lib/rubygems/commands/graph_command.rb +17 -0
- data/lib/rubygems_analyzer.rb +2 -0
- data/lib/rubygems_plugin.rb +4 -0
- data/test/test_graph.rb +92 -2
- metadata +17 -6
- metadata.gz.sig +0 -0
data.tar.gz.sig
CHANGED
Binary file
|
data/.autotest
CHANGED
data/History.txt
CHANGED
data/Manifest.txt
CHANGED
data/bin/graph
CHANGED
@@ -1,145 +1,5 @@
|
|
1
1
|
#!/usr/bin/env ruby -w
|
2
2
|
|
3
|
-
require 'graph'
|
4
|
-
require 'set'
|
5
|
-
require 'tsort'
|
6
|
-
|
7
|
-
class Cache
|
8
|
-
def initialize(cache, timeout=24)
|
9
|
-
@cache = cache
|
10
|
-
@timeout = timeout
|
11
|
-
end
|
12
|
-
|
13
|
-
def cache(id, timeout=@timeout)
|
14
|
-
Dir.mkdir @cache unless test ?d, @cache
|
15
|
-
path = File.join @cache, id
|
16
|
-
|
17
|
-
age = test(?f, path) ? (Time.now - test( ?M, path )) / 3600 : -1
|
18
|
-
|
19
|
-
if age >= 0 and timeout > age then
|
20
|
-
warn "from cache" if $DEBUG
|
21
|
-
data = File.read(path)
|
22
|
-
else
|
23
|
-
warn "NOT from cache (#{age} hours old)" if $DEBUG
|
24
|
-
data = yield
|
25
|
-
File.open(path, "w") do |f|
|
26
|
-
f.write data
|
27
|
-
end
|
28
|
-
end
|
29
|
-
return data
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
class Set
|
34
|
-
def push *v
|
35
|
-
v.each do |o|
|
36
|
-
add(o)
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
class Hash
|
42
|
-
include TSort
|
43
|
-
|
44
|
-
alias tsort_each_node each_key
|
45
|
-
|
46
|
-
def tsort_each_child(node, &block)
|
47
|
-
fetch(node).each(&block)
|
48
|
-
end
|
49
|
-
|
50
|
-
def minvert
|
51
|
-
r = Hash.new { |h,k| h[k] = [] }
|
52
|
-
invert.each do |keys, val|
|
53
|
-
keys.each do |key|
|
54
|
-
r[key] << val
|
55
|
-
end
|
56
|
-
end
|
57
|
-
r.each do |k,v|
|
58
|
-
r[k] = v.sort
|
59
|
-
end
|
60
|
-
r
|
61
|
-
end
|
62
|
-
|
63
|
-
def transitive
|
64
|
-
r = Hash.new { |h,k| h[k] = [] }
|
65
|
-
each do |k,v|
|
66
|
-
r[k].push(*t(k))
|
67
|
-
end
|
68
|
-
r
|
69
|
-
end
|
70
|
-
|
71
|
-
def t(k)
|
72
|
-
r = Set.new
|
73
|
-
self[k].each do |v|
|
74
|
-
r.push(v, *t(v))
|
75
|
-
end
|
76
|
-
r.to_a.sort
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
class DepAnalyzer < Cache
|
81
|
-
def initialize
|
82
|
-
super ".#{self.class}.cache"
|
83
|
-
end
|
84
|
-
|
85
|
-
def run
|
86
|
-
g = Graph.new
|
87
|
-
ports = {}
|
88
|
-
installed.each do |port|
|
89
|
-
ports[port] = nil
|
90
|
-
end
|
91
|
-
|
92
|
-
old = {}
|
93
|
-
outdated.each do |port|
|
94
|
-
old[port] = nil
|
95
|
-
end
|
96
|
-
|
97
|
-
all_ports = ports.keys
|
98
|
-
|
99
|
-
ports.each_key do |port|
|
100
|
-
deps = self.deps(port)
|
101
|
-
# remove things that don't intersect with installed list
|
102
|
-
deps -= (deps - all_ports)
|
103
|
-
deps.each do |dep|
|
104
|
-
g[port] << dep
|
105
|
-
end
|
106
|
-
ports[port] = deps
|
107
|
-
end
|
108
|
-
|
109
|
-
indies = ports.keys - ports.minvert.keys
|
110
|
-
indies.each do |k|
|
111
|
-
g.attribs[k] << "color = blue"
|
112
|
-
end
|
113
|
-
old.each do |k,v|
|
114
|
-
if indies.include? k then
|
115
|
-
g.attribs[k] << "color = purple4"
|
116
|
-
else
|
117
|
-
g.attribs[k] << "color = red"
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
puts "Looks like you can nuke:\n\t#{indies.join("\n\t")}"
|
122
|
-
|
123
|
-
unless ARGV.empty? then
|
124
|
-
ARGV.each do |pkg|
|
125
|
-
hits = ports.transitive[pkg]
|
126
|
-
sorted = ports.tsort.reverse
|
127
|
-
topo = [pkg] + sorted.select { |o| hits.include? o }
|
128
|
-
prune = ports.dup
|
129
|
-
topo.each do |k|
|
130
|
-
prune.delete(k)
|
131
|
-
end
|
132
|
-
topo -= prune.values.flatten.uniq
|
133
|
-
|
134
|
-
puts topo.join(' ')
|
135
|
-
end
|
136
|
-
end
|
137
|
-
|
138
|
-
g.save "#{self.class}"
|
139
|
-
system "open #{self.class}.png"
|
140
|
-
end
|
141
|
-
end
|
142
|
-
|
143
3
|
type = (ARGV.shift || "macports")
|
144
4
|
|
145
5
|
require "#{type}_analyzer"
|
data/lib/dep_analyzer.rb
ADDED
@@ -0,0 +1,140 @@
|
|
1
|
+
require 'graph'
|
2
|
+
require 'set'
|
3
|
+
require 'tsort'
|
4
|
+
|
5
|
+
class Cache
|
6
|
+
def initialize(cache, timeout=24)
|
7
|
+
@cache = cache
|
8
|
+
@timeout = timeout
|
9
|
+
end
|
10
|
+
|
11
|
+
def cache(id, timeout=@timeout)
|
12
|
+
Dir.mkdir @cache unless test ?d, @cache
|
13
|
+
path = File.join @cache, id
|
14
|
+
|
15
|
+
age = test(?f, path) ? (Time.now - test( ?M, path )) / 3600 : -1
|
16
|
+
|
17
|
+
if age >= 0 and timeout > age then
|
18
|
+
warn "from cache" if $DEBUG
|
19
|
+
data = File.read(path)
|
20
|
+
else
|
21
|
+
warn "NOT from cache (#{age} hours old)" if $DEBUG
|
22
|
+
data = yield
|
23
|
+
File.open(path, "w") do |f|
|
24
|
+
f.write data
|
25
|
+
end
|
26
|
+
end
|
27
|
+
return data
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class Set
|
32
|
+
def push *v
|
33
|
+
v.each do |o|
|
34
|
+
add(o)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class Hash
|
40
|
+
include TSort
|
41
|
+
|
42
|
+
alias tsort_each_node each_key
|
43
|
+
|
44
|
+
def tsort_each_child(node, &block)
|
45
|
+
fetch(node).each(&block)
|
46
|
+
end
|
47
|
+
|
48
|
+
def minvert
|
49
|
+
r = Hash.new { |h,k| h[k] = [] }
|
50
|
+
invert.each do |keys, val|
|
51
|
+
keys.each do |key|
|
52
|
+
r[key] << val
|
53
|
+
end
|
54
|
+
end
|
55
|
+
r.each do |k,v|
|
56
|
+
r[k] = v.sort
|
57
|
+
end
|
58
|
+
r
|
59
|
+
end
|
60
|
+
|
61
|
+
def transitive
|
62
|
+
r = Hash.new { |h,k| h[k] = [] }
|
63
|
+
each do |k,v|
|
64
|
+
r[k].push(*t(k))
|
65
|
+
end
|
66
|
+
r
|
67
|
+
end
|
68
|
+
|
69
|
+
def t(k)
|
70
|
+
r = Set.new
|
71
|
+
self[k].each do |v|
|
72
|
+
r.push(v, *t(v))
|
73
|
+
end
|
74
|
+
r.to_a.sort
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
class DepAnalyzer < Cache
|
79
|
+
def initialize
|
80
|
+
super ".#{self.class}.cache"
|
81
|
+
end
|
82
|
+
|
83
|
+
def run(argv = ARGV)
|
84
|
+
g = Graph.new
|
85
|
+
ports = {}
|
86
|
+
installed.each do |port|
|
87
|
+
ports[port] = nil
|
88
|
+
end
|
89
|
+
|
90
|
+
old = {}
|
91
|
+
outdated.each do |port|
|
92
|
+
old[port] = nil
|
93
|
+
end
|
94
|
+
|
95
|
+
all_ports = ports.keys
|
96
|
+
|
97
|
+
ports.each_key do |port|
|
98
|
+
deps = self.deps(port)
|
99
|
+
# remove things that don't intersect with installed list
|
100
|
+
deps -= (deps - all_ports)
|
101
|
+
deps.each do |dep|
|
102
|
+
g[port] << dep
|
103
|
+
end
|
104
|
+
ports[port] = deps
|
105
|
+
end
|
106
|
+
|
107
|
+
indies = ports.keys - ports.minvert.keys
|
108
|
+
indies.each do |k|
|
109
|
+
g.attribs[k] << "color = blue"
|
110
|
+
end
|
111
|
+
old.each do |k,v|
|
112
|
+
if indies.include? k then
|
113
|
+
g.attribs[k] << "color = purple4"
|
114
|
+
else
|
115
|
+
g.attribs[k] << "color = red"
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
puts "Looks like you can nuke:\n\t#{indies.join("\n\t")}"
|
120
|
+
|
121
|
+
unless argv.empty? then
|
122
|
+
argv.each do |pkg|
|
123
|
+
hits = ports.transitive[pkg]
|
124
|
+
sorted = ports.tsort.reverse
|
125
|
+
topo = [pkg] + sorted.select { |o| hits.include? o }
|
126
|
+
prune = ports.dup
|
127
|
+
topo.each do |k|
|
128
|
+
prune.delete(k)
|
129
|
+
end
|
130
|
+
topo -= prune.values.flatten.uniq
|
131
|
+
|
132
|
+
puts topo.join(' ')
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
g.save "#{self.class}"
|
137
|
+
system "open #{self.class}.png"
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
data/lib/freebsd_analyzer.rb
CHANGED
data/lib/graph.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
#!/usr/local/bin/ruby -w
|
2
2
|
|
3
3
|
class Graph < Hash
|
4
|
-
VERSION = '1.
|
4
|
+
VERSION = '1.1.0'
|
5
5
|
|
6
6
|
attr_reader :attribs
|
7
7
|
attr_reader :prefix
|
@@ -10,10 +10,10 @@ class Graph < Hash
|
|
10
10
|
|
11
11
|
def initialize
|
12
12
|
super { |h,k| h[k] = [] }
|
13
|
-
@prefix
|
13
|
+
@prefix = []
|
14
|
+
@order = []
|
14
15
|
@attribs = Hash.new { |h,k| h[k] = [] }
|
15
|
-
@edge
|
16
|
-
@order = []
|
16
|
+
@edge = Hash.new { |h,k| h[k] = Hash.new { |h2,k2| h2[k2] = [] } }
|
17
17
|
end
|
18
18
|
|
19
19
|
def []= key, val
|
@@ -65,25 +65,29 @@ class Graph < Hash
|
|
65
65
|
result = []
|
66
66
|
result << "digraph absent"
|
67
67
|
result << " {"
|
68
|
+
|
68
69
|
@prefix.each do |line|
|
69
|
-
result << line
|
70
|
+
result << " #{line};"
|
70
71
|
end
|
72
|
+
|
71
73
|
@attribs.sort.each do |node, attribs|
|
72
|
-
result << " #{node.inspect} [ #{attribs.join ','} ]"
|
74
|
+
result << " #{node.inspect} [ #{attribs.join ','} ];"
|
73
75
|
end
|
76
|
+
|
74
77
|
each_pair do |from, to|
|
75
78
|
edge = @edge[from][to].join ", "
|
76
79
|
edge = " [ #{edge} ]" unless edge.empty?
|
77
80
|
result << " #{from.inspect} -> #{to.inspect}#{edge};"
|
78
81
|
end
|
82
|
+
|
79
83
|
result << " }"
|
80
84
|
result.join "\n"
|
81
85
|
end
|
82
86
|
|
83
87
|
def save path, type="png"
|
84
88
|
File.open "#{path}.dot", "w" do |f|
|
85
|
-
f.
|
89
|
+
f.write self.to_s
|
86
90
|
end
|
87
|
-
system "dot -T#{type} #{path}.dot > #{path}.#{type}"
|
91
|
+
system "dot -T#{type} #{path}.dot > #{path}.#{type}" if type
|
88
92
|
end
|
89
93
|
end
|
data/lib/macports_analyzer.rb
CHANGED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'rubygems/command'
|
2
|
+
require 'rubygems_analyzer'
|
3
|
+
|
4
|
+
class Gem::Commands::GraphCommand < Gem::Command
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
super 'graph', 'Graph dependency relationships of installed gems'
|
8
|
+
end
|
9
|
+
|
10
|
+
def execute
|
11
|
+
RubygemsAnalyzer.new.run options[:args]
|
12
|
+
|
13
|
+
say "Graph saved to:\n\tRubygemsAnalyzer.png"
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
data/lib/rubygems_analyzer.rb
CHANGED
data/test/test_graph.rb
CHANGED
@@ -1,8 +1,98 @@
|
|
1
1
|
require "test/unit"
|
2
|
+
require "tmpdir"
|
2
3
|
require "graph"
|
3
4
|
|
4
5
|
class TestGraph < Test::Unit::TestCase
|
5
|
-
def
|
6
|
-
|
6
|
+
def setup
|
7
|
+
@graph = Graph.new
|
8
|
+
@graph["a"] << "b"
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_to_s_empty
|
12
|
+
assert_equal util_dot, Graph.new.to_s
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_delete
|
16
|
+
assert_equal %w(b), @graph.delete("a")
|
17
|
+
assert_equal [], @graph.order
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_filter_size
|
21
|
+
@graph.filter_size 2
|
22
|
+
assert @graph.empty?
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_invert
|
26
|
+
@graph["a"] << "c"
|
27
|
+
invert = @graph.invert
|
28
|
+
assert_equal %w(a), invert["b"]
|
29
|
+
assert_equal %w(a), invert["c"]
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_keys_by_count
|
33
|
+
@graph["a"] << "c"
|
34
|
+
@graph["d"] << "e" << "f" << "g"
|
35
|
+
assert_equal %w(d a), @graph.keys_by_count
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_order
|
39
|
+
assert_equal %w(a), @graph.order
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_save
|
43
|
+
util_save "png"
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_save_nil
|
47
|
+
util_save nil
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_to_s
|
51
|
+
expected = util_dot '"a" -> "b"'
|
52
|
+
assert_equal expected, @graph.to_s
|
53
|
+
|
54
|
+
@graph["a"] << "c"
|
55
|
+
|
56
|
+
expected = util_dot '"a" -> "b"', '"a" -> "c"'
|
57
|
+
assert_equal expected, @graph.to_s
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_to_s_prefix
|
61
|
+
@graph.prefix << "blah"
|
62
|
+
@graph["a"] << "c"
|
63
|
+
|
64
|
+
expected = util_dot('blah', '"a" -> "b"', '"a" -> "c"')
|
65
|
+
assert_equal expected, @graph.to_s
|
66
|
+
end
|
67
|
+
|
68
|
+
def test_to_s_attrib
|
69
|
+
@graph.attribs["a"] << "color = blue"
|
70
|
+
@graph["a"] << "c"
|
71
|
+
|
72
|
+
expected = util_dot('"a" [ color = blue ]', '"a" -> "b"', '"a" -> "c"')
|
73
|
+
assert_equal expected, @graph.to_s
|
74
|
+
end
|
75
|
+
|
76
|
+
def util_dot(*lines)
|
77
|
+
lines = lines.map { |l| " #{l};" }.join("\n")
|
78
|
+
"digraph absent\n {\n#{lines}\n }".sub(/\n\n/, "\n")
|
79
|
+
end
|
80
|
+
|
81
|
+
def util_save type
|
82
|
+
path = File.join(Dir.tmpdir, "blah.#{$$}")
|
83
|
+
|
84
|
+
$x = nil
|
85
|
+
|
86
|
+
def @graph.system(*args)
|
87
|
+
$x = args
|
88
|
+
end
|
89
|
+
|
90
|
+
@graph.save(path, type)
|
91
|
+
|
92
|
+
assert_equal @graph.to_s, File.read("#{path}.dot")
|
93
|
+
expected = ["dot -T#{type} #{path}.dot > #{path}.png"] if type
|
94
|
+
assert_equal expected, $x
|
95
|
+
ensure
|
96
|
+
File.unlink path rescue nil
|
7
97
|
end
|
8
98
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: graph
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ryan Davis
|
@@ -30,7 +30,7 @@ cert_chain:
|
|
30
30
|
FBHgymkyj/AOSqKRIpXPhjC6
|
31
31
|
-----END CERTIFICATE-----
|
32
32
|
|
33
|
-
date: 2009-
|
33
|
+
date: 2009-04-16 00:00:00 -07:00
|
34
34
|
default_executable:
|
35
35
|
dependencies:
|
36
36
|
- !ruby/object:Gem::Dependency
|
@@ -41,9 +41,15 @@ dependencies:
|
|
41
41
|
requirements:
|
42
42
|
- - ">="
|
43
43
|
- !ruby/object:Gem::Version
|
44
|
-
version: 1.12.
|
44
|
+
version: 1.12.2
|
45
45
|
version:
|
46
|
-
description:
|
46
|
+
description: |-
|
47
|
+
Graph is a type of hash that outputs in graphviz's dot format. It
|
48
|
+
comes with a command-line interface that is easily pluggable.
|
49
|
+
|
50
|
+
It ships with plugins to graph dependencies and status of installed
|
51
|
+
rubygems, mac ports, and freebsd ports, coloring leaf nodes blue,
|
52
|
+
outdated nodes red, and outdated leaf nodes purple (red+blue).
|
47
53
|
email:
|
48
54
|
- ryand-ruby@zenspider.com
|
49
55
|
executables:
|
@@ -61,13 +67,18 @@ files:
|
|
61
67
|
- README.txt
|
62
68
|
- Rakefile
|
63
69
|
- bin/graph
|
70
|
+
- lib/dep_analyzer.rb
|
64
71
|
- lib/freebsd_analyzer.rb
|
65
72
|
- lib/graph.rb
|
66
73
|
- lib/macports_analyzer.rb
|
74
|
+
- lib/rubygems/commands/graph_command.rb
|
67
75
|
- lib/rubygems_analyzer.rb
|
76
|
+
- lib/rubygems_plugin.rb
|
68
77
|
- test/test_graph.rb
|
69
78
|
has_rdoc: true
|
70
79
|
homepage: http://rubyforge.org/projects/seattlerb
|
80
|
+
licenses: []
|
81
|
+
|
71
82
|
post_install_message:
|
72
83
|
rdoc_options:
|
73
84
|
- --main
|
@@ -89,9 +100,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
89
100
|
requirements: []
|
90
101
|
|
91
102
|
rubyforge_project: seattlerb
|
92
|
-
rubygems_version: 1.3.
|
103
|
+
rubygems_version: 1.3.2
|
93
104
|
signing_key:
|
94
|
-
specification_version:
|
105
|
+
specification_version: 3
|
95
106
|
summary: Graph is a type of hash that outputs in graphviz's dot format
|
96
107
|
test_files:
|
97
108
|
- test/test_graph.rb
|
metadata.gz.sig
CHANGED
Binary file
|