deep_end 0.0.4

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/.gitignore ADDED
@@ -0,0 +1,53 @@
1
+ # Also see Global .gitignore: ~/.gitignore_global
2
+ # https://github.com/stationkeeping/osx/blob/master/git/.gitignore_global
3
+
4
+ # Environmental Variables
5
+ #########################
6
+
7
+ .env
8
+
9
+ # Rails
10
+ #########################
11
+
12
+ *.rbc
13
+ *.sassc
14
+ .sass-cache
15
+ capybara-*.html
16
+ .rspec
17
+ .rvmrc
18
+ /.bundle
19
+ /vendor/bundle
20
+ /log/*
21
+ /tmp/*
22
+ /db/*.sqlite3
23
+ /public/system/*
24
+ /coverage/
25
+ /spec/tmp/*
26
+ **.orig
27
+ rerun.txt
28
+ pickle-email-*.html
29
+ .project
30
+
31
+ # Ruby
32
+ #########################
33
+
34
+ *.gem
35
+ *.rbc
36
+ .bundle
37
+ .config
38
+ coverage
39
+ InstalledFiles
40
+ lib/bundler/man
41
+ pkg
42
+ rdoc
43
+ spec/reports
44
+ test/tmp
45
+ test/version_tmp
46
+ tmp
47
+
48
+ # YARD artifacts
49
+ .yardoc
50
+ _yardoc
51
+ doc/
52
+
53
+ Gemfile.lock
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in deep_end.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Pedr Browne
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,61 @@
1
+ # DeepEnd
2
+
3
+ This gem processes a list of objects and their dependencies, ordering them so that dependencies are correctly resolved and checking for circular dependencies.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'deep_end'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install deep_end
18
+
19
+ ## Usage
20
+
21
+ The Graph is the only class you have to worry about:
22
+
23
+ dependency_graph = Graph.new
24
+
25
+ Add any objects you like to it, along with an array of any other objects that they depend on:
26
+
27
+ # Create objects - you can use any objects/instances
28
+ dependency_a = {name: 'a'}
29
+ dependency_b = {name: 'b'}
30
+ dependency_c = {name: 'c'}
31
+
32
+ dependency_graph.add_dependency dependency_c, [dependency_b, dependency_a]
33
+ dependency_graph.add_dependency dependency_b, [dependency_a]
34
+ dependency_graph.add_dependency dependency_a
35
+
36
+ An array of all objects in an order that obeys dependencies is returned from 'add_dependency',
37
+ It is also available from the `resolved_dependencies` property.
38
+
39
+ dependency_graph.resolved_dependencies # [{:name=>"a"}, {:name=>"b"}, {:name=>"c"}]
40
+
41
+ To reuse the Graph you can remove all objects and dependencies using `reset`:
42
+
43
+ dependency_graph.reset
44
+
45
+ Whenever a new object is added with `add_dependency`, the graph re-calculates dependencies. An object can
46
+ be added multiple times with different dependencies. Dependencies are cumulative, so adding an object for
47
+ a second time with different dependencies will result in the object having both sets of dependencies.
48
+
49
+ If an object is added with itself as a dependency, a `SelfDependencyError` will be raised.
50
+
51
+ If a circular dependency is detected a `CircularDependencyError` will be raise.
52
+
53
+
54
+ ## Contributing
55
+
56
+ 1. Fork it
57
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
58
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
59
+ 4. Push to the branch (`git push origin my-new-feature`)
60
+ 5. Create new Pull Request
61
+
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new
5
+
6
+ task :default => :spec
7
+ task :test => :spec
data/deep_end.gemspec ADDED
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'deep_end/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "deep_end"
8
+ gem.version = DeepEnd::VERSION
9
+ gem.authors = ["Pedr Browne"]
10
+ gem.email = ["pedr.browne@gmail.com"]
11
+ gem.description = %q{Simple dependency resolver}
12
+ gem.summary = %q{This gem processes a list of objects and their dependencies, ordering them so that dependencies are correctly resolved and checking for circular dependencies.}
13
+ gem.homepage = "https://github.com/stationkeeping/Deep-End"
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+
20
+ gem.add_dependency "log4r"
21
+
22
+ gem.add_development_dependency 'rake'
23
+ gem.add_development_dependency 'rspec'
24
+ end
@@ -0,0 +1,8 @@
1
+ {
2
+ "folders":
3
+ [
4
+ {
5
+ "path": "/Users/pedr/Documents/Work/Projects/Code/Ruby/Gems/deep_end"
6
+ }
7
+ ]
8
+ }
@@ -0,0 +1,3 @@
1
+ module DeepEnd
2
+ VERSION = "0.0.4"
3
+ end
data/lib/deep_end.rb ADDED
@@ -0,0 +1,116 @@
1
+ require "deep_end/version"
2
+
3
+ module DeepEnd
4
+
5
+ # Errors
6
+ class SelfDependencyError < StandardError; end
7
+ class CircularDependencyError < StandardError; end
8
+
9
+ # Graph Node
10
+ class Node
11
+
12
+ attr_reader :key
13
+ attr_accessor :seen
14
+ attr_reader :edges
15
+
16
+ def initialize(key)
17
+ @key = key
18
+ @edges = []
19
+ end
20
+
21
+ def addEdge(node)
22
+ @edges << node
23
+ end
24
+
25
+ end
26
+
27
+ # Dependency Graph
28
+ class Graph
29
+
30
+ def resolved_dependencies
31
+ a = []
32
+ @resolved.each{|node| a << node.key}
33
+ return a
34
+ end
35
+
36
+ def initialize
37
+ reset
38
+ end
39
+
40
+ # Add a new node, causing dependencies to be re-evaluated
41
+ def add_dependency(key, dependencies = [])
42
+
43
+ raise SelfDependencyError, "An object's dependencies cannot contain itself" if dependencies.include? key
44
+
45
+ node = node_for_key_or_new key
46
+ dependencies.each do |dependency|
47
+ node.addEdge(node_for_key_or_new(dependency))
48
+ end
49
+ resolve_dependencies
50
+ return @resolved
51
+ end
52
+
53
+ # Return the graph to its virgin state
54
+ def reset
55
+ @resolved = []
56
+ @seen_this_pass
57
+ @nodes = []
58
+ end
59
+
60
+ protected
61
+
62
+ # Recurse through nodes
63
+ def resolve_dependencies
64
+ reset_seen
65
+ @resolved = []
66
+ @nodes.each do |node|
67
+ @seen_this_pass = []
68
+ resolve_dependency node unless node.seen
69
+ end
70
+ end
71
+
72
+ # Recurse through node edges
73
+ def resolve_dependency(node)
74
+ node.seen = true
75
+ @seen_this_pass << node
76
+
77
+ node.edges.each do |edge|
78
+ unless @resolved.include? edge
79
+ unless @seen_this_pass.include? edge
80
+ unless edge.seen
81
+ resolve_dependency edge
82
+ end
83
+ else
84
+ raise CircularDependencyError, "Circular reference detected: #{node.key.to_s} - #{edge.key.to_s}"
85
+ end
86
+ end
87
+ end
88
+ @resolved << node
89
+ end
90
+
91
+ def key_exists(key)
92
+ return node_for_key(key).present?
93
+ end
94
+
95
+ def node_for_key(key)
96
+ @nodes.each { |node| return node if node.key == key }
97
+ return
98
+ end
99
+
100
+ def node_for_key_or_new(key)
101
+ existing_node = node_for_key(key)
102
+ if existing_node
103
+ return existing_node
104
+ else
105
+ node = Node.new key
106
+ @nodes << node
107
+ return node
108
+ end
109
+ end
110
+
111
+ def reset_seen
112
+ @nodes.each{ |node| node.seen = false}
113
+ end
114
+
115
+ end
116
+ end
@@ -0,0 +1,94 @@
1
+ require 'spec_helper'
2
+
3
+ module DeepEnd
4
+
5
+ describe Graph do
6
+
7
+ before(:each) do
8
+ @graph = Graph.new
9
+ end
10
+
11
+ context 'when first created' do
12
+ it 'should have no resolved dependencies' do
13
+ @graph.resolved_dependencies.should be_empty
14
+ end
15
+ end
16
+
17
+ context 'when given dendencies' do
18
+
19
+ # Create dependencies
20
+ before(:each) do
21
+ @dependency_a = {name: 'a'}
22
+ @dependency_b = {name: 'b'}
23
+ @dependency_c = {name: 'c'}
24
+ @dependency_d = {name: 'd'}
25
+ @dependency_e = {name: 'e'}
26
+ end
27
+
28
+ it 'should not be empty' do
29
+ @graph.add_dependency @dependency_a
30
+ sorted_objects = @graph.resolved_dependencies.should_not be_empty
31
+ end
32
+
33
+ it 'should contain the dependency' do
34
+ @graph.add_dependency @dependency_a
35
+ sorted_objects = @graph.resolved_dependencies.should include(@dependency_a)
36
+ end
37
+
38
+ it 'should maintain order of non-interdependent objects' do
39
+ # Add dependencies
40
+ @graph.add_dependency @dependency_a
41
+ @graph.add_dependency @dependency_b
42
+ @graph.add_dependency @dependency_c
43
+ # Check order
44
+ sorted_objects = @graph.resolved_dependencies
45
+ sorted_objects[0].should == @dependency_a
46
+ sorted_objects[1].should == @dependency_b
47
+ sorted_objects[2].should == @dependency_c
48
+ end
49
+
50
+ it 'should correctly order interdependent objects' do
51
+ # Add dependencies
52
+ @graph.add_dependency @dependency_c, [@dependency_b, @dependency_a]
53
+ @graph.add_dependency @dependency_b, [@dependency_a]
54
+ @graph.add_dependency @dependency_a
55
+ # Check order
56
+ sorted_objects = @graph.resolved_dependencies
57
+ sorted_objects[0].should == @dependency_a
58
+ sorted_objects[1].should == @dependency_b
59
+ sorted_objects[2].should == @dependency_c
60
+ end
61
+
62
+ it 'should raise a SelfDependencyError if an object is added as its own dependency' do
63
+ # Add dependencies
64
+ expect { @graph.add_dependency @dependency_a, [@dependency_a] }.to raise_error(SelfDependencyError)
65
+ end
66
+
67
+ it 'should raise a CircularDependencyError if objects are added with direct circular dependencies' do
68
+ # Add dependencies
69
+ @graph.add_dependency @dependency_b, [@dependency_a]
70
+ expect { @graph.add_dependency @dependency_a, [@dependency_b] }.to raise_error(CircularDependencyError)
71
+ end
72
+
73
+ it 'should raise a CircularDependencyError if objects are added with indirect circular dependencies' do
74
+ # Add dependencies
75
+ @graph.add_dependency @dependency_b, [@dependency_c]
76
+ @graph.add_dependency @dependency_c, [@dependency_a]
77
+ expect { @graph.add_dependency @dependency_a, [@dependency_b] }.to raise_error(CircularDependencyError)
78
+ end
79
+
80
+ context "when reset" do
81
+
82
+ it 'should have no resolved dependencies' do
83
+ # Add dependencies
84
+ @graph.add_dependency @dependency_c, [@dependency_b, @dependency_a]
85
+ @graph.add_dependency @dependency_b, [@dependency_a]
86
+ @graph.add_dependency @dependency_a
87
+ @graph.reset
88
+ @graph.resolved_dependencies.length.should == 0
89
+ end
90
+ end
91
+
92
+ end
93
+ end
94
+ end
@@ -0,0 +1 @@
1
+ require 'deep_end'
metadata ADDED
@@ -0,0 +1,107 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: deep_end
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.4
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Pedr Browne
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-07-05 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: log4r
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rspec
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ description: Simple dependency resolver
63
+ email:
64
+ - pedr.browne@gmail.com
65
+ executables: []
66
+ extensions: []
67
+ extra_rdoc_files: []
68
+ files:
69
+ - .gitignore
70
+ - Gemfile
71
+ - LICENSE.txt
72
+ - README.md
73
+ - Rakefile
74
+ - deep_end.gemspec
75
+ - deep_end.sublime-project
76
+ - lib/deep_end.rb
77
+ - lib/deep_end/version.rb
78
+ - spec/lib/deep_end_spec.rb
79
+ - spec/spec_helper.rb
80
+ homepage: https://github.com/stationkeeping/Deep-End
81
+ licenses: []
82
+ post_install_message:
83
+ rdoc_options: []
84
+ require_paths:
85
+ - lib
86
+ required_ruby_version: !ruby/object:Gem::Requirement
87
+ none: false
88
+ requirements:
89
+ - - ! '>='
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ required_rubygems_version: !ruby/object:Gem::Requirement
93
+ none: false
94
+ requirements:
95
+ - - ! '>='
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ requirements: []
99
+ rubyforge_project:
100
+ rubygems_version: 1.8.23
101
+ signing_key:
102
+ specification_version: 3
103
+ summary: This gem processes a list of objects and their dependencies, ordering them
104
+ so that dependencies are correctly resolved and checking for circular dependencies.
105
+ test_files:
106
+ - spec/lib/deep_end_spec.rb
107
+ - spec/spec_helper.rb