graph 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.autotest +23 -0
- data/History.txt +6 -0
- data/Manifest.txt +11 -0
- data/README.txt +68 -0
- data/Rakefile +12 -0
- data/bin/graph +149 -0
- data/lib/freebsd_analyzer.rb +19 -0
- data/lib/graph.rb +89 -0
- data/lib/macports_analyzer.rb +19 -0
- data/lib/rubygems_analyzer.rb +20 -0
- data/test/test_graph.rb +8 -0
- data.tar.gz.sig +0 -0
- metadata +97 -0
- metadata.gz.sig +0 -0
data/.autotest
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
require 'autotest/restart'
|
4
|
+
|
5
|
+
# Autotest.add_hook :initialize do |at|
|
6
|
+
# at.extra_files << "../some/external/dependency.rb"
|
7
|
+
#
|
8
|
+
# at.libs << ":../some/external"
|
9
|
+
#
|
10
|
+
# at.add_exception 'vendor'
|
11
|
+
#
|
12
|
+
# at.add_mapping(/dependency.rb/) do |f, _|
|
13
|
+
# at.files_matching(/test_.*rb$/)
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# %w(TestA TestB).each do |klass|
|
17
|
+
# at.extra_class_map[klass] = "test/test_misc.rb"
|
18
|
+
# end
|
19
|
+
# end
|
20
|
+
|
21
|
+
# Autotest.add_hook :run_command do |at|
|
22
|
+
# system "rake build"
|
23
|
+
# end
|
data/History.txt
ADDED
data/Manifest.txt
ADDED
data/README.txt
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
= graph
|
2
|
+
|
3
|
+
* http://rubyforge.org/projects/seattlerb
|
4
|
+
|
5
|
+
== DESCRIPTION:
|
6
|
+
|
7
|
+
Graph is a type of hash that outputs in graphviz's dot format. It
|
8
|
+
comes with a command-line interface that is easily pluggable.
|
9
|
+
|
10
|
+
It ships with plugins to graph dependencies and status of installed
|
11
|
+
rubygems, mac ports, and freebsd ports, coloring leaf nodes blue,
|
12
|
+
outdated nodes red, and outdated leaf nodes purple (red+blue).
|
13
|
+
|
14
|
+
== FEATURES/PROBLEMS:
|
15
|
+
|
16
|
+
* Very easy hash interface.
|
17
|
+
* Saves to dot and automatically converts to png (or whatever).
|
18
|
+
* edge and node attributes are easy to set.
|
19
|
+
* bin/graph includes a caching mechanism for slower fairly static data.
|
20
|
+
|
21
|
+
== SYNOPSIS:
|
22
|
+
|
23
|
+
deps = Graph.new
|
24
|
+
|
25
|
+
ObjectSpace.each_object Class do |mod|
|
26
|
+
next if mod.name =~ /Errno/
|
27
|
+
next unless mod < Exception
|
28
|
+
deps[mod.to_s] = mod.superclass.to_s
|
29
|
+
end
|
30
|
+
|
31
|
+
deps.attribs["StandardError"] << "color = blue"
|
32
|
+
deps.attribs["RuntimeError"] << "color = red"
|
33
|
+
deps.prefix << "rankdir = BT" # put Exception on top
|
34
|
+
|
35
|
+
deps.save "exceptions"
|
36
|
+
|
37
|
+
== REQUIREMENTS:
|
38
|
+
|
39
|
+
* hoe
|
40
|
+
|
41
|
+
== INSTALL:
|
42
|
+
|
43
|
+
* sudo gem install graph
|
44
|
+
|
45
|
+
== LICENSE:
|
46
|
+
|
47
|
+
(The MIT License)
|
48
|
+
|
49
|
+
Copyright (c) 2009 Ryan Davis, seattle.rb
|
50
|
+
|
51
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
52
|
+
a copy of this software and associated documentation files (the
|
53
|
+
'Software'), to deal in the Software without restriction, including
|
54
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
55
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
56
|
+
permit persons to whom the Software is furnished to do so, subject to
|
57
|
+
the following conditions:
|
58
|
+
|
59
|
+
The above copyright notice and this permission notice shall be
|
60
|
+
included in all copies or substantial portions of the Software.
|
61
|
+
|
62
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
63
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
64
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
65
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
66
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
67
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
68
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
data/bin/graph
ADDED
@@ -0,0 +1,149 @@
|
|
1
|
+
#!/usr/bin/env ruby -w
|
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
|
+
type = (ARGV.shift || "macports")
|
144
|
+
|
145
|
+
require "#{type}_analyzer"
|
146
|
+
|
147
|
+
klass = Object.const_get "#{type.capitalize}Analyzer"
|
148
|
+
analyzer = klass.new
|
149
|
+
analyzer.run
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class FreebsdAnalyzer < DepAnalyzer
|
2
|
+
def installed
|
3
|
+
# don't cache so it updates every delete
|
4
|
+
puts "scanning installed ports"
|
5
|
+
`pkg_info`.split(/\n/).map { |s| s.split.first }
|
6
|
+
end
|
7
|
+
|
8
|
+
def outdated
|
9
|
+
# don't cache so it updates every delete
|
10
|
+
puts "scanning outdated ports"
|
11
|
+
`pkg_version -vL=`.split(/\n/)[1..-1].map { |s| s.split.first }
|
12
|
+
end
|
13
|
+
|
14
|
+
def deps port
|
15
|
+
cache("#{port}.deps") {
|
16
|
+
`pkg_info -r #{port}`
|
17
|
+
}.grep(/Dependency:/).map { |s| s.split[1] }
|
18
|
+
end
|
19
|
+
end
|
data/lib/graph.rb
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
#!/usr/local/bin/ruby -w
|
2
|
+
|
3
|
+
class Graph < Hash
|
4
|
+
VERSION = '1.0.0'
|
5
|
+
|
6
|
+
attr_reader :attribs
|
7
|
+
attr_reader :prefix
|
8
|
+
attr_reader :order
|
9
|
+
attr_reader :edge
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
super { |h,k| h[k] = [] }
|
13
|
+
@prefix = []
|
14
|
+
@attribs = Hash.new { |h,k| h[k] = [] }
|
15
|
+
@edge = Hash.new { |h,k| h[k] = Hash.new { |h2,k2| h2[k2] = [] } }
|
16
|
+
@order = []
|
17
|
+
end
|
18
|
+
|
19
|
+
def []= key, val
|
20
|
+
@order << key unless self.has_key? key
|
21
|
+
super
|
22
|
+
end
|
23
|
+
|
24
|
+
def delete key
|
25
|
+
@order.delete key
|
26
|
+
super
|
27
|
+
end
|
28
|
+
|
29
|
+
def filter_size minimum
|
30
|
+
counts.each do |node, count|
|
31
|
+
next unless count < minimum
|
32
|
+
delete node
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def each_pair
|
37
|
+
@order.each do |from|
|
38
|
+
self[from].each do |to|
|
39
|
+
yield from, to
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def invert
|
45
|
+
result = self.class.new
|
46
|
+
each_pair do |from, to|
|
47
|
+
result[to] << from
|
48
|
+
end
|
49
|
+
result
|
50
|
+
end
|
51
|
+
|
52
|
+
def counts
|
53
|
+
result = Hash.new 0
|
54
|
+
each_pair do |from, to|
|
55
|
+
result[from] += 1
|
56
|
+
end
|
57
|
+
result
|
58
|
+
end
|
59
|
+
|
60
|
+
def keys_by_count
|
61
|
+
counts.sort_by { |key, count| -count }.map {|key, count| key }
|
62
|
+
end
|
63
|
+
|
64
|
+
def to_s
|
65
|
+
result = []
|
66
|
+
result << "digraph absent"
|
67
|
+
result << " {"
|
68
|
+
@prefix.each do |line|
|
69
|
+
result << line
|
70
|
+
end
|
71
|
+
@attribs.sort.each do |node, attribs|
|
72
|
+
result << " #{node.inspect} [ #{attribs.join ','} ]"
|
73
|
+
end
|
74
|
+
each_pair do |from, to|
|
75
|
+
edge = @edge[from][to].join ", "
|
76
|
+
edge = " [ #{edge} ]" unless edge.empty?
|
77
|
+
result << " #{from.inspect} -> #{to.inspect}#{edge};"
|
78
|
+
end
|
79
|
+
result << " }"
|
80
|
+
result.join "\n"
|
81
|
+
end
|
82
|
+
|
83
|
+
def save path, type="png"
|
84
|
+
File.open "#{path}.dot", "w" do |f|
|
85
|
+
f.puts self.to_s
|
86
|
+
end
|
87
|
+
system "dot -T#{type} #{path}.dot > #{path}.#{type}"
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class MacportsAnalyzer < DepAnalyzer
|
2
|
+
def installed
|
3
|
+
# don't cache so it updates every delete
|
4
|
+
puts "scanning installed ports"
|
5
|
+
`port installed`.split(/\n/)[1..-1].map { |s| s.split.first }
|
6
|
+
end
|
7
|
+
|
8
|
+
def outdated
|
9
|
+
# don't cache so it updates every delete
|
10
|
+
puts "scanning outdated ports"
|
11
|
+
`port outdated`.split(/\n/)[1..-1].map { |s| s.split.first }
|
12
|
+
end
|
13
|
+
|
14
|
+
def deps port
|
15
|
+
cache("#{port}.deps") {
|
16
|
+
`port deps #{port}`
|
17
|
+
}.scan(/^\t(\S+)$/).flatten
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class RubygemsAnalyzer < DepAnalyzer
|
2
|
+
def installed
|
3
|
+
require 'rubygems'
|
4
|
+
ENV['GEM_PATH'] = `gem env home`
|
5
|
+
Gem.clear_paths
|
6
|
+
# don't cache so it updates every delete
|
7
|
+
puts "scanning installed rubygems"
|
8
|
+
Gem.source_index.gems.values.map { |gem| gem.name }.sort
|
9
|
+
end
|
10
|
+
|
11
|
+
def outdated
|
12
|
+
# don't cache so it updates every delete
|
13
|
+
puts "scanning outdated rubygems"
|
14
|
+
Gem.source_index.outdated.sort
|
15
|
+
end
|
16
|
+
|
17
|
+
def deps port
|
18
|
+
Gem.source_index.find_name(port).first.dependencies.map { |dep| dep.name }
|
19
|
+
end
|
20
|
+
end
|
data/test/test_graph.rb
ADDED
data.tar.gz.sig
ADDED
Binary file
|
metadata
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: graph
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ryan Davis
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain:
|
11
|
+
- |
|
12
|
+
-----BEGIN CERTIFICATE-----
|
13
|
+
MIIDPjCCAiagAwIBAgIBADANBgkqhkiG9w0BAQUFADBFMRMwEQYDVQQDDApyeWFu
|
14
|
+
ZC1ydWJ5MRkwFwYKCZImiZPyLGQBGRYJemVuc3BpZGVyMRMwEQYKCZImiZPyLGQB
|
15
|
+
GRYDY29tMB4XDTA5MDMwNjE4NTMxNVoXDTEwMDMwNjE4NTMxNVowRTETMBEGA1UE
|
16
|
+
AwwKcnlhbmQtcnVieTEZMBcGCgmSJomT8ixkARkWCXplbnNwaWRlcjETMBEGCgmS
|
17
|
+
JomT8ixkARkWA2NvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALda
|
18
|
+
b9DCgK+627gPJkB6XfjZ1itoOQvpqH1EXScSaba9/S2VF22VYQbXU1xQXL/WzCkx
|
19
|
+
taCPaLmfYIaFcHHCSY4hYDJijRQkLxPeB3xbOfzfLoBDbjvx5JxgJxUjmGa7xhcT
|
20
|
+
oOvjtt5P8+GSK9zLzxQP0gVLS/D0FmoE44XuDr3iQkVS2ujU5zZL84mMNqNB1znh
|
21
|
+
GiadM9GHRaDiaxuX0cIUBj19T01mVE2iymf9I6bEsiayK/n6QujtyCbTWsAS9Rqt
|
22
|
+
qhtV7HJxNKuPj/JFH0D2cswvzznE/a5FOYO68g+YCuFi5L8wZuuM8zzdwjrWHqSV
|
23
|
+
gBEfoTEGr7Zii72cx+sCAwEAAaM5MDcwCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAw
|
24
|
+
HQYDVR0OBBYEFEfFe9md/r/tj/Wmwpy+MI8d9k/hMA0GCSqGSIb3DQEBBQUAA4IB
|
25
|
+
AQAY59gYvDxqSqgC92nAP9P8dnGgfZgLxP237xS6XxFGJSghdz/nI6pusfCWKM8m
|
26
|
+
vzjjH2wUMSSf3tNudQ3rCGLf2epkcU13/rguI88wO6MrE0wi4ZqLQX+eZQFskJb/
|
27
|
+
w6x9W1ur8eR01s397LSMexySDBrJOh34cm2AlfKr/jokKCTwcM0OvVZnAutaovC0
|
28
|
+
l1SVZ0ecg88bsWHA0Yhh7NFxK1utWoIhtB6AFC/+trM0FQEB/jZkIS8SaNzn96Rl
|
29
|
+
n0sZEf77FLf5peR8TP/PtmIg7Cyqz23sLM4mCOoTGIy5OcZ8TdyiyINUHtb5ej/T
|
30
|
+
FBHgymkyj/AOSqKRIpXPhjC6
|
31
|
+
-----END CERTIFICATE-----
|
32
|
+
|
33
|
+
date: 2009-03-31 00:00:00 -07:00
|
34
|
+
default_executable:
|
35
|
+
dependencies:
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: hoe
|
38
|
+
type: :development
|
39
|
+
version_requirement:
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: 1.12.0
|
45
|
+
version:
|
46
|
+
description: Graph is a type of hash that outputs in graphviz's dot format. It comes with a command-line interface that is easily pluggable. It ships with plugins to graph dependencies and status of installed rubygems, mac ports, and freebsd ports, coloring leaf nodes blue, outdated nodes red, and outdated leaf nodes purple (red+blue).
|
47
|
+
email:
|
48
|
+
- ryand-ruby@zenspider.com
|
49
|
+
executables:
|
50
|
+
- graph
|
51
|
+
extensions: []
|
52
|
+
|
53
|
+
extra_rdoc_files:
|
54
|
+
- History.txt
|
55
|
+
- Manifest.txt
|
56
|
+
- README.txt
|
57
|
+
files:
|
58
|
+
- .autotest
|
59
|
+
- History.txt
|
60
|
+
- Manifest.txt
|
61
|
+
- README.txt
|
62
|
+
- Rakefile
|
63
|
+
- bin/graph
|
64
|
+
- lib/freebsd_analyzer.rb
|
65
|
+
- lib/graph.rb
|
66
|
+
- lib/macports_analyzer.rb
|
67
|
+
- lib/rubygems_analyzer.rb
|
68
|
+
- test/test_graph.rb
|
69
|
+
has_rdoc: true
|
70
|
+
homepage: http://rubyforge.org/projects/seattlerb
|
71
|
+
post_install_message:
|
72
|
+
rdoc_options:
|
73
|
+
- --main
|
74
|
+
- README.txt
|
75
|
+
require_paths:
|
76
|
+
- lib
|
77
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - ">="
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: "0"
|
82
|
+
version:
|
83
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
84
|
+
requirements:
|
85
|
+
- - ">="
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: "0"
|
88
|
+
version:
|
89
|
+
requirements: []
|
90
|
+
|
91
|
+
rubyforge_project: seattlerb
|
92
|
+
rubygems_version: 1.3.1
|
93
|
+
signing_key:
|
94
|
+
specification_version: 2
|
95
|
+
summary: Graph is a type of hash that outputs in graphviz's dot format
|
96
|
+
test_files:
|
97
|
+
- test/test_graph.rb
|
metadata.gz.sig
ADDED
Binary file
|