DepGraph 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt ADDED
@@ -0,0 +1 @@
1
+
data/License.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 DepGraph - Daniel Cadenas Ni�n
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Manifest.txt ADDED
@@ -0,0 +1,35 @@
1
+ History.txt
2
+ License.txt
3
+ Manifest.txt
4
+ README.txt
5
+ Rakefile
6
+ bin/depgraph
7
+ config/hoe.rb
8
+ config/requirements.rb
9
+ lib/DepGraph/version.rb
10
+ lib/dependable_filter_manager.rb
11
+ lib/dependency_types.yaml
12
+ lib/dependent.rb
13
+ lib/file_system_dependent_finder.rb
14
+ lib/graph.rb
15
+ lib/graph_creator.rb
16
+ script/destroy
17
+ script/destroy.cmd
18
+ script/generate
19
+ script/generate.cmd
20
+ script/txt2html
21
+ script/txt2html.cmd
22
+ setup.rb
23
+ spec/dependable_filter_manager_spec.rb
24
+ spec/dependent_spec.rb
25
+ spec/depgraph_spec.rb
26
+ spec/file_system_dependent_finder_spec.rb
27
+ spec/graph_creator_spec.rb
28
+ spec/graph_spec.rb
29
+ spec/spec.opts
30
+ spec/spec_helper.rb
31
+ tasks/deployment.rake
32
+ tasks/environment.rake
33
+ tasks/rspec.rake
34
+ tasks/website.rake
35
+ todo.txt
data/README.txt ADDED
@@ -0,0 +1 @@
1
+ README
data/Rakefile ADDED
@@ -0,0 +1,4 @@
1
+ require 'config/requirements'
2
+ require 'config/hoe'
3
+
4
+ Dir['tasks/**/*.rake'].each { |rake| load rake }
data/bin/depgraph ADDED
@@ -0,0 +1,69 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Created on 2008-3-9.
4
+ # Copyright (c) 2008. Daniel Cadenas. All rights reserved.
5
+ # http://depgraph.rubyforge.org
6
+
7
+ begin
8
+ require 'rubygems'
9
+ gem 'optiflag'
10
+ gem 'ruby-graphviz'
11
+ require 'optiflag'
12
+ require 'graphviz'
13
+ rescue LoadError
14
+ puts "depgraph requires the optiflag RubyGem."
15
+ puts "Installation: gem install optiflag -y"
16
+ exit
17
+ end
18
+
19
+ require 'dependable_filter_manager'
20
+ require 'graph_creator'
21
+
22
+ module CommandLineParameters extend OptiFlagSet
23
+ flag "type" do
24
+ dependency_types = DepGraph::DependableFilterManager.types
25
+ description "Type of dependencies to analyze: #{dependency_types.join(', ')}"
26
+ value_in_set dependency_types
27
+ end
28
+
29
+ optional_flag "output" do
30
+ description "The file name of the image."
31
+ end
32
+
33
+
34
+ optional_flag "from" do
35
+ description "Regular expresion that must be met by the dependency source."
36
+ end
37
+
38
+ optional_flag "to" do
39
+ description "Regular expresion that must be met by the dependency target."
40
+ end
41
+
42
+ optional_flag "dirs" do
43
+ description "Directories to traverse for dependency files. Use double quotes and commas for more than one directory."
44
+ end
45
+
46
+ usage_flag "h","help","?"
47
+
48
+
49
+ and_process!
50
+ end
51
+
52
+ type = CommandLineParameters.flags.type
53
+ output = CommandLineParameters.flags.output || 'dependency_graph.png'
54
+ dirs = CommandLineParameters.flags.dirs || '.'
55
+ from = CommandLineParameters.flags.from if CommandLineParameters.flags.from?
56
+ to = CommandLineParameters.flags.to if CommandLineParameters.flags.to?
57
+
58
+ puts "Creating #{type} dependency graph. Please wait..."
59
+
60
+ dep_grapher = DepGraph::GraphCreator.new(type)
61
+ dep_grapher.dirs = dirs.split(',')
62
+ dep_grapher.from= from
63
+ dep_grapher.to = to
64
+
65
+ if dep_grapher.create_image(output)
66
+ puts "Created #{output}.\nDone."
67
+ else
68
+ puts "No dependency graph image created."
69
+ end
data/config/hoe.rb ADDED
@@ -0,0 +1,70 @@
1
+ require 'DepGraph/version'
2
+
3
+ AUTHOR = 'Daniel Cadenas Ni�n' # can also be an array of Authors
4
+ EMAIL = "depgraph-devel@rubyforge.org"
5
+ DESCRIPTION = "A tool to create dependency graph images from source code directories"
6
+ GEM_NAME = 'DepGraph' # what ppl will type to install your gem
7
+ RUBYFORGE_PROJECT = 'DepGraph' # The unix name for your project
8
+ HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org"
9
+ DOWNLOAD_PATH = "http://rubyforge.org/projects/#{RUBYFORGE_PROJECT}"
10
+
11
+ @config_file = "~/.rubyforge/user-config.yml"
12
+ @config = nil
13
+ RUBYFORGE_USERNAME = "dcadenas"
14
+ def rubyforge_username
15
+ unless @config
16
+ begin
17
+ @config = YAML.load(File.read(File.expand_path(@config_file)))
18
+ rescue
19
+ puts <<-EOS
20
+ ERROR: No rubyforge config file found: #{@config_file}
21
+ Run 'rubyforge setup' to prepare your env for access to Rubyforge
22
+ - See http://newgem.rubyforge.org/rubyforge.html for more details
23
+ EOS
24
+ exit
25
+ end
26
+ end
27
+ RUBYFORGE_USERNAME.replace @config["username"]
28
+ end
29
+
30
+
31
+ REV = nil
32
+ # UNCOMMENT IF REQUIRED:
33
+ # REV = `svn info`.each {|line| if line =~ /^Revision:/ then k,v = line.split(': '); break v.chomp; else next; end} rescue nil
34
+ VERS = DepGraph::VERSION::STRING + (REV ? ".#{REV}" : "")
35
+ RDOC_OPTS = ['--quiet', '--title', 'DepGraph documentation',
36
+ "--opname", "index.html",
37
+ "--line-numbers",
38
+ "--main", "README",
39
+ "--inline-source"]
40
+
41
+ class Hoe
42
+ def extra_deps
43
+ @extra_deps.reject! { |x| Array(x).first == 'hoe' }
44
+ @extra_deps
45
+ end
46
+ end
47
+
48
+ # Generate all the Rake tasks
49
+ # Run 'rake -T' to see list of generated tasks (from gem root directory)
50
+ hoe = Hoe.new(GEM_NAME, VERS) do |p|
51
+ p.developer(AUTHOR, EMAIL)
52
+ p.description = DESCRIPTION
53
+ p.summary = DESCRIPTION
54
+ p.url = HOMEPATH
55
+ p.rubyforge_name = RUBYFORGE_PROJECT if RUBYFORGE_PROJECT
56
+ p.test_globs = ["spec/**/*.rb"]
57
+ p.clean_globs |= ['**/.*.sw?', '*.gem', '.config', '**/.DS_Store'] #An array of file patterns to delete on clean.
58
+
59
+ # == Optional
60
+ p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
61
+ #p.extra_deps = [] # An array of rubygem dependencies [name, version], e.g. [ ['active_support', '>= 1.3.1'] ]
62
+
63
+ #p.spec_extras = {} # A hash of extra values to set in the gemspec.
64
+
65
+ end
66
+
67
+ CHANGES = hoe.paragraphs_of('History.txt', 0..1).join("\\n\\n")
68
+ PATH = (RUBYFORGE_PROJECT == GEM_NAME) ? RUBYFORGE_PROJECT : "#{RUBYFORGE_PROJECT}/#{GEM_NAME}"
69
+ hoe.remote_rdoc_dir = File.join(PATH.gsub(/^#{RUBYFORGE_PROJECT}\/?/,''), 'rdoc')
70
+ hoe.rsync_args = '-av --delete --ignore-errors'
@@ -0,0 +1,17 @@
1
+ require 'fileutils'
2
+ include FileUtils
3
+
4
+ require 'rubygems'
5
+ %w[rake hoe newgem rubigen filetesthelper spec optiflag graphviz].each do |req_gem|
6
+ begin
7
+ require req_gem
8
+ rescue LoadError
9
+ req_gem = 'rspec' if req_gem == 'spec'
10
+ puts "This Rakefile requires the '#{req_gem}' RubyGem."
11
+ puts "Installation: gem install #{req_gem} -y"
12
+ exit
13
+ end
14
+ end
15
+
16
+ $:.unshift(File.join(File.dirname(__FILE__), %w[.. lib]))
17
+
@@ -0,0 +1,9 @@
1
+ module DepGraph #:nodoc:
2
+ module VERSION #:nodoc:
3
+ MAJOR = 0
4
+ MINOR = 8
5
+ TINY = 0
6
+
7
+ STRING = [MAJOR, MINOR, TINY].join('.')
8
+ end
9
+ end
@@ -0,0 +1,49 @@
1
+ require 'yaml'
2
+
3
+ module DepGraph
4
+ class DependableFilterManager
5
+ def self.dependency_types_file
6
+ File.join(File.dirname(__FILE__), 'dependency_types.yaml')
7
+ end
8
+
9
+ begin
10
+ @@dependable_dependency_types = YAML.load_file(dependency_types_file)
11
+ rescue => e
12
+ fail "Could not load file #{dependency_types_file}: #{e.message}"
13
+ end
14
+
15
+ def self.types
16
+ @@dependable_dependency_types.map {|type, _| type.to_sym}
17
+ end
18
+
19
+ def initialize(dependent_type = :anything)
20
+ @dependent_type = dependent_type.to_s
21
+ end
22
+
23
+ def dependable_regexp
24
+ get_dependent_type_parameters(@dependent_type)['dependable_regexp']
25
+ end
26
+
27
+ def dependable_regexp_capture_group_index
28
+ get_dependent_type_parameters(@dependent_type)['capture_group_index']
29
+ end
30
+
31
+ def file_name_pattern
32
+ get_dependent_type_parameters(@dependent_type)['file_name_pattern']
33
+ end
34
+
35
+ def get_dependent_type_parameters(dependent_type)
36
+ dependent_type_parameters = @@dependable_dependency_types[dependent_type]
37
+
38
+ if dependent_type_parameters
39
+ return dependent_type_parameters
40
+ else
41
+ return default_parameters = {
42
+ 'dependable_regexp' => /.+/,
43
+ 'capture_group_index' => 0,
44
+ 'file_name_pattern' => '*'
45
+ }
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,11 @@
1
+ ---
2
+ csproj:
3
+ file_name_pattern: "*.csproj"
4
+ dependable_regexp: !ruby/regexp /"([^"]*\\)?([^\\"]+?)\.(csproj|dll)"/
5
+ capture_group_index: 1
6
+ ruby_requires:
7
+ file_name_pattern: "*.rb"
8
+ dependable_regexp: !ruby/regexp /[\s]*require[\s]+["'](.*\/)?([^\s\/]+?)(\.rb)?["']/
9
+ capture_group_index: 1
10
+
11
+
data/lib/dependent.rb ADDED
@@ -0,0 +1,73 @@
1
+ module DepGraph
2
+
3
+ #A Dependent knows its dependables and knows how to get them from a string
4
+ class Dependent
5
+ include Comparable
6
+ attr_reader :name
7
+
8
+ def initialize(dependent_uri, dependent_type = :anything)
9
+ fail 'Empty uris are not allowed' if dependent_uri.empty?
10
+ @name = remove_extension dependent_uri
11
+ @dependencies = []
12
+
13
+ load_filter_type(dependent_type)
14
+ end
15
+
16
+ def load_filter_type(dependent_type)
17
+ dependable_filter_manager = DependableFilterManager.new dependent_type
18
+ @dependable_filter = dependable_filter_manager.dependable_regexp
19
+ @dependable_filter_capture_group_index = dependable_filter_manager.dependable_regexp_capture_group_index
20
+ end
21
+
22
+ def to_str
23
+ @name
24
+ end
25
+
26
+ def <=> other_dependent
27
+ @name <=> other_dependent.to_str
28
+ end
29
+
30
+ def eql? other_dependent
31
+ (self <=> other_dependent) == 0
32
+ end
33
+
34
+ def hash
35
+ @name.hash
36
+ end
37
+
38
+ def remove_extension file_path
39
+ file_extension_regexp = /\.[^\.]+$/
40
+ return File.basename(file_path).gsub(file_extension_regexp, '')
41
+ end
42
+
43
+ def depends_on dependent
44
+ @dependencies << dependent
45
+ end
46
+
47
+ def depends_on? dependent
48
+ @dependencies.include? dependent
49
+ end
50
+
51
+ def dependencies
52
+ @dependencies
53
+ end
54
+
55
+ def dependable_filter= filter
56
+ @dependable_filter = filter
57
+ @dependable_filter_capture_group_index = 0
58
+ end
59
+
60
+ def dependable_filter_capture_group_index= group_index
61
+ @dependable_filter_capture_group_index = group_index
62
+ end
63
+
64
+ def load_dependencies_from_string(dependencies_string)
65
+ fail 'The dependable finder Regexp was not set' unless @dependable_filter
66
+
67
+ dependencies_string.scan(@dependable_filter).each do |matches|
68
+ dependable = (matches.respond_to? :to_ary) ? matches[@dependable_filter_capture_group_index] : matches
69
+ depends_on(dependable) unless depends_on? dependable
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,44 @@
1
+ require 'dependent'
2
+ require 'dependable_filter_manager'
3
+
4
+ module DepGraph
5
+ class FileSystemDependentFinder
6
+ attr_reader :dependent_filter, :dependable_filter, :dependable_filter_capture_group_index
7
+ attr_writer :dirs
8
+
9
+ def initialize(dependent_type)
10
+
11
+ dependable_filter_manager = DependableFilterManager.new dependent_type
12
+ @file_name_pattern = dependable_filter_manager.file_name_pattern
13
+ @dependable_filter = dependable_filter_manager.dependable_regexp
14
+ @dependable_filter_capture_group_index = dependable_filter_manager.dependable_regexp_capture_group_index
15
+ @dirs = ['.']
16
+ end
17
+
18
+ def get_dependents
19
+ files = []
20
+ @dirs.each do |dir|
21
+ files += Dir.glob(dir.strip + '/**/' + @file_name_pattern)
22
+ end
23
+
24
+ dependents = []
25
+ files.each { |file| dependents << create_dependent_from_file(file) }
26
+ return dependents
27
+ end
28
+
29
+ private
30
+ def create_dependent_from_file file
31
+ dependent = Dependent.new file
32
+ dependent.dependable_filter = @dependable_filter
33
+ dependent.dependable_filter_capture_group_index = @dependable_filter_capture_group_index
34
+
35
+ File.open(file) do |f|
36
+ f.each_line do |line|
37
+ dependent.load_dependencies_from_string(line)
38
+ end
39
+ end
40
+
41
+ return dependent
42
+ end
43
+ end
44
+ end
data/lib/graph.rb ADDED
@@ -0,0 +1,89 @@
1
+ require 'rubygems'
2
+ require 'graphviz'
3
+
4
+ module DepGraph
5
+ class Graph
6
+ def initialize
7
+ @nodes = []
8
+ @edges = []
9
+ end
10
+
11
+ def node_count
12
+ @nodes.size
13
+ end
14
+
15
+ def add_node(node_name)
16
+ fail 'Node name not specified' if node_name.to_str.empty?
17
+ @nodes << node_name.to_str
18
+ end
19
+
20
+ def has_node?(node_name)
21
+ @nodes.include? node_name
22
+ end
23
+
24
+ def edge_count
25
+ @edges.size
26
+ end
27
+
28
+ def add_edge(source_node_name, target_node_name)
29
+ fail "Cannot create a dependency to the unregistered node #{target_node_name}" unless @nodes.include?(target_node_name.to_str)
30
+ fail "Cannot create a dependency from the unregistered node #{source_node_name}" unless @nodes.include?(source_node_name.to_str)
31
+
32
+ @edges << [source_node_name.to_str, target_node_name.to_str]
33
+ end
34
+
35
+ def has_edge?(node1, node2)
36
+ @edges.include? [node1, node2]
37
+ end
38
+
39
+ def reset
40
+ @nodes.clear
41
+ @edges.clear
42
+ end
43
+
44
+ def create_image(image_file_name)
45
+ begin
46
+ return false if @nodes.size == 0 or @edges.size == 0
47
+
48
+ set_default_output_generation_unless_is_set
49
+
50
+ return @output_generation.call(@nodes, @edges, image_file_name)
51
+
52
+ rescue => e
53
+ puts e.message
54
+ puts e.backtrace
55
+ return false
56
+ end
57
+ end
58
+
59
+ def output_generation= output_generation_lambda
60
+ @output_generation = output_generation_lambda
61
+ end
62
+
63
+ private
64
+ def quotify(a_string)
65
+ '"' + a_string + '"'
66
+ end
67
+
68
+ def set_default_output_generation_unless_is_set
69
+ unless @output_generation
70
+ @output_generation = lambda {|nodes, edges, image_file_name|
71
+ #TODO: Could we catch Graphviz errors that the wrapper couldn't catch?
72
+ g = GraphViz::new( "G", :use => 'dot', :mode => 'major', :rankdir => 'LR', :concentrate => 'true', :fontname => 'Arial', :file => image_file_name)
73
+
74
+
75
+ nodes.each do |node|
76
+ g.add_node(quotify(node))
77
+ end
78
+
79
+ edges.each do |from, to|
80
+ g.add_edge(quotify(from), quotify(to))
81
+ end
82
+
83
+ g.output( :output => "png")
84
+ return true
85
+ }
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,85 @@
1
+ require 'rubygems'
2
+ require 'dependent'
3
+ require 'graph'
4
+ require 'file_system_dependent_finder'
5
+
6
+ module DepGraph
7
+ class GraphCreator
8
+ attr_accessor :dependent_finder
9
+
10
+ def initialize(dependent_type = :none)
11
+ @dependent_finder = FileSystemDependentFinder.new(dependent_type)
12
+ @graph_class = Graph
13
+ end
14
+
15
+ def graph_class= the_graph_class
16
+ @graph_class = the_graph_class
17
+ end
18
+
19
+ def dirs=(directories)
20
+ @dependent_finder.dirs = directories
21
+ end
22
+
23
+ def from=(from_filter)
24
+ @from_filter = from_filter
25
+ end
26
+
27
+ def to=(to_filter)
28
+ @to_filter = to_filter
29
+ end
30
+
31
+
32
+ def create_graph
33
+ nodes = @dependent_finder.get_dependents.uniq
34
+ return nil if nodes.size < 2
35
+
36
+ dependents = apply_from_filter(nodes)
37
+ dependents = apply_to_filter(dependents)
38
+
39
+ graph = @graph_class.new
40
+
41
+ dependents.each do |dependent|
42
+ graph.add_node dependent
43
+ end
44
+
45
+ dependents.each do |dependent|
46
+ dependent.dependencies.each do |dependable|
47
+ graph.add_edge(dependent, dependable) if dependents.include? dependable
48
+ end
49
+ end
50
+
51
+ return graph
52
+ end
53
+
54
+ def apply_from_filter(nodes)
55
+ return nodes unless @from_filter
56
+
57
+ selected_dependents = nodes.select do |node|
58
+ node.name.match(@from_filter) or nodes.any?{|dependent| dependent.name.match(@from_filter) and dependent.depends_on?(node)}
59
+ end
60
+
61
+ return selected_dependents
62
+ end
63
+
64
+ def apply_to_filter(nodes)
65
+ return nodes unless @to_filter
66
+
67
+ selected_dependents = nodes.select do |node|
68
+ node.name.match(@to_filter) or nodes.any?{|dependable| node.depends_on?(dependable) and dependable.name.match(@to_filter)}
69
+ end
70
+
71
+ return selected_dependents
72
+ end
73
+
74
+
75
+ def create_image(image_file_name = 'dependency_graph.png')
76
+ graph = create_graph
77
+
78
+ if graph
79
+ return graph.create_image(image_file_name)
80
+ else
81
+ return false
82
+ end
83
+ end
84
+ end
85
+ 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)
@@ -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)
@@ -0,0 +1 @@
1
+ @ruby script/generate %*