depgraph 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +28 -0
- data/License.txt +20 -0
- data/README.rdoc +40 -0
- data/Rakefile +31 -0
- data/VERSION +1 -0
- data/bin/depgraph +89 -0
- data/config/hoe.rb +71 -0
- data/config/requirements.rb +19 -0
- data/lib/DepGraph/version.rb +9 -0
- data/lib/dependency_types.yaml +11 -0
- data/lib/dependency_types_manager.rb +49 -0
- data/lib/file_system_node_finder.rb +64 -0
- data/lib/graph_creator.rb +122 -0
- data/lib/graph_image_creator.rb +125 -0
- data/lib/node.rb +44 -0
- data/lib/nodefinders/gems_node_finder.rb +58 -0
- data/lib/nodefinders/test_node_finder.rb +26 -0
- data/script/destroy +14 -0
- data/script/destroy.cmd +1 -0
- data/script/generate +14 -0
- data/script/generate.cmd +1 -0
- data/script/txt2html +74 -0
- data/script/txt2html.cmd +1 -0
- data/setup.rb +1585 -0
- data/spec/IntegrationTests/depgraph_spec.rb +90 -0
- data/spec/IntegrationTests/file_system_node_finder_spec.rb +54 -0
- data/spec/IntegrationTests/gems_node_finder_spec.rb +20 -0
- data/spec/IntegrationTests/graph_creator_spec.rb +30 -0
- data/spec/IntegrationTests/graph_image_creator_spec.rb +50 -0
- data/spec/UnitTests/dependency_types_manager_spec.rb +46 -0
- data/spec/UnitTests/file_system_node_finder_spec.rb +62 -0
- data/spec/UnitTests/graph_creator_spec.rb +140 -0
- data/spec/UnitTests/graph_image_creator_spec.rb +56 -0
- data/spec/UnitTests/node_spec.rb +45 -0
- data/spec/integration_spec.opts +4 -0
- data/spec/spec.opts +6 -0
- data/spec/spec_helper.rb +179 -0
- data/spec/unit_spec.opts +4 -0
- data/tasks/deployment.rake +34 -0
- data/tasks/environment.rake +7 -0
- data/tasks/rspec.rake +35 -0
- data/tasks/website.rake +17 -0
- data/todo.txt +4 -0
- metadata +128 -0
@@ -0,0 +1,122 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'node'
|
3
|
+
require 'graph_image_creator'
|
4
|
+
require 'file_system_node_finder'
|
5
|
+
require 'dependency_types_manager'
|
6
|
+
|
7
|
+
module DepGraph
|
8
|
+
class GraphCreator
|
9
|
+
attr_writer :graph_image_creator_class, :from, :to, :node_finder, :trans
|
10
|
+
|
11
|
+
def initialize(node_type = :none)
|
12
|
+
@node_finder = get_node_finder(node_type)
|
13
|
+
@graph_image_creator_class = GraphImageCreator
|
14
|
+
@trans = false
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.types
|
18
|
+
node_finders_dir = File.join(File.dirname(__FILE__), 'nodefinders')
|
19
|
+
node_finders = Dir.glob(File.join(node_finders_dir, '*_node_finder.rb'))
|
20
|
+
node_finders = node_finders.map {|nf| nf.gsub(node_finders_dir + '/', '').gsub('_node_finder.rb', '').to_sym}
|
21
|
+
return DependencyTypesManager.types + node_finders
|
22
|
+
end
|
23
|
+
|
24
|
+
def location=(loc)
|
25
|
+
@node_finder.location = loc
|
26
|
+
end
|
27
|
+
|
28
|
+
def excluded_nodes=(exc)
|
29
|
+
@excluded_nodes = exc.map {|e| e.strip}
|
30
|
+
end
|
31
|
+
|
32
|
+
def create_image(image_file_name = 'dependency_graph.png')
|
33
|
+
create_graph.create_image(image_file_name)
|
34
|
+
end
|
35
|
+
|
36
|
+
def create_graph
|
37
|
+
nodes = @node_finder.get_nodes.uniq
|
38
|
+
nodes = apply_filters(nodes)
|
39
|
+
nodes = remove_disconnected_nodes(nodes)
|
40
|
+
|
41
|
+
graph = @graph_image_creator_class.new
|
42
|
+
return graph if nodes.size < 2
|
43
|
+
|
44
|
+
nodes.each do |node|
|
45
|
+
graph.add_node(node)
|
46
|
+
end
|
47
|
+
|
48
|
+
nodes.each do |node|
|
49
|
+
node.dependencies.each do |dependable|
|
50
|
+
graph.add_edge(node, dependable) if nodes.include? dependable
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
graph.trans = @trans
|
55
|
+
|
56
|
+
return graph
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
def get_node_finder(node_type)
|
61
|
+
begin
|
62
|
+
begin
|
63
|
+
require "nodefinders/#{node_type.to_s}_node_finder"
|
64
|
+
rescue LoadError
|
65
|
+
end
|
66
|
+
|
67
|
+
node_finder_class = deep_const_get("DepGraph::NodeFinders::#{camelize(node_type.to_s)}NodeFinder")
|
68
|
+
@node_finder = node_finder_class.new
|
69
|
+
rescue
|
70
|
+
@node_finder = FileSystemNodeFinder.new(node_type)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def remove_disconnected_nodes(nodes)
|
75
|
+
nodes.select do |node|
|
76
|
+
nodes.any? {|n| n.depends_on?(node) or node.depends_on?(n)}
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def apply_filters(nodes)
|
81
|
+
apply_exclude_filter apply_to_filter(apply_from_filter(nodes))
|
82
|
+
end
|
83
|
+
|
84
|
+
def apply_exclude_filter(nodes)
|
85
|
+
return nodes unless @excluded_nodes
|
86
|
+
|
87
|
+
regexps = Regexp.union(*@excluded_nodes)
|
88
|
+
|
89
|
+
nodes.reject do |node|
|
90
|
+
node.name.match(regexps)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def apply_from_filter(nodes)
|
95
|
+
return nodes unless @from
|
96
|
+
|
97
|
+
nodes.select do |node|
|
98
|
+
node.name.match(@from) or nodes.any?{|n| n.name.match(@from) and n.depends_on?(node)}
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def apply_to_filter(nodes)
|
103
|
+
return nodes unless @to
|
104
|
+
|
105
|
+
nodes.select do |node|
|
106
|
+
node.name.match(@to) or node.dependencies.any? {|d| d.name.match(@to)}
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def camelize(lower_case_and_underscored_word, first_letter_in_uppercase = true)
|
111
|
+
if first_letter_in_uppercase
|
112
|
+
lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::" + $1.upcase }.gsub(/(^|_)(.)/) { $2.upcase }
|
113
|
+
else
|
114
|
+
lower_case_and_underscored_word.first + camelize(lower_case_and_underscored_word)[1..-1]
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def deep_const_get str
|
119
|
+
return str.split("::").inject(Object) {|a,b| a.const_get(b) }
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'graphviz'
|
3
|
+
|
4
|
+
module DepGraph
|
5
|
+
class GraphImageCreator
|
6
|
+
attr_writer :trans
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@nodes = []
|
10
|
+
@edges = []
|
11
|
+
@trans = false
|
12
|
+
end
|
13
|
+
|
14
|
+
def node_count
|
15
|
+
@nodes.size
|
16
|
+
end
|
17
|
+
|
18
|
+
def add_node(node_name)
|
19
|
+
fail 'Node name not specified' if node_name.to_str.empty?
|
20
|
+
@nodes << node_name.to_str
|
21
|
+
end
|
22
|
+
|
23
|
+
def has_node?(node_name)
|
24
|
+
@nodes.include? node_name
|
25
|
+
end
|
26
|
+
|
27
|
+
def edge_count
|
28
|
+
@edges.size
|
29
|
+
end
|
30
|
+
|
31
|
+
def add_edge(source_node_name, target_node_name)
|
32
|
+
fail "Cannot create a dependency to the unregistered node #{target_node_name}" unless @nodes.include?(target_node_name.to_str)
|
33
|
+
fail "Cannot create a dependency from the unregistered node #{source_node_name}" unless @nodes.include?(source_node_name.to_str)
|
34
|
+
|
35
|
+
@edges << [source_node_name.to_str, target_node_name.to_str]
|
36
|
+
end
|
37
|
+
|
38
|
+
def has_edge?(node1, node2)
|
39
|
+
@edges.include? [node1, node2]
|
40
|
+
end
|
41
|
+
|
42
|
+
def reset
|
43
|
+
@nodes.clear
|
44
|
+
@edges.clear
|
45
|
+
end
|
46
|
+
|
47
|
+
def create_image(image_file_name)
|
48
|
+
begin
|
49
|
+
return false if @nodes.size == 0 or @edges.size == 0
|
50
|
+
|
51
|
+
set_default_output_generation_unless_is_set
|
52
|
+
|
53
|
+
return @output_generation.call(@nodes, @edges, image_file_name)
|
54
|
+
|
55
|
+
rescue => e
|
56
|
+
puts e.message
|
57
|
+
puts e.backtrace
|
58
|
+
return false
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def output_generation= output_generation_lambda
|
63
|
+
@output_generation = output_generation_lambda
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
def quotify(a_string)
|
68
|
+
'"' + a_string + '"'
|
69
|
+
end
|
70
|
+
|
71
|
+
def set_default_output_generation_unless_is_set
|
72
|
+
unless @output_generation
|
73
|
+
@output_generation = lambda {|nodes, edges, image_file_name|
|
74
|
+
#TODO: Could we catch Graphviz errors that the wrapper couldn't catch?
|
75
|
+
g = GraphViz::new( "G", :use => 'dot', :mode => 'major', :rankdir => 'LR', :concentrate => 'true', :fontname => 'Arial')
|
76
|
+
|
77
|
+
load_nodes(g, nodes)
|
78
|
+
load_edges(g, edges)
|
79
|
+
|
80
|
+
create_output(g, image_file_name)
|
81
|
+
|
82
|
+
return true
|
83
|
+
}
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def load_nodes(g, nodes)
|
88
|
+
nodes.each do |node|
|
89
|
+
g.add_nodes(quotify(node))
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def load_edges(g, edges)
|
94
|
+
edges.each do |from, to|
|
95
|
+
g.add_edges(quotify(from), quotify(to))
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def create_output(g, image_file_name)
|
100
|
+
output_type = get_output_type(image_file_name)
|
101
|
+
|
102
|
+
if @trans
|
103
|
+
begin
|
104
|
+
g.output(:dot => 'temp.dot')
|
105
|
+
system "tred temp.dot|dot -T#{output_type} > #{image_file_name}"
|
106
|
+
ensure
|
107
|
+
File.delete('temp.dot')
|
108
|
+
end
|
109
|
+
else
|
110
|
+
g.output(output_type => image_file_name)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def get_output_type(image_file_name)
|
115
|
+
#png is the default output type
|
116
|
+
output_type = 'png'
|
117
|
+
|
118
|
+
image_file_name.scan(/.+\.([^\.]*)$/) do |matches|
|
119
|
+
output_type = matches[0]
|
120
|
+
end
|
121
|
+
|
122
|
+
return output_type
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
data/lib/node.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
module DepGraph
|
2
|
+
|
3
|
+
#A node knows its dependables
|
4
|
+
class Node
|
5
|
+
include Comparable
|
6
|
+
attr_reader :name
|
7
|
+
|
8
|
+
def initialize(node_uri)
|
9
|
+
fail 'Empty uris are not allowed' if node_uri.empty?
|
10
|
+
@name = node_uri
|
11
|
+
@dependencies = []
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_str
|
15
|
+
@name
|
16
|
+
end
|
17
|
+
|
18
|
+
def <=> other_node
|
19
|
+
@name <=> other_node.to_str
|
20
|
+
end
|
21
|
+
|
22
|
+
def eql? other_node
|
23
|
+
(self <=> other_node) == 0
|
24
|
+
end
|
25
|
+
|
26
|
+
def hash
|
27
|
+
@name.hash
|
28
|
+
end
|
29
|
+
|
30
|
+
def depends_on node
|
31
|
+
node = Node.new(node) unless node.respond_to? :name
|
32
|
+
|
33
|
+
@dependencies << node
|
34
|
+
end
|
35
|
+
|
36
|
+
def depends_on? node
|
37
|
+
@dependencies.include? node
|
38
|
+
end
|
39
|
+
|
40
|
+
def dependencies
|
41
|
+
@dependencies
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'node'
|
2
|
+
|
3
|
+
module DepGraph
|
4
|
+
module NodeFinders
|
5
|
+
class GemsNodeFinder
|
6
|
+
def initialize
|
7
|
+
@spec_directories = Gem::Specification.dirs
|
8
|
+
end
|
9
|
+
|
10
|
+
def location=(loc)
|
11
|
+
@spec_directories = loc
|
12
|
+
end
|
13
|
+
|
14
|
+
def get_nodes
|
15
|
+
require 'rubygems'
|
16
|
+
|
17
|
+
fail 'The gem specification directories were not set' unless @spec_directories and @spec_directories.size > 0
|
18
|
+
|
19
|
+
nodes = {}
|
20
|
+
@spec_directories.each do |spec_directory|
|
21
|
+
Dir["#{spec_directory}/**/*.gemspec"].each do |gemspec_file_name|
|
22
|
+
add_nodes_from_gemspec(nodes, gemspec_file_name)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
return nodes.values.sort
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
def add_nodes_from_gemspec(nodes, gemspec_file_name)
|
31
|
+
gem_dependencies = get_gemspec_dependencies(gemspec_file_name)
|
32
|
+
gem_name = get_gemspec_name(gemspec_file_name)
|
33
|
+
|
34
|
+
nodes[gem_name] ||= Node.new(gem_name)
|
35
|
+
gem_dependencies.each do |gem_dependency|
|
36
|
+
nodes[gem_dependency] ||= Node.new(gem_dependency)
|
37
|
+
nodes[gem_name].depends_on(nodes[gem_dependency])
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def get_gemspec_dependencies(gemspec_file_name)
|
42
|
+
gem_dependencies = []
|
43
|
+
content = File.read(gemspec_file_name)
|
44
|
+
content.scan(/add_dependency\(%q<([^>]+)>/) do |matches|
|
45
|
+
matches.each do |match|
|
46
|
+
gem_dependencies << match
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
return gem_dependencies
|
51
|
+
end
|
52
|
+
|
53
|
+
def get_gemspec_name(gemspec_file_name)
|
54
|
+
File.basename(gemspec_file_name).match(/(.+)-\d+/)[1].to_s
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'node'
|
2
|
+
|
3
|
+
module DepGraph
|
4
|
+
module NodeFinders
|
5
|
+
|
6
|
+
#This is a simple example of a custom node finder with a hard coded graph.
|
7
|
+
#Note that the file must be named [nodetype]_node_finder.rb containing a class named [Nodetype]NodeFinder
|
8
|
+
#To use this example do: depgraph -type test
|
9
|
+
class TestNodeFinder
|
10
|
+
def location=(loc)
|
11
|
+
#we will ignore location in this example
|
12
|
+
end
|
13
|
+
|
14
|
+
def get_nodes
|
15
|
+
#let's return a hardcoded graph with 2 nodes and one dependency between them
|
16
|
+
|
17
|
+
node1 = Node.new('node1')
|
18
|
+
node2 = Node.new('node2')
|
19
|
+
|
20
|
+
node1.depends_on(node2)
|
21
|
+
|
22
|
+
return [node1, node2]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/script/destroy
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rubigen'
|
6
|
+
rescue LoadError
|
7
|
+
require 'rubygems'
|
8
|
+
require 'rubigen'
|
9
|
+
end
|
10
|
+
require 'rubigen/scripts/destroy'
|
11
|
+
|
12
|
+
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
13
|
+
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
|
14
|
+
RubiGen::Scripts::Destroy.new.run(ARGV)
|
data/script/destroy.cmd
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
@ruby script/destroy %*
|
data/script/generate
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rubigen'
|
6
|
+
rescue LoadError
|
7
|
+
require 'rubygems'
|
8
|
+
require 'rubigen'
|
9
|
+
end
|
10
|
+
require 'rubigen/scripts/generate'
|
11
|
+
|
12
|
+
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
13
|
+
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
|
14
|
+
RubiGen::Scripts::Generate.new.run(ARGV)
|
data/script/generate.cmd
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
@ruby script/generate %*
|
data/script/txt2html
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
begin
|
5
|
+
require 'newgem'
|
6
|
+
rescue LoadError
|
7
|
+
puts "\n\nGenerating the website requires the newgem RubyGem"
|
8
|
+
puts "Install: gem install newgem\n\n"
|
9
|
+
exit(1)
|
10
|
+
end
|
11
|
+
require 'redcloth'
|
12
|
+
require 'syntax/convertors/html'
|
13
|
+
require 'erb'
|
14
|
+
require File.dirname(__FILE__) + '/../lib/DepGraph/version.rb'
|
15
|
+
|
16
|
+
version = DepGraph::VERSION::STRING
|
17
|
+
download = 'http://rubyforge.org/projects/DepGraph'
|
18
|
+
|
19
|
+
class Fixnum
|
20
|
+
def ordinal
|
21
|
+
# teens
|
22
|
+
return 'th' if (10..19).include?(self % 100)
|
23
|
+
# others
|
24
|
+
case self % 10
|
25
|
+
when 1: return 'st'
|
26
|
+
when 2: return 'nd'
|
27
|
+
when 3: return 'rd'
|
28
|
+
else return 'th'
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class Time
|
34
|
+
def pretty
|
35
|
+
return "#{mday}#{mday.ordinal} #{strftime('%B')} #{year}"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def convert_syntax(syntax, source)
|
40
|
+
return Syntax::Convertors::HTML.for_syntax(syntax).convert(source).gsub(%r!^<pre>|</pre>$!,'')
|
41
|
+
end
|
42
|
+
|
43
|
+
if ARGV.length >= 1
|
44
|
+
src, template = ARGV
|
45
|
+
template ||= File.join(File.dirname(__FILE__), '/../website/template.rhtml')
|
46
|
+
|
47
|
+
else
|
48
|
+
puts("Usage: #{File.split($0).last} source.txt [template.rhtml] > output.html")
|
49
|
+
exit!
|
50
|
+
end
|
51
|
+
|
52
|
+
template = ERB.new(File.open(template).read)
|
53
|
+
|
54
|
+
title = nil
|
55
|
+
body = nil
|
56
|
+
File.open(src) do |fsrc|
|
57
|
+
title_text = fsrc.readline
|
58
|
+
body_text = fsrc.read
|
59
|
+
syntax_items = []
|
60
|
+
body_text.gsub!(%r!<(pre|code)[^>]*?syntax=['"]([^'"]+)[^>]*>(.*?)</\1>!m){
|
61
|
+
ident = syntax_items.length
|
62
|
+
element, syntax, source = $1, $2, $3
|
63
|
+
syntax_items << "<#{element} class='syntax'>#{convert_syntax(syntax, source)}</#{element}>"
|
64
|
+
"syntax-temp-#{ident}"
|
65
|
+
}
|
66
|
+
title = RedCloth.new(title_text).to_html.gsub(%r!<.*?>!,'').strip
|
67
|
+
body = RedCloth.new(body_text).to_html
|
68
|
+
body.gsub!(%r!(?:<pre><code>)?syntax-temp-(\d+)(?:</code></pre>)?!){ syntax_items[$1.to_i] }
|
69
|
+
end
|
70
|
+
stat = File.stat(src)
|
71
|
+
created = stat.ctime
|
72
|
+
modified = stat.mtime
|
73
|
+
|
74
|
+
$stdout << template.result(binding)
|