graph 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,6 @@
1
+ === 1.0.0 / 2009-03-27
2
+
3
+ * 1 major enhancement
4
+
5
+ * Birthday!
6
+
data/Manifest.txt ADDED
@@ -0,0 +1,11 @@
1
+ .autotest
2
+ History.txt
3
+ Manifest.txt
4
+ README.txt
5
+ Rakefile
6
+ bin/graph
7
+ lib/freebsd_analyzer.rb
8
+ lib/graph.rb
9
+ lib/macports_analyzer.rb
10
+ lib/rubygems_analyzer.rb
11
+ test/test_graph.rb
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
@@ -0,0 +1,12 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'rubygems'
4
+ require 'hoe'
5
+ require './lib/graph.rb'
6
+
7
+ Hoe.new('graph', Graph::VERSION) do |p|
8
+ p.rubyforge_name = 'seattlerb'
9
+ p.developer('Ryan Davis', 'ryand-ruby@zenspider.com')
10
+ end
11
+
12
+ # vim: syntax=Ruby
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
@@ -0,0 +1,8 @@
1
+ require "test/unit"
2
+ require "graph"
3
+
4
+ class TestGraph < Test::Unit::TestCase
5
+ def test_sanity
6
+ flunk "write tests or I will kneecap you"
7
+ end
8
+ end
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