dsort 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: e29b9d36c3e4bb2c6122b967ffdfa3e3609a01fe5dfe37cf6c1d4d319a8495b3
4
+ data.tar.gz: 81fffd543f6dd5518583cca18dd1ba73d69ab164fa88895943bb80e1553eee32
5
+ SHA512:
6
+ metadata.gz: 9a74383d8fc09440f1a53ecf03dccc6900c16a0db9633292d80ab06a8751452accbb4f4ad91ce4b41474d58e52a5f77b79acf555c7c354dfb984664da182610a
7
+ data.tar.gz: af569f855ea9d98b72909e6b4b9d607d8072bcd33b040e8a5eb434e26c4d3be75c9470d41fc6f1ac7d60e4ea4470ef8080358b5a95b2bb11518966abfb50ece5
data/.gitignore ADDED
@@ -0,0 +1,30 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+
19
+ # Ignore vim files
20
+ .*.swp
21
+
22
+ # Ignore t.* files
23
+ t
24
+ t.*
25
+ tt
26
+ tt.*
27
+ s
28
+ s.*
29
+ r.rb
30
+
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format documentation
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in dsort.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Claus Rasmussen
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,29 @@
1
+ # Dsort
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'dsort'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install dsort
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/dsort.gemspec ADDED
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'dsort/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "dsort"
8
+ spec.version = Dsort::VERSION
9
+ spec.authors = ["Claus Rasmussen"]
10
+ spec.email = ["claus.l.rasmussen@gmail.com"]
11
+ spec.description = %q{dsort - an easier to use version of TSort}
12
+ spec.summary = %q{
13
+ dsort provide methods for topological sorting of
14
+ data. It is intendended to be an easier-to-use
15
+ alternative to the standard TSort module
16
+ }
17
+ spec.homepage = ""
18
+ spec.license = "MIT"
19
+
20
+ spec.files = `git ls-files`.split($/)
21
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
22
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
23
+ spec.require_paths = ["lib"]
24
+
25
+ spec.add_development_dependency "bundler", "~> 1.3"
26
+ spec.add_development_dependency "rake"
27
+ spec.add_development_dependency "rspec"
28
+ end
@@ -0,0 +1,3 @@
1
+ module Dsort
2
+ VERSION = "0.2.1"
3
+ end
data/lib/dsort.rb ADDED
@@ -0,0 +1,153 @@
1
+ require "dsort/version"
2
+
3
+ require 'tsort'
4
+
5
+ module DSort
6
+ # Thrown if a cyclic dependency is detected
7
+ #
8
+ # DSort::Cyclic is inherited from TSort::Cyclic so that recue handling code
9
+ # written for TSort will still work. It provides a #cycles member that lists
10
+ # the cycles
11
+ class Cyclic < TSort::Cyclic
12
+ # List of detected cycles sorted from shortest to longest cycle
13
+ attr_reader :cycles
14
+
15
+ def initialize(dsort_object)
16
+ @cycles =
17
+ dsort_object.each_strongly_connected_component \
18
+ .select { |e| e.size > 1 } \
19
+ .sort { |l,r| r <=> l }
20
+ gram = cycles.size > 1 ? "ies" : "y"
21
+ super("Cyclic depedendenc#{gram} detected")
22
+ end
23
+ end
24
+
25
+ # dsort sorts its input in "dependency" order: The input can be thought of
26
+ # depends-on relations between objects and the output as sorted in the order
27
+ # needed to safisfy those dependencies so that no object comes before an
28
+ # object it depends on
29
+ #
30
+ # dsort can take an array or a hash argument, or be supplied with a block
31
+ #
32
+ # The Array argument should consist of pairs (two-element Arrays) with the
33
+ # first element being the depending object and the second an object or an
34
+ # array of objects it depends on: For example [:a, :b] means that :a depends
35
+ # on :b, and [:b, [:c, :d]] that :b depends on both :c and :d
36
+ #
37
+ # The Hash argument should be a hash from depending object to an object or
38
+ # array of objects it depends on. If h is a Hash then dsort(h) is equivalent
39
+ # to dsort(h.to_a)
40
+ #
41
+ # Note that if the elements are arrays themselves, then you should use the
42
+ # array form to list the dependencies even if there is only one dependency.
43
+ # Ie. use [:a, [:b]] or {:a => [:b] } instead of [:a, :b] or {:a => :b}
44
+ #
45
+ # If dsort is given a block, the block is given an element as argument and
46
+ # should return an (possibly empty) array of the objects the argument depends
47
+ # on. The argument to dsort should be an element or an array of elements to
48
+ # be given to the block. Note that if the elements are arrays themselves,
49
+ # then the arguments to dsort should use the array form even if there is only
50
+ # one element. Ie. Use dsort([:a]) instead of dsort(:a)
51
+ #
52
+ # dsort raise a DSort::Cyclic exception if a cycle detected
53
+ #
54
+ # Example: If we have that dsort depends on ruby and rspec, ruby depends
55
+ # on C to compile, and rspec depends on ruby, then in what order should we
56
+ # build them ? Using dsort we could do
57
+ #
58
+ # p dsort [[:dsort, [:ruby, :rspec]]], [:ruby, :C], [:rspec, :ruby]]
59
+ # => [:C, :ruby, :rspec, :dsort]
60
+ #
61
+ # Using a hash
62
+ #
63
+ # h = {
64
+ # :dsort => [:ruby, :rspec],
65
+ # :ruby => [:C],
66
+ # :rspec => [:ruby]
67
+ # }
68
+ # p dsort(h) # Same as dsort(h.to_a)
69
+ # => [:C, :ruby, :rspec, :dsort]
70
+ #
71
+ # or using a block
72
+ #
73
+ # p dsort(:dsort) { |e| h[e] }
74
+ # => [:C, :ruby, :rspec, :dsort]
75
+ #
76
+ def dsort(a, &block)
77
+ sort_object = DSortPrivate::DSortObject.new(a, &block)
78
+ begin
79
+ sort_object.tsort
80
+ rescue TSort::Cyclic
81
+ raise Cyclic.new(sort_object)
82
+ end
83
+ end
84
+
85
+ # tsort sort its input in topological order: The input can be thought of as
86
+ # comes-before relations between objects and the output will be in
87
+ # first-to-last order. This definition corresponds to the mathemacial
88
+ # defitionnn of topological sort. See
89
+ # http://en.wikipedia.org/wiki/Topological_sorting
90
+ #
91
+ # Arguments are the same as for dsort. tsort is equivalent to
92
+ # dsort(...).reverse
93
+ #
94
+ # tsort raise a DSort::Cyclic exception if a cycle is detected (DSort::Cyclic
95
+ # is an alias for TSort::Cyclic)
96
+ #
97
+ def tsort(a, &block) dsort(a, &block).reverse end
98
+
99
+ module_function :dsort, :tsort
100
+
101
+ module DSortPrivate
102
+ class DSortObject
103
+ include TSort
104
+
105
+ # Hash from element to array of dependencies
106
+ attr_reader :deps
107
+
108
+ # Create @deps hash from object to list of dependencies
109
+ def initialize(a, &block)
110
+ @deps = {}
111
+ if block_given?
112
+ a = [a] if !a.is_a?(Array)
113
+ a.each { |elem| find_dependencies(elem, &block) }
114
+ else
115
+ a.each { |obj, deps|
116
+ (@deps[obj] ||= []).concat(deps.is_a?(Array) ? deps : [deps])
117
+ }
118
+ end
119
+
120
+ # Make sure all dependent objects are also represented as depending
121
+ # objects: If we're given [:a, :b] we want the @deps hash to include
122
+ # both :a and :b as keys
123
+ @deps.values.each { |deps|
124
+ (deps.is_a?(Array) ? deps : [deps]).each { |d|
125
+ @deps[d] = [] if !@deps.key?(d)
126
+ }
127
+ }
128
+ end
129
+
130
+ # TSort virtual methods
131
+ def tsort_each_node(&block) @deps.each_key(&block) end
132
+ def tsort_each_child(node, &block) @deps[node].each(&block) end
133
+
134
+ private
135
+ def find_dependencies(a, &block)
136
+ block.call(a).each { |d|
137
+ (@deps[a] ||= []) << d
138
+ find_dependencies(d, &block)
139
+ }
140
+ end
141
+ end
142
+ end
143
+ end
144
+
145
+
146
+
147
+
148
+
149
+
150
+
151
+
152
+
153
+
@@ -0,0 +1,144 @@
1
+ require 'spec_helper.rb'
2
+
3
+ require 'dsort.rb'
4
+
5
+ # Build a hierarchy of dependencies
6
+ $HASH_DEPS = {
7
+ :a => [:b, :c],
8
+ :b => [:c, :d],
9
+ :c => :d
10
+ }
11
+
12
+ $BLOCK_DATA = {
13
+ :a => [:b, :c],
14
+ :b => [:c, :d],
15
+ :c => [:d]
16
+ }
17
+
18
+ describe DSort::Cyclic do
19
+ describe "#cycles" do
20
+ it "should be a list of cycles" do
21
+ pairs = [[:a, :b], [:b, :a]]
22
+ begin
23
+ DSort.dsort(pairs).should == 1
24
+ rescue DSort::Cyclic => e
25
+ e.cycles.size.should == 1
26
+ e.cycles[0].sort.should == [:a, :b]
27
+ end
28
+ end
29
+ it "should be sorted from shortest to longest cycle" do
30
+ pairs = [[:a, :b], [:b, :a], [:A, :B], [:B, :C], [:C, :A]]
31
+ begin
32
+ DSort.dsort(pairs).should == 1
33
+ rescue DSort::Cyclic => e
34
+ e.cycles.size.should == 2
35
+ e.cycles[0].sort.should == [:a, :b]
36
+ e.cycles[1].sort.should == [:A, :B, :C]
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ describe DSort do
43
+ describe "#dsort" do
44
+ context "with an array argument" do
45
+ it "should do a dependency sort" do
46
+ pairs = [[:a, :b], [:b, :c]]
47
+ DSort.dsort(pairs).should == [:c, :b, :a]
48
+ end
49
+ it "should accept self-dependencies" do
50
+ pairs = [[:a, :b], [:b, :c], [:c, :c]]
51
+ DSort.dsort(pairs).should == [:c, :b, :a]
52
+ end
53
+ it "should accept duplicate keys" do
54
+ pairs = [[:a, :b], [:b, :c], [:a, :c]]
55
+ DSort.dsort(pairs).should == [:c, :b, :a]
56
+ end
57
+ it "should accept duplicate dependencies" do
58
+ pairs = [[:a, :b], [:a, :b], [:b, :c], [:a, :c]]
59
+ DSort.dsort(pairs).should == [:c, :b, :a]
60
+ end
61
+ it "should accept lists of dependencies" do
62
+ pairs = [ [:a, [:b, :c]], [:b, [:c]] ]
63
+ DSort.dsort(pairs).should == [:c, :b, :a]
64
+ end
65
+ it "should accept empty lists of dependencies" do
66
+ pairs = [ [:a, [:b, :c]], [:b, [:c]], [:c, []] ]
67
+ DSort.dsort(pairs).should == [:c, :b, :a]
68
+ end
69
+ it "should accept arrays as values" do
70
+ a, b, c = [:a], [:b], [:c]
71
+ pairs = [ [a, [b, c]], [b, [c]] ]
72
+ DSort.dsort(pairs).should == [c, b, a]
73
+ end
74
+ end
75
+ context "with a hash argument" do
76
+ it "should do a dependency sort" do
77
+ DSort.dsort($HASH_DEPS).should == [:d, :c, :b, :a]
78
+ end
79
+ end
80
+ context "with objects and a block" do
81
+ it "should do a dependency sort" do
82
+ objs = [Integer, Numeric, Float, Object, BasicObject]
83
+ DSort.dsort(objs) { |obj| [obj.superclass].compact }.should ==
84
+ [BasicObject, Object, Numeric, Integer, Float]
85
+ end
86
+ it "should collect dependencies recursively" do
87
+ s = DSort.dsort(:a) { |obj| $BLOCK_DATA[obj] || [] }
88
+ s.should == [:d, :c, :b, :a]
89
+ end
90
+ end
91
+ context "with circular dependencies" do
92
+ it "should raise DSort::Cyclic" do
93
+ lambda {
94
+ pairs = [[:a, :b], [:b, :a]]
95
+ DSort.dsort(pairs).should == 1
96
+ }.should raise_error(DSort::Cyclic)
97
+ end
98
+ end
99
+ end
100
+ describe "#tsort" do
101
+ context "with an array argument pairs" do
102
+ it "should be equivalent to DSort::dsort(...).reverse" do
103
+ a, b, c = [:a], [:b], [:c]
104
+ test_pairs = [ # From the dsort tests above
105
+ [[:a, :b], [:b, :c]],
106
+ [[:a, :b], [:b, :c], [:c, :c]],
107
+ [[:a, :b], [:b, :c], [:a, :c]],
108
+ [[:a, :b], [:a, :b], [:b, :c], [:a, :c]],
109
+ [ [:a, [:b, :c]], [:b, [:c]] ],
110
+ [ [:a, [:b, :c]], [:b, [:c]], [:c, []] ],
111
+ [ [a, [b, c]], [b, [c]] ]
112
+ ]
113
+ test_pairs.each { |pairs|
114
+ DSort.tsort(pairs).should == DSort.dsort(pairs).reverse
115
+ }
116
+ end
117
+ end
118
+ context "with a hash argument" do
119
+ it "should be equivalent to DSort::dsort(...).reverse" do
120
+ pairs = { :a => :b, :b => :c }
121
+ DSort.tsort(pairs).should == DSort.dsort(pairs).reverse
122
+ end
123
+ end
124
+ context "with objects and a block" do
125
+ it "should be equivalent to DSort::dsort(...).reverse" do
126
+ objs = [Integer, Numeric, Float, Object, BasicObject]
127
+ DSort.tsort(objs) { |obj| [obj.superclass].compact }.should ==
128
+ DSort.dsort(objs) { |obj| [obj.superclass].compact }.reverse
129
+
130
+ s = DSort.tsort(:a) { |obj| $BLOCK_DATA [obj] || [] }
131
+ s.should == [:a, :b, :c, :d]
132
+ end
133
+ end
134
+ context "with circular dependencies" do
135
+ it "should raise DSort::Cyclic" do
136
+ lambda {
137
+ pairs = [[:a, :b], [:b, :a]]
138
+ DSort.tsort(pairs).should == 1
139
+ }.should raise_error(DSort::Cyclic)
140
+ end
141
+ end
142
+ end
143
+ end
144
+
@@ -0,0 +1,10 @@
1
+ # This file was generated by the `rspec --init` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # Require this file using `require "spec_helper"` to ensure that it is only
4
+ # loaded once.
5
+ #
6
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
7
+ RSpec.configure do |config|
8
+ # Use old "should" syntax
9
+ config.expect_with(:rspec) { |c| c.syntax = [:should, :expect] }
10
+ end
metadata ADDED
@@ -0,0 +1,99 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dsort
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.1
5
+ platform: ruby
6
+ authors:
7
+ - Claus Rasmussen
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2022-12-23 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: dsort - an easier to use version of TSort
56
+ email:
57
+ - claus.l.rasmussen@gmail.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".gitignore"
63
+ - ".rspec"
64
+ - Gemfile
65
+ - LICENSE.txt
66
+ - README.md
67
+ - Rakefile
68
+ - dsort.gemspec
69
+ - lib/dsort.rb
70
+ - lib/dsort/version.rb
71
+ - spec/dsort_spec.rb
72
+ - spec/spec_helper.rb
73
+ homepage: ''
74
+ licenses:
75
+ - MIT
76
+ metadata: {}
77
+ post_install_message:
78
+ rdoc_options: []
79
+ require_paths:
80
+ - lib
81
+ required_ruby_version: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ required_rubygems_version: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ requirements: []
92
+ rubygems_version: 3.3.18
93
+ signing_key:
94
+ specification_version: 4
95
+ summary: dsort provide methods for topological sorting of data. It is intendended
96
+ to be an easier-to-use alternative to the standard TSort module
97
+ test_files:
98
+ - spec/dsort_spec.rb
99
+ - spec/spec_helper.rb