DepGraph 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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 %*