depgraph 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. data/History.txt +28 -0
  2. data/License.txt +20 -0
  3. data/README.rdoc +40 -0
  4. data/Rakefile +31 -0
  5. data/VERSION +1 -0
  6. data/bin/depgraph +89 -0
  7. data/config/hoe.rb +71 -0
  8. data/config/requirements.rb +19 -0
  9. data/lib/DepGraph/version.rb +9 -0
  10. data/lib/dependency_types.yaml +11 -0
  11. data/lib/dependency_types_manager.rb +49 -0
  12. data/lib/file_system_node_finder.rb +64 -0
  13. data/lib/graph_creator.rb +122 -0
  14. data/lib/graph_image_creator.rb +125 -0
  15. data/lib/node.rb +44 -0
  16. data/lib/nodefinders/gems_node_finder.rb +58 -0
  17. data/lib/nodefinders/test_node_finder.rb +26 -0
  18. data/script/destroy +14 -0
  19. data/script/destroy.cmd +1 -0
  20. data/script/generate +14 -0
  21. data/script/generate.cmd +1 -0
  22. data/script/txt2html +74 -0
  23. data/script/txt2html.cmd +1 -0
  24. data/setup.rb +1585 -0
  25. data/spec/IntegrationTests/depgraph_spec.rb +90 -0
  26. data/spec/IntegrationTests/file_system_node_finder_spec.rb +54 -0
  27. data/spec/IntegrationTests/gems_node_finder_spec.rb +20 -0
  28. data/spec/IntegrationTests/graph_creator_spec.rb +30 -0
  29. data/spec/IntegrationTests/graph_image_creator_spec.rb +50 -0
  30. data/spec/UnitTests/dependency_types_manager_spec.rb +46 -0
  31. data/spec/UnitTests/file_system_node_finder_spec.rb +62 -0
  32. data/spec/UnitTests/graph_creator_spec.rb +140 -0
  33. data/spec/UnitTests/graph_image_creator_spec.rb +56 -0
  34. data/spec/UnitTests/node_spec.rb +45 -0
  35. data/spec/integration_spec.opts +4 -0
  36. data/spec/spec.opts +6 -0
  37. data/spec/spec_helper.rb +179 -0
  38. data/spec/unit_spec.opts +4 -0
  39. data/tasks/deployment.rake +34 -0
  40. data/tasks/environment.rake +7 -0
  41. data/tasks/rspec.rake +35 -0
  42. data/tasks/website.rake +17 -0
  43. data/todo.txt +4 -0
  44. 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
@@ -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
@@ -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)
@@ -0,0 +1 @@
1
+ @ruby script/destroy %*
@@ -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)
@@ -0,0 +1 @@
1
+ @ruby script/generate %*
@@ -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)