codependency 0.3.1 → 1.0.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/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
|