codependency 0.3.1 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +2 -4
- data/codependency.gemspec +2 -2
- data/lib/codependency.rb +0 -3
- data/lib/codependency/graph.rb +45 -28
- data/lib/codependency/parser.rb +3 -1
- data/lib/codependency/version.rb +1 -1
- data/spec/codependency/graph_spec.rb +43 -14
- data/spec/codependency/parser_spec.rb +17 -17
- data/spec/support/breakfasts_context.rb +4 -4
- data/spec/support/circular_context.rb +3 -3
- data/spec/support/planets_context.rb +5 -5
- data/spec/support/subdirectories_context.rb +4 -4
- metadata +4 -7
- data/lib/codependency/node.rb +0 -26
- data/spec/codependency/node_spec.rb +0 -25
data/README.md
CHANGED
@@ -2,8 +2,6 @@
|
|
2
2
|
|
3
3
|
Codependency is a simple comment-based dependency graph that you can use on arbitrary files.
|
4
4
|
|
5
|
-
It uses the program `tsort` under the hood, so sorry windoze I guess?
|
6
|
-
|
7
5
|
## Installation
|
8
6
|
|
9
7
|
Add this line to your application's Gemfile:
|
@@ -41,8 +39,8 @@ end
|
|
41
39
|
Then, we create a dependency graph to determine the order in which the files might need to be loaded, inserted, or compiled:
|
42
40
|
|
43
41
|
``` rb
|
44
|
-
graph = Codependency::Graph.new
|
45
|
-
graph.files # => ["foo.rb", "bar.rb"]
|
42
|
+
graph = Codependency::Graph.new "bar.rb"
|
43
|
+
graph.files # => ["./foo.rb", "./bar.rb"]
|
46
44
|
```
|
47
45
|
|
48
46
|
## Contributing
|
data/codependency.gemspec
CHANGED
@@ -4,8 +4,8 @@ require File.expand_path('../lib/codependency/version', __FILE__)
|
|
4
4
|
Gem::Specification.new do |gem|
|
5
5
|
gem.authors = ["Jeremy Ruppel"]
|
6
6
|
gem.email = ["jeremy.ruppel@gmail.com"]
|
7
|
-
gem.description = %q{
|
8
|
-
gem.summary = %q{
|
7
|
+
gem.description = %q{A pure ruby, comment-based dependency graph}
|
8
|
+
gem.summary = %q{A pure ruby, comment-based dependency graph}
|
9
9
|
gem.homepage = ""
|
10
10
|
|
11
11
|
gem.files = `git ls-files`.split($\)
|
data/lib/codependency.rb
CHANGED
data/lib/codependency/graph.rb
CHANGED
@@ -1,46 +1,63 @@
|
|
1
|
-
require '
|
1
|
+
require 'tsort'
|
2
2
|
|
3
3
|
module Codependency
|
4
|
-
class Graph
|
5
|
-
def initialize(
|
6
|
-
@options = options
|
7
|
-
@nodes = Hash.new { |h, k| h[ k ] = Node.new( k, parser ) }
|
8
|
-
@start = @nodes[ start ]
|
9
|
-
end
|
10
|
-
|
11
|
-
##
|
12
|
-
# a topologically sorted list of all dependencies of the `start` file.
|
13
|
-
def files
|
14
|
-
deps = resolve( @start, [ ] ).map( &:dependencies ).join ' '
|
4
|
+
class Graph < Hash
|
5
|
+
def initialize( path, options={} )
|
6
|
+
@path, @options = path, options
|
15
7
|
|
16
|
-
|
8
|
+
super( ){ |h, k| h[ k ] = parser.parse( k ) }
|
9
|
+
end
|
10
|
+
attr_reader :path, :options
|
17
11
|
|
18
|
-
|
19
|
-
raise CircularDependencyError, msg
|
20
|
-
end
|
12
|
+
include TSort
|
21
13
|
|
22
|
-
|
14
|
+
##
|
15
|
+
# the dirname to use for this graph, based on the path
|
16
|
+
def dirname
|
17
|
+
File.dirname path
|
23
18
|
end
|
24
19
|
|
25
|
-
protected
|
26
|
-
|
27
20
|
##
|
28
|
-
#
|
29
|
-
|
30
|
-
|
31
|
-
|
21
|
+
# the extname to use for this graph, based on the path
|
22
|
+
def extname
|
23
|
+
File.extname path
|
24
|
+
end
|
32
25
|
|
33
|
-
|
34
|
-
|
35
|
-
|
26
|
+
##
|
27
|
+
# walk the entire graph and return self
|
28
|
+
def populate
|
29
|
+
walk path
|
30
|
+
self
|
31
|
+
end
|
36
32
|
|
37
|
-
|
33
|
+
##
|
34
|
+
# discover all nodes in this graph by walking it
|
35
|
+
def walk( path )
|
36
|
+
self[ path ].each { |path| walk( path ) unless has_key?( path ) }
|
38
37
|
end
|
39
38
|
|
40
39
|
##
|
41
40
|
# the parser to use for this graph. shared by all nodes.
|
42
41
|
def parser
|
43
|
-
@parser ||=
|
42
|
+
@parser ||= begin
|
43
|
+
Parser.new options.merge( :dirname => dirname, :extname => extname )
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
##
|
48
|
+
# a topologically sorted list of all dependencies of the `start` file.
|
49
|
+
def files
|
50
|
+
populate.tsort
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
# tsort interface
|
56
|
+
alias :tsort_each_node :each_key
|
57
|
+
|
58
|
+
# tsort interface
|
59
|
+
def tsort_each_child( node, &block )
|
60
|
+
fetch( node ).each( &block )
|
44
61
|
end
|
45
62
|
end
|
46
63
|
end
|
data/lib/codependency/parser.rb
CHANGED
@@ -4,6 +4,8 @@ module Codependency
|
|
4
4
|
def initialize( options={} )
|
5
5
|
@options = options
|
6
6
|
@comment = options.delete( :comment ) || '#'
|
7
|
+
@dirname = options.delete( :dirname ) || '.'
|
8
|
+
@extname = options.delete( :extname ) || '.rb'
|
7
9
|
end
|
8
10
|
|
9
11
|
##
|
@@ -11,7 +13,7 @@ module Codependency
|
|
11
13
|
def parse( file )
|
12
14
|
IO.readlines( file ).take_while do |line|
|
13
15
|
line =~ pattern
|
14
|
-
end.map { |line| line[ pattern, 1 ] }
|
16
|
+
end.map { |line| "#{@dirname}/#{line[ pattern, 1 ]}#{@extname}" }
|
15
17
|
end
|
16
18
|
|
17
19
|
protected
|
data/lib/codependency/version.rb
CHANGED
@@ -1,45 +1,74 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Codependency::Graph do
|
4
|
+
describe 'accessors' do
|
5
|
+
subject { described_class.new 'planets/phobos.rb' }
|
6
|
+
its( :path ){ should eq( 'planets/phobos.rb' ) }
|
7
|
+
its( :dirname ){ should eq( 'planets' ) }
|
8
|
+
its( :extname ){ should eq( '.rb' ) }
|
9
|
+
its( :options ){ should eq( { } ) }
|
10
|
+
end
|
11
|
+
|
12
|
+
describe 'populate', :files => :planets do
|
13
|
+
subject { described_class.new './phobos.rb' }
|
14
|
+
|
15
|
+
context 'initially' do
|
16
|
+
it { should be_empty }
|
17
|
+
end
|
18
|
+
|
19
|
+
context 'after calling populate' do
|
20
|
+
before { subject.populate }
|
21
|
+
|
22
|
+
it { should include( './phobos.rb' => [ './body.rb', './mars.rb' ] ) }
|
23
|
+
it { should include( './mars.rb' => [ './planet.rb' ] ) }
|
24
|
+
it { should include( './planet.rb' => [ './body.rb' ] ) }
|
25
|
+
it { should include( './body.rb' => [ ] ) }
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should return itself' do
|
29
|
+
subject.populate.should == subject
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
4
33
|
context 'planets', :files => :planets do
|
5
|
-
subject {
|
34
|
+
subject { described_class.new start }
|
6
35
|
|
7
36
|
context 'earth' do
|
8
|
-
let( :
|
9
|
-
its( :files ){ should eq( %w| body.rb planet.rb earth.rb | ) }
|
37
|
+
let( :start ){ './earth.rb' }
|
38
|
+
its( :files ){ should eq( %w| ./body.rb ./planet.rb ./earth.rb | ) }
|
10
39
|
end
|
11
40
|
|
12
41
|
context 'phobos' do
|
13
|
-
let( :
|
14
|
-
its( :files ){ should eq( %w| body.rb planet.rb mars.rb phobos.rb | ) }
|
42
|
+
let( :start ){ './phobos.rb' }
|
43
|
+
its( :files ){ should eq( %w| ./body.rb ./planet.rb ./mars.rb ./phobos.rb | ) }
|
15
44
|
end
|
16
45
|
end
|
17
46
|
|
18
47
|
context 'breakfasts', :files => :breakfasts do
|
19
|
-
subject {
|
48
|
+
subject { described_class.new start, :comment => '//' }
|
20
49
|
|
21
50
|
context 'sandwich' do
|
22
|
-
let( :
|
23
|
-
its( :files ){ should eq( %w| butter.js egg.js toast.js sandwich.js | ) }
|
51
|
+
let( :start ){ './sandwich.js' }
|
52
|
+
its( :files ){ should eq( %w| ./butter.js ./egg.js ./toast.js ./sandwich.js | ) }
|
24
53
|
end
|
25
54
|
end
|
26
55
|
|
27
56
|
context 'circular dependencies', :files => :circular do
|
28
|
-
subject {
|
57
|
+
subject { described_class.new start }
|
29
58
|
|
30
|
-
let( :
|
59
|
+
let( :start ){ './money.rb' }
|
31
60
|
|
32
61
|
it 'should raise an error' do
|
33
|
-
expect { subject.files }.to raise_error(
|
62
|
+
expect { subject.files }.to raise_error( TSort::Cyclic )
|
34
63
|
end
|
35
64
|
end
|
36
65
|
|
37
66
|
context 'subdirectories', :files => :subdirectories do
|
38
|
-
subject {
|
67
|
+
subject { described_class.new start, :comment => '//' }
|
39
68
|
|
40
69
|
context 'application.js' do
|
41
|
-
let( :
|
42
|
-
its( :files ){ should eq( %w| templates/user/history.js templates/user/account.js templates/user.js application.js | ) }
|
70
|
+
let( :start ){ 'assets/application.js' }
|
71
|
+
its( :files ){ should eq( %w| assets/templates/user/history.js assets/templates/user/account.js assets/templates/user.js assets/application.js | ) }
|
43
72
|
end
|
44
73
|
end
|
45
74
|
end
|
@@ -5,45 +5,45 @@ describe Codependency::Parser do
|
|
5
5
|
let( :parser ){ Codependency::Parser.new }
|
6
6
|
|
7
7
|
context 'body' do
|
8
|
-
subject { parser.parse( 'body.rb' ) }
|
8
|
+
subject { parser.parse( './body.rb' ) }
|
9
9
|
it { should eq( [ ] ) }
|
10
10
|
end
|
11
11
|
context 'earth' do
|
12
|
-
subject { parser.parse( 'earth.rb' ) }
|
13
|
-
it { should eq( [ 'planet' ] ) }
|
12
|
+
subject { parser.parse( './earth.rb' ) }
|
13
|
+
it { should eq( [ './planet.rb' ] ) }
|
14
14
|
end
|
15
15
|
context 'mars' do
|
16
|
-
subject { parser.parse( 'mars.rb' ) }
|
17
|
-
it { should eq( [ 'planet' ] ) }
|
16
|
+
subject { parser.parse( './mars.rb' ) }
|
17
|
+
it { should eq( [ './planet.rb' ] ) }
|
18
18
|
end
|
19
19
|
context 'phobos' do
|
20
|
-
subject { parser.parse( 'phobos.rb' ) }
|
21
|
-
it { should eq( [ 'body', 'mars' ] ) }
|
20
|
+
subject { parser.parse( './phobos.rb' ) }
|
21
|
+
it { should eq( [ './body.rb', './mars.rb' ] ) }
|
22
22
|
end
|
23
23
|
context 'planet' do
|
24
|
-
subject { parser.parse( 'planet.rb' ) }
|
25
|
-
it { should eq( [ 'body' ] ) }
|
24
|
+
subject { parser.parse( './planet.rb' ) }
|
25
|
+
it { should eq( [ './body.rb' ] ) }
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
29
|
context 'breakfasts', :files => :breakfasts do
|
30
|
-
let( :parser ){ Codependency::Parser.new :comment => '//' }
|
30
|
+
let( :parser ){ Codependency::Parser.new :comment => '//', :extname => '.js' }
|
31
31
|
|
32
32
|
context 'butter' do
|
33
|
-
subject { parser.parse( 'butter.js' ) }
|
33
|
+
subject { parser.parse( './butter.js' ) }
|
34
34
|
it { should eq( [ ] ) }
|
35
35
|
end
|
36
36
|
context 'egg' do
|
37
|
-
subject { parser.parse( 'egg.js' ) }
|
38
|
-
it { should eq( [ 'butter' ] ) }
|
37
|
+
subject { parser.parse( './egg.js' ) }
|
38
|
+
it { should eq( [ './butter.js' ] ) }
|
39
39
|
end
|
40
40
|
context 'toast' do
|
41
|
-
subject { parser.parse( 'toast.js' ) }
|
42
|
-
it { should eq( [ 'butter' ] ) }
|
41
|
+
subject { parser.parse( './toast.js' ) }
|
42
|
+
it { should eq( [ './butter.js' ] ) }
|
43
43
|
end
|
44
44
|
context 'sandwich' do
|
45
|
-
subject { parser.parse( 'sandwich.js' ) }
|
46
|
-
it { should eq( [ 'egg', 'toast' ] ) }
|
45
|
+
subject { parser.parse( './sandwich.js' ) }
|
46
|
+
it { should eq( [ './egg.js', './toast.js' ] ) }
|
47
47
|
end
|
48
48
|
end
|
49
49
|
end
|
@@ -1,21 +1,21 @@
|
|
1
1
|
shared_context 'breakfast foods', :files => :breakfasts do
|
2
2
|
|
3
3
|
before do
|
4
|
-
file 'butter.js', <<-EOS
|
4
|
+
file './butter.js', <<-EOS
|
5
5
|
var Butter = { }
|
6
6
|
EOS
|
7
7
|
|
8
|
-
file 'egg.js', <<-EOS
|
8
|
+
file './egg.js', <<-EOS
|
9
9
|
// require butter
|
10
10
|
var Egg = { }
|
11
11
|
EOS
|
12
12
|
|
13
|
-
file 'toast.js', <<-EOS
|
13
|
+
file './toast.js', <<-EOS
|
14
14
|
// require butter
|
15
15
|
var Toast = { }
|
16
16
|
EOS
|
17
17
|
|
18
|
-
file 'sandwich.js', <<-EOS
|
18
|
+
file './sandwich.js', <<-EOS
|
19
19
|
// require egg
|
20
20
|
// require toast
|
21
21
|
var Sandwich = { }
|
@@ -1,21 +1,21 @@
|
|
1
1
|
shared_context 'circular dependencies', :files => :circular do
|
2
2
|
|
3
3
|
before do
|
4
|
-
file 'money.rb', <<-EOS
|
4
|
+
file './money.rb', <<-EOS
|
5
5
|
# require power
|
6
6
|
|
7
7
|
class Money
|
8
8
|
end
|
9
9
|
EOS
|
10
10
|
|
11
|
-
file 'power.rb', <<-EOS
|
11
|
+
file './power.rb', <<-EOS
|
12
12
|
# require respect
|
13
13
|
|
14
14
|
class Power
|
15
15
|
end
|
16
16
|
EOS
|
17
17
|
|
18
|
-
file 'respect.rb', <<-EOS
|
18
|
+
file './respect.rb', <<-EOS
|
19
19
|
# require money
|
20
20
|
|
21
21
|
class Respect
|
@@ -1,26 +1,26 @@
|
|
1
1
|
shared_context 'solar system', :files => :planets do
|
2
2
|
|
3
3
|
before do
|
4
|
-
file 'body.rb', <<-EOS
|
4
|
+
file './body.rb', <<-EOS
|
5
5
|
class Body
|
6
6
|
end
|
7
7
|
EOS
|
8
8
|
|
9
|
-
file 'earth.rb', <<-EOS
|
9
|
+
file './earth.rb', <<-EOS
|
10
10
|
# require planet
|
11
11
|
|
12
12
|
class Earth
|
13
13
|
end
|
14
14
|
EOS
|
15
15
|
|
16
|
-
file 'mars.rb', <<-EOS
|
16
|
+
file './mars.rb', <<-EOS
|
17
17
|
# require planet
|
18
18
|
|
19
19
|
class Mars
|
20
20
|
end
|
21
21
|
EOS
|
22
22
|
|
23
|
-
file 'phobos.rb', <<-EOS
|
23
|
+
file './phobos.rb', <<-EOS
|
24
24
|
# require body
|
25
25
|
# require mars
|
26
26
|
|
@@ -28,7 +28,7 @@ shared_context 'solar system', :files => :planets do
|
|
28
28
|
end
|
29
29
|
EOS
|
30
30
|
|
31
|
-
file 'planet.rb', <<-EOS
|
31
|
+
file './planet.rb', <<-EOS
|
32
32
|
# require body
|
33
33
|
|
34
34
|
class Planet
|
@@ -1,20 +1,20 @@
|
|
1
1
|
shared_context 'subdirectories', :files => :subdirectories do
|
2
2
|
|
3
3
|
before do
|
4
|
-
file 'application.js', <<-EOS
|
4
|
+
file 'assets/application.js', <<-EOS
|
5
5
|
// require templates/user
|
6
6
|
EOS
|
7
7
|
|
8
|
-
file 'templates/user.js', <<-EOS
|
8
|
+
file 'assets/templates/user.js', <<-EOS
|
9
9
|
// require templates/user/history
|
10
10
|
// require templates/user/account
|
11
11
|
EOS
|
12
12
|
|
13
|
-
file 'templates/user/history.js', <<-EOS
|
13
|
+
file 'assets/templates/user/history.js', <<-EOS
|
14
14
|
var History = { }
|
15
15
|
EOS
|
16
16
|
|
17
|
-
file 'templates/user/account.js', <<-EOS
|
17
|
+
file 'assets/templates/user/account.js', <<-EOS
|
18
18
|
var Account = { }
|
19
19
|
EOS
|
20
20
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: codependency
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-09-
|
12
|
+
date: 2012-09-24 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
@@ -59,7 +59,7 @@ dependencies:
|
|
59
59
|
- - '='
|
60
60
|
- !ruby/object:Gem::Version
|
61
61
|
version: 0.9.1
|
62
|
-
description:
|
62
|
+
description: A pure ruby, comment-based dependency graph
|
63
63
|
email:
|
64
64
|
- jeremy.ruppel@gmail.com
|
65
65
|
executables: []
|
@@ -75,11 +75,9 @@ files:
|
|
75
75
|
- codependency.gemspec
|
76
76
|
- lib/codependency.rb
|
77
77
|
- lib/codependency/graph.rb
|
78
|
-
- lib/codependency/node.rb
|
79
78
|
- lib/codependency/parser.rb
|
80
79
|
- lib/codependency/version.rb
|
81
80
|
- spec/codependency/graph_spec.rb
|
82
|
-
- spec/codependency/node_spec.rb
|
83
81
|
- spec/codependency/parser_spec.rb
|
84
82
|
- spec/spec_helper.rb
|
85
83
|
- spec/support/breakfasts_context.rb
|
@@ -110,10 +108,9 @@ rubyforge_project:
|
|
110
108
|
rubygems_version: 1.8.19
|
111
109
|
signing_key:
|
112
110
|
specification_version: 3
|
113
|
-
summary:
|
111
|
+
summary: A pure ruby, comment-based dependency graph
|
114
112
|
test_files:
|
115
113
|
- spec/codependency/graph_spec.rb
|
116
|
-
- spec/codependency/node_spec.rb
|
117
114
|
- spec/codependency/parser_spec.rb
|
118
115
|
- spec/spec_helper.rb
|
119
116
|
- spec/support/breakfasts_context.rb
|
data/lib/codependency/node.rb
DELETED
@@ -1,26 +0,0 @@
|
|
1
|
-
module Codependency
|
2
|
-
class Node
|
3
|
-
def initialize( filename, parser )
|
4
|
-
raise Errno::ENOENT, filename unless File.exist?( filename )
|
5
|
-
@filename = filename
|
6
|
-
@parser = parser
|
7
|
-
end
|
8
|
-
attr_reader :filename, :parser
|
9
|
-
|
10
|
-
##
|
11
|
-
# all of this node's edges
|
12
|
-
def edges
|
13
|
-
@edges ||= begin
|
14
|
-
parser.parse( filename ).map do |f|
|
15
|
-
"#{f}#{File.extname( filename )}"
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
##
|
21
|
-
# a string representing this node's edges, formatted for `tsort`.
|
22
|
-
def dependencies
|
23
|
-
edges.map { |edge| [ filename, edge ] }.flatten.join ' '
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
@@ -1,25 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe Codependency::Node do
|
4
|
-
let( :parser ){ double 'Parser', :parse => [ 'body' ] }
|
5
|
-
|
6
|
-
context 'when the file exists', :files => :planets do
|
7
|
-
subject { Codependency::Node.new 'planet.rb', parser }
|
8
|
-
its( :edges ){ should eq( [ 'body.rb' ] ) }
|
9
|
-
end
|
10
|
-
|
11
|
-
context 'when the file does not exist' do
|
12
|
-
it 'should raise an error' do
|
13
|
-
expect {
|
14
|
-
Codependency::Node.new 'pluto.rb', parser
|
15
|
-
}.to raise_error( Errno::ENOENT, 'No such file or directory - pluto.rb' )
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
describe 'dependencies', :files => :planets do
|
20
|
-
let( :parser ){ double 'Parser', :parse => [ 'body', 'mars' ] }
|
21
|
-
subject { Codependency::Node.new 'phobos.rb', parser }
|
22
|
-
|
23
|
-
its( :dependencies ){ should eq( 'phobos.rb body.rb phobos.rb mars.rb' ) }
|
24
|
-
end
|
25
|
-
end
|