graph 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
data.tar.gz.sig CHANGED
Binary file
data/.autotest CHANGED
@@ -1,6 +1,7 @@
1
1
  # -*- ruby -*-
2
2
 
3
3
  require 'autotest/restart'
4
+ require 'autotest/rcov'
4
5
 
5
6
  # Autotest.add_hook :initialize do |at|
6
7
  # at.extra_files << "../some/external/dependency.rb"
@@ -1,3 +1,11 @@
1
+ === 1.1.0 / 2009-04-16
2
+
3
+ * 3 minor enhancements:
4
+
5
+ * Don't run dot if type is nil
6
+ * Provide a command plugin for RubyGems, `gem graph`
7
+ * YAY tests
8
+
1
9
  === 1.0.0 / 2009-03-27
2
10
 
3
11
  * 1 major enhancement
@@ -4,8 +4,11 @@ Manifest.txt
4
4
  README.txt
5
5
  Rakefile
6
6
  bin/graph
7
+ lib/dep_analyzer.rb
7
8
  lib/freebsd_analyzer.rb
8
9
  lib/graph.rb
9
10
  lib/macports_analyzer.rb
11
+ lib/rubygems/commands/graph_command.rb
10
12
  lib/rubygems_analyzer.rb
13
+ lib/rubygems_plugin.rb
11
14
  test/test_graph.rb
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"
@@ -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
+
@@ -1,3 +1,5 @@
1
+ require 'dep_analyzer'
2
+
1
3
  class FreebsdAnalyzer < DepAnalyzer
2
4
  def installed
3
5
  # don't cache so it updates every delete
@@ -1,7 +1,7 @@
1
1
  #!/usr/local/bin/ruby -w
2
2
 
3
3
  class Graph < Hash
4
- VERSION = '1.0.0'
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 = Hash.new { |h,k| h[k] = Hash.new { |h2,k2| h2[k2] = [] } }
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.puts self.to_s
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
@@ -1,3 +1,5 @@
1
+ require 'dep_analyzer'
2
+
1
3
  class MacportsAnalyzer < DepAnalyzer
2
4
  def installed
3
5
  # don't cache so it updates every delete
@@ -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
+
@@ -1,3 +1,5 @@
1
+ require 'dep_analyzer'
2
+
1
3
  class RubygemsAnalyzer < DepAnalyzer
2
4
  def installed
3
5
  require 'rubygems'
@@ -0,0 +1,4 @@
1
+ require 'rubygems/command_manager'
2
+
3
+ Gem::CommandManager.instance.register_command :graph
4
+
@@ -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 test_sanity
6
- flunk "write tests or I will kneecap you"
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.0.0
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-03-31 00:00:00 -07:00
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.0
44
+ version: 1.12.2
45
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).
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.1
103
+ rubygems_version: 1.3.2
93
104
  signing_key:
94
- specification_version: 2
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